From 5e98dacf77c1ec0e79e4a91b582b3278519a84a5 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Sun, 21 Jul 2024 17:38:16 +0200 Subject: [PATCH] Upgraded to FSR 3.1 Upscaler - Moved FSR core classes and shaders into their own package - Added FSR 2.2 implementation side-by-side with FSR 3.1 - Updated BiRP and PPV2 integrations for FSR 3.1 - Added experimental HDRP 17 integration --- Assets/FSR3 Upscaler Assets.asset | 26 + ...t.meta => FSR3 Upscaler Assets.asset.meta} | 0 Assets/Fsr3UpscalerAssets.asset | 23 - Assets/Scenes/SampleScenePPV2.unity | 74 +- Assets/Scripts/Core.meta | 8 - Assets/Scripts/Core/Fsr3ShaderIDs.cs | 80 - Assets/Scripts/Core/Fsr3Upscaler.cs | 333 -- Assets/Scripts/Core/Fsr3UpscalerAssets.cs | 151 - Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs | 81 - Assets/Scripts/Core/Fsr3UpscalerContext.cs | 610 ---- Assets/Scripts/Core/Fsr3UpscalerPass.cs | 372 -- Assets/Scripts/Core/Fsr3UpscalerResources.cs | 258 -- Assets/Scripts/Fsr3UpscalerImageEffect.cs | 12 +- .../Scripts/Fsr3UpscalerImageEffectHelper.cs | 1 + Assets/Shaders.meta | 8 - Assets/Shaders/FSR3.meta | 8 - .../ffx_fsr3upscaler_accumulate_pass.compute | 41 - ...fsr3upscaler_autogen_reactive_pass.compute | 32 - ...ler_compute_luminance_pyramid_pass.compute | 42 - .../ffx_fsr3upscaler_depth_clip_pass.compute | 32 - .../FSR3/ffx_fsr3upscaler_lock_pass.compute | 30 - .../FSR3/ffx_fsr3upscaler_rcas_pass.compute | 31 - ...er_reconstruct_previous_depth_pass.compute | 33 - .../ffx_fsr3upscaler_tcr_autogen_pass.compute | 32 - .../FSR3/ffx_fsr3upscaler_unity_common.cginc | 82 - Assets/Shaders/FSR3/shaders.meta | 8 - .../ffx_fsr3upscaler_accumulate_pass.hlsl | 79 - ...ffx_fsr3upscaler_accumulate_pass.hlsl.meta | 7 - ...fx_fsr3upscaler_autogen_reactive_pass.hlsl | 77 - ...r3upscaler_autogen_reactive_pass.hlsl.meta | 7 - ...scaler_compute_luminance_pyramid_pass.hlsl | 55 - ...r_compute_luminance_pyramid_pass.hlsl.meta | 7 - .../ffx_fsr3upscaler_depth_clip_pass.hlsl | 67 - ...ffx_fsr3upscaler_depth_clip_pass.hlsl.meta | 7 - .../shaders/ffx_fsr3upscaler_lock_pass.hlsl | 56 - .../ffx_fsr3upscaler_lock_pass.hlsl.meta | 7 - .../shaders/ffx_fsr3upscaler_rcas_pass.hlsl | 53 - .../ffx_fsr3upscaler_rcas_pass.hlsl.meta | 7 - ...caler_reconstruct_previous_depth_pass.hlsl | 64 - ..._reconstruct_previous_depth_pass.hlsl.meta | 7 - .../ffx_fsr3upscaler_tcr_autogen_pass.hlsl | 90 - ...fx_fsr3upscaler_tcr_autogen_pass.hlsl.meta | 7 - Assets/Shaders/FSR3/shaders/fsr3upscaler.meta | 8 - .../shaders/fsr3upscaler/ffx_common_types.h | 616 ---- .../fsr3upscaler/ffx_common_types.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/ffx_core.h | 80 - .../FSR3/shaders/fsr3upscaler/ffx_core.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/ffx_core_cpu.h | 338 -- .../shaders/fsr3upscaler/ffx_core_cpu.h.meta | 65 - .../fsr3upscaler/ffx_core_gpu_common.h | 2784 --------------- .../fsr3upscaler/ffx_core_gpu_common.h.meta | 65 - .../fsr3upscaler/ffx_core_gpu_common_half.h | 2979 ---------------- .../ffx_core_gpu_common_half.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h | 1651 --------- .../shaders/fsr3upscaler/ffx_core_hlsl.h.meta | 65 - .../fsr3upscaler/ffx_core_portability.h | 51 - .../fsr3upscaler/ffx_core_portability.h.meta | 65 - .../ffx_fsr3upscaler_accumulate.h | 288 -- .../ffx_fsr3upscaler_accumulate.h.meta | 65 - .../ffx_fsr3upscaler_callbacks_hlsl.h | 928 ----- .../ffx_fsr3upscaler_callbacks_hlsl.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_common.h | 566 ---- .../ffx_fsr3upscaler_common.h.meta | 65 - ...x_fsr3upscaler_compute_luminance_pyramid.h | 176 - ...3upscaler_compute_luminance_pyramid.h.meta | 65 - .../ffx_fsr3upscaler_depth_clip.h | 259 -- .../ffx_fsr3upscaler_depth_clip.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_lock.h | 116 - .../fsr3upscaler/ffx_fsr3upscaler_lock.h.meta | 65 - ...ffx_fsr3upscaler_postprocess_lock_status.h | 107 - ...sr3upscaler_postprocess_lock_status.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_rcas.h | 67 - .../fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta | 65 - ...ruct_dilated_velocity_and_previous_depth.h | 146 - ...dilated_velocity_and_previous_depth.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_reproject.h | 137 - .../ffx_fsr3upscaler_reproject.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_resources.h | 104 - .../ffx_fsr3upscaler_resources.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_sample.h | 606 ---- .../ffx_fsr3upscaler_sample.h.meta | 65 - .../ffx_fsr3upscaler_tcr_autogen.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_upsample.h | 195 -- .../ffx_fsr3upscaler_upsample.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/fsr1.meta | 8 - .../FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h | 1252 ------- .../shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta | 67 - .../FSR3/shaders/fsr3upscaler/spd.meta | 8 - .../FSR3/shaders/fsr3upscaler/spd/ffx_spd.h | 1009 ------ .../shaders/fsr3upscaler/spd/ffx_spd.h.meta | 67 - LICENSE.txt | 38 +- .../com.unity.postprocessing}/.signature | 0 .../com.unity.postprocessing}/CHANGELOG.md | 0 .../CHANGELOG.md.meta | 0 .../Documentation~/Ambient-Occlusion.md | 0 .../Documentation~/Anti-aliasing.md | 0 .../Documentation~/Auto-Exposure.md | 0 .../Documentation~/Bloom.md | 0 .../Documentation~/Chromatic-Aberration.md | 0 .../Documentation~/Color-Grading.md | 0 .../Debugging-Post-processing-effects.md | 0 .../Documentation~/Deferred-Fog.md | 0 .../Documentation~/Depth-of-Field.md | 0 .../Documentation~/Grain.md | 0 .../Documentation~/Installation.md | 0 .../Documentation~/Lens-Distortion.md | 0 .../Documentation~/Manipulating-the-Stack.md | 0 .../Documentation~/Motion-Blur.md | 0 .../Documentation~/Quick-start.md | 0 .../Screen-Space-Reflections.md | 0 .../Documentation~/TableOfContents.md | 0 .../Documentation~/Vignette.md | 0 .../Documentation~/Writing-Custom-Effects.md | 0 .../Documentation~/images/Grain_image_0.png | Bin .../Documentation~/images/Grain_image_1.png | Bin .../images/PostProcessing-Bloom-0.png | Bin .../PostProcessing-ChromaticAberration-0.png | Bin .../PostProcessing-ChromaticAberration-1.png | Bin .../images/PostProcessing-Vignette-1.png | Bin .../images/PostProcessing-Vignette-2.png | Bin .../Ppv2 _ Debugging_Light meter_Graph.png | Bin .../Ppv2 _ Debugging_Vectorscope_Graph.png | Bin .../images/Ppv2-Debugging-Histogram-Graph.png | Bin .../Ppv2-Debugging-Light-Meter-Graph.png | Bin .../Ppv2-Debugging-Vectorscope-Graph.png | Bin .../images/Ppv2-Debugging-Waveform-Graph.png | Bin .../images/Ppv2_Debugging_Histogram.png | Bin .../images/Ppv2_Debugging_Waveform_Graph.png | Bin .../images/Ppv2_Light Meter.png | Bin .../images/Ppv2_Post-processing-Debug.png | Bin .../Documentation~/images/aa-1.png | Bin .../Documentation~/images/aa-2.png | Bin .../Documentation~/images/aa-3.png | Bin .../Documentation~/images/ao-off.png | Bin .../Documentation~/images/ao-on.png | Bin .../images/auto-exposure-off.png | Bin .../images/auto-exposure-on.png | Bin .../Documentation~/images/autoexposure.png | Bin .../Documentation~/images/bloom.png | Bin .../Documentation~/images/chroma.png | Bin .../images/custom-effect-sorting.png | Bin .../Documentation~/images/deferredfog.png | Bin .../Documentation~/images/dof.png | Bin .../Documentation~/images/grading-1.png | Bin .../Documentation~/images/grading-10.png | Bin .../Documentation~/images/grading-11.png | Bin .../Documentation~/images/grading-2.png | Bin .../Documentation~/images/grading-3.png | Bin .../Documentation~/images/grading-4.png | Bin .../Documentation~/images/grading-5.png | Bin .../Documentation~/images/grading-6.png | Bin .../Documentation~/images/grading-7.png | Bin .../Documentation~/images/grading-8.png | Bin .../Documentation~/images/grading-9.png | Bin .../Documentation~/images/grain.png | Bin .../Documentation~/images/home-after.png | Bin .../Documentation~/images/home-before.png | Bin .../Documentation~/images/hue-vs-hue.png | Bin .../Documentation~/images/hue-vs-sat.png | Bin .../Documentation~/images/lens-distortion.png | Bin .../Documentation~/images/lensdistortion.png | Bin .../Documentation~/images/lum-vs-sat.png | Bin .../Documentation~/images/motionblur.png | Bin .../images/no-lens-distortion.png | Bin .../Documentation~/images/quickstart-1.png | Bin .../Documentation~/images/quickstart-2.png | Bin .../Documentation~/images/quickstart-3.png | Bin .../Documentation~/images/sat-vs-sat.png | Bin .../Documentation~/images/ssao-1.png | Bin .../Documentation~/images/ssao-2.png | Bin .../Documentation~/images/ssr.png | Bin .../Documentation~/images/tonemapping.png | Bin .../Documentation~/images/trackballs.png | Bin .../Documentation~/images/vignette-1.png | Bin .../Documentation~/images/vignette-2.png | Bin .../Documentation~/images/yrgb-curves.png | Bin .../Documentation~/index.md | 0 .../Documentation~/known-issues.md | 0 .../Documentation~/requirements.md | 0 .../com.unity.postprocessing}/LICENSE.md | 0 .../com.unity.postprocessing}/LICENSE.md.meta | 0 .../PostProcessing.meta | 0 .../PostProcessing/Editor.meta | 0 .../PostProcessing/Editor/Attributes.meta | 0 .../Editor/Attributes/DecoratorAttribute.cs | 0 .../Attributes/DecoratorAttribute.cs.meta | 0 .../Attributes/PostProcessEditorAttribute.cs | 0 .../PostProcessEditorAttribute.cs.meta | 0 .../PostProcessing/Editor/BaseEditor.cs | 0 .../PostProcessing/Editor/BaseEditor.cs.meta | 0 .../PostProcessing/Editor/Decorators.meta | 0 .../Editor/Decorators/AttributeDecorator.cs | 0 .../Decorators/AttributeDecorator.cs.meta | 0 .../Editor/Decorators/Decorators.cs | 0 .../Editor/Decorators/Decorators.cs.meta | 0 .../Editor/Decorators/TrackballDecorator.cs | 0 .../Decorators/TrackballDecorator.cs.meta | 0 .../PostProcessing/Editor/EffectListEditor.cs | 0 .../Editor/EffectListEditor.cs.meta | 0 .../PostProcessing/Editor/Effects.meta | 0 .../Editor/Effects/AmbientOcclusionEditor.cs | 0 .../Effects/AmbientOcclusionEditor.cs.meta | 0 .../Editor/Effects/AutoExposureEditor.cs | 0 .../Editor/Effects/AutoExposureEditor.cs.meta | 0 .../Editor/Effects/BloomEditor.cs | 0 .../Editor/Effects/BloomEditor.cs.meta | 0 .../Effects/ChromaticAberrationEditor.cs | 0 .../Effects/ChromaticAberrationEditor.cs.meta | 0 .../Editor/Effects/ColorGradingEditor.cs | 0 .../Editor/Effects/ColorGradingEditor.cs.meta | 0 .../Effects/DefaultPostProcessEffectEditor.cs | 0 .../DefaultPostProcessEffectEditor.cs.meta | 0 .../Editor/Effects/DepthOfFieldEditor.cs | 0 .../Editor/Effects/DepthOfFieldEditor.cs.meta | 0 .../Editor/Effects/LensDistortionEditor.cs | 0 .../Effects/LensDistortionEditor.cs.meta | 0 .../Editor/Effects/MotionBlurEditor.cs | 0 .../Editor/Effects/MotionBlurEditor.cs.meta | 0 .../Effects/ScreenSpaceReflectionsEditor.cs | 0 .../ScreenSpaceReflectionsEditor.cs.meta | 0 .../Editor/Effects/VignetteEditor.cs | 0 .../Editor/Effects/VignetteEditor.cs.meta | 0 .../Editor/PostProcessDebugEditor.cs | 0 .../Editor/PostProcessDebugEditor.cs.meta | 0 .../Editor/PostProcessEffectBaseEditor.cs | 0 .../PostProcessEffectBaseEditor.cs.meta | 0 .../Editor/PostProcessEffectEditor.cs | 0 .../Editor/PostProcessEffectEditor.cs.meta | 0 .../Editor/PostProcessLayerEditor.cs | 475 +++ .../Editor/PostProcessLayerEditor.cs.meta | 0 .../Editor/PostProcessProfileEditor.cs | 0 .../Editor/PostProcessProfileEditor.cs.meta | 0 .../Editor/PostProcessVolumeEditor.cs | 0 .../Editor/PostProcessVolumeEditor.cs.meta | 0 .../PostProcessing/Editor/Tools.meta | 0 .../Editor/Tools/CubeLutAssetFactory.cs | 0 .../Editor/Tools/CubeLutAssetFactory.cs.meta | 0 .../Editor/Tools/CubeLutAssetImporter.cs | 0 .../Editor/Tools/CubeLutAssetImporter.cs.meta | 0 .../Editor/Tools/DefineSetter.cs | 0 .../Editor/Tools/DefineSetter.cs.meta | 0 .../Editor/Tools/ProfileFactory.cs | 0 .../Editor/Tools/ProfileFactory.cs.meta | 0 .../Editor/Tools/ResourceAssetFactory.cs | 0 .../Editor/Tools/ResourceAssetFactory.cs.meta | 0 .../Editor/Tools/VolumeFactory.cs | 0 .../Editor/Tools/VolumeFactory.cs.meta | 0 .../Editor/Unity.Postprocessing.Editor.asmdef | 37 + .../Unity.Postprocessing.Editor.asmdef.meta | 0 .../PostProcessing/Editor/Utils.meta | 0 .../Editor/Utils/CurveEditor.cs | 0 .../Editor/Utils/CurveEditor.cs.meta | 0 .../Editor/Utils/EditorUtilities.cs | 0 .../Editor/Utils/EditorUtilities.cs.meta | 0 .../Editor/Utils/GlobalSettings.cs | 0 .../Editor/Utils/GlobalSettings.cs.meta | 0 .../Utils/SerializedParameterOverride.cs | 0 .../Utils/SerializedParameterOverride.cs.meta | 0 .../PostProcessing/Editor/Utils/Styling.cs | 0 .../Editor/Utils/Styling.cs.meta | 0 .../PostProcessing/Gizmos.meta | 0 .../Gizmos/PostProcessLayer.png | Bin .../Gizmos/PostProcessLayer.png.meta | 0 .../PostProcessing/PostProcessResources.asset | 141 + .../PostProcessResources.asset.meta | 0 .../PostProcessing/Runtime.meta | 0 .../PostProcessing/Runtime/Attributes.meta | 0 .../Attributes/DisplayNameAttribute.cs | 0 .../Attributes/DisplayNameAttribute.cs.meta | 0 .../Runtime/Attributes/MaxAttribute.cs | 0 .../Runtime/Attributes/MaxAttribute.cs.meta | 0 .../Runtime/Attributes/MinAttribute.cs | 0 .../Runtime/Attributes/MinAttribute.cs.meta | 0 .../Runtime/Attributes/MinMaxAttribute.cs | 0 .../Attributes/MinMaxAttribute.cs.meta | 0 .../Attributes/PostProcessAttribute.cs | 0 .../Attributes/PostProcessAttribute.cs.meta | 0 .../Runtime/Attributes/TrackballAttribute.cs | 0 .../Attributes/TrackballAttribute.cs.meta | 0 .../PostProcessing/Runtime/Effects.meta | 0 .../Runtime/Effects/AmbientOcclusion.cs | 0 .../Runtime/Effects/AmbientOcclusion.cs.meta | 0 .../Runtime/Effects/AutoExposure.cs | 0 .../Runtime/Effects/AutoExposure.cs.meta | 0 .../PostProcessing/Runtime/Effects/Bloom.cs | 0 .../Runtime/Effects/Bloom.cs.meta | 0 .../Runtime/Effects/ChromaticAberration.cs | 0 .../Effects/ChromaticAberration.cs.meta | 0 .../Runtime/Effects/ColorGrading.cs | 0 .../Runtime/Effects/ColorGrading.cs.meta | 0 .../Runtime/Effects/DepthOfField.cs | 0 .../Runtime/Effects/DepthOfField.cs.meta | 0 .../Runtime/Effects/Dithering.cs | 0 .../Runtime/Effects/Dithering.cs.meta | 0 .../Effects/FastApproximateAntialiasing.cs | 0 .../FastApproximateAntialiasing.cs.meta | 0 .../PostProcessing/Runtime/Effects/Fog.cs | 0 .../Runtime/Effects/Fog.cs.meta | 0 .../PostProcessing/Runtime/Effects/Grain.cs | 0 .../Runtime/Effects/Grain.cs.meta | 0 .../Runtime/Effects/LensDistortion.cs | 0 .../Runtime/Effects/LensDistortion.cs.meta | 0 .../Runtime/Effects/MotionBlur.cs | 0 .../Runtime/Effects/MotionBlur.cs.meta | 0 .../Runtime/Effects/MultiScaleVO.cs | 0 .../Runtime/Effects/MultiScaleVO.cs.meta | 0 .../Runtime/Effects/ScalableAO.cs | 0 .../Runtime/Effects/ScalableAO.cs.meta | 0 .../Runtime/Effects/ScreenSpaceReflections.cs | 0 .../Effects/ScreenSpaceReflections.cs.meta | 0 .../SubpixelMorphologicalAntialiasing.cs | 0 .../SubpixelMorphologicalAntialiasing.cs.meta | 0 .../Runtime/Effects/SuperResolution.cs | 339 ++ .../Runtime/Effects/SuperResolution.cs.meta | 0 .../Runtime/Effects/TemporalAntialiasing.cs | 0 .../Effects/TemporalAntialiasing.cs.meta | 0 .../Runtime/Effects/Vignette.cs | 0 .../Runtime/Effects/Vignette.cs.meta | 0 .../PostProcessing/Runtime/Monitors.meta | 0 .../Runtime/Monitors/HistogramMonitor.cs | 0 .../Runtime/Monitors/HistogramMonitor.cs.meta | 0 .../Runtime/Monitors/LightMeterMonitor.cs | 0 .../Monitors/LightMeterMonitor.cs.meta | 0 .../Runtime/Monitors/Monitor.cs | 0 .../Runtime/Monitors/Monitor.cs.meta | 0 .../Runtime/Monitors/VectorscopeMonitor.cs | 0 .../Monitors/VectorscopeMonitor.cs.meta | 0 .../Runtime/Monitors/WaveformMonitor.cs | 0 .../Runtime/Monitors/WaveformMonitor.cs.meta | 0 .../Runtime/ParameterOverride.cs | 0 .../Runtime/ParameterOverride.cs.meta | 0 .../Runtime/PostProcessBundle.cs | 0 .../Runtime/PostProcessBundle.cs.meta | 0 .../Runtime/PostProcessDebug.cs | 0 .../Runtime/PostProcessDebug.cs.meta | 0 .../Runtime/PostProcessDebugLayer.cs | 0 .../Runtime/PostProcessDebugLayer.cs.meta | 0 .../Runtime/PostProcessEffectRenderer.cs | 0 .../Runtime/PostProcessEffectRenderer.cs.meta | 0 .../Runtime/PostProcessEffectSettings.cs | 0 .../Runtime/PostProcessEffectSettings.cs.meta | 0 .../Runtime/PostProcessEvent.cs | 0 .../Runtime/PostProcessEvent.cs.meta | 0 .../Runtime/PostProcessLayer.cs | 0 .../Runtime/PostProcessLayer.cs.meta | 0 .../Runtime/PostProcessManager.cs | 0 .../Runtime/PostProcessManager.cs.meta | 0 .../Runtime/PostProcessProfile.cs | 0 .../Runtime/PostProcessProfile.cs.meta | 0 .../Runtime/PostProcessRenderContext.cs | 0 .../Runtime/PostProcessRenderContext.cs.meta | 0 .../Runtime/PostProcessResources.cs | 293 ++ .../Runtime/PostProcessResources.cs.meta | 0 .../Runtime/PostProcessVolume.cs | 0 .../Runtime/PostProcessVolume.cs.meta | 0 .../Unity.Postprocessing.Runtime.asmdef | 37 + .../Unity.Postprocessing.Runtime.asmdef.meta | 0 .../PostProcessing/Runtime/Utils.meta | 0 .../Runtime/Utils/ColorUtilities.cs | 0 .../Runtime/Utils/ColorUtilities.cs.meta | 0 .../Runtime/Utils/HableCurve.cs | 0 .../Runtime/Utils/HableCurve.cs.meta | 0 .../PostProcessing/Runtime/Utils/HaltonSeq.cs | 0 .../Runtime/Utils/HaltonSeq.cs.meta | 0 .../Runtime/Utils/LogHistogram.cs | 0 .../Runtime/Utils/LogHistogram.cs.meta | 0 .../Runtime/Utils/MeshUtilities.cs | 0 .../Runtime/Utils/MeshUtilities.cs.meta | 0 .../PostProcessEffectRendererExtensions.cs | 0 ...ostProcessEffectRendererExtensions.cs.meta | 0 .../Runtime/Utils/PropertySheet.cs | 0 .../Runtime/Utils/PropertySheet.cs.meta | 0 .../Runtime/Utils/PropertySheetFactory.cs | 0 .../Utils/PropertySheetFactory.cs.meta | 0 .../Runtime/Utils/RuntimeUtilities.cs | 0 .../Runtime/Utils/RuntimeUtilities.cs.meta | 0 .../PostProcessing/Runtime/Utils/ShaderIDs.cs | 0 .../Runtime/Utils/ShaderIDs.cs.meta | 0 .../PostProcessing/Runtime/Utils/Spline.cs | 0 .../Runtime/Utils/Spline.cs.meta | 0 .../Runtime/Utils/TargetPool.cs | 0 .../Runtime/Utils/TargetPool.cs.meta | 0 .../Runtime/Utils/TextureFormatUtilities.cs | 0 .../Utils/TextureFormatUtilities.cs.meta | 0 .../Runtime/Utils/TextureLerper.cs | 0 .../Runtime/Utils/TextureLerper.cs.meta | 0 .../Runtime/Utils/XRSettings.cs | 0 .../Runtime/Utils/XRSettings.cs.meta | 0 .../PostProcessing/Shaders.meta | 0 .../PostProcessing/Shaders/ACES.hlsl | 0 .../PostProcessing/Shaders/ACES.hlsl.meta | 0 .../PostProcessing/Shaders/API.meta | 0 .../PostProcessing/Shaders/API/D3D11.hlsl | 0 .../Shaders/API/D3D11.hlsl.meta | 0 .../PostProcessing/Shaders/API/D3D12.hlsl | 0 .../Shaders/API/D3D12.hlsl.meta | 0 .../PostProcessing/Shaders/API/D3D9.hlsl | 0 .../PostProcessing/Shaders/API/D3D9.hlsl.meta | 0 .../PostProcessing/Shaders/API/Metal.hlsl | 0 .../Shaders/API/Metal.hlsl.meta | 0 .../PostProcessing/Shaders/API/OpenGL.hlsl | 0 .../Shaders/API/OpenGL.hlsl.meta | 0 .../PostProcessing/Shaders/API/PSP2.hlsl | 0 .../PostProcessing/Shaders/API/PSP2.hlsl.meta | 0 .../PostProcessing/Shaders/API/PSSL.hlsl | 0 .../PostProcessing/Shaders/API/PSSL.hlsl.meta | 0 .../PostProcessing/Shaders/API/Switch.hlsl | 0 .../Shaders/API/Switch.hlsl.meta | 0 .../PostProcessing/Shaders/API/Vulkan.hlsl | 0 .../Shaders/API/Vulkan.hlsl.meta | 0 .../PostProcessing/Shaders/API/WebGPU.hlsl | 0 .../Shaders/API/WebGPU.hlsl.meta | 0 .../PostProcessing/Shaders/API/XboxOne.hlsl | 0 .../Shaders/API/XboxOne.hlsl.meta | 0 .../PostProcessing/Shaders/Builtins.meta | 0 .../Shaders/Builtins/AutoExposure.compute | 0 .../Builtins/AutoExposure.compute.meta | 0 .../Shaders/Builtins/Bloom.shader | 0 .../Shaders/Builtins/Bloom.shader.meta | 0 .../Shaders/Builtins/Copy.shader | 0 .../Shaders/Builtins/Copy.shader.meta | 0 .../Shaders/Builtins/CopyStd.shader | 0 .../Shaders/Builtins/CopyStd.shader.meta | 0 .../Builtins/CopyStdFromDoubleWide.shader | 0 .../CopyStdFromDoubleWide.shader.meta | 0 .../Builtins/CopyStdFromTexArray.shader | 0 .../Builtins/CopyStdFromTexArray.shader.meta | 0 .../Shaders/Builtins/DeferredFog.shader | 0 .../Shaders/Builtins/DeferredFog.shader.meta | 0 .../Shaders/Builtins/DepthOfField.hlsl | 0 .../Shaders/Builtins/DepthOfField.hlsl.meta | 0 .../Shaders/Builtins/DepthOfField.shader | 0 .../Shaders/Builtins/DepthOfField.shader.meta | 0 .../Shaders/Builtins/DiscardAlpha.shader | 0 .../Shaders/Builtins/DiscardAlpha.shader.meta | 0 .../Shaders/Builtins/DiskKernels.hlsl | 0 .../Shaders/Builtins/DiskKernels.hlsl.meta | 0 .../Shaders/Builtins/Distortion.hlsl | 0 .../Shaders/Builtins/Distortion.hlsl.meta | 0 .../Shaders/Builtins/Dithering.hlsl | 0 .../Shaders/Builtins/Dithering.hlsl.meta | 0 .../Builtins/ExposureHistogram.compute | 0 .../Builtins/ExposureHistogram.compute.meta | 0 .../Shaders/Builtins/ExposureHistogram.hlsl | 0 .../Builtins/ExposureHistogram.hlsl.meta | 0 .../Builtins/FastApproximateAntialiasing.hlsl | 0 .../FastApproximateAntialiasing.hlsl.meta | 0 .../Shaders/Builtins/FinalPass.shader | 0 .../Shaders/Builtins/FinalPass.shader.meta | 0 .../PostProcessing/Shaders/Builtins/Fog.hlsl | 0 .../Shaders/Builtins/Fog.hlsl.meta | 0 .../Builtins/GaussianDownsample.compute | 0 .../Builtins/GaussianDownsample.compute.meta | 0 .../Shaders/Builtins/GrainBaker.shader | 0 .../Shaders/Builtins/GrainBaker.shader.meta | 0 .../Shaders/Builtins/Lut2DBaker.shader | 0 .../Shaders/Builtins/Lut2DBaker.shader.meta | 0 .../Shaders/Builtins/Lut3DBaker.compute | 0 .../Shaders/Builtins/Lut3DBaker.compute.meta | 0 .../Shaders/Builtins/MotionBlur.shader | 0 .../Shaders/Builtins/MotionBlur.shader.meta | 0 .../Shaders/Builtins/MultiScaleVO.shader | 0 .../Shaders/Builtins/MultiScaleVO.shader.meta | 0 .../Builtins/MultiScaleVODownsample1.compute | 0 .../MultiScaleVODownsample1.compute.meta | 0 .../Builtins/MultiScaleVODownsample2.compute | 0 .../MultiScaleVODownsample2.compute.meta | 0 .../Builtins/MultiScaleVORender.compute | 0 .../Builtins/MultiScaleVORender.compute.meta | 0 .../Builtins/MultiScaleVOUpsample.compute | 0 .../MultiScaleVOUpsample.compute.meta | 0 .../Shaders/Builtins/ScalableAO.hlsl | 0 .../Shaders/Builtins/ScalableAO.hlsl.meta | 0 .../Shaders/Builtins/ScalableAO.shader | 0 .../Shaders/Builtins/ScalableAO.shader.meta | 0 .../Builtins/ScreenSpaceReflections.hlsl | 0 .../Builtins/ScreenSpaceReflections.hlsl.meta | 0 .../Builtins/ScreenSpaceReflections.shader | 0 .../ScreenSpaceReflections.shader.meta | 0 .../SubpixelMorphologicalAntialiasing.hlsl | 0 ...ubpixelMorphologicalAntialiasing.hlsl.meta | 0 .../SubpixelMorphologicalAntialiasing.shader | 0 ...pixelMorphologicalAntialiasing.shader.meta | 0 ...bpixelMorphologicalAntialiasingBridge.hlsl | 0 ...lMorphologicalAntialiasingBridge.hlsl.meta | 0 .../Builtins/TemporalAntialiasing.shader | 0 .../Builtins/TemporalAntialiasing.shader.meta | 0 .../Shaders/Builtins/Texture2DLerp.shader | 0 .../Builtins/Texture2DLerp.shader.meta | 0 .../Shaders/Builtins/Texture3DLerp.compute | 0 .../Builtins/Texture3DLerp.compute.meta | 0 .../Shaders/Builtins/Uber.shader | 0 .../Shaders/Builtins/Uber.shader.meta | 0 .../PostProcessing/Shaders/Colors.hlsl | 0 .../PostProcessing/Shaders/Colors.hlsl.meta | 0 .../PostProcessing/Shaders/Debug.meta | 0 .../Shaders/Debug/Histogram.compute | 0 .../Shaders/Debug/Histogram.compute.meta | 0 .../Shaders/Debug/Histogram.shader | 0 .../Shaders/Debug/Histogram.shader.meta | 0 .../Shaders/Debug/LightMeter.shader | 0 .../Shaders/Debug/LightMeter.shader.meta | 0 .../Shaders/Debug/Overlays.shader | 0 .../Shaders/Debug/Overlays.shader.meta | 0 .../Shaders/Debug/Vectorscope.compute | 0 .../Shaders/Debug/Vectorscope.compute.meta | 0 .../Shaders/Debug/Vectorscope.shader | 0 .../Shaders/Debug/Vectorscope.shader.meta | 0 .../Shaders/Debug/Waveform.compute | 0 .../Shaders/Debug/Waveform.compute.meta | 0 .../Shaders/Debug/Waveform.shader | 0 .../Shaders/Debug/Waveform.shader.meta | 0 .../PostProcessing/Shaders/Editor.meta | 0 .../Shaders/Editor/ConvertToLog.shader | 0 .../Shaders/Editor/ConvertToLog.shader.meta | 0 .../Shaders/Editor/CurveGrid.shader | 0 .../Shaders/Editor/CurveGrid.shader.meta | 0 .../Shaders/Editor/Trackball.shader | 0 .../Shaders/Editor/Trackball.shader.meta | 0 .../PostProcessing/Shaders/Sampling.hlsl | 0 .../PostProcessing/Shaders/Sampling.hlsl.meta | 0 .../PostProcessing/Shaders/StdLib.hlsl | 0 .../PostProcessing/Shaders/StdLib.hlsl.meta | 0 .../PostProcessing/Shaders/xRLib.hlsl | 0 .../PostProcessing/Shaders/xRLib.hlsl.meta | 0 .../PostProcessing/Textures.meta | 0 .../Textures/BlueNoise256px.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_0.png | Bin .../BlueNoise256px/LDR_LLL1_0.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_1.png | Bin .../BlueNoise256px/LDR_LLL1_1.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_2.png | Bin .../BlueNoise256px/LDR_LLL1_2.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_3.png | Bin .../BlueNoise256px/LDR_LLL1_3.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_4.png | Bin .../BlueNoise256px/LDR_LLL1_4.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_5.png | Bin .../BlueNoise256px/LDR_LLL1_5.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_6.png | Bin .../BlueNoise256px/LDR_LLL1_6.png.meta | 0 .../Textures/BlueNoise256px/LDR_LLL1_7.png | Bin .../BlueNoise256px/LDR_LLL1_7.png.meta | 0 .../Textures/BlueNoise64px.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_0.png | Bin .../BlueNoise64px/LDR_LLL1_0.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_1.png | Bin .../BlueNoise64px/LDR_LLL1_1.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_10.png | Bin .../BlueNoise64px/LDR_LLL1_10.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_11.png | Bin .../BlueNoise64px/LDR_LLL1_11.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_12.png | Bin .../BlueNoise64px/LDR_LLL1_12.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_13.png | Bin .../BlueNoise64px/LDR_LLL1_13.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_14.png | Bin .../BlueNoise64px/LDR_LLL1_14.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_15.png | Bin .../BlueNoise64px/LDR_LLL1_15.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_16.png | Bin .../BlueNoise64px/LDR_LLL1_16.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_17.png | Bin .../BlueNoise64px/LDR_LLL1_17.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_18.png | Bin .../BlueNoise64px/LDR_LLL1_18.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_19.png | Bin .../BlueNoise64px/LDR_LLL1_19.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_2.png | Bin .../BlueNoise64px/LDR_LLL1_2.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_20.png | Bin .../BlueNoise64px/LDR_LLL1_20.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_21.png | Bin .../BlueNoise64px/LDR_LLL1_21.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_22.png | Bin .../BlueNoise64px/LDR_LLL1_22.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_23.png | Bin .../BlueNoise64px/LDR_LLL1_23.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_24.png | Bin .../BlueNoise64px/LDR_LLL1_24.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_25.png | Bin .../BlueNoise64px/LDR_LLL1_25.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_26.png | Bin .../BlueNoise64px/LDR_LLL1_26.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_27.png | Bin .../BlueNoise64px/LDR_LLL1_27.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_28.png | Bin .../BlueNoise64px/LDR_LLL1_28.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_29.png | Bin .../BlueNoise64px/LDR_LLL1_29.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_3.png | Bin .../BlueNoise64px/LDR_LLL1_3.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_30.png | Bin .../BlueNoise64px/LDR_LLL1_30.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_31.png | Bin .../BlueNoise64px/LDR_LLL1_31.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_32.png | Bin .../BlueNoise64px/LDR_LLL1_32.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_33.png | Bin .../BlueNoise64px/LDR_LLL1_33.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_34.png | Bin .../BlueNoise64px/LDR_LLL1_34.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_35.png | Bin .../BlueNoise64px/LDR_LLL1_35.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_36.png | Bin .../BlueNoise64px/LDR_LLL1_36.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_37.png | Bin .../BlueNoise64px/LDR_LLL1_37.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_38.png | Bin .../BlueNoise64px/LDR_LLL1_38.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_39.png | Bin .../BlueNoise64px/LDR_LLL1_39.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_4.png | Bin .../BlueNoise64px/LDR_LLL1_4.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_40.png | Bin .../BlueNoise64px/LDR_LLL1_40.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_41.png | Bin .../BlueNoise64px/LDR_LLL1_41.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_42.png | Bin .../BlueNoise64px/LDR_LLL1_42.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_43.png | Bin .../BlueNoise64px/LDR_LLL1_43.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_44.png | Bin .../BlueNoise64px/LDR_LLL1_44.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_45.png | Bin .../BlueNoise64px/LDR_LLL1_45.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_46.png | Bin .../BlueNoise64px/LDR_LLL1_46.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_47.png | Bin .../BlueNoise64px/LDR_LLL1_47.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_48.png | Bin .../BlueNoise64px/LDR_LLL1_48.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_49.png | Bin .../BlueNoise64px/LDR_LLL1_49.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_5.png | Bin .../BlueNoise64px/LDR_LLL1_5.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_50.png | Bin .../BlueNoise64px/LDR_LLL1_50.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_51.png | Bin .../BlueNoise64px/LDR_LLL1_51.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_52.png | Bin .../BlueNoise64px/LDR_LLL1_52.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_53.png | Bin .../BlueNoise64px/LDR_LLL1_53.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_54.png | Bin .../BlueNoise64px/LDR_LLL1_54.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_55.png | Bin .../BlueNoise64px/LDR_LLL1_55.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_56.png | Bin .../BlueNoise64px/LDR_LLL1_56.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_57.png | Bin .../BlueNoise64px/LDR_LLL1_57.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_58.png | Bin .../BlueNoise64px/LDR_LLL1_58.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_59.png | Bin .../BlueNoise64px/LDR_LLL1_59.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_6.png | Bin .../BlueNoise64px/LDR_LLL1_6.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_60.png | Bin .../BlueNoise64px/LDR_LLL1_60.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_61.png | Bin .../BlueNoise64px/LDR_LLL1_61.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_62.png | Bin .../BlueNoise64px/LDR_LLL1_62.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_63.png | Bin .../BlueNoise64px/LDR_LLL1_63.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_7.png | Bin .../BlueNoise64px/LDR_LLL1_7.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_8.png | Bin .../BlueNoise64px/LDR_LLL1_8.png.meta | 0 .../Textures/BlueNoise64px/LDR_LLL1_9.png | Bin .../BlueNoise64px/LDR_LLL1_9.png.meta | 0 .../PostProcessing/Textures/Cubes.meta | 0 .../Cubes/Linear_to_Unity_Log_r1.cube | 0 .../Cubes/Linear_to_Unity_Log_r1.cube.meta | 0 .../Textures/Cubes/Linear_to_sRGB_r1.cube | 0 .../Cubes/Linear_to_sRGB_r1.cube.meta | 0 .../Cubes/Unity_Log_to_Linear_r1.cube | 0 .../Cubes/Unity_Log_to_Linear_r1.cube.meta | 0 .../Textures/Cubes/Unity_Log_to_sRGB_r1.cube | 0 .../Cubes/Unity_Log_to_sRGB_r1.cube.meta | 0 .../Textures/Cubes/sRGB_to_Linear_r1.cube | 0 .../Cubes/sRGB_to_Linear_r1.cube.meta | 0 .../Textures/Cubes/sRGB_to_Unity_Log_r1.cube | 0 .../Cubes/sRGB_to_Unity_Log_r1.cube.meta | 0 .../PostProcessing/Textures/LUTs.meta | 0 .../Textures/LUTs/NeutralLdrLut.png | Bin .../Textures/LUTs/NeutralLdrLut.png.meta | 0 .../PostProcessing/Textures/LensDirt.meta | 0 .../Textures/LensDirt/LensDirt00.png | Bin .../Textures/LensDirt/LensDirt00.png.meta | 0 .../Textures/LensDirt/LensDirt01.png | Bin .../Textures/LensDirt/LensDirt01.png.meta | 0 .../Textures/LensDirt/LensDirt02.png | Bin .../Textures/LensDirt/LensDirt02.png.meta | 0 .../Textures/LensDirt/LensDirt03.png | Bin .../Textures/LensDirt/LensDirt03.png.meta | 0 .../PostProcessing/Textures/SMAA.meta | 0 .../PostProcessing/Textures/SMAA/AreaTex.tga | Bin .../Textures/SMAA/AreaTex.tga.meta | 0 .../Textures/SMAA/SearchTex.tga | Bin .../Textures/SMAA/SearchTex.tga.meta | 0 .../PostProcessing/Textures/SpectralLUTs.meta | 0 .../SpectralLUTs/SpectralLut_BlueRed.tga | Bin .../SpectralLUTs/SpectralLut_BlueRed.tga.meta | 0 .../SpectralLUTs/SpectralLut_GreenPurple.tga | Bin .../SpectralLut_GreenPurple.tga.meta | 0 .../SpectralLUTs/SpectralLut_PurpleGreen.tga | Bin .../SpectralLut_PurpleGreen.tga.meta | 0 .../SpectralLUTs/SpectralLut_RedBlue.tga | Bin .../SpectralLUTs/SpectralLut_RedBlue.tga.meta | 0 .../com.unity.postprocessing}/Tests.meta | 0 .../Tests/.tests.json | 0 .../Tests/Editor.meta | 0 .../Tests/Editor/PostProcessEditorTests.cs | 0 .../Editor/PostProcessEditorTests.cs.meta | 0 .../Unity.Postprocessing.Editor.Tests.asmdef | 0 ...ty.Postprocessing.Editor.Tests.asmdef.meta | 0 .../Tests/Runtime.meta | 0 .../Runtime/PostProcessingRuntimeTests.cs | 0 .../PostProcessingRuntimeTests.cs.meta | 0 .../Unity.Postprocessing.Runtime.Tests.asmdef | 0 ...y.Postprocessing.Runtime.Tests.asmdef.meta | 0 .../com.unity.postprocessing/package.json | 11 + .../package.json.meta | 0 .../RenderPass/Fsr3Upscaler.meta | 8 + .../Fsr3Upscaler/Fsr3UpscalerPlugin.cs | 294 ++ .../Fsr3Upscaler/Fsr3UpscalerPlugin.cs.meta | 2 + .../RenderPass/Fsr3Upscaler/Resources.meta | 8 + .../Resources/FSR3 Upscaler Assets.asset | 30 + .../Resources/FSR3 Upscaler Assets.asset.meta | 8 + Packages/fidelityfx.fsr/LICENSE.md | 19 + Packages/fidelityfx.fsr/LICENSE.md.meta | 7 + Packages/fidelityfx.fsr/Runtime.meta | 8 + Packages/fidelityfx.fsr/Runtime/Common.meta | 8 + .../Runtime/Common/ResourceView.cs | 55 + .../Runtime/Common/ResourceView.cs.meta | 11 + Packages/fidelityfx.fsr/Runtime/FSR2.meta | 8 + Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs | 302 ++ .../fidelityfx.fsr/Runtime/FSR2/Fsr2.cs.meta | 0 .../fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs | 152 + .../Runtime/FSR2/Fsr2Assets.cs.meta | 0 .../Runtime/FSR2/Fsr2Callbacks.cs | 81 + .../Runtime/FSR2/Fsr2Callbacks.cs.meta | 0 .../Runtime/FSR2/Fsr2Context.cs | 618 ++++ .../Runtime/FSR2/Fsr2Context.cs.meta | 0 .../fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs | 380 +++ .../Runtime/FSR2/Fsr2Pass.cs.meta | 0 .../Runtime/FSR2/Fsr2Resources.cs | 258 ++ .../Runtime/FSR2/Fsr2Resources.cs.meta | 0 .../Runtime/FSR2/Fsr2ShaderIDs.cs | 80 + .../Runtime/FSR2/Fsr2ShaderIDs.cs.meta | 0 Packages/fidelityfx.fsr/Runtime/FSR3.meta | 8 + .../Runtime/FSR3/Fsr3ShaderIDs.cs | 91 + .../Runtime/FSR3/Fsr3ShaderIDs.cs.meta | 3 + .../Runtime/FSR3/Fsr3Upscaler.cs | 313 ++ .../Runtime/FSR3/Fsr3Upscaler.cs.meta | 3 + .../Runtime/FSR3/Fsr3UpscalerAssets.cs | 177 + .../Runtime/FSR3/Fsr3UpscalerAssets.cs.meta | 11 + .../Runtime/FSR3/Fsr3UpscalerCallbacks.cs | 81 + .../FSR3/Fsr3UpscalerCallbacks.cs.meta | 11 + .../Runtime/FSR3/Fsr3UpscalerContext.cs | 657 ++++ .../Runtime/FSR3/Fsr3UpscalerContext.cs.meta | 3 + .../Runtime/FSR3/Fsr3UpscalerPass.cs | 472 +++ .../Runtime/FSR3/Fsr3UpscalerPass.cs.meta | 3 + .../Runtime/FSR3/Fsr3UpscalerResources.cs | 245 ++ .../FSR3/Fsr3UpscalerResources.cs.meta | 3 + .../Runtime/FidelityFX.FSR.asmdef | 14 + .../Runtime/FidelityFX.FSR.asmdef.meta | 7 + Packages/fidelityfx.fsr/Shaders.meta | 8 + .../Shaders/ffx_fsr2_accumulate_pass.compute | 41 + .../ffx_fsr2_accumulate_pass.compute.meta | 8 + .../ffx_fsr2_autogen_reactive_pass.compute | 32 + ...fx_fsr2_autogen_reactive_pass.compute.meta | 8 + ...sr2_compute_luminance_pyramid_pass.compute | 42 + ...ompute_luminance_pyramid_pass.compute.meta | 8 + .../Shaders/ffx_fsr2_depth_clip_pass.compute | 32 + .../ffx_fsr2_depth_clip_pass.compute.meta | 8 + .../Shaders/ffx_fsr2_lock_pass.compute | 32 + .../Shaders/ffx_fsr2_lock_pass.compute.meta | 8 + .../Shaders/ffx_fsr2_rcas_pass.compute | 31 + .../Shaders/ffx_fsr2_rcas_pass.compute.meta | 8 + ...r2_reconstruct_previous_depth_pass.compute | 33 + ...construct_previous_depth_pass.compute.meta | 8 + .../Shaders/ffx_fsr2_tcr_autogen_pass.compute | 32 + .../ffx_fsr2_tcr_autogen_pass.compute.meta | 8 + .../ffx_fsr3upscaler_accumulate_pass.compute | 39 + ..._fsr3upscaler_accumulate_pass.compute.meta | 0 ...fsr3upscaler_autogen_reactive_pass.compute | 29 + ...pscaler_autogen_reactive_pass.compute.meta | 0 .../ffx_fsr3upscaler_debug_view_pass.compute | 29 + ..._fsr3upscaler_debug_view_pass.compute.meta | 8 + ...fsr3upscaler_luma_instability_pass.compute | 29 + ...pscaler_luma_instability_pass.compute.meta | 0 ...ffx_fsr3upscaler_luma_pyramid_pass.compute | 39 + ...sr3upscaler_luma_pyramid_pass.compute.meta | 0 ...x_fsr3upscaler_prepare_inputs_pass.compute | 31 + ...3upscaler_prepare_inputs_pass.compute.meta | 0 ...r3upscaler_prepare_reactivity_pass.compute | 29 + ...caler_prepare_reactivity_pass.compute.meta | 0 .../ffx_fsr3upscaler_rcas_pass.compute | 27 + .../ffx_fsr3upscaler_rcas_pass.compute.meta | 0 ...x_fsr3upscaler_shading_change_pass.compute | 29 + ...3upscaler_shading_change_pass.compute.meta | 8 + ...scaler_shading_change_pyramid_pass.compute | 32 + ...r_shading_change_pyramid_pass.compute.meta | 8 + .../ffx_fsr3upscaler_tcr_autogen_pass.compute | 30 + ...fsr3upscaler_tcr_autogen_pass.compute.meta | 0 .../Shaders/ffx_fsr_unity_common.cginc | 82 + .../Shaders/ffx_fsr_unity_common.cginc.meta | 0 Packages/fidelityfx.fsr/Shaders/shaders.meta | 8 + .../Shaders/shaders/ffx_common_types.h | 654 ++++ .../Shaders/shaders/ffx_common_types.h.meta | 60 + .../fidelityfx.fsr/Shaders/shaders/ffx_core.h | 80 + .../Shaders/shaders/ffx_core.h.meta | 60 + .../Shaders/shaders/ffx_core_gpu_common.h | 2736 +++++++++++++++ .../shaders/ffx_core_gpu_common.h.meta | 60 + .../shaders/ffx_core_gpu_common_half.h | 2981 +++++++++++++++++ .../shaders/ffx_core_gpu_common_half.h.meta | 60 + .../Shaders/shaders/ffx_core_hlsl.h | 1898 +++++++++++ .../Shaders/shaders/ffx_core_hlsl.h.meta | 60 + .../Shaders/shaders/ffx_core_portability.h | 46 + .../shaders/ffx_core_portability.h.meta | 60 + .../shaders/ffx_fsr2_accumulate_pass.hlsl | 79 + .../ffx_fsr2_accumulate_pass.hlsl.meta | 7 + .../ffx_fsr2_autogen_reactive_pass.hlsl | 78 + .../ffx_fsr2_autogen_reactive_pass.hlsl.meta | 7 + ...x_fsr2_compute_luminance_pyramid_pass.hlsl | 56 + ...2_compute_luminance_pyramid_pass.hlsl.meta | 7 + .../shaders/ffx_fsr2_depth_clip_pass.hlsl | 67 + .../ffx_fsr2_depth_clip_pass.hlsl.meta | 7 + .../Shaders/shaders/ffx_fsr2_lock_pass.hlsl | 56 + .../shaders/ffx_fsr2_lock_pass.hlsl.meta | 7 + .../Shaders/shaders/ffx_fsr2_rcas_pass.hlsl | 54 + .../shaders/ffx_fsr2_rcas_pass.hlsl.meta | 7 + ..._fsr2_reconstruct_previous_depth_pass.hlsl | 64 + ..._reconstruct_previous_depth_pass.hlsl.meta | 7 + .../shaders/ffx_fsr2_tcr_autogen_pass.hlsl | 92 + .../ffx_fsr2_tcr_autogen_pass.hlsl.meta | 7 + .../ffx_fsr3upscaler_accumulate_pass.hlsl | 70 + ...ffx_fsr3upscaler_accumulate_pass.hlsl.meta | 7 + ...fx_fsr3upscaler_autogen_reactive_pass.hlsl | 78 + ...r3upscaler_autogen_reactive_pass.hlsl.meta | 7 + .../ffx_fsr3upscaler_debug_view_pass.hlsl | 56 + ...ffx_fsr3upscaler_debug_view_pass.hlsl.meta | 7 + ...fx_fsr3upscaler_luma_instability_pass.hlsl | 59 + ...r3upscaler_luma_instability_pass.hlsl.meta | 7 + .../ffx_fsr3upscaler_luma_pyramid_pass.hlsl | 62 + ...x_fsr3upscaler_luma_pyramid_pass.hlsl.meta | 7 + .../ffx_fsr3upscaler_prepare_inputs_pass.hlsl | 58 + ...fsr3upscaler_prepare_inputs_pass.hlsl.meta | 7 + ..._fsr3upscaler_prepare_reactivity_pass.hlsl | 62 + ...upscaler_prepare_reactivity_pass.hlsl.meta | 7 + .../shaders/ffx_fsr3upscaler_rcas_pass.hlsl | 53 + .../ffx_fsr3upscaler_rcas_pass.hlsl.meta | 7 + .../ffx_fsr3upscaler_shading_change_pass.hlsl | 52 + ...fsr3upscaler_shading_change_pass.hlsl.meta | 7 + ...3upscaler_shading_change_pyramid_pass.hlsl | 63 + ...aler_shading_change_pyramid_pass.hlsl.meta | 7 + .../ffx_fsr3upscaler_tcr_autogen_pass.hlsl | 90 + ...fx_fsr3upscaler_tcr_autogen_pass.hlsl.meta | 7 + .../fidelityfx.fsr/Shaders/shaders/fsr1.meta | 8 + .../Shaders/shaders/fsr1/ffx_fsr1.h | 1252 +++++++ .../Shaders/shaders/fsr1/ffx_fsr1.h.meta | 60 + .../fidelityfx.fsr/Shaders/shaders/fsr2.meta | 8 + .../shaders/fsr2/ffx_fsr2_accumulate.h | 295 ++ .../shaders/fsr2/ffx_fsr2_accumulate.h.meta | 60 + .../shaders/fsr2/ffx_fsr2_callbacks_hlsl.h | 945 ++++++ .../fsr2/ffx_fsr2_callbacks_hlsl.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_common.h | 566 ++++ .../shaders/fsr2/ffx_fsr2_common.h.meta | 60 + .../fsr2/ffx_fsr2_compute_luminance_pyramid.h | 176 + .../ffx_fsr2_compute_luminance_pyramid.h.meta | 60 + .../shaders/fsr2/ffx_fsr2_depth_clip.h | 259 ++ .../shaders/fsr2/ffx_fsr2_depth_clip.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_lock.h | 116 + .../Shaders/shaders/fsr2/ffx_fsr2_lock.h.meta | 60 + .../fsr2/ffx_fsr2_postprocess_lock_status.h | 107 + .../ffx_fsr2_postprocess_lock_status.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_rcas.h | 67 + .../Shaders/shaders/fsr2/ffx_fsr2_rcas.h.meta | 60 + ...ruct_dilated_velocity_and_previous_depth.h | 146 + ...dilated_velocity_and_previous_depth.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_reproject.h | 137 + .../shaders/fsr2/ffx_fsr2_reproject.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_resources.h | 106 + .../shaders/fsr2/ffx_fsr2_resources.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_sample.h | 606 ++++ .../shaders/fsr2/ffx_fsr2_sample.h.meta | 60 + .../shaders/fsr2/ffx_fsr2_tcr_autogen.h | 251 ++ .../shaders/fsr2/ffx_fsr2_tcr_autogen.h.meta | 60 + .../Shaders/shaders/fsr2/ffx_fsr2_upsample.h | 191 ++ .../shaders/fsr2/ffx_fsr2_upsample.h.meta | 60 + .../Shaders/shaders/fsr3upscaler.meta | 8 + .../ffx_fsr3upscaler_accumulate.h | 172 + .../ffx_fsr3upscaler_accumulate.h.meta | 60 + .../ffx_fsr3upscaler_callbacks_hlsl.h | 980 ++++++ .../ffx_fsr3upscaler_callbacks_hlsl.h.meta | 60 + .../fsr3upscaler/ffx_fsr3upscaler_common.h | 403 +++ .../ffx_fsr3upscaler_common.h.meta | 60 + .../ffx_fsr3upscaler_debug_view.h | 159 + .../ffx_fsr3upscaler_debug_view.h.meta | 60 + .../ffx_fsr3upscaler_luma_instability.h | 115 + .../ffx_fsr3upscaler_luma_instability.h.meta | 60 + .../ffx_fsr3upscaler_luma_pyramid.h | 192 ++ .../ffx_fsr3upscaler_luma_pyramid.h.meta | 60 + .../ffx_fsr3upscaler_prepare_inputs.h | 152 + .../ffx_fsr3upscaler_prepare_inputs.h.meta | 60 + .../ffx_fsr3upscaler_prepare_reactivity.h | 270 ++ ...ffx_fsr3upscaler_prepare_reactivity.h.meta | 60 + .../fsr3upscaler/ffx_fsr3upscaler_rcas.h | 67 + .../fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta | 60 + .../fsr3upscaler/ffx_fsr3upscaler_reproject.h | 64 + .../ffx_fsr3upscaler_reproject.h.meta | 60 + .../fsr3upscaler/ffx_fsr3upscaler_resources.h | 100 + .../ffx_fsr3upscaler_resources.h.meta | 60 + .../fsr3upscaler/ffx_fsr3upscaler_sample.h | 602 ++++ .../ffx_fsr3upscaler_sample.h.meta | 60 + .../ffx_fsr3upscaler_shading_change.h | 68 + .../ffx_fsr3upscaler_shading_change.h.meta | 60 + .../ffx_fsr3upscaler_shading_change_pyramid.h | 299 ++ ...fsr3upscaler_shading_change_pyramid.h.meta | 60 + .../ffx_fsr3upscaler_tcr_autogen.h | 0 .../ffx_fsr3upscaler_tcr_autogen.h.meta | 60 + .../fsr3upscaler/ffx_fsr3upscaler_upsample.h | 184 + .../ffx_fsr3upscaler_upsample.h.meta | 60 + .../fidelityfx.fsr/Shaders/shaders/spd.meta | 8 + .../Shaders/shaders/spd/ffx_spd.h | 1007 ++++++ .../Shaders/shaders/spd/ffx_spd.h.meta | 60 + Packages/fidelityfx.fsr/package.json | 12 + Packages/fidelityfx.fsr/package.json.meta | 7 + Packages/packages-lock.json | 16 +- README.md | 15 +- .../Editor/PostProcessLayerEditor.cs | 472 --- .../Editor/Unity.Postprocessing.Editor.asmdef | 36 - .../PostProcessing/PostProcessResources.asset | 138 - .../Runtime/Effects/SuperResolution.cs | 334 -- .../PostProcessing/Runtime/FSR3.meta | 8 - .../Runtime/FSR3/Fsr3ShaderIDs.cs | 80 - .../Runtime/FSR3/Fsr3ShaderIDs.cs.meta | 3 - .../Runtime/FSR3/Fsr3Upscaler.cs | 333 -- .../Runtime/FSR3/Fsr3Upscaler.cs.meta | 3 - .../Runtime/FSR3/Fsr3UpscalerAssets.cs | 151 - .../Runtime/FSR3/Fsr3UpscalerAssets.cs.meta | 11 - .../Runtime/FSR3/Fsr3UpscalerCallbacks.cs | 81 - .../FSR3/Fsr3UpscalerCallbacks.cs.meta | 11 - .../Runtime/FSR3/Fsr3UpscalerContext.cs | 610 ---- .../Runtime/FSR3/Fsr3UpscalerContext.cs.meta | 3 - .../Runtime/FSR3/Fsr3UpscalerPass.cs | 372 -- .../Runtime/FSR3/Fsr3UpscalerPass.cs.meta | 3 - .../Runtime/FSR3/Fsr3UpscalerResources.cs | 258 -- .../FSR3/Fsr3UpscalerResources.cs.meta | 3 - .../Runtime/PostProcessResources.cs | 293 -- .../Unity.Postprocessing.Runtime.asmdef | 29 - .../PostProcessing/Shaders/FSR3.meta | 8 - .../ffx_fsr3upscaler_accumulate_pass.compute | 41 - ..._fsr3upscaler_accumulate_pass.compute.meta | 8 - ...fsr3upscaler_autogen_reactive_pass.compute | 32 - ...pscaler_autogen_reactive_pass.compute.meta | 8 - ...ler_compute_luminance_pyramid_pass.compute | 42 - ...ompute_luminance_pyramid_pass.compute.meta | 8 - .../ffx_fsr3upscaler_depth_clip_pass.compute | 32 - ..._fsr3upscaler_depth_clip_pass.compute.meta | 8 - .../FSR3/ffx_fsr3upscaler_lock_pass.compute | 30 - .../ffx_fsr3upscaler_lock_pass.compute.meta | 8 - .../FSR3/ffx_fsr3upscaler_rcas_pass.compute | 31 - .../ffx_fsr3upscaler_rcas_pass.compute.meta | 8 - ...er_reconstruct_previous_depth_pass.compute | 33 - ...construct_previous_depth_pass.compute.meta | 8 - .../ffx_fsr3upscaler_tcr_autogen_pass.compute | 32 - ...fsr3upscaler_tcr_autogen_pass.compute.meta | 8 - .../FSR3/ffx_fsr3upscaler_unity_common.cginc | 82 - .../ffx_fsr3upscaler_unity_common.cginc.meta | 7 - .../PostProcessing/Shaders/FSR3/shaders.meta | 8 - .../ffx_fsr3upscaler_accumulate_pass.hlsl | 79 - ...ffx_fsr3upscaler_accumulate_pass.hlsl.meta | 7 - ...fx_fsr3upscaler_autogen_reactive_pass.hlsl | 77 - ...r3upscaler_autogen_reactive_pass.hlsl.meta | 7 - ...scaler_compute_luminance_pyramid_pass.hlsl | 55 - ...r_compute_luminance_pyramid_pass.hlsl.meta | 7 - .../ffx_fsr3upscaler_depth_clip_pass.hlsl | 67 - ...ffx_fsr3upscaler_depth_clip_pass.hlsl.meta | 7 - .../shaders/ffx_fsr3upscaler_lock_pass.hlsl | 56 - .../ffx_fsr3upscaler_lock_pass.hlsl.meta | 7 - .../shaders/ffx_fsr3upscaler_rcas_pass.hlsl | 53 - .../ffx_fsr3upscaler_rcas_pass.hlsl.meta | 7 - ...caler_reconstruct_previous_depth_pass.hlsl | 64 - ..._reconstruct_previous_depth_pass.hlsl.meta | 7 - .../ffx_fsr3upscaler_tcr_autogen_pass.hlsl | 90 - ...fx_fsr3upscaler_tcr_autogen_pass.hlsl.meta | 7 - .../Shaders/FSR3/shaders/fsr3upscaler.meta | 8 - .../shaders/fsr3upscaler/ffx_common_types.h | 616 ---- .../fsr3upscaler/ffx_common_types.h.meta | 60 - .../FSR3/shaders/fsr3upscaler/ffx_core.h | 80 - .../FSR3/shaders/fsr3upscaler/ffx_core.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/ffx_core_cpu.h | 338 -- .../shaders/fsr3upscaler/ffx_core_cpu.h.meta | 65 - .../fsr3upscaler/ffx_core_gpu_common.h | 2784 --------------- .../fsr3upscaler/ffx_core_gpu_common.h.meta | 65 - .../fsr3upscaler/ffx_core_gpu_common_half.h | 2979 ---------------- .../ffx_core_gpu_common_half.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h | 1651 --------- .../shaders/fsr3upscaler/ffx_core_hlsl.h.meta | 65 - .../fsr3upscaler/ffx_core_portability.h | 51 - .../fsr3upscaler/ffx_core_portability.h.meta | 65 - .../ffx_fsr3upscaler_accumulate.h | 288 -- .../ffx_fsr3upscaler_accumulate.h.meta | 65 - .../ffx_fsr3upscaler_callbacks_hlsl.h | 928 ----- .../ffx_fsr3upscaler_callbacks_hlsl.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_common.h | 566 ---- .../ffx_fsr3upscaler_common.h.meta | 65 - ...x_fsr3upscaler_compute_luminance_pyramid.h | 176 - ...3upscaler_compute_luminance_pyramid.h.meta | 65 - .../ffx_fsr3upscaler_depth_clip.h | 259 -- .../ffx_fsr3upscaler_depth_clip.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_lock.h | 116 - .../fsr3upscaler/ffx_fsr3upscaler_lock.h.meta | 65 - ...ffx_fsr3upscaler_postprocess_lock_status.h | 107 - ...sr3upscaler_postprocess_lock_status.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_rcas.h | 67 - .../fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta | 65 - ...ruct_dilated_velocity_and_previous_depth.h | 146 - ...dilated_velocity_and_previous_depth.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_reproject.h | 137 - .../ffx_fsr3upscaler_reproject.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_resources.h | 104 - .../ffx_fsr3upscaler_resources.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_sample.h | 606 ---- .../ffx_fsr3upscaler_sample.h.meta | 65 - .../ffx_fsr3upscaler_tcr_autogen.h | 250 -- .../ffx_fsr3upscaler_tcr_autogen.h.meta | 65 - .../fsr3upscaler/ffx_fsr3upscaler_upsample.h | 195 -- .../ffx_fsr3upscaler_upsample.h.meta | 65 - .../FSR3/shaders/fsr3upscaler/fsr1.meta | 8 - .../FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h | 1252 ------- .../shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta | 67 - .../FSR3/shaders/fsr3upscaler/spd.meta | 8 - .../FSR3/shaders/fsr3upscaler/spd/ffx_spd.h | 1009 ------ .../shaders/fsr3upscaler/spd/ffx_spd.h.meta | 67 - com.unity.postprocessing/package.json | 11 - images/comparison-fsr3.1.png | Bin 0 -> 884986 bytes images/comparison.png | Bin 1055873 -> 0 bytes 1042 files changed, 28837 insertions(+), 39458 deletions(-) create mode 100644 Assets/FSR3 Upscaler Assets.asset rename Assets/{Fsr3UpscalerAssets.asset.meta => FSR3 Upscaler Assets.asset.meta} (100%) delete mode 100644 Assets/Fsr3UpscalerAssets.asset delete mode 100644 Assets/Scripts/Core.meta delete mode 100644 Assets/Scripts/Core/Fsr3ShaderIDs.cs delete mode 100644 Assets/Scripts/Core/Fsr3Upscaler.cs delete mode 100644 Assets/Scripts/Core/Fsr3UpscalerAssets.cs delete mode 100644 Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs delete mode 100644 Assets/Scripts/Core/Fsr3UpscalerContext.cs delete mode 100644 Assets/Scripts/Core/Fsr3UpscalerPass.cs delete mode 100644 Assets/Scripts/Core/Fsr3UpscalerResources.cs delete mode 100644 Assets/Shaders.meta delete mode 100644 Assets/Shaders/FSR3.meta delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute delete mode 100644 Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc delete mode 100644 Assets/Shaders/FSR3/shaders.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl delete mode 100644 Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/spd.meta delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h delete mode 100644 Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/.signature (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/CHANGELOG.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/CHANGELOG.md.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Ambient-Occlusion.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Anti-aliasing.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Auto-Exposure.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Bloom.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Chromatic-Aberration.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Color-Grading.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Debugging-Post-processing-effects.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Deferred-Fog.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Depth-of-Field.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Grain.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Installation.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Lens-Distortion.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Manipulating-the-Stack.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Motion-Blur.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Quick-start.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Screen-Space-Reflections.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/TableOfContents.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Vignette.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/Writing-Custom-Effects.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Grain_image_0.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Grain_image_1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/PostProcessing-Bloom-0.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/PostProcessing-ChromaticAberration-0.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/PostProcessing-ChromaticAberration-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/PostProcessing-Vignette-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/PostProcessing-Vignette-2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2 _ Debugging_Light meter_Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2 _ Debugging_Vectorscope_Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2-Debugging-Histogram-Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2-Debugging-Light-Meter-Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2-Debugging-Vectorscope-Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2-Debugging-Waveform-Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2_Debugging_Histogram.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2_Debugging_Waveform_Graph.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2_Light Meter.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/Ppv2_Post-processing-Debug.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/aa-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/aa-2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/aa-3.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/ao-off.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/ao-on.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/auto-exposure-off.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/auto-exposure-on.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/autoexposure.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/bloom.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/chroma.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/custom-effect-sorting.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/deferredfog.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/dof.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-10.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-11.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-3.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-4.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-5.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-6.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-7.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-8.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grading-9.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/grain.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/home-after.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/home-before.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/hue-vs-hue.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/hue-vs-sat.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/lens-distortion.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/lensdistortion.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/lum-vs-sat.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/motionblur.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/no-lens-distortion.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/quickstart-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/quickstart-2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/quickstart-3.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/sat-vs-sat.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/ssao-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/ssao-2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/ssr.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/tonemapping.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/trackballs.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/vignette-1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/vignette-2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/images/yrgb-curves.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/index.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/known-issues.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Documentation~/requirements.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/LICENSE.md (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/LICENSE.md.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Attributes.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Attributes/DecoratorAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Attributes/DecoratorAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/BaseEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/BaseEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators/AttributeDecorator.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators/AttributeDecorator.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators/Decorators.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators/Decorators.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators/TrackballDecorator.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Decorators/TrackballDecorator.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/EffectListEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/EffectListEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/AutoExposureEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/AutoExposureEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/BloomEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/BloomEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/ColorGradingEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/ColorGradingEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/LensDistortionEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/LensDistortionEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/MotionBlurEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/MotionBlurEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/VignetteEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Effects/VignetteEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessDebugEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessDebugEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessEffectBaseEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessEffectBaseEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessEffectEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessEffectEditor.cs.meta (100%) create mode 100644 Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessLayerEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessProfileEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessProfileEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessVolumeEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/PostProcessVolumeEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/DefineSetter.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/DefineSetter.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/ProfileFactory.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/ProfileFactory.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/ResourceAssetFactory.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/ResourceAssetFactory.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/VolumeFactory.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Tools/VolumeFactory.cs.meta (100%) create mode 100644 Packages/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/CurveEditor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/CurveEditor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/EditorUtilities.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/EditorUtilities.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/GlobalSettings.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/GlobalSettings.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/SerializedParameterOverride.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/SerializedParameterOverride.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/Styling.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Editor/Utils/Styling.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Gizmos.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Gizmos/PostProcessLayer.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Gizmos/PostProcessLayer.png.meta (100%) create mode 100644 Packages/com.unity.postprocessing/PostProcessing/PostProcessResources.asset rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/PostProcessResources.asset.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/MaxAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/MaxAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/MinAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/MinAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/TrackballAttribute.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Attributes/TrackballAttribute.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/AmbientOcclusion.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/AmbientOcclusion.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/AutoExposure.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/AutoExposure.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Bloom.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Bloom.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ChromaticAberration.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ChromaticAberration.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ColorGrading.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ColorGrading.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/DepthOfField.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/DepthOfField.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Dithering.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Dithering.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Fog.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Fog.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Grain.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Grain.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/LensDistortion.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/LensDistortion.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/MotionBlur.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/MotionBlur.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/MultiScaleVO.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/MultiScaleVO.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ScalableAO.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ScalableAO.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs.meta (100%) create mode 100644 Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/SuperResolution.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Vignette.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Effects/Vignette.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/HistogramMonitor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/HistogramMonitor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/Monitor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/Monitor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/WaveformMonitor.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Monitors/WaveformMonitor.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/ParameterOverride.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/ParameterOverride.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessBundle.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessBundle.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessDebug.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessDebug.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessDebugLayer.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessDebugLayer.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessEffectRenderer.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessEffectRenderer.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessEffectSettings.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessEffectSettings.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessEvent.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessEvent.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessLayer.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessLayer.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessManager.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessManager.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessProfile.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessProfile.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessRenderContext.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessRenderContext.cs.meta (100%) create mode 100644 Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessResources.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessVolume.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/PostProcessVolume.cs.meta (100%) create mode 100644 Packages/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/ColorUtilities.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/ColorUtilities.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/HableCurve.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/HableCurve.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/HaltonSeq.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/HaltonSeq.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/LogHistogram.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/LogHistogram.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/MeshUtilities.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/MeshUtilities.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/PropertySheet.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/PropertySheet.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/PropertySheetFactory.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/PropertySheetFactory.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/RuntimeUtilities.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/RuntimeUtilities.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/ShaderIDs.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/ShaderIDs.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/Spline.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/Spline.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/TargetPool.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/TargetPool.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/TextureLerper.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/TextureLerper.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/XRSettings.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Runtime/Utils/XRSettings.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/ACES.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/ACES.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/D3D11.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/D3D11.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/D3D12.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/D3D12.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/D3D9.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/D3D9.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/Metal.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/Metal.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/OpenGL.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/OpenGL.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/PSP2.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/PSP2.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/PSSL.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/PSSL.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/Switch.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/Switch.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/Vulkan.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/Vulkan.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/WebGPU.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/WebGPU.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/XboxOne.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/API/XboxOne.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/AutoExposure.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/AutoExposure.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Bloom.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Bloom.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Copy.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Copy.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/CopyStd.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/CopyStd.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DeferredFog.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DeferredFog.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DepthOfField.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DepthOfField.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DepthOfField.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DepthOfField.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DiscardAlpha.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DiscardAlpha.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DiskKernels.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/DiskKernels.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Distortion.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Distortion.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Dithering.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Dithering.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ExposureHistogram.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ExposureHistogram.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/FinalPass.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/FinalPass.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Fog.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Fog.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/GaussianDownsample.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/GaussianDownsample.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/GrainBaker.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/GrainBaker.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Lut2DBaker.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Lut2DBaker.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Lut3DBaker.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Lut3DBaker.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MotionBlur.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MotionBlur.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVO.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVO.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScalableAO.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScalableAO.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScalableAO.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScalableAO.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Texture2DLerp.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Texture2DLerp.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Texture3DLerp.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Texture3DLerp.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Uber.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Builtins/Uber.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Colors.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Colors.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Histogram.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Histogram.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Histogram.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Histogram.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/LightMeter.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/LightMeter.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Overlays.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Overlays.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Vectorscope.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Vectorscope.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Vectorscope.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Vectorscope.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Waveform.compute (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Waveform.compute.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Waveform.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Debug/Waveform.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor/ConvertToLog.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor/ConvertToLog.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor/CurveGrid.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor/CurveGrid.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor/Trackball.shader (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Editor/Trackball.shader.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Sampling.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/Sampling.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/StdLib.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/StdLib.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/xRLib.hlsl (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Shaders/xRLib.hlsl.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LUTs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LUTs/NeutralLdrLut.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LUTs/NeutralLdrLut.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt00.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt00.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt01.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt01.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt02.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt02.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt03.png (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/LensDirt/LensDirt03.png.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SMAA.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SMAA/AreaTex.tga (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SMAA/AreaTex.tga.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SMAA/SearchTex.tga (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SMAA/SearchTex.tga.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/.tests.json (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Editor.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Editor/PostProcessEditorTests.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Editor/PostProcessEditorTests.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Runtime.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Runtime/PostProcessingRuntimeTests.cs (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Runtime/PostProcessingRuntimeTests.cs.meta (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef (100%) rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef.meta (100%) create mode 100644 Packages/com.unity.postprocessing/package.json rename {com.unity.postprocessing => Packages/com.unity.postprocessing}/package.json.meta (100%) create mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler.meta create mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs create mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs.meta create mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources.meta create mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset create mode 100644 Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset.meta create mode 100644 Packages/fidelityfx.fsr/LICENSE.md create mode 100644 Packages/fidelityfx.fsr/LICENSE.md.meta create mode 100644 Packages/fidelityfx.fsr/Runtime.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/Common.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs rename Assets/Scripts/Core/Fsr3Upscaler.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs rename Assets/Scripts/Core/Fsr3UpscalerAssets.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs rename Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs rename Assets/Scripts/Core/Fsr3UpscalerContext.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs rename Assets/Scripts/Core/Fsr3UpscalerPass.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs rename Assets/Scripts/Core/Fsr3UpscalerResources.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs rename Assets/Scripts/Core/Fsr3ShaderIDs.cs.meta => Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs create mode 100644 Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs.meta create mode 100644 Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef create mode 100644 Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef.meta create mode 100644 Packages/fidelityfx.fsr/Shaders.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_accumulate_pass.compute rename {Assets/Shaders/FSR3 => Packages/fidelityfx.fsr/Shaders}/ffx_fsr3upscaler_accumulate_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_autogen_reactive_pass.compute rename {Assets/Shaders/FSR3 => Packages/fidelityfx.fsr/Shaders}/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute rename Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta => Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute rename Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta => Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute rename Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta => Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute rename Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta => Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_rcas_pass.compute rename {Assets/Shaders/FSR3 => Packages/fidelityfx.fsr/Shaders}/ffx_fsr3upscaler_rcas_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_tcr_autogen_pass.compute rename {Assets/Shaders/FSR3 => Packages/fidelityfx.fsr/Shaders}/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc rename Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta => Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc.meta (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr1.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h.meta rename {Assets/Shaders/FSR3 => Packages/fidelityfx.fsr/Shaders}/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h (100%) create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/spd.meta create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h create mode 100644 Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h.meta create mode 100644 Packages/fidelityfx.fsr/package.json create mode 100644 Packages/fidelityfx.fsr/package.json.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef delete mode 100644 com.unity.postprocessing/PostProcessing/PostProcessResources.asset delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs delete mode 100644 com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd.meta delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h delete mode 100644 com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta delete mode 100644 com.unity.postprocessing/package.json create mode 100644 images/comparison-fsr3.1.png delete mode 100644 images/comparison.png diff --git a/Assets/FSR3 Upscaler Assets.asset b/Assets/FSR3 Upscaler Assets.asset new file mode 100644 index 0000000..b3d83b8 --- /dev/null +++ b/Assets/FSR3 Upscaler Assets.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2dbcc608a4754d049a14a0bcce2eb40b, type: 3} + m_Name: FSR3 Upscaler Assets + m_EditorClassIdentifier: + shaders: + prepareInputsPass: {fileID: 7200000, guid: 4f59e5b9179d74844ae06a30ae1e0629, type: 3} + lumaPyramidPass: {fileID: 7200000, guid: d253be05abcdc80428503d3e4cce3a36, type: 3} + shadingChangePyramidPass: {fileID: 7200000, guid: 251e663738905fa4d8817001682d802f, type: 3} + shadingChangePass: {fileID: 7200000, guid: 9a2bff2f97619ed4989d9b0577ba0641, type: 3} + prepareReactivityPass: {fileID: 7200000, guid: 20e44016ed34b0d4b8de499d1b566c69, type: 3} + lumaInstabilityPass: {fileID: 7200000, guid: a135306e6d1857e43a86ef20db2a47fe, type: 3} + accumulatePass: {fileID: 7200000, guid: c9b45f0ae7673694ba57a4aadfe212e9, type: 3} + sharpenPass: {fileID: 7200000, guid: 7aaf5cfff022de2499e9b0412f947f6c, type: 3} + autoGenReactivePass: {fileID: 7200000, guid: 5716b91fdaa4e9e439df6b96a796fe6e, type: 3} + tcrAutoGenPass: {fileID: 7200000, guid: 75cdc6ef23f08ed498d4da511923fcea, type: 3} + debugViewPass: {fileID: 7200000, guid: cb24a71d54164c54eb5e86839acd48c5, type: 3} diff --git a/Assets/Fsr3UpscalerAssets.asset.meta b/Assets/FSR3 Upscaler Assets.asset.meta similarity index 100% rename from Assets/Fsr3UpscalerAssets.asset.meta rename to Assets/FSR3 Upscaler Assets.asset.meta diff --git a/Assets/Fsr3UpscalerAssets.asset b/Assets/Fsr3UpscalerAssets.asset deleted file mode 100644 index f3f30ea..0000000 --- a/Assets/Fsr3UpscalerAssets.asset +++ /dev/null @@ -1,23 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: db26e15a33db6ab42a38daab0ba2712f, type: 3} - m_Name: Fsr3UpscalerAssets - m_EditorClassIdentifier: - shaders: - computeLuminancePyramidPass: {fileID: 7200000, guid: d253be05abcdc80428503d3e4cce3a36, type: 3} - reconstructPreviousDepthPass: {fileID: 7200000, guid: 4f59e5b9179d74844ae06a30ae1e0629, type: 3} - depthClipPass: {fileID: 7200000, guid: 20e44016ed34b0d4b8de499d1b566c69, type: 3} - lockPass: {fileID: 7200000, guid: a135306e6d1857e43a86ef20db2a47fe, type: 3} - accumulatePass: {fileID: 7200000, guid: c9b45f0ae7673694ba57a4aadfe212e9, type: 3} - sharpenPass: {fileID: 7200000, guid: 7aaf5cfff022de2499e9b0412f947f6c, type: 3} - autoGenReactivePass: {fileID: 7200000, guid: 5716b91fdaa4e9e439df6b96a796fe6e, type: 3} - tcrAutoGenPass: {fileID: 7200000, guid: 75cdc6ef23f08ed498d4da511923fcea, type: 3} diff --git a/Assets/Scenes/SampleScenePPV2.unity b/Assets/Scenes/SampleScenePPV2.unity index 600d5e0..578bb3b 100644 --- a/Assets/Scenes/SampleScenePPV2.unity +++ b/Assets/Scenes/SampleScenePPV2.unity @@ -228,10 +228,8 @@ GameObject: - component: {fileID: 963194228} - component: {fileID: 963194227} - component: {fileID: 963194226} - - component: {fileID: 963194234} - component: {fileID: 963194232} - component: {fileID: 963194233} - - component: {fileID: 963194229} m_Layer: 6 m_Name: Main Camera m_TagString: MainCamera @@ -305,40 +303,6 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 9.931, y: 109.145, z: 0} ---- !u!114 &963194229 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 955cb66a9ecc20441a7e32934c9b4690, type: 3} - m_Name: - m_EditorClassIdentifier: - qualityMode: 2 - performSharpenPass: 1 - sharpness: 0.8 - enableFP16: 0 - enableAutoExposure: 1 - preExposure: 1 - exposure: {fileID: 0} - reactiveMask: {fileID: 0} - transparencyAndCompositionMask: {fileID: 0} - autoGenerateReactiveMask: 1 - generateReactiveParameters: - scale: 0.5 - cutoffThreshold: 0.2 - binaryValue: 0.9 - flags: 13 - autoGenerateTransparencyAndComposition: 0 - generateTransparencyAndCompositionParameters: - autoTcThreshold: 0.05 - autoTcScale: 1 - autoReactiveScale: 5 - autoReactiveMax: 0.9 - assets: {fileID: 11400000, guid: 4151befafd86e8740ab09463b4f1bdbb, type: 2} --- !u!114 &963194232 MonoBehaviour: m_ObjectHideFlags: 0 @@ -357,12 +321,35 @@ MonoBehaviour: m_Bits: 64 stopNaNPropagation: 0 finalBlitToCameraTarget: 0 - antialiasingMode: 0 + antialiasingMode: 4 temporalAntialiasing: jitterSpread: 0.75 sharpness: 0.25 stationaryBlending: 0.95 motionBlending: 0.85 + superResolution: + qualityMode: 2 + performSharpenPass: 1 + sharpness: 0.8 + enableFP16: 0 + exposureSource: 1 + preExposure: 1 + exposure: {fileID: 0} + enableDebugView: 0 + reactiveMask: {fileID: 0} + transparencyAndCompositionMask: {fileID: 0} + autoGenerateReactiveMask: 1 + generateReactiveParameters: + scale: 0.5 + cutoffThreshold: 0.2 + binaryValue: 0.9 + flags: 13 + autoGenerateTransparencyAndComposition: 0 + generateTransparencyAndCompositionParameters: + autoTcThreshold: 0.05 + autoTcScale: 1 + autoReactiveScale: 5 + autoReactiveMax: 0.9 subpixelMorphologicalAntialiasing: quality: 2 fastApproximateAntialiasing: @@ -397,6 +384,7 @@ MonoBehaviour: m_ShowCustomSorter: 0 breakBeforeColorGrading: 0 m_BeforeTransparentBundles: [] + m_BeforeUpscalingBundles: [] m_BeforeStackBundles: [] m_AfterStackBundles: [] --- !u!114 &963194233 @@ -416,18 +404,6 @@ MonoBehaviour: blendDistance: 0 weight: 1 priority: 1 ---- !u!114 &963194234 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 963194225} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 56db3edb28006274aab71644ef3e4f35, type: 3} - m_Name: - m_EditorClassIdentifier: --- !u!1001 &1313173313 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Core.meta b/Assets/Scripts/Core.meta deleted file mode 100644 index b102114..0000000 --- a/Assets/Scripts/Core.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 975411b519480574290d74d7e34d119d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Core/Fsr3ShaderIDs.cs b/Assets/Scripts/Core/Fsr3ShaderIDs.cs deleted file mode 100644 index 3a28843..0000000 --- a/Assets/Scripts/Core/Fsr3ShaderIDs.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2023 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; - -namespace FidelityFX -{ - internal static class Fsr3ShaderIDs - { - // 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_dilated_depth"); - 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"); - internal static readonly int SrvPrevColorPreAlpha = Shader.PropertyToID("r_input_prev_color_pre_alpha"); - internal static readonly int SrvPrevColorPostAlpha = Shader.PropertyToID("r_input_prev_color_post_alpha"); - - // 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_dilated_depth"); - 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"); - internal static readonly int UavAutoComposition = Shader.PropertyToID("rw_output_autocomposition"); - internal static readonly int UavPrevColorPreAlpha = Shader.PropertyToID("rw_output_prev_color_pre_alpha"); - internal static readonly int UavPrevColorPostAlpha = Shader.PropertyToID("rw_output_prev_color_post_alpha"); - - // Constant buffer bindings - internal static readonly int CbFsr3Upscaler = Shader.PropertyToID("cbFSR3Upscaler"); - internal static readonly int CbSpd = Shader.PropertyToID("cbSPD"); - internal static readonly int CbRcas = Shader.PropertyToID("cbRCAS"); - internal static readonly int CbGenReactive = Shader.PropertyToID("cbGenerateReactive"); - } -} diff --git a/Assets/Scripts/Core/Fsr3Upscaler.cs b/Assets/Scripts/Core/Fsr3Upscaler.cs deleted file mode 100644 index c636c93..0000000 --- a/Assets/Scripts/Core/Fsr3Upscaler.cs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Runtime.InteropServices; -using UnityEngine; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// A collection of helper functions and data structures required by the FSR3 Upscaler process. - /// - public static class Fsr3Upscaler - { - /// - /// Creates a new FSR3 Upscaler context with standard parameters that are appropriate for the current platform. - /// - public static Fsr3UpscalerContext CreateContext(Vector2Int displaySize, Vector2Int maxRenderSize, Fsr3UpscalerShaders shaders, InitializationFlags flags = 0) - { - if (SystemInfo.usesReversedZBuffer) - flags |= InitializationFlags.EnableDepthInverted; - else - flags &= ~InitializationFlags.EnableDepthInverted; - -#if UNITY_EDITOR || DEVELOPMENT_BUILD - flags |= InitializationFlags.EnableDebugChecking; -#endif - - Debug.Log($"Setting up FSR3 Upscaler with render size: {maxRenderSize.x}x{maxRenderSize.y}, display size: {displaySize.x}x{displaySize.y}, flags: {flags}"); - - var contextDescription = new ContextDescription - { - Flags = flags, - DisplaySize = displaySize, - MaxRenderSize = maxRenderSize, - Shaders = shaders, - }; - - var context = new Fsr3UpscalerContext(); - context.Create(contextDescription); - return context; - } - - public static float GetUpscaleRatioFromQualityMode(QualityMode qualityMode) - { - switch (qualityMode) - { - case QualityMode.NativeAA: - return 1.0f; - case QualityMode.UltraQuality: - return 1.2f; - case QualityMode.Quality: - return 1.5f; - case QualityMode.Balanced: - return 1.7f; - case QualityMode.Performance: - return 2.0f; - case QualityMode.UltraPerformance: - return 3.0f; - default: - return 1.0f; - } - } - - public static void GetRenderResolutionFromQualityMode( - out int renderWidth, out int renderHeight, - int displayWidth, int displayHeight, QualityMode qualityMode) - { - float ratio = GetUpscaleRatioFromQualityMode(qualityMode); - renderWidth = Mathf.RoundToInt(displayWidth / ratio); - renderHeight = Mathf.RoundToInt(displayHeight / ratio); - } - - public static float GetMipmapBiasOffset(int renderWidth, int displayWidth) - { - return Mathf.Log((float)renderWidth / displayWidth, 2.0f) - 1.0f; - } - - public static int GetJitterPhaseCount(int renderWidth, int displayWidth) - { - const float basePhaseCount = 8.0f; - int jitterPhaseCount = (int)(basePhaseCount * Mathf.Pow((float)displayWidth / renderWidth, 2.0f)); - return jitterPhaseCount; - } - - public static void GetJitterOffset(out float outX, out float outY, int index, int phaseCount) - { - outX = Halton((index % phaseCount) + 1, 2) - 0.5f; - outY = Halton((index % phaseCount) + 1, 3) - 0.5f; - } - - // Calculate halton number for index and base. - private static float Halton(int index, int @base) - { - float f = 1.0f, result = 0.0f; - - for (int currentIndex = index; currentIndex > 0;) { - - f /= @base; - result += f * (currentIndex % @base); - currentIndex = (int)Mathf.Floor((float)currentIndex / @base); - } - - return result; - } - - public static float Lanczos2(float value) - { - return Mathf.Abs(value) < Mathf.Epsilon ? 1.0f : Mathf.Sin(Mathf.PI * value) / (Mathf.PI * value) * (Mathf.Sin(0.5f * Mathf.PI * value) / (0.5f * Mathf.PI * value)); - } - -#if !UNITY_2021_1_OR_NEWER - internal static void SetBufferData(this CommandBuffer commandBuffer, ComputeBuffer computeBuffer, Array data) - { - commandBuffer.SetComputeBufferData(computeBuffer, data); - } -#endif - - public enum QualityMode - { - NativeAA = 0, - UltraQuality = 1, - Quality = 2, - Balanced = 3, - Performance = 4, - UltraPerformance = 5, - } - - [Flags] - public enum InitializationFlags - { - EnableHighDynamicRange = 1 << 0, - EnableDisplayResolutionMotionVectors = 1 << 1, - EnableMotionVectorsJitterCancellation = 1 << 2, - EnableDepthInverted = 1 << 3, - EnableDepthInfinite = 1 << 4, - EnableAutoExposure = 1 << 5, - EnableDynamicResolution = 1 << 6, - EnableFP16Usage = 1 << 7, - EnableDebugChecking = 1 << 8, - } - - /// - /// A structure encapsulating the parameters required to initialize FidelityFX Super Resolution 3 upscaling. - /// - public struct ContextDescription - { - public InitializationFlags Flags; - public Vector2Int MaxRenderSize; - public Vector2Int DisplaySize; - public Fsr3UpscalerShaders Shaders; - } - - /// - /// A structure encapsulating the parameters for dispatching the various passes of FidelityFX Super Resolution 3. - /// - public class DispatchDescription - { - public ResourceView Color; - public ResourceView Depth; - public ResourceView MotionVectors; - public ResourceView Exposure; // optional - public ResourceView Reactive; // optional - public ResourceView TransparencyAndComposition; // optional - public ResourceView Output; - public Vector2 JitterOffset; - public Vector2 MotionVectorScale; - public Vector2Int RenderSize; - public Vector2Int InputResourceSize; - public bool EnableSharpening; - public float Sharpness; - public float FrameTimeDelta; // in seconds - public float PreExposure; - public bool Reset; - public float CameraNear; - public float CameraFar; - public float CameraFovAngleVertical; - public float ViewSpaceToMetersFactor; - - // EXPERIMENTAL reactive mask generation parameters - public bool EnableAutoReactive; - public ResourceView ColorOpaqueOnly; - public float AutoTcThreshold = 0.05f; - public float AutoTcScale = 1.0f; - public float AutoReactiveScale = 5.0f; - public float AutoReactiveMax = 0.9f; - } - - /// - /// A structure encapsulating the parameters for automatic generation of a reactive mask. - /// The default values for Scale, CutoffThreshold, BinaryValue and Flags were taken from the FSR3 demo project. - /// - public class GenerateReactiveDescription - { - public ResourceView ColorOpaqueOnly; - public ResourceView ColorPreUpscale; - public ResourceView OutReactive; - public Vector2Int RenderSize; - public float Scale = 0.5f; - public float CutoffThreshold = 0.2f; - public float BinaryValue = 0.9f; - public GenerateReactiveFlags Flags = GenerateReactiveFlags.ApplyTonemap | GenerateReactiveFlags.ApplyThreshold | GenerateReactiveFlags.UseComponentsMax; - } - - [Flags] - public enum GenerateReactiveFlags - { - ApplyTonemap = 1 << 0, - ApplyInverseTonemap = 1 << 1, - ApplyThreshold = 1 << 2, - UseComponentsMax = 1 << 3, - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct UpscalerConstants - { - public Vector2Int renderSize; - public Vector2Int maxRenderSize; - public Vector2Int displaySize; - public Vector2Int inputColorResourceDimensions; - public Vector2Int lumaMipDimensions; - public int lumaMipLevelToUse; - public int frameIndex; - - public Vector4 deviceToViewDepth; - public Vector2 jitterOffset; - public Vector2 motionVectorScale; - public Vector2 downscaleFactor; - public Vector2 motionVectorJitterCancellation; - public float preExposure; - public float previousFramePreExposure; - public float tanHalfFOV; - public float jitterPhaseCount; - public float deltaTime; - public float dynamicResChangeFactor; - public float viewSpaceToMetersFactor; - - public int dummy; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct SpdConstants - { - public uint mips; - public uint numWorkGroups; - public uint workGroupOffsetX, workGroupOffsetY; - public uint renderSizeX, renderSizeY; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct GenerateReactiveConstants - { - public float scale; - public float threshold; - public float binaryValue; - public uint flags; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct GenerateReactiveConstants2 - { - public float autoTcThreshold; - public float autoTcScale; - public float autoReactiveScale; - public float autoReactiveMax; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct RcasConstants - { - public RcasConstants(uint sharpness, uint halfSharp) - { - this.sharpness = sharpness; - this.halfSharp = halfSharp; - dummy0 = dummy1 = 0; - } - - public readonly uint sharpness; - public readonly uint halfSharp; - public readonly uint dummy0; - public readonly uint dummy1; - } - } - - /// - /// An immutable structure wrapping all of the necessary information to bind a specific buffer or attachment of a render target to a compute shader. - /// - public readonly struct ResourceView - { - /// - /// This value is the equivalent of not setting any value at all; all struct fields will have their default values. - /// It does not refer to a valid texture, therefore any variable set to this value should be checked for IsValid and reassigned before being bound to a shader. - /// - public static readonly ResourceView Unassigned = new ResourceView(default); - - /// - /// This value contains a valid texture reference that can be bound to a shader, however it is just an empty placeholder texture. - /// Binding this to a shader can be seen as setting the texture variable inside the shader to null. - /// - public static readonly ResourceView None = new ResourceView(BuiltinRenderTextureType.None); - - public ResourceView(in RenderTargetIdentifier renderTarget, RenderTextureSubElement subElement = RenderTextureSubElement.Default, int mipLevel = 0) - { - RenderTarget = renderTarget; - SubElement = subElement; - MipLevel = mipLevel; - } - - public bool IsValid => !RenderTarget.Equals(default); - - public readonly RenderTargetIdentifier RenderTarget; - public readonly RenderTextureSubElement SubElement; - public readonly int MipLevel; - } -} diff --git a/Assets/Scripts/Core/Fsr3UpscalerAssets.cs b/Assets/Scripts/Core/Fsr3UpscalerAssets.cs deleted file mode 100644 index 3e4e24f..0000000 --- a/Assets/Scripts/Core/Fsr3UpscalerAssets.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) 2023 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; - -namespace FidelityFX -{ - /// - /// Scriptable object containing all shader resources required by FidelityFX Super Resolution 3 (FSR3) Upscaler. - /// These can be stored in an asset file and referenced from a scene or prefab, avoiding the need to load the shaders from a Resources folder. - /// - public class Fsr3UpscalerAssets : ScriptableObject - { - public Fsr3UpscalerShaders shaders; - -#if UNITY_EDITOR - private void Reset() - { - shaders = new Fsr3UpscalerShaders - { - computeLuminancePyramidPass = FindComputeShader("ffx_fsr3upscaler_compute_luminance_pyramid_pass"), - reconstructPreviousDepthPass = FindComputeShader("ffx_fsr3upscaler_reconstruct_previous_depth_pass"), - depthClipPass = FindComputeShader("ffx_fsr3upscaler_depth_clip_pass"), - lockPass = FindComputeShader("ffx_fsr3upscaler_lock_pass"), - accumulatePass = FindComputeShader("ffx_fsr3upscaler_accumulate_pass"), - sharpenPass = FindComputeShader("ffx_fsr3upscaler_rcas_pass"), - autoGenReactivePass = FindComputeShader("ffx_fsr3upscaler_autogen_reactive_pass"), - tcrAutoGenPass = FindComputeShader("ffx_fsr3upscaler_tcr_autogen_pass"), - }; - } - - private static ComputeShader FindComputeShader(string name) - { - string[] assetGuids = UnityEditor.AssetDatabase.FindAssets($"t:ComputeShader {name}"); - if (assetGuids == null || assetGuids.Length == 0) - return null; - - string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assetGuids[0]); - return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); - } -#endif - } - - /// - /// All the compute shaders used by the FSR3 Upscaler. - /// - [System.Serializable] - public class Fsr3UpscalerShaders - { - /// - /// The compute shader used by the luminance pyramid computation pass. - /// - public ComputeShader computeLuminancePyramidPass; - - /// - /// The compute shader used by the previous depth reconstruction pass. - /// - public ComputeShader reconstructPreviousDepthPass; - - /// - /// The compute shader used by the depth clip pass. - /// - public ComputeShader depthClipPass; - - /// - /// The compute shader used by the lock pass. - /// - public ComputeShader lockPass; - - /// - /// The compute shader used by the accumulation pass. - /// - public ComputeShader accumulatePass; - - /// - /// The compute shader used by the RCAS sharpening pass. - /// - public ComputeShader sharpenPass; - - /// - /// The compute shader used to auto-generate a reactive mask. - /// - public ComputeShader autoGenReactivePass; - - /// - /// The compute shader used to auto-generate a transparency & composition mask. - /// - public ComputeShader tcrAutoGenPass; - - /// - /// Returns a copy of this class and its contents. - /// - public Fsr3UpscalerShaders Clone() - { - return (Fsr3UpscalerShaders)MemberwiseClone(); - } - - /// - /// Returns a copy of this class with clones of all its shaders. - /// This can be useful if you're running multiple FSR3 Upscaler instances with different shader configurations. - /// Be sure to clean up these clones through Dispose once you're done with them. - /// - public Fsr3UpscalerShaders DeepCopy() - { - return new Fsr3UpscalerShaders - { - computeLuminancePyramidPass = Object.Instantiate(computeLuminancePyramidPass), - reconstructPreviousDepthPass = Object.Instantiate(reconstructPreviousDepthPass), - depthClipPass = Object.Instantiate(depthClipPass), - lockPass = Object.Instantiate(lockPass), - accumulatePass = Object.Instantiate(accumulatePass), - sharpenPass = Object.Instantiate(sharpenPass), - autoGenReactivePass = Object.Instantiate(autoGenReactivePass), - tcrAutoGenPass = Object.Instantiate(tcrAutoGenPass), - }; - } - - /// - /// Destroy all the shaders within this instance. - /// Use this only on clones created through DeepCopy. - /// - public void Dispose() - { - Object.Destroy(computeLuminancePyramidPass); - Object.Destroy(reconstructPreviousDepthPass); - Object.Destroy(depthClipPass); - Object.Destroy(lockPass); - Object.Destroy(accumulatePass); - Object.Destroy(sharpenPass); - Object.Destroy(autoGenReactivePass); - Object.Destroy(tcrAutoGenPass); - } - } -} diff --git a/Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs b/Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs deleted file mode 100644 index 5b2c89c..0000000 --- a/Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2023 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; - -namespace FidelityFX -{ - /// - /// A collection of callbacks required by the FSR3 Upscaler process. - /// This allows some customization by the game dev on how to integrate FSR3 upscaling into their own game setup. - /// - public interface IFsr3UpscalerCallbacks - { - /// - /// Apply a mipmap bias to in-game textures to prevent them from becoming blurry as the internal rendering resolution lowers. - /// This will need to be customized on a per-game basis, as there is no clear universal way to determine what are "in-game" textures. - /// The default implementation will simply apply a mipmap bias to all 2D textures, which will include things like UI textures and which might miss things like terrain texture arrays. - /// - /// Depending on how your game organizes its assets, you will want to create a filter that more specifically selects the textures that need to have this mipmap bias applied. - /// You may also want to store the bias offset value and apply it to any assets that are loaded in on demand. - /// - void ApplyMipmapBias(float biasOffset); - - void UndoMipmapBias(); - } - - /// - /// Default implementation of IFsr3UpscalerCallbacks using simple Resources calls. - /// These are fine for testing but a proper game will want to extend and override these methods. - /// - public class Fsr3UpscalerCallbacksBase: IFsr3UpscalerCallbacks - { - protected float CurrentBiasOffset = 0; - - public virtual void ApplyMipmapBias(float biasOffset) - { - if (float.IsNaN(biasOffset) || float.IsInfinity(biasOffset)) - return; - - CurrentBiasOffset += biasOffset; - - if (Mathf.Approximately(CurrentBiasOffset, 0f)) - { - CurrentBiasOffset = 0f; - } - - foreach (var texture in Resources.FindObjectsOfTypeAll()) - { - if (texture.mipmapCount <= 1) - continue; - - texture.mipMapBias += biasOffset; - } - } - - public virtual void UndoMipmapBias() - { - if (CurrentBiasOffset == 0f) - return; - - ApplyMipmapBias(-CurrentBiasOffset); - } - } -} diff --git a/Assets/Scripts/Core/Fsr3UpscalerContext.cs b/Assets/Scripts/Core/Fsr3UpscalerContext.cs deleted file mode 100644 index da02d9f..0000000 --- a/Assets/Scripts/Core/Fsr3UpscalerContext.cs +++ /dev/null @@ -1,610 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Runtime.InteropServices; -using UnityEngine; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// This class loosely matches the FfxFsr3UpscalerContext struct from the original FSR3 codebase. - /// It manages the various resources and compute passes required by the FSR3 Upscaler process. - /// Note that this class does not know anything about Unity render pipelines; all it knows is CommandBuffers and RenderTargetIdentifiers. - /// This should make it suitable for integration with any of the available Unity render pipelines. - /// - public class Fsr3UpscalerContext - { - private const int MaxQueuedFrames = 16; - - private Fsr3Upscaler.ContextDescription _contextDescription; - private CommandBuffer _commandBuffer; - - private Fsr3UpscalerPass _depthClipPass; - private Fsr3UpscalerPass _reconstructPreviousDepthPass; - private Fsr3UpscalerPass _lockPass; - private Fsr3UpscalerPass _accumulatePass; - private Fsr3UpscalerPass _sharpenPass; - private Fsr3UpscalerPass _computeLuminancePyramidPass; - private Fsr3UpscalerPass _generateReactivePass; - private Fsr3UpscalerPass _tcrAutogeneratePass; - - private readonly Fsr3UpscalerResources _resources = new Fsr3UpscalerResources(); - - private ComputeBuffer _upscalerConstantsBuffer; - private readonly Fsr3Upscaler.UpscalerConstants[] _upscalerConstantsArray = { new Fsr3Upscaler.UpscalerConstants() }; - private ref Fsr3Upscaler.UpscalerConstants UpscalerConsts => ref _upscalerConstantsArray[0]; - - private ComputeBuffer _spdConstantsBuffer; - private readonly Fsr3Upscaler.SpdConstants[] _spdConstantsArray = { new Fsr3Upscaler.SpdConstants() }; - private ref Fsr3Upscaler.SpdConstants SpdConsts => ref _spdConstantsArray[0]; - - private ComputeBuffer _rcasConstantsBuffer; - private readonly Fsr3Upscaler.RcasConstants[] _rcasConstantsArray = new Fsr3Upscaler.RcasConstants[1]; - private ref Fsr3Upscaler.RcasConstants RcasConsts => ref _rcasConstantsArray[0]; - - private ComputeBuffer _generateReactiveConstantsBuffer; - private readonly Fsr3Upscaler.GenerateReactiveConstants[] _generateReactiveConstantsArray = { new Fsr3Upscaler.GenerateReactiveConstants() }; - private ref Fsr3Upscaler.GenerateReactiveConstants GenReactiveConsts => ref _generateReactiveConstantsArray[0]; - - private ComputeBuffer _tcrAutogenerateConstantsBuffer; - private readonly Fsr3Upscaler.GenerateReactiveConstants2[] _tcrAutogenerateConstantsArray = { new Fsr3Upscaler.GenerateReactiveConstants2() }; - private ref Fsr3Upscaler.GenerateReactiveConstants2 TcrAutoGenConsts => ref _tcrAutogenerateConstantsArray[0]; - - private bool _firstExecution; - private Vector2 _previousJitterOffset; - private int _resourceFrameIndex; - - public void Create(Fsr3Upscaler.ContextDescription contextDescription) - { - _contextDescription = contextDescription; - _commandBuffer = new CommandBuffer { name = "FSR3 Upscaler" }; - - _upscalerConstantsBuffer = CreateConstantBuffer(); - _spdConstantsBuffer = CreateConstantBuffer(); - _rcasConstantsBuffer = CreateConstantBuffer(); - _generateReactiveConstantsBuffer = CreateConstantBuffer(); - _tcrAutogenerateConstantsBuffer = CreateConstantBuffer(); - - // Set defaults - _firstExecution = true; - _resourceFrameIndex = 0; - - UpscalerConsts.displaySize = _contextDescription.DisplaySize; - - _resources.Create(_contextDescription); - CreatePasses(); - } - - private void CreatePasses() - { - _computeLuminancePyramidPass = new Fsr3UpscalerComputeLuminancePyramidPass(_contextDescription, _resources, _upscalerConstantsBuffer, _spdConstantsBuffer); - _reconstructPreviousDepthPass = new Fsr3UpscalerReconstructPreviousDepthPass(_contextDescription, _resources, _upscalerConstantsBuffer); - _depthClipPass = new Fsr3UpscalerDepthClipPass(_contextDescription, _resources, _upscalerConstantsBuffer); - _lockPass = new Fsr3UpscalerLockPass(_contextDescription, _resources, _upscalerConstantsBuffer); - _accumulatePass = new Fsr3UpscalerAccumulatePass(_contextDescription, _resources, _upscalerConstantsBuffer); - _sharpenPass = new Fsr3UpscalerSharpenPass(_contextDescription, _resources, _upscalerConstantsBuffer, _rcasConstantsBuffer); - _generateReactivePass = new Fsr3UpscalerGenerateReactivePass(_contextDescription, _resources, _generateReactiveConstantsBuffer); - _tcrAutogeneratePass = new Fsr3UpscalerTcrAutogeneratePass(_contextDescription, _resources, _upscalerConstantsBuffer, _tcrAutogenerateConstantsBuffer); - } - - public void Destroy() - { - DestroyPass(ref _tcrAutogeneratePass); - DestroyPass(ref _generateReactivePass); - DestroyPass(ref _computeLuminancePyramidPass); - DestroyPass(ref _sharpenPass); - DestroyPass(ref _accumulatePass); - DestroyPass(ref _lockPass); - DestroyPass(ref _reconstructPreviousDepthPass); - DestroyPass(ref _depthClipPass); - - _resources.Destroy(); - - DestroyConstantBuffer(ref _tcrAutogenerateConstantsBuffer); - DestroyConstantBuffer(ref _generateReactiveConstantsBuffer); - DestroyConstantBuffer(ref _rcasConstantsBuffer); - DestroyConstantBuffer(ref _spdConstantsBuffer); - DestroyConstantBuffer(ref _upscalerConstantsBuffer); - - _commandBuffer.Dispose(); - _commandBuffer = null; - } - - public void Dispatch(Fsr3Upscaler.DispatchDescription dispatchParams) - { - _commandBuffer.Clear(); - Dispatch(dispatchParams, _commandBuffer); - Graphics.ExecuteCommandBuffer(_commandBuffer); - } - - public void Dispatch(Fsr3Upscaler.DispatchDescription dispatchParams, CommandBuffer commandBuffer) - { - if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDebugChecking) != 0) - { - DebugCheckDispatch(dispatchParams); - } - - if (_firstExecution) - { - commandBuffer.SetRenderTarget(_resources.LockStatus[0]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - commandBuffer.SetRenderTarget(_resources.LockStatus[1]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - } - - int frameIndex = _resourceFrameIndex % 2; - bool resetAccumulation = dispatchParams.Reset || _firstExecution; - _firstExecution = false; - - // If auto exposure is enabled use the auto exposure SRV, otherwise what the app sends - if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableAutoExposure) != 0) - dispatchParams.Exposure = new ResourceView(_resources.AutoExposure); - else if (!dispatchParams.Exposure.IsValid) - dispatchParams.Exposure = new ResourceView(_resources.DefaultExposure); - - if (dispatchParams.EnableAutoReactive) - { - // Create the auto-TCR resources only when we need them - if (_resources.AutoReactive == null) - _resources.CreateTcrAutogenResources(_contextDescription); - - if (resetAccumulation) - { - RenderTargetIdentifier opaqueOnly = dispatchParams.ColorOpaqueOnly.IsValid ? dispatchParams.ColorOpaqueOnly.RenderTarget : Fsr3ShaderIDs.SrvOpaqueOnly; - commandBuffer.Blit(_resources.PrevPreAlpha[frameIndex ^ 1], opaqueOnly); - } - } - else if (_resources.AutoReactive != null) - { - // Destroy the auto-TCR resources if we don't use the feature - _resources.DestroyTcrAutogenResources(); - } - - if (!dispatchParams.Reactive.IsValid) dispatchParams.Reactive = new ResourceView(_resources.DefaultReactive); - if (!dispatchParams.TransparencyAndComposition.IsValid) dispatchParams.TransparencyAndComposition = new ResourceView(_resources.DefaultReactive); - Fsr3UpscalerResources.CreateAliasableResources(commandBuffer, _contextDescription, dispatchParams); - - SetupConstants(dispatchParams, resetAccumulation); - - // Reactive mask bias - const int threadGroupWorkRegionDim = 8; - int dispatchSrcX = (UpscalerConsts.renderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchSrcY = (UpscalerConsts.renderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchDstX = (_contextDescription.DisplaySize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchDstY = (_contextDescription.DisplaySize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - // Clear reconstructed depth for max depth store - if (resetAccumulation) - { - commandBuffer.SetRenderTarget(_resources.LockStatus[frameIndex ^ 1]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - - commandBuffer.SetRenderTarget(_resources.InternalUpscaled[frameIndex ^ 1]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - - commandBuffer.SetRenderTarget(_resources.SceneLuminance); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - - // Auto exposure always used to track luma changes in locking logic - commandBuffer.SetRenderTarget(_resources.AutoExposure); - commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1e8f, 0f, 0f)); - - // Reset atomic counter to 0 - commandBuffer.SetRenderTarget(_resources.SpdAtomicCounter); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - } - - // FSR3: need to clear here since we need the content of this surface for frame interpolation, so clearing in the lock pass is not an option - bool depthInverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) == Fsr3Upscaler.InitializationFlags.EnableDepthInverted; - commandBuffer.SetRenderTarget(Fsr3ShaderIDs.UavReconstructedPrevNearestDepth); - commandBuffer.ClearRenderTarget(false, true, depthInverted ? Color.clear : Color.white); - - // Auto exposure - SetupSpdConstants(dispatchParams, out var dispatchThreadGroupCount); - - // Initialize constant buffers data - commandBuffer.SetBufferData(_upscalerConstantsBuffer, _upscalerConstantsArray); - commandBuffer.SetBufferData(_spdConstantsBuffer, _spdConstantsArray); - - // Auto reactive - if (dispatchParams.EnableAutoReactive) - { - GenerateTransparencyCompositionReactive(dispatchParams, commandBuffer, frameIndex); - dispatchParams.Reactive = new ResourceView(_resources.AutoReactive); - dispatchParams.TransparencyAndComposition = new ResourceView(_resources.AutoComposition); - } - - // Compute luminance pyramid - _computeLuminancePyramidPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); - - // Reconstruct previous depth - _reconstructPreviousDepthPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - - // Depth clip - _depthClipPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - - // Create locks - _lockPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - - // Accumulate - _accumulatePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchDstX, dispatchDstY); - - if (dispatchParams.EnableSharpening) - { - // Compute the constants - SetupRcasConstants(dispatchParams); - commandBuffer.SetBufferData(_rcasConstantsBuffer, _rcasConstantsArray); - - // Dispatch RCAS - const int threadGroupWorkRegionDimRcas = 16; - int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - _sharpenPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, threadGroupsX, threadGroupsY); - } - - _resourceFrameIndex = (_resourceFrameIndex + 1) % MaxQueuedFrames; - - Fsr3UpscalerResources.DestroyAliasableResources(commandBuffer); - } - - public void GenerateReactiveMask(Fsr3Upscaler.GenerateReactiveDescription dispatchParams) - { - _commandBuffer.Clear(); - GenerateReactiveMask(dispatchParams, _commandBuffer); - Graphics.ExecuteCommandBuffer(_commandBuffer); - } - - public void GenerateReactiveMask(Fsr3Upscaler.GenerateReactiveDescription dispatchParams, CommandBuffer commandBuffer) - { - const int threadGroupWorkRegionDim = 8; - int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - GenReactiveConsts.scale = dispatchParams.Scale; - GenReactiveConsts.threshold = dispatchParams.CutoffThreshold; - GenReactiveConsts.binaryValue = dispatchParams.BinaryValue; - GenReactiveConsts.flags = (uint)dispatchParams.Flags; - commandBuffer.SetBufferData(_generateReactiveConstantsBuffer, _generateReactiveConstantsArray); - - ((Fsr3UpscalerGenerateReactivePass)_generateReactivePass).ScheduleDispatch(commandBuffer, dispatchParams, dispatchSrcX, dispatchSrcY); - } - - private void GenerateTransparencyCompositionReactive(Fsr3Upscaler.DispatchDescription dispatchParams, CommandBuffer commandBuffer, int frameIndex) - { - const int threadGroupWorkRegionDim = 8; - int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - TcrAutoGenConsts.autoTcThreshold = dispatchParams.AutoTcThreshold; - TcrAutoGenConsts.autoTcScale = dispatchParams.AutoTcScale; - TcrAutoGenConsts.autoReactiveScale = dispatchParams.AutoReactiveScale; - TcrAutoGenConsts.autoReactiveMax = dispatchParams.AutoReactiveMax; - commandBuffer.SetBufferData(_tcrAutogenerateConstantsBuffer, _tcrAutogenerateConstantsArray); - - _tcrAutogeneratePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - } - - private void SetupConstants(Fsr3Upscaler.DispatchDescription dispatchParams, bool resetAccumulation) - { - ref Fsr3Upscaler.UpscalerConstants constants = ref UpscalerConsts; - - constants.jitterOffset = dispatchParams.JitterOffset; - constants.renderSize = dispatchParams.RenderSize; - constants.maxRenderSize = _contextDescription.MaxRenderSize; - constants.inputColorResourceDimensions = dispatchParams.InputResourceSize; - - // Compute the horizontal FOV for the shader from the vertical one - float aspectRatio = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; - float cameraAngleHorizontal = Mathf.Atan(Mathf.Tan(dispatchParams.CameraFovAngleVertical / 2.0f) * aspectRatio) * 2.0f; - constants.tanHalfFOV = Mathf.Tan(cameraAngleHorizontal * 0.5f); - constants.viewSpaceToMetersFactor = (dispatchParams.ViewSpaceToMetersFactor > 0.0f) ? dispatchParams.ViewSpaceToMetersFactor : 1.0f; - - // Compute params to enable device depth to view space depth computation in shader - constants.deviceToViewDepth = SetupDeviceDepthToViewSpaceDepthParams(dispatchParams); - - // To be updated if resource is larger than the actual image size - constants.downscaleFactor = new Vector2((float)constants.renderSize.x / _contextDescription.DisplaySize.x, (float)constants.renderSize.y / _contextDescription.DisplaySize.y); - constants.previousFramePreExposure = constants.preExposure; - constants.preExposure = (dispatchParams.PreExposure != 0) ? dispatchParams.PreExposure : 1.0f; - - // Motion vector data - Vector2Int motionVectorsTargetSize = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) != 0 ? constants.displaySize : constants.renderSize; - constants.motionVectorScale = dispatchParams.MotionVectorScale / motionVectorsTargetSize; - - // Compute jitter cancellation - if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) - { - constants.motionVectorJitterCancellation = (_previousJitterOffset - constants.jitterOffset) / motionVectorsTargetSize; - _previousJitterOffset = constants.jitterOffset; - } - - int jitterPhaseCount = Fsr3Upscaler.GetJitterPhaseCount(dispatchParams.RenderSize.x, _contextDescription.DisplaySize.x); - if (resetAccumulation || constants.jitterPhaseCount == 0) - { - constants.jitterPhaseCount = jitterPhaseCount; - } - else - { - int jitterPhaseCountDelta = (int)(jitterPhaseCount - constants.jitterPhaseCount); - if (jitterPhaseCountDelta > 0) - constants.jitterPhaseCount++; - else if (jitterPhaseCountDelta < 0) - constants.jitterPhaseCount--; - } - - // Convert delta time to seconds and clamp to [0, 1] - constants.deltaTime = Mathf.Clamp01(dispatchParams.FrameTimeDelta); - - if (resetAccumulation) - constants.frameIndex = 0; - else - constants.frameIndex++; - - // Shading change usage of the SPD mip levels - constants.lumaMipLevelToUse = Fsr3UpscalerPass.ShadingChangeMipLevel; - - float mipDiv = 2 << constants.lumaMipLevelToUse; - constants.lumaMipDimensions.x = (int)(constants.maxRenderSize.x / mipDiv); - constants.lumaMipDimensions.y = (int)(constants.maxRenderSize.y / mipDiv); - } - - private Vector4 SetupDeviceDepthToViewSpaceDepthParams(Fsr3Upscaler.DispatchDescription dispatchParams) - { - bool inverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0; - bool infinite = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInfinite) != 0; - - // make sure it has no impact if near and far plane values are swapped in dispatch params - // the flags "inverted" and "infinite" will decide what transform to use - float min = Mathf.Min(dispatchParams.CameraNear, dispatchParams.CameraFar); - float max = Mathf.Max(dispatchParams.CameraNear, dispatchParams.CameraFar); - - if (inverted) - { - (min, max) = (max, min); - } - - float q = max / (min - max); - float d = -1.0f; - - Vector4 matrixElemC = new Vector4(q, -1.0f - Mathf.Epsilon, q, 0.0f + Mathf.Epsilon); - Vector4 matrixElemE = new Vector4(q * min, -min - Mathf.Epsilon, q * min, max); - - // Revert x and y coords - float aspect = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; - float cotHalfFovY = Mathf.Cos(0.5f * dispatchParams.CameraFovAngleVertical) / Mathf.Sin(0.5f * dispatchParams.CameraFovAngleVertical); - - int matrixIndex = (inverted ? 2 : 0) + (infinite ? 1 : 0); - return new Vector4( - d * matrixElemC[matrixIndex], - matrixElemE[matrixIndex], - aspect / cotHalfFovY, - 1.0f / cotHalfFovY); - } - - private void SetupRcasConstants(Fsr3Upscaler.DispatchDescription dispatchParams) - { - int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(dispatchParams.Sharpness) * (RcasConfigs.Length - 1)); - RcasConsts = RcasConfigs[sharpnessIndex]; - } - - private void SetupSpdConstants(Fsr3Upscaler.DispatchDescription dispatchParams, out Vector2Int dispatchThreadGroupCount) - { - RectInt rectInfo = new RectInt(0, 0, dispatchParams.RenderSize.x, dispatchParams.RenderSize.y); - SpdSetup(rectInfo, out dispatchThreadGroupCount, out var workGroupOffset, out var numWorkGroupsAndMips); - - // Downsample - ref Fsr3Upscaler.SpdConstants spdConstants = ref SpdConsts; - spdConstants.numWorkGroups = (uint)numWorkGroupsAndMips.x; - spdConstants.mips = (uint)numWorkGroupsAndMips.y; - spdConstants.workGroupOffsetX = (uint)workGroupOffset.x; - spdConstants.workGroupOffsetY = (uint)workGroupOffset.y; - spdConstants.renderSizeX = (uint)dispatchParams.RenderSize.x; - spdConstants.renderSizeY = (uint)dispatchParams.RenderSize.y; - } - - private static void SpdSetup(RectInt rectInfo, out Vector2Int dispatchThreadGroupCount, out Vector2Int workGroupOffset, out Vector2Int numWorkGroupsAndMips, int mips = -1) - { - workGroupOffset = new Vector2Int(rectInfo.x / 64, rectInfo.y / 64); - - int endIndexX = (rectInfo.x + rectInfo.width - 1) / 64; - int endIndexY = (rectInfo.y + rectInfo.height - 1) / 64; - - dispatchThreadGroupCount = new Vector2Int(endIndexX + 1 - workGroupOffset.x, endIndexY + 1 - workGroupOffset.y); - - numWorkGroupsAndMips = new Vector2Int(dispatchThreadGroupCount.x * dispatchThreadGroupCount.y, mips); - if (mips < 0) - { - float resolution = Math.Max(rectInfo.width, rectInfo.height); - numWorkGroupsAndMips.y = Math.Min(Mathf.FloorToInt(Mathf.Log(resolution, 2.0f)), 12); - } - } - - private void DebugCheckDispatch(Fsr3Upscaler.DispatchDescription dispatchParams) - { - if (!dispatchParams.Color.IsValid) - { - Debug.LogError("Color resource is null"); - } - - if (!dispatchParams.Depth.IsValid) - { - Debug.LogError("Depth resource is null"); - } - - if (!dispatchParams.MotionVectors.IsValid) - { - Debug.LogError("MotionVectors resource is null"); - } - - if (!dispatchParams.Output.IsValid) - { - Debug.LogError("Output resource is null"); - } - - if (dispatchParams.Exposure.IsValid && (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableAutoExposure) != 0) - { - Debug.LogWarning("Exposure resource provided, however auto exposure flag is present"); - } - - if (Mathf.Abs(dispatchParams.JitterOffset.x) > 1.0f || Mathf.Abs(dispatchParams.JitterOffset.y) > 1.0f) - { - Debug.LogWarning("JitterOffset contains value outside of expected range [-1.0, 1.0]"); - } - - if (dispatchParams.MotionVectorScale.x > _contextDescription.MaxRenderSize.x || dispatchParams.MotionVectorScale.y > _contextDescription.MaxRenderSize.y) - { - Debug.LogWarning("MotionVectorScale contains scale value greater than MaxRenderSize"); - } - - if (dispatchParams.MotionVectorScale.x == 0.0f || dispatchParams.MotionVectorScale.y == 0.0f) - { - Debug.LogWarning("MotionVectorScale contains zero scale value"); - } - - if (dispatchParams.RenderSize.x > _contextDescription.MaxRenderSize.x || dispatchParams.RenderSize.y > _contextDescription.MaxRenderSize.y) - { - Debug.LogWarning("RenderSize is greater than context MaxRenderSize"); - } - - if (dispatchParams.RenderSize.x == 0 || dispatchParams.RenderSize.y == 0) - { - Debug.LogWarning("RenderSize contains zero dimension"); - } - - if (dispatchParams.FrameTimeDelta > 1.0f) - { - Debug.LogWarning("FrameTimeDelta is greater than 1.0f - this value should be seconds (~0.0166 for 60fps)"); - } - - if (dispatchParams.PreExposure == 0.0f) - { - Debug.LogError("PreExposure provided as 0.0f which is invalid"); - } - - bool infiniteDepth = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInfinite) != 0; - bool inverseDepth = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0; - - if (inverseDepth) - { - if (dispatchParams.CameraNear < dispatchParams.CameraFar) - { - Debug.LogWarning("EnableDepthInverted flag is present yet CameraNear is less than CameraFar"); - } - - if (infiniteDepth) - { - if (dispatchParams.CameraNear < float.MaxValue) - { - Debug.LogWarning("EnableDepthInfinite and EnableDepthInverted present, yet CameraNear != float.MaxValue"); - } - } - - if (dispatchParams.CameraFar < 0.075f) - { - Debug.LogWarning("EnableDepthInverted present, CameraFar value is very low which may result in depth separation artefacting"); - } - } - else - { - if (dispatchParams.CameraNear > dispatchParams.CameraFar) - { - Debug.LogWarning("CameraNear is greater than CameraFar in non-inverted-depth context"); - } - - if (infiniteDepth) - { - if (dispatchParams.CameraFar < float.MaxValue) - { - Debug.LogWarning("EnableDepthInfinite present, yet CameraFar != float.MaxValue"); - } - } - - if (dispatchParams.CameraNear < 0.075f) - { - Debug.LogWarning("CameraNear value is very low which may result in depth separation artefacting"); - } - } - - if (dispatchParams.CameraFovAngleVertical <= 0.0f) - { - Debug.LogError("CameraFovAngleVertical is 0.0f - this value should be > 0.0f"); - } - - if (dispatchParams.CameraFovAngleVertical > Mathf.PI) - { - Debug.LogError("CameraFovAngleVertical is greater than 180 degrees/PI"); - } - } - - /// - /// The FSR3 C++ codebase uses floats bitwise converted to ints to pass sharpness parameters to the RCAS shader. - /// This is not possible in C# without enabling unsafe code compilation, so to avoid that we instead use a table of precomputed values. - /// - private static readonly Fsr3Upscaler.RcasConstants[] RcasConfigs = new [] - { - new Fsr3Upscaler.RcasConstants(1048576000u, 872428544u), - new Fsr3Upscaler.RcasConstants(1049178080u, 877212745u), - new Fsr3Upscaler.RcasConstants(1049823372u, 882390168u), - new Fsr3Upscaler.RcasConstants(1050514979u, 887895276u), - new Fsr3Upscaler.RcasConstants(1051256227u, 893859143u), - new Fsr3Upscaler.RcasConstants(1052050675u, 900216232u), - new Fsr3Upscaler.RcasConstants(1052902144u, 907032080u), - new Fsr3Upscaler.RcasConstants(1053814727u, 914306687u), - new Fsr3Upscaler.RcasConstants(1054792807u, 922105590u), - new Fsr3Upscaler.RcasConstants(1055841087u, 930494326u), - new Fsr3Upscaler.RcasConstants(1056964608u, 939538432u), - new Fsr3Upscaler.RcasConstants(1057566688u, 944322633u), - new Fsr3Upscaler.RcasConstants(1058211980u, 949500056u), - new Fsr3Upscaler.RcasConstants(1058903587u, 955005164u), - new Fsr3Upscaler.RcasConstants(1059644835u, 960969031u), - new Fsr3Upscaler.RcasConstants(1060439283u, 967326120u), - new Fsr3Upscaler.RcasConstants(1061290752u, 974141968u), - new Fsr3Upscaler.RcasConstants(1062203335u, 981416575u), - new Fsr3Upscaler.RcasConstants(1063181415u, 989215478u), - new Fsr3Upscaler.RcasConstants(1064229695u, 997604214u), - new Fsr3Upscaler.RcasConstants(1065353216u, 1006648320), - }; - - private static ComputeBuffer CreateConstantBuffer() where TConstants: struct - { - return new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); - } - - private static void DestroyConstantBuffer(ref ComputeBuffer bufferRef) - { - if (bufferRef == null) - return; - - bufferRef.Release(); - bufferRef = null; - } - - private static void DestroyPass(ref Fsr3UpscalerPass pass) - { - if (pass == null) - return; - - pass.Dispose(); - pass = null; - } - } -} diff --git a/Assets/Scripts/Core/Fsr3UpscalerPass.cs b/Assets/Scripts/Core/Fsr3UpscalerPass.cs deleted file mode 100644 index e1dc225..0000000 --- a/Assets/Scripts/Core/Fsr3UpscalerPass.cs +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Runtime.InteropServices; -using UnityEngine; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// Base class for all of the compute passes that make up the FSR3 Upscaler process. - /// This loosely matches the FfxPipelineState struct from the original FSR3 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 Fsr3UpscalerPass: IDisposable - { - internal const int ShadingChangeMipLevel = 4; // This matches the FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL define - - protected readonly Fsr3Upscaler.ContextDescription ContextDescription; - protected readonly Fsr3UpscalerResources Resources; - protected readonly ComputeBuffer Constants; - - protected ComputeShader ComputeShader; - protected int KernelIndex; - - protected Fsr3UpscalerPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - { - ContextDescription = contextDescription; - Resources = resources; - Constants = constants; - } - - public virtual void Dispose() - { - } - - public abstract void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); - - protected void InitComputeShader(string passName, ComputeShader shader) - { - InitComputeShader(passName, shader, ContextDescription.Flags); - } - - private void InitComputeShader(string passName, ComputeShader shader, Fsr3Upscaler.InitializationFlags flags) - { - if (shader == null) - { - throw new MissingReferenceException($"Shader for FSR3 Upscaler '{passName}' could not be loaded! Please ensure it is included in the project correctly."); - } - - ComputeShader = shader; - KernelIndex = ComputeShader.FindKernel("CS"); - - 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 & Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH"); - if (useLut) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableFP16Usage) != 0) ComputeShader.EnableKeyword("FFX_HALF"); - - // Inform the shader which render pipeline we're currently using - var pipeline = GraphicsSettings.currentRenderPipeline; - if (pipeline != null && pipeline.GetType().Name.Contains("HDRenderPipeline")) - { - ComputeShader.EnableKeyword("UNITY_FSR3UPSCALER_HDRP"); - } - } - } - - internal class Fsr3UpscalerComputeLuminancePyramidPass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _spdConstants; - - public Fsr3UpscalerComputeLuminancePyramidPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer spdConstants) - : base(contextDescription, resources, constants) - { - _spdConstants = spdConstants; - - InitComputeShader("compute_luminance_pyramid_pass", contextDescription.Shaders.computeLuminancePyramidPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavExposureMipLumaChange, Resources.SceneLuminance, ShadingChangeMipLevel); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavExposureMip5, Resources.SceneLuminance, 5); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoExposure, Resources.AutoExposure); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbSpd, _spdConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerReconstructPreviousDepthPass : Fsr3UpscalerPass - { - public Fsr3UpscalerReconstructPreviousDepthPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("reconstruct_previous_depth_pass", contextDescription.Shaders.reconstructPreviousDepthPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - ref var depth = ref dispatchParams.Depth; - ref var motionVectors = ref dispatchParams.MotionVectors; - ref var exposure = ref dispatchParams.Exposure; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerDepthClipPass : Fsr3UpscalerPass - { - public Fsr3UpscalerDepthClipPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("depth_clip_pass", contextDescription.Shaders.depthClipPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - ref var depth = ref dispatchParams.Depth; - ref var motionVectors = ref dispatchParams.MotionVectors; - ref var exposure = ref dispatchParams.Exposure; - ref var reactive = ref dispatchParams.Reactive; - ref var tac = ref dispatchParams.TransparencyAndComposition; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReconstructedPrevNearestDepth, Fsr3ShaderIDs.UavReconstructedPrevNearestDepth); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedDepth, Fsr3ShaderIDs.UavDilatedDepth); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex ^ 1]); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerLockPass : Fsr3UpscalerPass - { - public Fsr3UpscalerLockPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("lock_pass", contextDescription.Shaders.lockPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLockInputLuma, Fsr3ShaderIDs.UavLockInputLuma); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerAccumulatePass : Fsr3UpscalerPass - { - private const string SharpeningKeyword = "FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING"; - -#if UNITY_2021_2_OR_NEWER - private readonly LocalKeyword _sharpeningKeyword; -#endif - - public Fsr3UpscalerAccumulatePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("accumulate_pass", contextDescription.Shaders.accumulatePass); -#if UNITY_2021_2_OR_NEWER - _sharpeningKeyword = new LocalKeyword(ComputeShader, SharpeningKeyword); -#endif - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { -#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 ((ContextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) - { - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); - } - else - { - ref var motionVectors = ref dispatchParams.MotionVectors; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - } - - ref var exposure = ref dispatchParams.Exposure; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedReactiveMasks, Fsr3ShaderIDs.UavDilatedReactiveMasks); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInternalUpscaled, Resources.InternalUpscaled[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLockStatus, Resources.LockStatus[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPreparedInputColor, Fsr3ShaderIDs.UavPreparedInputColor); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLanczosLut, Resources.LanczosLut); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvUpscaleMaximumBiasLut, Resources.MaximumBiasLut); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvSceneLuminanceMips, Resources.SceneLuminance); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvAutoExposure, Resources.AutoExposure); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLumaHistory, Resources.LumaHistory[frameIndex ^ 1]); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavInternalUpscaled, Resources.InternalUpscaled[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavLockStatus, Resources.LockStatus[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavLumaHistory, Resources.LumaHistory[frameIndex]); - - ref var output = ref dispatchParams.Output; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerSharpenPass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _rcasConstants; - - public Fsr3UpscalerSharpenPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer rcasConstants) - : base(contextDescription, resources, constants) - { - _rcasConstants = rcasConstants; - - InitComputeShader("rcas_pass", contextDescription.Shaders.sharpenPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var exposure = ref dispatchParams.Exposure; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvRcasInput, Resources.InternalUpscaled[frameIndex]); - - ref var output = ref dispatchParams.Output; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbRcas, _rcasConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerGenerateReactivePass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _generateReactiveConstants; - - public Fsr3UpscalerGenerateReactivePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer generateReactiveConstants) - : base(contextDescription, resources, null) - { - _generateReactiveConstants = generateReactiveConstants; - - InitComputeShader("autogen_reactive_pass", contextDescription.Shaders.autoGenReactivePass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - } - - public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.GenerateReactiveDescription dispatchParams, int dispatchX, int dispatchY) - { - ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; - ref var color = ref dispatchParams.ColorPreUpscale; - ref var reactive = ref dispatchParams.OutReactive; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoReactive, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbGenReactive, _generateReactiveConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerTcrAutogeneratePass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _tcrAutogenerateConstants; - - public Fsr3UpscalerTcrAutogeneratePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer tcrAutogenerateConstants) - : base(contextDescription, resources, constants) - { - _tcrAutogenerateConstants = tcrAutogenerateConstants; - - InitComputeShader("tcr_autogen_pass", contextDescription.Shaders.tcrAutoGenPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - ref var motionVectors = ref dispatchParams.MotionVectors; - ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; - ref var reactive = ref dispatchParams.Reactive; - ref var tac = ref dispatchParams.TransparencyAndComposition; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoReactive, Resources.AutoReactive); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoComposition, Resources.AutoComposition); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex]); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbGenReactive, _tcrAutogenerateConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } -} diff --git a/Assets/Scripts/Core/Fsr3UpscalerResources.cs b/Assets/Scripts/Core/Fsr3UpscalerResources.cs deleted file mode 100644 index 344a371..0000000 --- a/Assets/Scripts/Core/Fsr3UpscalerResources.cs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2023 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 System; -using UnityEngine; -using UnityEngine.Experimental.Rendering; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// Helper class for bundling and managing persistent resources required by the FSR3 Upscaler process. - /// This includes lookup tables, default fallback resources and double-buffered resources that get swapped between frames. - /// - internal class Fsr3UpscalerResources - { - public Texture2D DefaultExposure; - public Texture2D DefaultReactive; - public Texture2D LanczosLut; - public Texture2D MaximumBiasLut; - public RenderTexture SpdAtomicCounter; - public RenderTexture AutoExposure; - public RenderTexture SceneLuminance; - public RenderTexture AutoReactive; - public RenderTexture AutoComposition; - public readonly RenderTexture[] DilatedMotionVectors = new RenderTexture[2]; - public readonly RenderTexture[] LockStatus = new RenderTexture[2]; - public readonly RenderTexture[] InternalUpscaled = new RenderTexture[2]; - public readonly RenderTexture[] LumaHistory = new RenderTexture[2]; - public readonly RenderTexture[] PrevPreAlpha = new RenderTexture[2]; - public readonly RenderTexture[] PrevPostAlpha = new RenderTexture[2]; - - public void Create(Fsr3Upscaler.ContextDescription contextDescription) - { - // Generate the data for the LUT - const int lanczos2LutWidth = 128; - float[] lanczos2Weights = new float[lanczos2LutWidth]; - for (int currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex) - { - float x = 2.0f * currentLanczosWidthIndex / (lanczos2LutWidth - 1); - float y = Fsr3Upscaler.Lanczos2(x); - lanczos2Weights[currentLanczosWidthIndex] = y; - } - - float[] maximumBias = new float[MaximumBiasTextureWidth * MaximumBiasTextureHeight]; - for (int i = 0; i < maximumBias.Length; ++i) - { - maximumBias[i] = MaximumBias[i] / 2.0f; - } - - // Resource FSR3UPSCALER_LanczosLutData: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE - // R16_SNorm textures are not supported by Unity on most platforms, strangely enough. So instead we use R32_SFloat and upload pre-normalized float data. - LanczosLut = new Texture2D(lanczos2LutWidth, 1, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_LanczosLutData" }; - LanczosLut.SetPixelData(lanczos2Weights, 0); - LanczosLut.Apply(); - - // Resource FSR3UPSCALER_MaximumUpsampleBias: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE - MaximumBiasLut = new Texture2D(MaximumBiasTextureWidth, MaximumBiasTextureHeight, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_MaximumUpsampleBias" }; - MaximumBiasLut.SetPixelData(maximumBias, 0); - MaximumBiasLut.Apply(); - - // Resource FSR3UPSCALER_DefaultExposure: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE - DefaultExposure = new Texture2D(1, 1, GraphicsFormat.R32G32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_DefaultExposure" }; - DefaultExposure.SetPixel(0, 0, Color.clear); - DefaultExposure.Apply(); - - // Resource FSR3UPSCALER_DefaultReactivityMask: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE - DefaultReactive = new Texture2D(1, 1, GraphicsFormat.R8_UNorm, TextureCreationFlags.None) { name = "FSR3UPSCALER_DefaultReactivityMask" }; - DefaultReactive.SetPixel(0, 0, Color.clear); - DefaultReactive.Apply(); - - // Resource FSR3UPSCALER_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE - // Despite what the original FSR3 codebase says, this resource really isn't aliasable. Resetting this counter to 0 every frame breaks auto-exposure on MacOS Metal. - SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR3UPSCALER_SpdAtomicCounter", enableRandomWrite = true }; - SpdAtomicCounter.Create(); - - // Resource FSR3UPSCALER_AutoExposure: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE - AutoExposure = new RenderTexture(1, 1, 0, GraphicsFormat.R32G32_SFloat) { name = "FSR3UPSCALER_AutoExposure", enableRandomWrite = true }; - AutoExposure.Create(); - - // Resource FSR3UPSCALER_ExposureMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - // This is a rather special case: it's an aliasable resource, but because we require a mipmap chain and bind specific mip levels per shader, we can't easily use temporary RTs for this. - int w = contextDescription.MaxRenderSize.x / 2, h = contextDescription.MaxRenderSize.y / 2; - int mipCount = 1 + Mathf.FloorToInt(Mathf.Log(Math.Max(w, h), 2.0f)); - SceneLuminance = new RenderTexture(w, h, 0, GraphicsFormat.R16_SFloat, mipCount) { name = "FSR3UPSCALER_ExposureMips", enableRandomWrite = true, useMipMap = true, autoGenerateMips = false }; - SceneLuminance.Create(); - - // Resources FSR3UPSCALER_InternalDilatedVelocity1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(DilatedMotionVectors, "FSR3UPSCALER_InternalDilatedVelocity", contextDescription.MaxRenderSize, GraphicsFormat.R16G16_SFloat); - - // Resources FSR3UPSCALER_LockStatus1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(LockStatus, "FSR3UPSCALER_LockStatus", contextDescription.DisplaySize, GraphicsFormat.R16G16_SFloat); - - // Resources FSR3UPSCALER_InternalUpscaled1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(InternalUpscaled, "FSR3UPSCALER_InternalUpscaled", contextDescription.DisplaySize, GraphicsFormat.R16G16B16A16_SFloat); - - // Resources FSR3UPSCALER_LumaHistory1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8B8A8_UNORM, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(LumaHistory, "FSR3UPSCALER_LumaHistory", contextDescription.DisplaySize, GraphicsFormat.R8G8B8A8_UNorm); - } - - public void CreateTcrAutogenResources(Fsr3Upscaler.ContextDescription contextDescription) - { - // Resource FSR3UPSCALER_AutoReactive: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE - AutoReactive = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR3UPSCALER_AutoReactive", enableRandomWrite = true }; - AutoReactive.Create(); - - // Resource FSR3UPSCALER_AutoComposition: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE - AutoComposition = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR3UPSCALER_AutoComposition", enableRandomWrite = true }; - AutoComposition.Create(); - - // Resources FSR3UPSCALER_PrevPreAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(PrevPreAlpha, "FSR3UPSCALER_PrevPreAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); - - // Resources FSR3UPSCALER_PrevPostAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(PrevPostAlpha, "FSR3UPSCALER_PrevPostAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); - } - - // 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 - public static void CreateAliasableResources(CommandBuffer commandBuffer, Fsr3Upscaler.ContextDescription contextDescription, Fsr3Upscaler.DispatchDescription dispatchParams) - { - Vector2Int displaySize = contextDescription.DisplaySize; - Vector2Int maxRenderSize = contextDescription.MaxRenderSize; - - // FSR3UPSCALER_ReconstructedPrevNearestDepth: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavReconstructedPrevNearestDepth, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R32_UInt, 1, true); - - // FSR3UPSCALER_DilatedDepth: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavDilatedDepth, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R32_SFloat, 1, true); - - // FSR3UPSCALER_LockInputLuma: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavLockInputLuma, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16_SFloat, 1, true); - - // FSR3UPSCALER_DilatedReactiveMasks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavDilatedReactiveMasks, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R8G8_UNorm, 1, true); - - // FSR3UPSCALER_PreparedInputColor: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavPreparedInputColor, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true); - - // FSR3UPSCALER_NewLocks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavNewLocks, displaySize.x, displaySize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); - } - - public static void DestroyAliasableResources(CommandBuffer commandBuffer) - { - // Release all of the aliasable resources used this frame - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavReconstructedPrevNearestDepth); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavDilatedDepth); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavLockInputLuma); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavDilatedReactiveMasks); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavPreparedInputColor); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavNewLocks); - } - - private static void CreateDoubleBufferedResource(RenderTexture[] resource, string name, Vector2Int size, GraphicsFormat format) - { - for (int i = 0; i < 2; ++i) - { - resource[i] = new RenderTexture(size.x, size.y, 0, format) { name = name + (i + 1), enableRandomWrite = true }; - resource[i].Create(); - } - } - - public void Destroy() - { - DestroyTcrAutogenResources(); - - DestroyResource(LumaHistory); - DestroyResource(InternalUpscaled); - DestroyResource(LockStatus); - DestroyResource(DilatedMotionVectors); - DestroyResource(ref SceneLuminance); - DestroyResource(ref AutoExposure); - DestroyResource(ref DefaultReactive); - DestroyResource(ref DefaultExposure); - DestroyResource(ref MaximumBiasLut); - DestroyResource(ref LanczosLut); - } - - public void DestroyTcrAutogenResources() - { - DestroyResource(PrevPostAlpha); - DestroyResource(PrevPreAlpha); - DestroyResource(ref AutoComposition); - DestroyResource(ref AutoReactive); - } - - private static void DestroyResource(ref Texture2D resource) - { - if (resource == null) - return; - -#if UNITY_EDITOR - if (Application.isPlaying && !UnityEditor.EditorApplication.isPaused) - UnityEngine.Object.Destroy(resource); - else - UnityEngine.Object.DestroyImmediate(resource); -#else - UnityEngine.Object.Destroy(resource); -#endif - resource = null; - } - - private static void DestroyResource(ref RenderTexture resource) - { - if (resource == null) - return; - - resource.Release(); - resource = null; - } - - private static void DestroyResource(RenderTexture[] resource) - { - for (int i = 0; i < resource.Length; ++i) - DestroyResource(ref resource[i]); - } - - private const int MaximumBiasTextureWidth = 16; - private const int MaximumBiasTextureHeight = 16; - private static readonly float[] MaximumBias = - { - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.876f, 1.809f, 1.772f, 1.753f, 1.748f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.869f, 1.801f, 1.764f, 1.745f, 1.739f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.976f, 1.841f, 1.774f, 1.737f, 1.716f, 1.71f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.914f, 1.784f, 1.716f, 1.673f, 1.649f, 1.641f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.793f, 1.676f, 1.604f, 1.562f, 1.54f, 1.533f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.619f, 1.536f, 1.492f, 1.467f, 1.454f, 1.449f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.575f, 1.496f, 1.456f, 1.432f, 1.416f, 1.408f, 1.405f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.555f, 1.479f, 1.438f, 1.413f, 1.398f, 1.387f, 1.381f, 1.379f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.555f, 1.474f, 1.43f, 1.404f, 1.387f, 1.376f, 1.368f, 1.363f, 1.362f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.575f, 1.479f, 1.43f, 1.401f, 1.382f, 1.369f, 1.36f, 1.354f, 1.351f, 1.35f, - 2.0f, 2.0f, 1.976f, 1.914f, 1.793f, 1.619f, 1.496f, 1.438f, 1.404f, 1.382f, 1.367f, 1.357f, 1.349f, 1.344f, 1.341f, 1.34f, - 1.876f, 1.869f, 1.841f, 1.784f, 1.676f, 1.536f, 1.456f, 1.413f, 1.387f, 1.369f, 1.357f, 1.347f, 1.341f, 1.336f, 1.333f, 1.332f, - 1.809f, 1.801f, 1.774f, 1.716f, 1.604f, 1.492f, 1.432f, 1.398f, 1.376f, 1.36f, 1.349f, 1.341f, 1.335f, 1.33f, 1.328f, 1.327f, - 1.772f, 1.764f, 1.737f, 1.673f, 1.562f, 1.467f, 1.416f, 1.387f, 1.368f, 1.354f, 1.344f, 1.336f, 1.33f, 1.326f, 1.323f, 1.323f, - 1.753f, 1.745f, 1.716f, 1.649f, 1.54f, 1.454f, 1.408f, 1.381f, 1.363f, 1.351f, 1.341f, 1.333f, 1.328f, 1.323f, 1.321f, 1.32f, - 1.748f, 1.739f, 1.71f, 1.641f, 1.533f, 1.449f, 1.405f, 1.379f, 1.362f, 1.35f, 1.34f, 1.332f, 1.327f, 1.323f, 1.32f, 1.319f, - }; - } -} diff --git a/Assets/Scripts/Fsr3UpscalerImageEffect.cs b/Assets/Scripts/Fsr3UpscalerImageEffect.cs index a5591a5..bbde974 100644 --- a/Assets/Scripts/Fsr3UpscalerImageEffect.cs +++ b/Assets/Scripts/Fsr3UpscalerImageEffect.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Nico de Poel +// 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 @@ -22,6 +22,7 @@ using System; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; +using FidelityFX.FSR3; namespace FidelityFX { @@ -54,6 +55,10 @@ namespace FidelityFX [Tooltip("Optional 1x1 texture containing the exposure value for the current frame.")] public Texture exposure = null; + [Header("Debug")] + [Tooltip("Enable a debug view to analyze the upscaling process.")] + public bool enableDebugView = false; + [Header("Reactivity, Transparency & Composition")] [Tooltip("Optional texture to control the influence of the current frame on the reconstructed output. If unset, either an auto-generated or a default cleared reactive mask will be used.")] public Texture reactiveMask = null; @@ -326,12 +331,14 @@ namespace FidelityFX _dispatchDescription.MotionVectorScale.x = -scaledRenderSize.x; _dispatchDescription.MotionVectorScale.y = -scaledRenderSize.y; _dispatchDescription.RenderSize = scaledRenderSize; + _dispatchDescription.UpscaleSize = _displaySize; _dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime; _dispatchDescription.CameraNear = _renderCamera.nearClipPlane; _dispatchDescription.CameraFar = _renderCamera.farClipPlane; _dispatchDescription.CameraFovAngleVertical = _renderCamera.fieldOfView * Mathf.Deg2Rad; _dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit is 1 meter in Unity _dispatchDescription.Reset = _resetHistory; + _dispatchDescription.Flags = enableDebugView ? Fsr3Upscaler.DispatchFlags.DrawDebugView : 0; _resetHistory = false; // Set up the parameters for the optional experimental auto-TCR feature @@ -390,9 +397,6 @@ namespace FidelityFX _renderCamera.rect = _originalRect; _renderCamera.ResetProjectionMatrix(); - // Update the input resource descriptions - _dispatchDescription.InputResourceSize = new Vector2Int(src.width, src.height); - _dispatchCommandBuffer.Clear(); if (autoGenerateReactiveMask) diff --git a/Assets/Scripts/Fsr3UpscalerImageEffectHelper.cs b/Assets/Scripts/Fsr3UpscalerImageEffectHelper.cs index 785ab8c..87bc8c9 100644 --- a/Assets/Scripts/Fsr3UpscalerImageEffectHelper.cs +++ b/Assets/Scripts/Fsr3UpscalerImageEffectHelper.cs @@ -20,6 +20,7 @@ using System.Collections; using UnityEngine; +using FidelityFX.FSR3; namespace FidelityFX { diff --git a/Assets/Shaders.meta b/Assets/Shaders.meta deleted file mode 100644 index a1e9f1a..0000000 --- a/Assets/Shaders.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: f1c65c7377a34fe41beb57484d87a08b -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3.meta b/Assets/Shaders/FSR3.meta deleted file mode 100644 index ca8a4cc..0000000 --- a/Assets/Shaders/FSR3.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 661ffc6dbd7389b4da99fb9f749745de -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute deleted file mode 100644 index 5cbfb80..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -// Ensure the correct value is defined for this keyword, as it is used to select one of multiple sampler functions -#ifdef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#undef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 1 -#endif - -#include "shaders/ffx_fsr3upscaler_accumulate_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute deleted file mode 100644 index e13c001..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute deleted file mode 100644 index d5903c0..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -// Wave operations require shader model 6.0; this can only be enabled when using DXC on D3D12 -// These pragmas are commented out by default as Unity will sometimes ignore the #if's and try to enable these features anyway. -// Uncomment the below lines if you intend to try wave operations on DX12 with the DXC compiler. -//#if defined(UNITY_COMPILER_DXC) && defined(SHADER_API_D3D12) -//#pragma require WaveBasic // Required for WaveGetLaneIndex -//#pragma require WaveBallot // Required for WaveReadLaneAt -//#else -#define FFX_SPD_NO_WAVE_OPERATIONS -//#endif - -#include "shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute deleted file mode 100644 index 0ccd388..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute deleted file mode 100644 index e38ad99..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_lock_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute deleted file mode 100644 index be7bbb5..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_rcas_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute deleted file mode 100644 index ee2f276..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute b/Assets/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute deleted file mode 100644 index 6338918..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc b/Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc deleted file mode 100644 index 758bb0c..0000000 --- a/Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2023 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. - -// Suppress a few warnings produced by FFX's HLSL code -#pragma warning(disable: 3078) // Loop control variable conflicts -#pragma warning(disable: 3203) // Signed/unsigned mismatch - -#define FFX_GPU // Compiling for GPU -#define FFX_HLSL // Compile for plain HLSL - -// Use the DXC shader compiler on modern graphics APIs to enable a few advanced features -// The DXC-related pragmas are disabled by default, as DXC doesn't support all platforms yet and will break on some platforms when enabled. -// Consider this to be an experimental feature. If you want to benefit from 16-bit floating point and wave operations, and don't care about supporting older graphics APIs, then it's worth a try. -//#if defined(SHADER_API_D3D12) || defined(SHADER_API_VULKAN) || defined(SHADER_API_METAL) -//#pragma use_dxc // Using DXC will currently break DX11 support since DX11 and DX12 share the same shader bytecode in Unity. -//#endif - -// Enable half precision data types on platforms that support it -//#if defined(UNITY_COMPILER_DXC) && defined(FFX_HALF) -//#pragma require Native16Bit -//#endif - -// Hack to work around the lack of texture atomics on Metal -#if defined(SHADER_API_METAL) -#define InterlockedAdd(dest, val, orig) { (orig) = (dest); (dest) += (val); } -#define InterlockedMin(dest, val) { (dest) = min((dest), (val)); } -#define InterlockedMax(dest, val) { (dest) = max((dest), (val)); } -#endif - -// Workaround for HDRP using texture arrays for its camera buffers on some platforms -// The below defines are copied from: Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/TextureXR.hlsl -#if defined(UNITY_FSR3UPSCALER_HDRP) - // Must be in sync with C# with property useTexArray in TextureXR.cs - #if ((defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12)) && !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_GAMECORE)) || defined(SHADER_API_PSSL) || defined(SHADER_API_VULKAN) - #define UNITY_TEXTURE2D_X_ARRAY_SUPPORTED - #endif - - // Control if TEXTURE2D_X macros will expand to texture arrays - #if defined(UNITY_TEXTURE2D_X_ARRAY_SUPPORTED) && !defined(DISABLE_TEXTURE2D_X_ARRAY) - #define USE_TEXTURE2D_X_AS_ARRAY - #endif - - // Early defines for single-pass instancing - #if defined(STEREO_INSTANCING_ON) && defined(UNITY_TEXTURE2D_X_ARRAY_SUPPORTED) - #define UNITY_STEREO_INSTANCING_ENABLED - #endif - - // Helper macros to handle XR single-pass with Texture2DArray - #if defined(USE_TEXTURE2D_X_AS_ARRAY) - - // Only single-pass stereo instancing used array indexing - #if defined(UNITY_STEREO_INSTANCING_ENABLED) - #define SLICE_ARRAY_INDEX unity_StereoEyeIndex - #else - #define SLICE_ARRAY_INDEX 0 - #endif - - // Declare and sample camera buffers as texture arrays - #define UNITY_FSR3_TEX2D(type) Texture2DArray - #define UNITY_FSR3_RWTEX2D(type) RWTexture2DArray - #define UNITY_FSR3_POS(pxPos) FfxUInt32x3(pxPos, SLICE_ARRAY_INDEX) - #define UNITY_FSR3_UV(uv) FfxFloat32x3(uv, SLICE_ARRAY_INDEX) - - #endif -#endif diff --git a/Assets/Shaders/FSR3/shaders.meta b/Assets/Shaders/FSR3/shaders.meta deleted file mode 100644 index 8a4ff2b..0000000 --- a/Assets/Shaders/FSR3/shaders.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8364d4f86c613ec4d999d062f5f773b8 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl deleted file mode 100644 index d2f1b32..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 -#define FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS 1 -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 2 -#else -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 2 -#endif -#define FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED 3 -#define FSR3UPSCALER_BIND_SRV_LOCK_STATUS 4 -#define FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR 5 -#define FSR3UPSCALER_BIND_SRV_LANCZOS_LUT 6 -#define FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT 7 -#define FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS 8 -#define FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE 9 -#define FSR3UPSCALER_BIND_SRV_LUMA_HISTORY 10 - -#define FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED 0 -#define FSR3UPSCALER_BIND_UAV_LOCK_STATUS 1 -#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 2 -#define FSR3UPSCALER_BIND_UAV_NEW_LOCKS 3 -#define FSR3UPSCALER_BIND_UAV_LUMA_HISTORY 4 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_upsample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h" -#include "fsr3upscaler/ffx_fsr3upscaler_reproject.h" -#include "fsr3upscaler/ffx_fsr3upscaler_accumulate.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - const uint GroupRows = (uint(DisplaySize().y) + FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT - 1) / FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT; - uGroupId.y = GroupRows - uGroupId.y - 1; - - uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; - - Accumulate(uDispatchThreadId); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta deleted file mode 100644 index 80f209e..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 507ab779c38eddb429cdcedf9c108d1b -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl deleted file mode 100644 index 0d6e2eb..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl +++ /dev/null @@ -1,77 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY 0 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 1 - -#define FSR3UPSCALER_BIND_UAV_AUTOREACTIVE 0 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_REACTIVE 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; - - float3 ColorPreAlpha = LoadOpaqueOnly( FFX_MIN16_I2(uDispatchThreadId) ).rgb; - float3 ColorPostAlpha = LoadInputColor(uDispatchThreadId).rgb; - - if (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP) - { - ColorPreAlpha = Tonemap(ColorPreAlpha); - ColorPostAlpha = Tonemap(ColorPostAlpha); - } - - if (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP) - { - ColorPreAlpha = InverseTonemap(ColorPreAlpha); - ColorPostAlpha = InverseTonemap(ColorPostAlpha); - } - - float out_reactive_value = 0.f; - float3 delta = abs(ColorPostAlpha - ColorPreAlpha); - - out_reactive_value = (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX) ? max(delta.x, max(delta.y, delta.z)) : length(delta); - out_reactive_value *= GenReactiveScale(); - - out_reactive_value = (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD) ? (out_reactive_value < GenReactiveThreshold() ? 0 : GenReactiveBinaryValue()) : out_reactive_value; - - rw_output_autoreactive[uDispatchThreadId] = out_reactive_value; -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta deleted file mode 100644 index c55f004..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 52cdb7a7c30cb614984908593ed19082 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl deleted file mode 100644 index 93b7332..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 0 - -#define FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC 0 -#define FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE 1 -#define FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 2 -#define FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE 3 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_SPD 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 256 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT -void CS(uint3 WorkGroupId : SV_GroupID, uint LocalThreadIndex : SV_GroupIndex) -{ - ComputeAutoExposure(WorkGroupId, LocalThreadIndex); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta deleted file mode 100644 index 508b43e..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 2d149b52ba0f5bb468a94a71dbbcb66f -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl deleted file mode 100644 index 70cc7ba..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 -#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 1 -#define FSR3UPSCALER_BIND_SRV_DILATED_DEPTH 2 -#define FSR3UPSCALER_BIND_SRV_REACTIVE_MASK 3 -#define FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 4 -#define FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS 5 -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 6 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 7 -#define FSR3UPSCALER_BIND_SRV_INPUT_DEPTH 8 -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 9 - -#define FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS 0 -#define FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR 1 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_depth_clip.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS( - int2 iGroupId : SV_GroupID, - int2 iDispatchThreadId : SV_DispatchThreadID, - int2 iGroupThreadId : SV_GroupThreadID, - int iGroupIndex : SV_GroupIndex) -{ - DepthClip(iDispatchThreadId); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta deleted file mode 100644 index cde3a5e..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: da435b71cf57e2247b80ae0f0f86d1f8 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl deleted file mode 100644 index 26b28f0..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl +++ /dev/null @@ -1,56 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA 0 - -#define FSR3UPSCALER_BIND_UAV_NEW_LOCKS 0 -#define FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 1 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_lock.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; - - ComputeLock(uDispatchThreadId); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta deleted file mode 100644 index 45c99dc..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 98d2cbbda5e90dd4ebd1d70abbb63a09 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl deleted file mode 100644 index bebdeb3..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl +++ /dev/null @@ -1,53 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 -#define FSR3UPSCALER_BIND_SRV_RCAS_INPUT 1 - -#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 0 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_RCAS 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_rcas.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 64 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT -void CS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID) -{ - RCAS(LocalThreadId, WorkGroupId, Dtid); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta deleted file mode 100644 index fb9bfe2..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 9a15fc73170a9bc478801c8fa4d8d574 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl deleted file mode 100644 index f277fd1..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 0 -#define FSR3UPSCALER_BIND_SRV_INPUT_DEPTH 1 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 2 -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 3 - -#define FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 -#define FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS 1 -#define FSR3UPSCALER_BIND_UAV_DILATED_DEPTH 2 -#define FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA 3 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS( - int2 iGroupId : SV_GroupID, - int2 iDispatchThreadId : SV_DispatchThreadID, - int2 iGroupThreadId : SV_GroupThreadID, - int iGroupIndex : SV_GroupIndex -) -{ - ReconstructAndDilate(iDispatchThreadId); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta deleted file mode 100644 index 6489d6d..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: bafb3726a76b97a49bb343d8a4323754 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl deleted file mode 100644 index 6180885..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl +++ /dev/null @@ -1,90 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - -#define FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY 0 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 1 -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 2 -#define FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR 3 -#define FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR 4 -#define FSR3UPSCALER_BIND_SRV_REACTIVE_MASK 4 -#define FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 5 - -#define FSR3UPSCALER_BIND_UAV_AUTOREACTIVE 0 -#define FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION 1 -#define FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR 2 -#define FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR 3 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_AUTOREACTIVE 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - FFX_MIN16_I2 uDispatchThreadId = FFX_MIN16_I2(uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId); - - // ToDo: take into account jitter (i.e. add delta of previous jitter and current jitter to previous UV - // fetch pre- and post-alpha color values - FFX_MIN16_F2 fUv = ( FFX_MIN16_F2(uDispatchThreadId) + FFX_MIN16_F2(0.5f, 0.5f) ) / FFX_MIN16_F2( RenderSize() ); - FFX_MIN16_F2 fPrevUV = fUv + FFX_MIN16_F2( LoadInputMotionVector(uDispatchThreadId) ); - FFX_MIN16_I2 iPrevIdx = FFX_MIN16_I2(fPrevUV * FFX_MIN16_F2(RenderSize()) - 0.5f); - - FFX_MIN16_F3 colorPreAlpha = FFX_MIN16_F3( LoadOpaqueOnly( uDispatchThreadId ) ); - FFX_MIN16_F3 colorPostAlpha = FFX_MIN16_F3( LoadInputColor( uDispatchThreadId ) ); - - FFX_MIN16_F2 outReactiveMask = 0; - - outReactiveMask.y = ComputeTransparencyAndComposition(uDispatchThreadId, iPrevIdx); - - if (outReactiveMask.y > 0.5f) - { - outReactiveMask.x = ComputeReactive(uDispatchThreadId, iPrevIdx); - outReactiveMask.x *= FFX_MIN16_F(fReactiveScale); - outReactiveMask.x = outReactiveMask.x < fReactiveMax ? outReactiveMask.x : FFX_MIN16_F( fReactiveMax ); - } - - outReactiveMask.y *= FFX_MIN16_F(fTcScale ); - - outReactiveMask.x = max( outReactiveMask.x, FFX_MIN16_F( LoadReactiveMask(uDispatchThreadId) ) ); - outReactiveMask.y = max( outReactiveMask.y, FFX_MIN16_F( LoadTransparencyAndCompositionMask(uDispatchThreadId) ) ); - - StoreAutoReactive(uDispatchThreadId, outReactiveMask); - - StorePrevPreAlpha(uDispatchThreadId, colorPreAlpha); - StorePrevPostAlpha(uDispatchThreadId, colorPostAlpha); -} diff --git a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta b/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta deleted file mode 100644 index 02c5f46..0000000 --- a/Assets/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 712d171118b59fc4fb28d0d487060d42 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler.meta deleted file mode 100644 index 2626a2e..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ae9c6d015ae76544f9e8117e79ea862b -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h deleted file mode 100644 index f0b62ab..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h +++ /dev/null @@ -1,616 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_COMMON_TYPES_H -#define FFX_COMMON_TYPES_H - -#if defined(FFX_CPU) -#define FFX_PARAMETER_IN -#define FFX_PARAMETER_OUT -#define FFX_PARAMETER_INOUT -#define FFX_PARAMETER_UNIFORM -#elif defined(FFX_HLSL) -#define FFX_PARAMETER_IN in -#define FFX_PARAMETER_OUT out -#define FFX_PARAMETER_INOUT inout -#define FFX_PARAMETER_UNIFORM uniform -#elif defined(FFX_GLSL) -#define FFX_PARAMETER_IN in -#define FFX_PARAMETER_OUT out -#define FFX_PARAMETER_INOUT inout -#define FFX_PARAMETER_UNIFORM const //[cacao_placeholder] until a better fit is found! -#endif // #if defined(FFX_CPU) - -#if defined(FFX_CPU) -/// A typedef for a boolean value. -/// -/// @ingroup CPUTypes -typedef bool FfxBoolean; - -/// A typedef for a unsigned 8bit integer. -/// -/// @ingroup CPUTypes -typedef uint8_t FfxUInt8; - -/// A typedef for a unsigned 16bit integer. -/// -/// @ingroup CPUTypes -typedef uint16_t FfxUInt16; - -/// A typedef for a unsigned 32bit integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32; - -/// A typedef for a unsigned 64bit integer. -/// -/// @ingroup CPUTypes -typedef uint64_t FfxUInt64; - -/// A typedef for a signed 8bit integer. -/// -/// @ingroup CPUTypes -typedef int8_t FfxInt8; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup CPUTypes -typedef int16_t FfxInt16; - -/// A typedef for a signed 32bit integer. -/// -/// @ingroup CPUTypes -typedef int32_t FfxInt32; - -/// A typedef for a signed 64bit integer. -/// -/// @ingroup CPUTypes -typedef int64_t FfxInt64; - -/// A typedef for a floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32; - -/// A typedef for a 2-dimensional floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32x2[2]; - -/// A typedef for a 3-dimensional floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32x3[3]; - -/// A typedef for a 4-dimensional floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32x4[4]; - -/// A typedef for a 2-dimensional 32bit unsigned integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32x2[2]; - -/// A typedef for a 3-dimensional 32bit unsigned integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32x3[3]; - -/// A typedef for a 4-dimensional 32bit unsigned integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32x4[4]; -#endif // #if defined(FFX_CPU) - -#if defined(FFX_HLSL) - -#define FfxFloat32Mat4 matrix -#define FfxFloat32Mat3 matrix - -/// A typedef for a boolean value. -/// -/// @ingroup HLSLTypes -typedef bool FfxBoolean; - -#if FFX_HLSL_SM>=62 - -/// @defgroup HLSL62Types HLSL 6.2 And Above Types -/// HLSL 6.2 and above type defines for all commonly used variables -/// -/// @ingroup HLSLTypes - -/// A typedef for a floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t FfxFloat32; - -/// A typedef for a 2-dimensional floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t2 FfxFloat32x2; - -/// A typedef for a 3-dimensional floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t3 FfxFloat32x3; - -/// A typedef for a 4-dimensional floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t4 FfxFloat32x4; - -/// A [cacao_placeholder] typedef for matrix type until confirmed. -typedef float4x4 FfxFloat32x4x4; -typedef float3x3 FfxFloat32x3x3; -typedef float2x2 FfxFloat32x2x2; - -/// A typedef for a unsigned 32bit integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t FfxUInt32; - -/// A typedef for a 2-dimensional 32bit unsigned integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t2 FfxUInt32x2; - -/// A typedef for a 3-dimensional 32bit unsigned integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t3 FfxUInt32x3; - -/// A typedef for a 4-dimensional 32bit unsigned integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t4 FfxUInt32x4; - -/// A typedef for a signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t FfxInt32; - -/// A typedef for a 2-dimensional signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t2 FfxInt32x2; - -/// A typedef for a 3-dimensional signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t3 FfxInt32x3; - -/// A typedef for a 4-dimensional signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t4 FfxInt32x4; - -#else // #if FFX_HLSL_SM>=62 - -/// @defgroup HLSLBaseTypes HLSL 6.1 And Below Types -/// HLSL 6.1 and below type defines for all commonly used variables -/// -/// @ingroup HLSLTypes - -#define FfxFloat32 float -#define FfxFloat32x2 float2 -#define FfxFloat32x3 float3 -#define FfxFloat32x4 float4 - -/// A [cacao_placeholder] typedef for matrix type until confirmed. -#define FfxFloat32x4x4 float4x4 -#define FfxFloat32x3x3 float3x3 -#define FfxFloat32x2x2 float2x2 - -/// A typedef for a unsigned 32bit integer. -/// -/// @ingroup GPU -typedef uint FfxUInt32; -typedef uint2 FfxUInt32x2; -typedef uint3 FfxUInt32x3; -typedef uint4 FfxUInt32x4; - -typedef int FfxInt32; -typedef int2 FfxInt32x2; -typedef int3 FfxInt32x3; -typedef int4 FfxInt32x4; - -#endif // #if FFX_HLSL_SM>=62 - -#if FFX_HALF - -#if FFX_HLSL_SM >= 62 - -typedef float16_t FfxFloat16; -typedef float16_t2 FfxFloat16x2; -typedef float16_t3 FfxFloat16x3; -typedef float16_t4 FfxFloat16x4; - -/// A typedef for an unsigned 16bit integer. -/// -/// @ingroup HLSLTypes -typedef uint16_t FfxUInt16; -typedef uint16_t2 FfxUInt16x2; -typedef uint16_t3 FfxUInt16x3; -typedef uint16_t4 FfxUInt16x4; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup HLSLTypes -typedef int16_t FfxInt16; -typedef int16_t2 FfxInt16x2; -typedef int16_t3 FfxInt16x3; -typedef int16_t4 FfxInt16x4; -#elif SHADER_API_PSSL -#pragma argument(realtypes) // Enable true 16-bit types - -typedef half FfxFloat16; -typedef half2 FfxFloat16x2; -typedef half3 FfxFloat16x3; -typedef half4 FfxFloat16x4; - -/// A typedef for an unsigned 16bit integer. -/// -/// @ingroup GPU -typedef ushort FfxUInt16; -typedef ushort2 FfxUInt16x2; -typedef ushort3 FfxUInt16x3; -typedef ushort4 FfxUInt16x4; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup GPU -typedef short FfxInt16; -typedef short2 FfxInt16x2; -typedef short3 FfxInt16x3; -typedef short4 FfxInt16x4; -#else // #if FFX_HLSL_SM>=62 -typedef min16float FfxFloat16; -typedef min16float2 FfxFloat16x2; -typedef min16float3 FfxFloat16x3; -typedef min16float4 FfxFloat16x4; - -/// A typedef for an unsigned 16bit integer. -/// -/// @ingroup HLSLTypes -typedef min16uint FfxUInt16; -typedef min16uint2 FfxUInt16x2; -typedef min16uint3 FfxUInt16x3; -typedef min16uint4 FfxUInt16x4; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup HLSLTypes -typedef min16int FfxInt16; -typedef min16int2 FfxInt16x2; -typedef min16int3 FfxInt16x3; -typedef min16int4 FfxInt16x4; -#endif // #if FFX_HLSL_SM>=62 - -#endif // FFX_HALF - -#endif // #if defined(FFX_HLSL) - -#if defined(FFX_GLSL) - -#define FfxFloat32Mat4 mat4 -#define FfxFloat32Mat3 mat3 - -/// A typedef for a boolean value. -/// -/// @ingroup GLSLTypes -#define FfxBoolean bool -#define FfxFloat32 float -#define FfxFloat32x2 vec2 -#define FfxFloat32x3 vec3 -#define FfxFloat32x4 vec4 -#define FfxUInt32 uint -#define FfxUInt32x2 uvec2 -#define FfxUInt32x3 uvec3 -#define FfxUInt32x4 uvec4 -#define FfxInt32 int -#define FfxInt32x2 ivec2 -#define FfxInt32x3 ivec3 -#define FfxInt32x4 ivec4 - -/// A [cacao_placeholder] typedef for matrix type until confirmed. -#define FfxFloat32x4x4 mat4 -#define FfxFloat32x3x3 mat3 -#define FfxFloat32x2x2 mat2 - -#if FFX_HALF -#define FfxFloat16 float16_t -#define FfxFloat16x2 f16vec2 -#define FfxFloat16x3 f16vec3 -#define FfxFloat16x4 f16vec4 -#define FfxUInt16 uint16_t -#define FfxUInt16x2 u16vec2 -#define FfxUInt16x3 u16vec3 -#define FfxUInt16x4 u16vec4 -#define FfxInt16 int16_t -#define FfxInt16x2 i16vec2 -#define FfxInt16x3 i16vec3 -#define FfxInt16x4 i16vec4 -#endif // FFX_HALF -#endif // #if defined(FFX_GLSL) - -// Global toggles: -// #define FFX_HALF (1) -// #define FFX_HLSL_SM (62) - -#if FFX_HALF && !defined(SHADER_API_PSSL) - -#if FFX_HLSL_SM >= 62 - -#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType##16_t TypeName; -#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType##16_t TypeName; -#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#else //FFX_HLSL_SM>=62 - -#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef min16##BaseComponentType TypeName; -#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) FFX_MIN16_SCALAR( TypeName, BaseComponentType ); -#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ); -#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ); - -#endif //FFX_HLSL_SM>=62 - -#else //FFX_HALF - -#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType TypeName; -#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType TypeName; -#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#endif //FFX_HALF - -#if defined(FFX_GPU) -// Common typedefs: -#if defined(FFX_HLSL) && !defined(SHADER_API_PSSL) -FFX_MIN16_SCALAR( FFX_MIN16_F , float ); -FFX_MIN16_VECTOR( FFX_MIN16_F2, float, 2 ); -FFX_MIN16_VECTOR( FFX_MIN16_F3, float, 3 ); -FFX_MIN16_VECTOR( FFX_MIN16_F4, float, 4 ); - -FFX_MIN16_SCALAR( FFX_MIN16_I, int ); -FFX_MIN16_VECTOR( FFX_MIN16_I2, int, 2 ); -FFX_MIN16_VECTOR( FFX_MIN16_I3, int, 3 ); -FFX_MIN16_VECTOR( FFX_MIN16_I4, int, 4 ); - -FFX_MIN16_SCALAR( FFX_MIN16_U, uint ); -FFX_MIN16_VECTOR( FFX_MIN16_U2, uint, 2 ); -FFX_MIN16_VECTOR( FFX_MIN16_U3, uint, 3 ); -FFX_MIN16_VECTOR( FFX_MIN16_U4, uint, 4 ); - -FFX_16BIT_SCALAR( FFX_F16_t , float ); -FFX_16BIT_VECTOR( FFX_F16_t2, float, 2 ); -FFX_16BIT_VECTOR( FFX_F16_t3, float, 3 ); -FFX_16BIT_VECTOR( FFX_F16_t4, float, 4 ); - -FFX_16BIT_SCALAR( FFX_I16_t, int ); -FFX_16BIT_VECTOR( FFX_I16_t2, int, 2 ); -FFX_16BIT_VECTOR( FFX_I16_t3, int, 3 ); -FFX_16BIT_VECTOR( FFX_I16_t4, int, 4 ); - -FFX_16BIT_SCALAR( FFX_U16_t, uint ); -FFX_16BIT_VECTOR( FFX_U16_t2, uint, 2 ); -FFX_16BIT_VECTOR( FFX_U16_t3, uint, 3 ); -FFX_16BIT_VECTOR( FFX_U16_t4, uint, 4 ); - -#define TYPEDEF_MIN16_TYPES(Prefix) \ -typedef FFX_MIN16_F Prefix##_F; \ -typedef FFX_MIN16_F2 Prefix##_F2; \ -typedef FFX_MIN16_F3 Prefix##_F3; \ -typedef FFX_MIN16_F4 Prefix##_F4; \ -typedef FFX_MIN16_I Prefix##_I; \ -typedef FFX_MIN16_I2 Prefix##_I2; \ -typedef FFX_MIN16_I3 Prefix##_I3; \ -typedef FFX_MIN16_I4 Prefix##_I4; \ -typedef FFX_MIN16_U Prefix##_U; \ -typedef FFX_MIN16_U2 Prefix##_U2; \ -typedef FFX_MIN16_U3 Prefix##_U3; \ -typedef FFX_MIN16_U4 Prefix##_U4; - -#define TYPEDEF_16BIT_TYPES(Prefix) \ -typedef FFX_16BIT_F Prefix##_F; \ -typedef FFX_16BIT_F2 Prefix##_F2; \ -typedef FFX_16BIT_F3 Prefix##_F3; \ -typedef FFX_16BIT_F4 Prefix##_F4; \ -typedef FFX_16BIT_I Prefix##_I; \ -typedef FFX_16BIT_I2 Prefix##_I2; \ -typedef FFX_16BIT_I3 Prefix##_I3; \ -typedef FFX_16BIT_I4 Prefix##_I4; \ -typedef FFX_16BIT_U Prefix##_U; \ -typedef FFX_16BIT_U2 Prefix##_U2; \ -typedef FFX_16BIT_U3 Prefix##_U3; \ -typedef FFX_16BIT_U4 Prefix##_U4; - -#define TYPEDEF_FULL_PRECISION_TYPES(Prefix) \ -typedef FfxFloat32 Prefix##_F; \ -typedef FfxFloat32x2 Prefix##_F2; \ -typedef FfxFloat32x3 Prefix##_F3; \ -typedef FfxFloat32x4 Prefix##_F4; \ -typedef FfxInt32 Prefix##_I; \ -typedef FfxInt32x2 Prefix##_I2; \ -typedef FfxInt32x3 Prefix##_I3; \ -typedef FfxInt32x4 Prefix##_I4; \ -typedef FfxUInt32 Prefix##_U; \ -typedef FfxUInt32x2 Prefix##_U2; \ -typedef FfxUInt32x3 Prefix##_U3; \ -typedef FfxUInt32x4 Prefix##_U4; -#endif // #if defined(FFX_HLSL) - -#if defined(SHADER_API_PSSL) - -#define unorm -#define globallycoherent - -#if FFX_HALF - -#define FFX_MIN16_F half -#define FFX_MIN16_F2 half2 -#define FFX_MIN16_F3 half3 -#define FFX_MIN16_F4 half4 - -#define FFX_MIN16_I short -#define FFX_MIN16_I2 short2 -#define FFX_MIN16_I3 short3 -#define FFX_MIN16_I4 short4 - -#define FFX_MIN16_U ushort -#define FFX_MIN16_U2 ushort2 -#define FFX_MIN16_U3 ushort3 -#define FFX_MIN16_U4 ushort4 - -#define FFX_16BIT_F half -#define FFX_16BIT_F2 half2 -#define FFX_16BIT_F3 half3 -#define FFX_16BIT_F4 half4 - -#define FFX_16BIT_I short -#define FFX_16BIT_I2 short2 -#define FFX_16BIT_I3 short3 -#define FFX_16BIT_I4 short4 - -#define FFX_16BIT_U ushort -#define FFX_16BIT_U2 ushort2 -#define FFX_16BIT_U3 ushort3 -#define FFX_16BIT_U4 ushort4 - -#else // FFX_HALF - -#define FFX_MIN16_F float -#define FFX_MIN16_F2 float2 -#define FFX_MIN16_F3 float3 -#define FFX_MIN16_F4 float4 - -#define FFX_MIN16_I int -#define FFX_MIN16_I2 int2 -#define FFX_MIN16_I3 int3 -#define FFX_MIN16_I4 int4 - -#define FFX_MIN16_U uint -#define FFX_MIN16_U2 uint2 -#define FFX_MIN16_U3 uint3 -#define FFX_MIN16_U4 uint4 - -#define FFX_16BIT_F float -#define FFX_16BIT_F2 float2 -#define FFX_16BIT_F3 float3 -#define FFX_16BIT_F4 float4 - -#define FFX_16BIT_I int -#define FFX_16BIT_I2 int2 -#define FFX_16BIT_I3 int3 -#define FFX_16BIT_I4 int4 - -#define FFX_16BIT_U uint -#define FFX_16BIT_U2 uint2 -#define FFX_16BIT_U3 uint3 -#define FFX_16BIT_U4 uint4 - -#endif // FFX_HALF - -#endif // #if defined(SHADER_API_PSSL) - -#if defined(FFX_GLSL) - -#if FFX_HALF - -#define FFX_MIN16_F float16_t -#define FFX_MIN16_F2 f16vec2 -#define FFX_MIN16_F3 f16vec3 -#define FFX_MIN16_F4 f16vec4 - -#define FFX_MIN16_I int16_t -#define FFX_MIN16_I2 i16vec2 -#define FFX_MIN16_I3 i16vec3 -#define FFX_MIN16_I4 i16vec4 - -#define FFX_MIN16_U uint16_t -#define FFX_MIN16_U2 u16vec2 -#define FFX_MIN16_U3 u16vec3 -#define FFX_MIN16_U4 u16vec4 - -#define FFX_16BIT_F float16_t -#define FFX_16BIT_F2 f16vec2 -#define FFX_16BIT_F3 f16vec3 -#define FFX_16BIT_F4 f16vec4 - -#define FFX_16BIT_I int16_t -#define FFX_16BIT_I2 i16vec2 -#define FFX_16BIT_I3 i16vec3 -#define FFX_16BIT_I4 i16vec4 - -#define FFX_16BIT_U uint16_t -#define FFX_16BIT_U2 u16vec2 -#define FFX_16BIT_U3 u16vec3 -#define FFX_16BIT_U4 u16vec4 - -#else // FFX_HALF - -#define FFX_MIN16_F float -#define FFX_MIN16_F2 vec2 -#define FFX_MIN16_F3 vec3 -#define FFX_MIN16_F4 vec4 - -#define FFX_MIN16_I int -#define FFX_MIN16_I2 ivec2 -#define FFX_MIN16_I3 ivec3 -#define FFX_MIN16_I4 ivec4 - -#define FFX_MIN16_U uint -#define FFX_MIN16_U2 uvec2 -#define FFX_MIN16_U3 uvec3 -#define FFX_MIN16_U4 uvec4 - -#define FFX_16BIT_F float -#define FFX_16BIT_F2 vec2 -#define FFX_16BIT_F3 vec3 -#define FFX_16BIT_F4 vec4 - -#define FFX_16BIT_I int -#define FFX_16BIT_I2 ivec2 -#define FFX_16BIT_I3 ivec3 -#define FFX_16BIT_I4 ivec4 - -#define FFX_16BIT_U uint -#define FFX_16BIT_U2 uvec2 -#define FFX_16BIT_U3 uvec3 -#define FFX_16BIT_U4 uvec4 - -#endif // FFX_HALF - -#endif // #if defined(FFX_GLSL) - -#endif // #if defined(FFX_GPU) -#endif // #ifndef FFX_COMMON_TYPES_H diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta deleted file mode 100644 index bbd7f6a..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 7974b728d5c1b6d4a8a8e3965d03f96d -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h deleted file mode 100644 index 02f6b3f..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h +++ /dev/null @@ -1,80 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup FfxGPU GPU -/// The FidelityFX SDK GPU References -/// -/// @ingroup ffxSDK - -/// @defgroup FfxHLSL HLSL References -/// FidelityFX SDK HLSL GPU References -/// -/// @ingroup FfxGPU - -/// @defgroup FfxGLSL GLSL References -/// FidelityFX SDK GLSL GPU References -/// -/// @ingroup FfxGPU - -/// @defgroup FfxGPUEffects FidelityFX GPU References -/// FidelityFX Effect GPU Reference Documentation -/// -/// @ingroup FfxGPU - -/// @defgroup GPUCore GPU Core -/// GPU defines and functions -/// -/// @ingroup FfxGPU - -#if !defined(FFX_CORE_H) -#define FFX_CORE_H - -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic push -#pragma dxc diagnostic ignored "-Wambig-lit-shift" -#endif //__hlsl_dx_compiler - -#include "ffx_common_types.h" - -#if defined(FFX_CPU) - #include "ffx_core_cpu.h" -#endif // #if defined(FFX_CPU) - -#if defined(FFX_GLSL) && defined(FFX_GPU) - #include "ffx_core_glsl.h" -#endif // #if defined(FFX_GLSL) && defined(FFX_GPU) - -#if defined(FFX_HLSL) && defined(FFX_GPU) - #include "ffx_core_hlsl.h" -#endif // #if defined(FFX_HLSL) && defined(FFX_GPU) - -#if defined(FFX_GPU) - #include "ffx_core_gpu_common.h" - #include "ffx_core_gpu_common_half.h" - #include "ffx_core_portability.h" -#endif // #if defined(FFX_GPU) - -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic pop -#endif //__hlsl_dx_compiler - -#endif // #if !defined(FFX_CORE_H) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta deleted file mode 100644 index 8533462..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: b91c5f52b89ff554dacb51045a802ed8 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h deleted file mode 100644 index 865258d..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h +++ /dev/null @@ -1,338 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// A define for a true value in a boolean expression. -/// -/// @ingroup CPUTypes -#define FFX_TRUE (1) - -/// A define for a false value in a boolean expression. -/// -/// @ingroup CPUTypes -#define FFX_FALSE (0) - -#if !defined(FFX_STATIC) -/// A define to abstract declaration of static variables and functions. -/// -/// @ingroup CPUTypes -#define FFX_STATIC static -#endif // #if !defined(FFX_STATIC) - -/// @defgroup CPUCore CPU Core -/// Core CPU-side defines and functions -/// -/// @ingroup ffxHost - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wunused-variable" -#endif - -/// Interpret the bit layout of an IEEE-754 floating point value as an unsigned integer. -/// -/// @param [in] x A 32bit floating value. -/// -/// @returns -/// An unsigned 32bit integer value containing the bit pattern of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxUInt32 ffxAsUInt32(FfxFloat32 x) -{ - union - { - FfxFloat32 f; - FfxUInt32 u; - } bits; - - bits.f = x; - return bits.u; -} - -FFX_STATIC FfxFloat32 ffxDot2(FfxFloat32x2 a, FfxFloat32x2 b) -{ - return a[0] * b[0] + a[1] * b[1]; -} - -FFX_STATIC FfxFloat32 ffxDot3(FfxFloat32x3 a, FfxFloat32x3 b) -{ - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -FFX_STATIC FfxFloat32 ffxDot4(FfxFloat32x4 a, FfxFloat32x4 b) -{ - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the GLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxLerp(FfxFloat32 x, FfxFloat32 y, FfxFloat32 t) -{ - return y * t + (-x * t + x); -} - -/// Compute the reciprocal of a value. -/// -/// @param [in] x The value to compute the reciprocal for. -/// -/// @returns -/// The reciprocal value of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxReciprocal(FfxFloat32 x) -{ - return 1.0f / x; -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxSqrt(FfxFloat32 x) -{ - return sqrt(x); -} - -FFX_STATIC FfxUInt32 AShrSU1(FfxUInt32 a, FfxUInt32 b) -{ - return FfxUInt32(FfxInt32(a) >> FfxInt32(b)); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxFract(FfxFloat32 x) -{ - return x - floor(x); -} - -/// Compute the reciprocal square root of a value. -/// -/// @param [in] x The value to compute the reciprocal for. -/// -/// @returns -/// The reciprocal square root value of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 rsqrt(FfxFloat32 x) -{ - return ffxReciprocal(ffxSqrt(x)); -} - -FFX_STATIC FfxFloat32 ffxMin(FfxFloat32 x, FfxFloat32 y) -{ - return x < y ? x : y; -} - -FFX_STATIC FfxUInt32 ffxMin(FfxUInt32 x, FfxUInt32 y) -{ - return x < y ? x : y; -} - -FFX_STATIC FfxFloat32 ffxMax(FfxFloat32 x, FfxFloat32 y) -{ - return x > y ? x : y; -} - -FFX_STATIC FfxUInt32 ffxMax(FfxUInt32 x, FfxUInt32 y) -{ - return x > y ? x : y; -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxSaturate(FfxFloat32 x) -{ - return ffxMin(1.0f, ffxMax(0.0f, x)); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -FFX_STATIC void opAAddOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d[0] = a[0] + b; - d[1] = a[1] + b; - d[2] = a[2] + b; - return; -} - -FFX_STATIC void opACpyF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d[0] = a[0]; - d[1] = a[1]; - d[2] = a[2]; - return; -} - -FFX_STATIC void opAMulF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32x3 b) -{ - d[0] = a[0] * b[0]; - d[1] = a[1] * b[1]; - d[2] = a[2] * b[2]; - return; -} - -FFX_STATIC void opAMulOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d[0] = a[0] * b; - d[1] = a[1] * b; - d[2] = a[2] * b; - return; -} - -FFX_STATIC void opARcpF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d[0] = ffxReciprocal(a[0]); - d[1] = ffxReciprocal(a[1]); - d[2] = ffxReciprocal(a[2]); - return; -} - -/// Convert FfxFloat32 to half (in lower 16-bits of output). -/// -/// This function implements the same fast technique that is documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf -/// -/// The function supports denormals. -/// -/// Some conversion rules are to make computations possibly "safer" on the GPU, -/// -INF & -NaN -> -65504 -/// +INF & +NaN -> +65504 -/// -/// @param [in] f The 32bit floating point value to convert. -/// -/// @returns -/// The closest 16bit floating point value to f. -/// -/// @ingroup CPUCore -FFX_STATIC FfxUInt32 f32tof16(FfxFloat32 f) -{ - static FfxUInt16 base[512] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, - 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00, 0x5000, - 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, - 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, - 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff - }; - - static FfxUInt8 shift[512] = { - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 - }; - - union - { - FfxFloat32 f; - FfxUInt32 u; - } bits; - - bits.f = f; - FfxUInt32 u = bits.u; - FfxUInt32 i = u >> 23; - return (FfxUInt32)(base[i]) + ((u & 0x7fffff) >> shift[i]); -} - -/// Pack 2x32-bit floating point values in a single 32bit value. -/// -/// This function first converts each component of value into their nearest 16-bit floating -/// point representation, and then stores the X and Y components in the lower and upper 16 bits of the -/// 32bit unsigned integer respectively. -/// -/// @param [in] x A 2-dimensional floating point value to convert and pack. -/// -/// @returns -/// A packed 32bit value containing 2 16bit floating point values. -/// -/// @ingroup CPUCore -FFX_STATIC FfxUInt32 packHalf2x16(FfxFloat32x2 x) -{ - return f32tof16(x[0]) + (f32tof16(x[1]) << 16); -} diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta deleted file mode 100644 index f6508bc..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 4c88c0b7a4dec1e479272449c19ca981 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h deleted file mode 100644 index 2f687df..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h +++ /dev/null @@ -1,2784 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// A define for a true value in a boolean expression. -/// -/// @ingroup GPUCore -#define FFX_TRUE (true) - -/// A define for a false value in a boolean expression. -/// -/// @ingroup GPUCore -#define FFX_FALSE (false) - -/// A define value for positive infinity. -/// -/// @ingroup GPUCore -#define FFX_POSITIVE_INFINITY_FLOAT ffxAsFloat(0x7f800000u) - -/// A define value for negative infinity. -/// -/// @ingroup GPUCore -#define FFX_NEGATIVE_INFINITY_FLOAT ffxAsFloat(0xff800000u) - -/// A define value for PI. -/// -/// @ingroup GPUCore -#define FFX_PI (3.14159) - -FFX_STATIC const FfxFloat32 FFX_FP16_MIN = 6.10e-05f; -FFX_STATIC const FfxFloat32 FFX_FP16_MAX = 65504.0f; -FFX_STATIC const FfxFloat32 FFX_TONEMAP_EPSILON = 1.0f / FFX_FP16_MAX; - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32 ffxReciprocal(FfxFloat32 value) -{ - return rcp(value); -} - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxReciprocal(FfxFloat32x2 value) -{ - return rcp(value); -} - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxReciprocal(FfxFloat32x3 value) -{ - return rcp(value); -} - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxReciprocal(FfxFloat32x4 value) -{ - return rcp(value); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32 ffxMin(FfxFloat32 x, FfxFloat32 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxMin(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxMin(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxMin(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32 ffxMin(FfxInt32 x, FfxInt32 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x2 ffxMin(FfxInt32x2 x, FfxInt32x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x3 ffxMin(FfxInt32x3 x, FfxInt32x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x4 ffxMin(FfxInt32x4 x, FfxInt32x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32 ffxMin(FfxUInt32 x, FfxUInt32 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxMin(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxMin(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxMin(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return min(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32 ffxMax(FfxFloat32 x, FfxFloat32 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxMax(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxMax(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxMax(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32 ffxMax(FfxInt32 x, FfxInt32 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x2 ffxMax(FfxInt32x2 x, FfxInt32x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x3 ffxMax(FfxInt32x3 x, FfxInt32x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x4 ffxMax(FfxInt32x4 x, FfxInt32x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32 ffxMax(FfxUInt32 x, FfxUInt32 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxMax(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxMax(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxMax(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return max(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32 ffxPow(FfxFloat32 x, FfxFloat32 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxPow(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxPow(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxPow(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return pow(x, y); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32 ffxSqrt(FfxFloat32 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxSqrt(FfxFloat32x2 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxSqrt(FfxFloat32x3 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxSqrt(FfxFloat32x4 x) -{ - return sqrt(x); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32 ffxCopySignBit(FfxFloat32 d, FfxFloat32 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & FfxUInt32(0x80000000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxCopySignBit(FfxFloat32x2 d, FfxFloat32x2 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast2(0x80000000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxCopySignBit(FfxFloat32x3 d, FfxFloat32x3 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast3(0x80000000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxCopySignBit(FfxFloat32x4 d, FfxFloat32x4 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast4(0x80000000u))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat32 ffxIsSigned(FfxFloat32 m) -{ - return ffxSaturate(m * FfxFloat32(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxIsSigned(FfxFloat32x2 m) -{ - return ffxSaturate(m * ffxBroadcast2(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxIsSigned(FfxFloat32x3 m) -{ - return ffxSaturate(m * ffxBroadcast3(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against for have the sign set. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or positive. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxIsSigned(FfxFloat32x4 m) -{ - return ffxSaturate(m * ffxBroadcast4(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32 ffxIsGreaterThanZero(FfxFloat32 m) -{ - return ffxSaturate(m * FfxFloat32(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxIsGreaterThanZero(FfxFloat32x2 m) -{ - return ffxSaturate(m * ffxBroadcast2(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxIsGreaterThanZero(FfxFloat32x3 m) -{ - return ffxSaturate(m * ffxBroadcast3(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxIsGreaterThanZero(FfxFloat32x4 m) -{ - return ffxSaturate(m * ffxBroadcast4(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Convert a 32bit floating point value to sortable integer. -/// -/// - If sign bit=0, flip the sign bit (positives). -/// - If sign bit=1, flip all bits (negatives). -/// -/// The function has the side effects that: -/// - Larger integers are more positive values. -/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). -/// -/// @param [in] value The floating point value to make sortable. -/// -/// @returns -/// The sortable integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxFloatToSortableInteger(FfxUInt32 value) -{ - return value ^ ((AShrSU1(value, FfxUInt32(31))) | FfxUInt32(0x80000000)); -} - -/// Convert a sortable integer to a 32bit floating point value. -/// -/// The function has the side effects that: -/// - If sign bit=1, flip the sign bit (positives). -/// - If sign bit=0, flip all bits (negatives). -/// -/// @param [in] value The floating point value to make sortable. -/// -/// @returns -/// The sortable integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxSortableIntegerToFloat(FfxUInt32 value) -{ - return value ^ ((~AShrSU1(value, FfxUInt32(31))) | FfxUInt32(0x80000000)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateSqrt(FfxFloat32 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> FfxUInt32(1)) + FfxUInt32(0x1fbc4639)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateReciprocal(FfxFloat32 value) -{ - return ffxAsFloat(FfxUInt32(0x7ef07ebb) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateReciprocalMedium(FfxFloat32 value) -{ - FfxFloat32 b = ffxAsFloat(FfxUInt32(0x7ef19fff) - ffxAsUInt32(value)); - return b * (-b * value + FfxFloat32(2.0)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal square root for. -/// -/// @returns -/// An approximation of the reciprocal square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateReciprocalSquareRoot(FfxFloat32 value) -{ - return ffxAsFloat(FfxUInt32(0x5f347d74) - (ffxAsUInt32(value) >> FfxUInt32(1))); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateSqrt(FfxFloat32x2 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast2(1u)) + ffxBroadcast2(0x1fbc4639u)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateReciprocal(FfxFloat32x2 value) -{ - return ffxAsFloat(ffxBroadcast2(0x7ef07ebbu) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateReciprocalMedium(FfxFloat32x2 value) -{ - FfxFloat32x2 b = ffxAsFloat(ffxBroadcast2(0x7ef19fffu) - ffxAsUInt32(value)); - return b * (-b * value + ffxBroadcast2(2.0f)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateReciprocalSquareRoot(FfxFloat32x2 value) -{ - return ffxAsFloat(ffxBroadcast2(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast2(1u))); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateSqrt(FfxFloat32x3 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast3(1u)) + ffxBroadcast3(0x1fbc4639u)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateReciprocal(FfxFloat32x3 value) -{ - return ffxAsFloat(ffxBroadcast3(0x7ef07ebbu) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateReciprocalMedium(FfxFloat32x3 value) -{ - FfxFloat32x3 b = ffxAsFloat(ffxBroadcast3(0x7ef19fffu) - ffxAsUInt32(value)); - return b * (-b * value + ffxBroadcast3(2.0f)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateReciprocalSquareRoot(FfxFloat32x3 value) -{ - return ffxAsFloat(ffxBroadcast3(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast3(1u))); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateSqrt(FfxFloat32x4 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast4(1u)) + ffxBroadcast4(0x1fbc4639u)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateReciprocal(FfxFloat32x4 value) -{ - return ffxAsFloat(ffxBroadcast4(0x7ef07ebbu) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateReciprocalMedium(FfxFloat32x4 value) -{ - FfxFloat32x4 b = ffxAsFloat(ffxBroadcast4(0x7ef19fffu) - ffxAsUInt32(value)); - return b * (-b * value + ffxBroadcast4(2.0f)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateReciprocalSquareRoot(FfxFloat32x4 value) -{ - return ffxAsFloat(ffxBroadcast4(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast4(1u))); -} - -/// Calculate dot product of 'a' and 'b'. -/// -/// @param [in] a First vector input. -/// @param [in] b Second vector input. -/// -/// @returns -/// The value of a dot b. -/// -/// @ingroup GPUCore -FfxFloat32 ffxDot2(FfxFloat32x2 a, FfxFloat32x2 b) -{ - return dot(a, b); -} - -/// Calculate dot product of 'a' and 'b'. -/// -/// @param [in] a First vector input. -/// @param [in] b Second vector input. -/// -/// @returns -/// The value of a dot b. -/// -/// @ingroup GPUCore -FfxFloat32 ffxDot3(FfxFloat32x3 a, FfxFloat32x3 b) -{ - return dot(a, b); -} - -/// Calculate dot product of 'a' and 'b'. -/// -/// @param [in] a First vector input. -/// @param [in] b Second vector input. -/// -/// @returns -/// The value of a dot b. -/// -/// @ingroup GPUCore -FfxFloat32 ffxDot4(FfxFloat32x4 a, FfxFloat32x4 b) -{ - return dot(a, b); -} - - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximatePQToGamma2Medium(FfxFloat32 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximatePQToLinear(FfxFloat32 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateGamma2ToPQ(FfxFloat32 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(2)) + FfxUInt32(0x2F9A4E46)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateGamma2ToPQMedium(FfxFloat32 a) -{ - FfxFloat32 b = ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(2)) + FfxUInt32(0x2F9A4E46)); - FfxFloat32 b4 = b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateGamma2ToPQHigh(FfxFloat32 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateLinearToPQ(FfxFloat32 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(3)) + FfxUInt32(0x378D8723)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateLinearToPQMedium(FfxFloat32 a) -{ - FfxFloat32 b = ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(3)) + FfxUInt32(0x378D8723)); - FfxFloat32 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateLinearToPQHigh(FfxFloat32 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximatePQToGamma2Medium(FfxFloat32x2 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximatePQToLinear(FfxFloat32x2 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateGamma2ToPQ(FfxFloat32x2 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(2u)) + ffxBroadcast2(0x2F9A4E46u)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateGamma2ToPQMedium(FfxFloat32x2 a) -{ - FfxFloat32x2 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(2u)) + ffxBroadcast2(0x2F9A4E46u)); - FfxFloat32x2 b4 = b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateGamma2ToPQHigh(FfxFloat32x2 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateLinearToPQ(FfxFloat32x2 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(3u)) + ffxBroadcast2(0x378D8723u)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateLinearToPQMedium(FfxFloat32x2 a) -{ - FfxFloat32x2 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(3u)) + ffxBroadcast2(0x378D8723u)); - FfxFloat32x2 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateLinearToPQHigh(FfxFloat32x2 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximatePQToGamma2Medium(FfxFloat32x3 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximatePQToLinear(FfxFloat32x3 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateGamma2ToPQ(FfxFloat32x3 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(2u)) + ffxBroadcast3(0x2F9A4E46u)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateGamma2ToPQMedium(FfxFloat32x3 a) -{ - FfxFloat32x3 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(2u)) + ffxBroadcast3(0x2F9A4E46u)); - FfxFloat32x3 b4 = b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateGamma2ToPQHigh(FfxFloat32x3 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateLinearToPQ(FfxFloat32x3 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(3u)) + ffxBroadcast3(0x378D8723u)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateLinearToPQMedium(FfxFloat32x3 a) -{ - FfxFloat32x3 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(3u)) + ffxBroadcast3(0x378D8723u)); - FfxFloat32x3 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateLinearToPQHigh(FfxFloat32x3 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximatePQToGamma2Medium(FfxFloat32x4 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximatePQToLinear(FfxFloat32x4 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateGamma2ToPQ(FfxFloat32x4 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(2u)) + ffxBroadcast4(0x2F9A4E46u)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateGamma2ToPQMedium(FfxFloat32x4 a) -{ - FfxFloat32x4 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(2u)) + ffxBroadcast4(0x2F9A4E46u)); - FfxFloat32x4 b4 = b * b * b * b * b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateGamma2ToPQHigh(FfxFloat32x4 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateLinearToPQ(FfxFloat32x4 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(3u)) + ffxBroadcast4(0x378D8723u)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateLinearToPQMedium(FfxFloat32x4 a) -{ - FfxFloat32x4 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(3u)) + ffxBroadcast4(0x378D8723u)); - FfxFloat32x4 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateLinearToPQHigh(FfxFloat32x4 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -// An approximation of sine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate sine for. -// -// @returns -// The approximate sine of value. -FfxFloat32 ffxParabolicSin(FfxFloat32 value) -{ - return value * abs(value) - value; -} - -// An approximation of sine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate sine for. -// -// @returns -// The approximate sine of value. -FfxFloat32x2 ffxParabolicSin(FfxFloat32x2 x) -{ - return x * abs(x) - x; -} - -// An approximation of cosine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate cosine for. -// -// @returns -// The approximate cosine of value. -FfxFloat32 ffxParabolicCos(FfxFloat32 x) -{ - x = ffxFract(x * FfxFloat32(0.5) + FfxFloat32(0.75)); - x = x * FfxFloat32(2.0) - FfxFloat32(1.0); - return ffxParabolicSin(x); -} - -// An approximation of cosine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate cosine for. -// -// @returns -// The approximate cosine of value. -FfxFloat32x2 ffxParabolicCos(FfxFloat32x2 x) -{ - x = ffxFract(x * ffxBroadcast2(0.5f) + ffxBroadcast2(0.75f)); - x = x * ffxBroadcast2(2.0f) - ffxBroadcast2(1.0f); - return ffxParabolicSin(x); -} - -// An approximation of both sine and cosine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate cosine for. -// -// @returns -// A FfxFloat32x2 containing approximations of both sine and cosine of value. -FfxFloat32x2 ffxParabolicSinCos(FfxFloat32 x) -{ - FfxFloat32 y = ffxFract(x * FfxFloat32(0.5) + FfxFloat32(0.75)); - y = y * FfxFloat32(2.0) - FfxFloat32(1.0); - return ffxParabolicSin(FfxFloat32x2(x, y)); -} - -/// Conditional free logic AND operation using values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneAnd(FfxUInt32 x, FfxUInt32 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneAnd(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneAnd(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneAnd(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return min(x, y); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneAnd(FfxUInt32 x) -{ - return x ^ FfxUInt32(1); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneAnd(FfxUInt32x2 x) -{ - return x ^ ffxBroadcast2(1u); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneAnd(FfxUInt32x3 x) -{ - return x ^ ffxBroadcast3(1u); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneAnd(FfxUInt32x4 x) -{ - return x ^ ffxBroadcast4(1u); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneOr(FfxUInt32 x, FfxUInt32 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneOr(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneOr(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneOr(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return max(x, y); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneAndToU1(FfxFloat32 x) -{ - return FfxUInt32(FfxFloat32(1.0) - x); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneAndToU2(FfxFloat32x2 x) -{ - return FfxUInt32x2(ffxBroadcast2(1.0) - x); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneAndToU3(FfxFloat32x3 x) -{ - return FfxUInt32x3(ffxBroadcast3(1.0) - x); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneAndToU4(FfxFloat32x4 x) -{ - return FfxUInt32x4(ffxBroadcast4(1.0) - x); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneAndOr(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return ffxSaturate(x * y + z); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneAndOr(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return ffxSaturate(x * y + z); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneAndOr(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return ffxSaturate(x * y + z); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneAndOr(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return ffxSaturate(x * y + z); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneIsGreaterThanZero(FfxFloat32 x) -{ - return ffxSaturate(x * FfxFloat32(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneIsGreaterThanZero(FfxFloat32x2 x) -{ - return ffxSaturate(x * ffxBroadcast2(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneIsGreaterThanZero(FfxFloat32x3 x) -{ - return ffxSaturate(x * ffxBroadcast3(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneIsGreaterThanZero(FfxFloat32x4 x) -{ - return ffxSaturate(x * ffxBroadcast4(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneAnd(FfxFloat32 x) -{ - return FfxFloat32(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneAnd(FfxFloat32x2 x) -{ - return ffxBroadcast2(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneAnd(FfxFloat32x3 x) -{ - return ffxBroadcast3(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneAnd(FfxFloat32x4 x) -{ - return ffxBroadcast4(1.0) - x; -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneOr(FfxFloat32 x, FfxFloat32 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneOr(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneOr(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneOr(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return max(x, y); -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneSelect(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - FfxFloat32 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneSelect(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - FfxFloat32x2 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneSelect(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - FfxFloat32x3 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneSelect(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - FfxFloat32x4 r = (-x) * z + z; - return x * y + r; -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneIsSigned(FfxFloat32 x) -{ - return ffxSaturate(x * FfxFloat32(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneIsSigned(FfxFloat32x2 x) -{ - return ffxSaturate(x * ffxBroadcast2(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneIsSigned(FfxFloat32x3 x) -{ - return ffxSaturate(x * ffxBroadcast3(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneIsSigned(FfxFloat32x4 x) -{ - return ffxSaturate(x * ffxBroadcast4(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] color The color to convert to Rec. 709. -/// -/// @returns -/// The color in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxRec709FromLinear(FfxFloat32 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); - return clamp(j.x, color * j.y, pow(color, j.z) * k.x + k.y); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] color The color to convert to Rec. 709. -/// -/// @returns -/// The color in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxRec709FromLinear(FfxFloat32x2 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); - return clamp(j.xx, color * j.yy, pow(color, j.zz) * k.xx + k.yy); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] color The color to convert to Rec. 709. -/// -/// @returns -/// The color in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxRec709FromLinear(FfxFloat32x3 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); - return clamp(j.xxx, color * j.yyy, pow(color, j.zzz) * k.xxx + k.yyy); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. -/// -/// @param [in] value The value to convert to gamma space from linear. -/// @param [in] power The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxGammaFromLinear(FfxFloat32 value, FfxFloat32 power) -{ - return pow(value, FfxFloat32(power)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. -/// -/// @param [in] value The value to convert to gamma space from linear. -/// @param [in] power The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxGammaFromLinear(FfxFloat32x2 value, FfxFloat32 power) -{ - return pow(value, ffxBroadcast2(power)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. -/// -/// @param [in] value The value to convert to gamma space from linear. -/// @param [in] power The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxGammaFromLinear(FfxFloat32x3 value, FfxFloat32 power) -{ - return pow(value, ffxBroadcast3(power)); -} - -/// Compute a PQ value from a linear value. -/// -/// @param [in] value The value to convert to PQ from linear. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxPQToLinear(FfxFloat32 value) -{ - FfxFloat32 p = pow(value, FfxFloat32(0.159302)); - return pow((FfxFloat32(0.835938) + FfxFloat32(18.8516) * p) / (FfxFloat32(1.0) + FfxFloat32(18.6875) * p), FfxFloat32(78.8438)); -} - -/// Compute a PQ value from a linear value. -/// -/// @param [in] value The value to convert to PQ from linear. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxPQToLinear(FfxFloat32x2 value) -{ - FfxFloat32x2 p = pow(value, ffxBroadcast2(0.159302)); - return pow((ffxBroadcast2(0.835938) + ffxBroadcast2(18.8516) * p) / (ffxBroadcast2(1.0) + ffxBroadcast2(18.6875) * p), ffxBroadcast2(78.8438)); -} - -/// Compute a PQ value from a linear value. -/// -/// @param [in] value The value to convert to PQ from linear. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxPQToLinear(FfxFloat32x3 value) -{ - FfxFloat32x3 p = pow(value, ffxBroadcast3(0.159302)); - return pow((ffxBroadcast3(0.835938) + ffxBroadcast3(18.8516) * p) / (ffxBroadcast3(1.0) + ffxBroadcast3(18.6875) * p), ffxBroadcast3(78.8438)); -} - -/// Compute a linear value from a SRGB value. -/// -/// @param [in] value The value to convert to linear from SRGB. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxSrgbToLinear(FfxFloat32 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); - return clamp(j.x, value * j.y, pow(value, j.z) * k.x + k.y); -} - -/// Compute a linear value from a SRGB value. -/// -/// @param [in] value The value to convert to linear from SRGB. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxSrgbToLinear(FfxFloat32x2 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); - return clamp(j.xx, value * j.yy, pow(value, j.zz) * k.xx + k.yy); -} - -/// Compute a linear value from a SRGB value. -/// -/// @param [in] value The value to convert to linear from SRGB. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxSrgbToLinear(FfxFloat32x3 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); - return clamp(j.xxx, value * j.yyy, pow(value, j.zzz) * k.xxx + k.yyy); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] color The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromRec709(FfxFloat32 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.x), color * j.y, pow(color * k.x + k.y, j.z)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] color The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromRec709(FfxFloat32x2 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.xx), color * j.yy, pow(color * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] color The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromRec709(FfxFloat32x3 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.xxx), color * j.yyy, pow(color * k.xxx + k.yyy, j.zzz)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] color The value to convert to linear in gamma space. -/// @param [in] power The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromGamma(FfxFloat32 color, FfxFloat32 power) -{ - return pow(color, FfxFloat32(power)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] color The value to convert to linear in gamma space. -/// @param [in] power The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromGamma(FfxFloat32x2 color, FfxFloat32 power) -{ - return pow(color, ffxBroadcast2(power)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] color The value to convert to linear in gamma space. -/// @param [in] power The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromGamma(FfxFloat32x3 color, FfxFloat32 power) -{ - return pow(color, ffxBroadcast3(power)); -} - -/// Compute a linear value from a value in a PQ space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in PQ space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromPQ(FfxFloat32 value) -{ - FfxFloat32 p = pow(value, FfxFloat32(0.0126833)); - return pow(ffxSaturate(p - FfxFloat32(0.835938)) / (FfxFloat32(18.8516) - FfxFloat32(18.6875) * p), FfxFloat32(6.27739)); -} - -/// Compute a linear value from a value in a PQ space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in PQ space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromPQ(FfxFloat32x2 value) -{ - FfxFloat32x2 p = pow(value, ffxBroadcast2(0.0126833)); - return pow(ffxSaturate(p - ffxBroadcast2(0.835938)) / (ffxBroadcast2(18.8516) - ffxBroadcast2(18.6875) * p), ffxBroadcast2(6.27739)); -} - -/// Compute a linear value from a value in a PQ space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in PQ space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromPQ(FfxFloat32x3 value) -{ - FfxFloat32x3 p = pow(value, ffxBroadcast3(0.0126833)); - return pow(ffxSaturate(p - ffxBroadcast3(0.835938)) / (ffxBroadcast3(18.8516) - ffxBroadcast3(18.6875) * p), ffxBroadcast3(6.27739)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromSrgb(FfxFloat32 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.x), value * j.y, pow(value * k.x + k.y, j.z)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromSrgb(FfxFloat32x2 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.xx), value * j.yy, pow(value * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromSrgb(FfxFloat32x3 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.xxx), value * j.yyy, pow(value * k.xxx + k.yyy, j.zzz)); -} - -/// A remapping of 64x1 to 8x8 imposing rotated 2x2 pixel quads in quad linear. -/// -/// 543210 -/// ====== -/// ..xxx. -/// yy...y -/// -/// @param [in] a The input 1D coordinates to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxRemapForQuad(FfxUInt32 a) -{ - return FfxUInt32x2(bitfieldExtract(a, 1u, 3u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), a, 1u)); -} - -/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. -/// -/// The 64-wide lane indices to 8x8 remapping is performed as follows: -/// -/// 00 01 08 09 10 11 18 19 -/// 02 03 0a 0b 12 13 1a 1b -/// 04 05 0c 0d 14 15 1c 1d -/// 06 07 0e 0f 16 17 1e 1f -/// 20 21 28 29 30 31 38 39 -/// 22 23 2a 2b 32 33 3a 3b -/// 24 25 2c 2d 34 35 3c 3d -/// 26 27 2e 2f 36 37 3e 3f -/// -/// @param [in] a The input 1D coordinate to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxRemapForWaveReduction(FfxUInt32 a) -{ - return FfxUInt32x2(bitfieldInsertMask(bitfieldExtract(a, 2u, 3u), a, 1u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), bitfieldExtract(a, 1u, 2u), 2u)); -} diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta deleted file mode 100644 index 14292a9..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 05b921699d1374a429e32afca13137e2 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h deleted file mode 100644 index 4c73daf..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h +++ /dev/null @@ -1,2979 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#if FFX_HALF -#if FFX_HLSL_SM >= 62 -/// A define value for 16bit positive infinity. -/// -/// @ingroup GPUCore -#define FFX_POSITIVE_INFINITY_HALF FFX_TO_FLOAT16((uint16_t)0x7c00u) - -/// A define value for 16bit negative infinity. -/// -/// @ingroup GPUCore -#define FFX_NEGATIVE_INFINITY_HALF FFX_TO_FLOAT16((uint16_t)0xfc00u) -#else -/// A define value for 16bit positive infinity. -/// -/// @ingroup GPUCore -#define FFX_POSITIVE_INFINITY_HALF FFX_TO_FLOAT16(0x7c00u) - -/// A define value for 16bit negative infinity. -/// -/// @ingroup GPUCore -#define FFX_NEGATIVE_INFINITY_HALF FFX_TO_FLOAT16(0xfc00u) -#endif // #if FFX_HLSL_SM>=62 - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16 ffxMin(FfxFloat16 x, FfxFloat16 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxMin(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxMin(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxMin(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16 ffxMin(FfxInt16 x, FfxInt16 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x2 ffxMin(FfxInt16x2 x, FfxInt16x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x3 ffxMin(FfxInt16x3 x, FfxInt16x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x4 ffxMin(FfxInt16x4 x, FfxInt16x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16 ffxMin(FfxUInt16 x, FfxUInt16 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxMin(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxMin(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxMin(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return min(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16 ffxMax(FfxFloat16 x, FfxFloat16 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxMax(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxMax(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxMax(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16 ffxMax(FfxInt16 x, FfxInt16 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x2 ffxMax(FfxInt16x2 x, FfxInt16x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x3 ffxMax(FfxInt16x3 x, FfxInt16x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x4 ffxMax(FfxInt16x4 x, FfxInt16x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16 ffxMax(FfxUInt16 x, FfxUInt16 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxMax(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxMax(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxMax(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return max(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16 ffxPow(FfxFloat16 x, FfxFloat16 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPow(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxPow(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxPow(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return pow(x, y); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSqrt(FfxFloat16 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSqrt(FfxFloat16x2 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSqrt(FfxFloat16x3 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxSqrt(FfxFloat16x4 x) -{ - return sqrt(x); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16 ffxCopySignBitHalf(FfxFloat16 d, FfxFloat16 s) -{ - return FFX_TO_FLOAT16(FFX_TO_UINT16(d) | (FFX_TO_UINT16(s) & FFX_BROADCAST_UINT16(0x8000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxCopySignBitHalf(FfxFloat16x2 d, FfxFloat16x2 s) -{ - return FFX_TO_FLOAT16X2(FFX_TO_UINT16X2(d) | (FFX_TO_UINT16X2(s) & FFX_BROADCAST_UINT16X2(0x8000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxCopySignBitHalf(FfxFloat16x3 d, FfxFloat16x3 s) -{ - return FFX_TO_FLOAT16X3(FFX_TO_UINT16X3(d) | (FFX_TO_UINT16X3(s) & FFX_BROADCAST_UINT16X3(0x8000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxCopySignBitHalf(FfxFloat16x4 d, FfxFloat16x4 s) -{ - return FFX_TO_FLOAT16X4(FFX_TO_UINT16X4(d) | (FFX_TO_UINT16X4(s) & FFX_BROADCAST_UINT16X4(0x8000u))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16 ffxIsSignedHalf(FfxFloat16 m) -{ - return FfxFloat16(ffxSaturate(m * FFX_BROADCAST_FLOAT16(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxIsSignedHalf(FfxFloat16x2 m) -{ - return FfxFloat16x2(ffxSaturate(m * FFX_BROADCAST_FLOAT16X2(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxIsSignedHalf(FfxFloat16x3 m) -{ - return FfxFloat16x3(ffxSaturate(m * FFX_BROADCAST_FLOAT16X3(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxIsSignedHalf(FfxFloat16x4 m) -{ - return FfxFloat16x4(ffxSaturate(m * FFX_BROADCAST_FLOAT16X4(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16 ffxIsGreaterThanZeroHalf(FfxFloat16 m) -{ - return FfxFloat16(ffxSaturate(m * FFX_BROADCAST_FLOAT16(FFX_POSITIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxIsGreaterThanZeroHalf(FfxFloat16x2 m) -{ - return FfxFloat16x2(ffxSaturate(m * FFX_BROADCAST_FLOAT16X2(FFX_POSITIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxIsGreaterThanZeroHalf(FfxFloat16x3 m) -{ - return FfxFloat16x3(ffxSaturate(m * FFX_BROADCAST_FLOAT16X3(FFX_POSITIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxIsGreaterThanZeroHalf(FfxFloat16x4 m) -{ - return FfxFloat16x4(ffxSaturate(m * FFX_BROADCAST_FLOAT16X4(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Convert a 16bit floating point value to sortable integer. -/// -/// - If sign bit=0, flip the sign bit (positives). -/// - If sign bit=1, flip all bits (negatives). -/// -/// The function has the side effects that: -/// - Larger integers are more positive values. -/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). -/// -/// @param [in] x The floating point value to make sortable. -/// -/// @returns -/// The sortable integer value. -/// -/// @ingroup GPUCore -FfxUInt16 ffxFloatToSortableIntegerHalf(FfxUInt16 x) -{ - return x ^ ((ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16(15))) | FFX_BROADCAST_UINT16(0x8000)); -} - -/// Convert a sortable integer to a 16bit floating point value. -/// -/// The function has the side effects that: -/// - If sign bit=1, flip the sign bit (positives). -/// - If sign bit=0, flip all bits (negatives). -/// -/// @param [in] x The sortable integer value to make floating point. -/// -/// @returns -/// The floating point value. -/// -/// @ingroup GPUCore -FfxUInt16 ffxSortableIntegerToFloatHalf(FfxUInt16 x) -{ - return x ^ ((~ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16(15))) | FFX_BROADCAST_UINT16(0x8000)); -} - -/// Convert a pair of 16bit floating point values to a pair of sortable integers. -/// -/// - If sign bit=0, flip the sign bit (positives). -/// - If sign bit=1, flip all bits (negatives). -/// -/// The function has the side effects that: -/// - Larger integers are more positive values. -/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). -/// -/// @param [in] x The floating point values to make sortable. -/// -/// @returns -/// The sortable integer values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxFloatToSortableIntegerHalf(FfxUInt16x2 x) -{ - return x ^ ((ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16X2(15))) | FFX_BROADCAST_UINT16X2(0x8000)); -} - -/// Convert a pair of sortable integers to a pair of 16bit floating point values. -/// -/// The function has the side effects that: -/// - If sign bit=1, flip the sign bit (positives). -/// - If sign bit=0, flip all bits (negatives). -/// -/// @param [in] x The sortable integer values to make floating point. -/// -/// @returns -/// The floating point values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxSortableIntegerToFloatHalf(FfxUInt16x2 x) -{ - return x ^ ((~ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16X2(15))) | FFX_BROADCAST_UINT16X2(0x8000)); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y0 [Zero] X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY0ZeroX0(FfxUInt32x2 i) -{ - return ((i.x) & 0xffu) | ((i.y << 16) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y1 [Zero] X1 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY1ZeroX1(FfxUInt32x2 i) -{ - return ((i.x >> 8) & 0xffu) | ((i.y << 8) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y2 [Zero] X2 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY2ZeroX2(FfxUInt32x2 i) -{ - return ((i.x >> 16) & 0xffu) | ((i.y) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y3 [Zero] X3 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY3ZeroX3(FfxUInt32x2 i) -{ - return ((i.x >> 24) & 0xffu) | ((i.y >> 8) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 Y1 X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2Y1X0(FfxUInt32x2 i) -{ - return ((i.x) & 0x000000ffu) | (i.y & 0xffffff00u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 Y1 X2 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2Y1X2(FfxUInt32x2 i) -{ - return ((i.x >> 16) & 0x000000ffu) | (i.y & 0xffffff00u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 X0 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2X0Y0(FfxUInt32x2 i) -{ - return ((i.x << 8) & 0x0000ff00u) | (i.y & 0xffff00ffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 X2 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2X2Y0(FfxUInt32x2 i) -{ - return ((i.x >> 8) & 0x0000ff00u) | (i.y & 0xffff00ffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 X0 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3X0Y1Y0(FfxUInt32x2 i) -{ - return ((i.x << 16) & 0x00ff0000u) | (i.y & 0xff00ffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 X2 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3X2Y1Y0(FfxUInt32x2 i) -{ - return ((i.x) & 0x00ff0000u) | (i.y & 0xff00ffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// X0 Y2 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesX0Y2Y1Y0(FfxUInt32x2 i) -{ - return ((i.x << 24) & 0xff000000u) | (i.y & 0x00ffffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// X2 Y2 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesX2Y2Y1Y0(FfxUInt32x2 i) -{ - return ((i.x << 8) & 0xff000000u) | (i.y & 0x00ffffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y2 X2 Y0 X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY2X2Y0X0(FfxUInt32x2 i) -{ - return ((i.x) & 0x00ff00ffu) | ((i.y << 8) & 0xff00ff00u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y2 Y0 X2 X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY2Y0X2X0(FfxUInt32x2 i) -{ - return (((i.x) & 0xffu) | ((i.x >> 8) & 0xff00u) | ((i.y << 16) & 0xff0000u) | ((i.y << 8) & 0xff000000u)); -} - -/// Takes two Float16x2 values x and y, normalizes them and builds a single Uint16x2 value in the format {{x0,y0},{x1,y1}}. -/// -/// @param [in] x The first float16x2 value to pack. -/// @param [in] y The second float16x2 value to pack. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxPackX0Y0X1Y1UnsignedToUint16x2(FfxFloat16x2 x, FfxFloat16x2 y) -{ - x *= FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0); - y *= FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0); - return FFX_UINT32_TO_UINT16X2(ffxPackBytesY2X2Y0X0(FfxUInt32x2(FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(x)), FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(y))))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], -/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// r=ffxPermuteUByte0Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], -/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// r=ffxPermuteUByte1Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], -/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. -/// -/// r=ffxPermuteUByte2Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], -/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. -/// -/// r=ffxPermuteUByte3Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte0Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte1Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte2Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte3Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Takes two Float16x2 values x and y, normalizes them and builds a single Uint16x2 value in the format {{x0,y0},{x1,y1}}. -/// -/// @param [in] x The first float16x2 value to pack. -/// @param [in] y The second float16x2 value to pack. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxPackX0Y0X1Y1SignedToUint16x2(FfxFloat16x2 x, FfxFloat16x2 y) -{ - x = x * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0); - y = y * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0); - return FFX_UINT32_TO_UINT16X2(ffxPackBytesY2X2Y0X0(FfxUInt32x2(FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(x)), FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(y))))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], -/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], -/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], -/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], -/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], -/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], -/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], -/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], -/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte0Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte1Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte2Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte3Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte0Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte1Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte2Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte3Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Calculate a half-precision low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateSqrtHalf(FfxFloat16 a) -{ - return FFX_TO_FLOAT16((FFX_TO_UINT16(a) >> FFX_BROADCAST_UINT16(1)) + FFX_BROADCAST_UINT16(0x1de2)); -} - -/// Calculate a half-precision low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateSqrtHalf(FfxFloat16x2 a) -{ - return FFX_TO_FLOAT16X2((FFX_TO_UINT16X2(a) >> FFX_BROADCAST_UINT16X2(1)) + FFX_BROADCAST_UINT16X2(0x1de2)); -} - -/// Calculate a half-precision low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateSqrtHalf(FfxFloat16x3 a) -{ - return FFX_TO_FLOAT16X3((FFX_TO_UINT16X3(a) >> FFX_BROADCAST_UINT16X3(1)) + FFX_BROADCAST_UINT16X3(0x1de2)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateReciprocalHalf(FfxFloat16 a) -{ - return FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x7784) - FFX_TO_UINT16(a)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateReciprocalHalf(FfxFloat16x2 a) -{ - return FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x7784) - FFX_TO_UINT16X2(a)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateReciprocalHalf(FfxFloat16x3 a) -{ - return FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x7784) - FFX_TO_UINT16X3(a)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxApproximateReciprocalHalf(FfxFloat16x4 a) -{ - return FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x7784) - FFX_TO_UINT16X4(a)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateReciprocalMediumHalf(FfxFloat16 a) -{ - FfxFloat16 b = FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x778d) - FFX_TO_UINT16(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16(2.0)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateReciprocalMediumHalf(FfxFloat16x2 a) -{ - FfxFloat16x2 b = FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x778d) - FFX_TO_UINT16X2(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16X2(2.0)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateReciprocalMediumHalf(FfxFloat16x3 a) -{ - FfxFloat16x3 b = FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x778d) - FFX_TO_UINT16X3(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16X3(2.0)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxApproximateReciprocalMediumHalf(FfxFloat16x4 a) -{ - FfxFloat16x4 b = FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x778d) - FFX_TO_UINT16X4(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16X4(2.0)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateReciprocalSquareRootHalf(FfxFloat16 a) -{ - return FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x59a3) - (FFX_TO_UINT16(a) >> FFX_BROADCAST_UINT16(1))); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x2 a) -{ - return FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x59a3) - (FFX_TO_UINT16X2(a) >> FFX_BROADCAST_UINT16X2(1))); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x3 a) -{ - return FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x59a3) - (FFX_TO_UINT16X3(a) >> FFX_BROADCAST_UINT16X3(1))); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x4 a) -{ - return FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x59a3) - (FFX_TO_UINT16X4(a) >> FFX_BROADCAST_UINT16X4(1))); -} - -/// An approximation of sine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate sine for. -/// -/// @returns -/// The approximate sine of value. -FfxFloat16 ffxParabolicSinHalf(FfxFloat16 x) -{ - return x * abs(x) - x; -} - -/// An approximation of sine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate sine for. -/// -/// @returns -/// The approximate sine of value. -FfxFloat16x2 ffxParabolicSinHalf(FfxFloat16x2 x) -{ - return x * abs(x) - x; -} - -/// An approximation of cosine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate cosine for. -/// -/// @returns -/// The approximate cosine of value. -FfxFloat16 ffxParabolicCosHalf(FfxFloat16 x) -{ - x = ffxFract(x * FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16(0.75)); - x = x * FFX_BROADCAST_FLOAT16(2.0) - FFX_BROADCAST_FLOAT16(1.0); - return ffxParabolicSinHalf(x); -} - -/// An approximation of cosine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate cosine for. -/// -/// @returns -/// The approximate cosine of value. -FfxFloat16x2 ffxParabolicCosHalf(FfxFloat16x2 x) -{ - x = ffxFract(x * FFX_BROADCAST_FLOAT16X2(0.5) + FFX_BROADCAST_FLOAT16X2(0.75)); - x = x * FFX_BROADCAST_FLOAT16X2(2.0) - FFX_BROADCAST_FLOAT16X2(1.0); - return ffxParabolicSinHalf(x); -} - -/// An approximation of both sine and cosine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate cosine for. -/// -/// @returns -/// A FfxFloat32x2 containing approximations of both sine and cosine of value. -FfxFloat16x2 ffxParabolicSinCosHalf(FfxFloat16 x) -{ - FfxFloat16 y = ffxFract(x * FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16(0.75)); - y = y * FFX_BROADCAST_FLOAT16(2.0) - FFX_BROADCAST_FLOAT16(1.0); - return ffxParabolicSinHalf(FfxFloat16x2(x, y)); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneAndHalf(FfxUInt16 x, FfxUInt16 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneAndHalf(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneAndHalf(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneAndHalf(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return min(x, y); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneNotHalf(FfxUInt16 x) -{ - return x ^ FFX_BROADCAST_UINT16(1); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneNotHalf(FfxUInt16x2 x) -{ - return x ^ FFX_BROADCAST_UINT16X2(1); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneNotHalf(FfxUInt16x3 x) -{ - return x ^ FFX_BROADCAST_UINT16X3(1); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneNotHalf(FfxUInt16x4 x) -{ - return x ^ FFX_BROADCAST_UINT16X4(1); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneOrHalf(FfxUInt16 x, FfxUInt16 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneOrHalf(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneOrHalf(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneOrHalf(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return max(x, y); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneFloat16ToUint16(FfxFloat16 x) -{ - return FFX_TO_UINT16(x * FFX_TO_FLOAT16(FFX_TO_UINT16(1))); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneFloat16x2ToUint16x2(FfxFloat16x2 x) -{ - return FFX_TO_UINT16X2(x * FFX_TO_FLOAT16X2(FfxUInt16x2(1, 1))); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneFloat16x3ToUint16x3(FfxFloat16x3 x) -{ - return FFX_TO_UINT16X3(x * FFX_TO_FLOAT16X3(FfxUInt16x3(1, 1, 1))); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneFloat16x4ToUint16x4(FfxFloat16x4 x) -{ - return FFX_TO_UINT16X4(x * FFX_TO_FLOAT16X4(FfxUInt16x4(1, 1, 1, 1))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneUint16ToFloat16(FfxUInt16 x) -{ - return FFX_TO_FLOAT16(x * FFX_TO_UINT16(FFX_TO_FLOAT16(1.0))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneUint16x2ToFloat16x2(FfxUInt16x2 x) -{ - return FFX_TO_FLOAT16X2(x * FFX_TO_UINT16X2(FfxUInt16x2(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneUint16x3ToFloat16x3(FfxUInt16x3 x) -{ - return FFX_TO_FLOAT16X3(x * FFX_TO_UINT16X3(FfxUInt16x3(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneUint16x4ToFloat16x4(FfxUInt16x4 x) -{ - return FFX_TO_FLOAT16X4(x * FFX_TO_UINT16X4(FfxUInt16x4(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneAndHalf(FfxFloat16 x, FfxFloat16 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneAndHalf(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneAndHalf(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneAndHalf(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return min(x, y); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSignedZeroOneAndOrHalf(FfxFloat16 x, FfxFloat16 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16(1.0); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSignedZeroOneAndOrHalf(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16X2(1.0); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSignedZeroOneAndOrHalf(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16X3(1.0); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxSignedZeroOneAndOrHalf(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16X4(1.0); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneAndOrHalf(FfxFloat16 x, FfxFloat16 y, FfxFloat16 z) -{ - return FfxFloat16(ffxSaturate(x * y + z)); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneAndOrHalf(FfxFloat16x2 x, FfxFloat16x2 y, FfxFloat16x2 z) -{ - return FfxFloat16x2(ffxSaturate(x * y + z)); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneAndOrHalf(FfxFloat16x3 x, FfxFloat16x3 y, FfxFloat16x3 z) -{ - return FfxFloat16x3(ffxSaturate(x * y + z)); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneAndOrHalf(FfxFloat16x4 x, FfxFloat16x4 y, FfxFloat16x4 z) -{ - return FfxFloat16x4(ffxSaturate(x * y + z)); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16 x) -{ - return FfxFloat16(ffxSaturate(x * FFX_BROADCAST_FLOAT16(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x2 x) -{ - return FfxFloat16x2(ffxSaturate(x * FFX_BROADCAST_FLOAT16X2(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x3 x) -{ - return FfxFloat16x3(ffxSaturate(x * FFX_BROADCAST_FLOAT16X3(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x4 x) -{ - return FfxFloat16x4(ffxSaturate(x * FFX_BROADCAST_FLOAT16X4(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneNotHalf(FfxFloat16 x) -{ - return FFX_BROADCAST_FLOAT16(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneNotHalf(FfxFloat16x2 x) -{ - return FFX_BROADCAST_FLOAT16X2(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneNotHalf(FfxFloat16x3 x) -{ - return FFX_BROADCAST_FLOAT16X3(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneNotHalf(FfxFloat16x4 x) -{ - return FFX_BROADCAST_FLOAT16X4(1.0) - x; -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneOrHalf(FfxFloat16 x, FfxFloat16 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneOrHalf(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneOrHalf(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneOrHalf(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return max(x, y); -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneSelectHalf(FfxFloat16 x, FfxFloat16 y, FfxFloat16 z) -{ - FfxFloat16 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneSelectHalf(FfxFloat16x2 x, FfxFloat16x2 y, FfxFloat16x2 z) -{ - FfxFloat16x2 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneSelectHalf(FfxFloat16x3 x, FfxFloat16x3 y, FfxFloat16x3 z) -{ - FfxFloat16x3 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneSelectHalf(FfxFloat16x4 x, FfxFloat16x4 y, FfxFloat16x4 z) -{ - FfxFloat16x4 r = (-x) * z + z; - return x * y + r; -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneIsSignedHalf(FfxFloat16 x) -{ - return FfxFloat16(ffxSaturate(x * FFX_BROADCAST_FLOAT16(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneIsSignedHalf(FfxFloat16x2 x) -{ - return FfxFloat16x2(ffxSaturate(x * FFX_BROADCAST_FLOAT16X2(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneIsSignedHalf(FfxFloat16x3 x) -{ - return FfxFloat16x3(ffxSaturate(x * FFX_BROADCAST_FLOAT16X3(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneIsSignedHalf(FfxFloat16x4 x) -{ - return FfxFloat16x4(ffxSaturate(x * FFX_BROADCAST_FLOAT16X4(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] c The color to convert to Rec. 709. -/// -/// @returns -/// The color in Rec.709 space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxRec709FromLinearHalf(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); - return clamp(j.x, c * j.y, pow(c, j.z) * k.x + k.y); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] c The color to convert to Rec. 709. -/// -/// @returns -/// The color in Rec.709 space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxRec709FromLinearHalf(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); - return clamp(j.xx, c * j.yy, pow(c, j.zz) * k.xx + k.yy); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] c The color to convert to Rec. 709. -/// -/// @returns -/// The color in Rec.709 space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxRec709FromLinearHalf(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); - return clamp(j.xxx, c * j.yyy, pow(c, j.zzz) * k.xxx + k.yyy); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. -/// -/// @param [in] c The value to convert to gamma space from linear. -/// @param [in] rcpX The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxGammaFromLinearHalf(FfxFloat16 c, FfxFloat16 rcpX) -{ - return pow(c, FFX_BROADCAST_FLOAT16(rcpX)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. -/// -/// @param [in] c The value to convert to gamma space from linear. -/// @param [in] rcpX The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxGammaFromLinearHalf(FfxFloat16x2 c, FfxFloat16 rcpX) -{ - return pow(c, FFX_BROADCAST_FLOAT16X2(rcpX)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. -/// -/// @param [in] c The value to convert to gamma space from linear. -/// @param [in] rcpX The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxGammaFromLinearHalf(FfxFloat16x3 c, FfxFloat16 rcpX) -{ - return pow(c, FFX_BROADCAST_FLOAT16X3(rcpX)); -} - -/// Compute an SRGB value from a linear value. -/// -/// @param [in] c The value to convert to SRGB from linear. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSrgbFromLinearHalf(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); - return clamp(j.x, c * j.y, pow(c, j.z) * k.x + k.y); -} - -/// Compute an SRGB value from a linear value. -/// -/// @param [in] c The value to convert to SRGB from linear. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSrgbFromLinearHalf(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); - return clamp(j.xx, c * j.yy, pow(c, j.zz) * k.xx + k.yy); -} - -/// Compute an SRGB value from a linear value. -/// -/// @param [in] c The value to convert to SRGB from linear. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSrgbFromLinearHalf(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); - return clamp(j.xxx, c * j.yyy, pow(c, j.zzz) * k.xxx + k.yyy); -} - -/// Compute the square root of a value. -/// -/// @param [in] c The value to compute the square root for. -/// -/// @returns -/// A square root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSquareRootHalf(FfxFloat16 c) -{ - return sqrt(c); -} - -/// Compute the square root of a value. -/// -/// @param [in] c The value to compute the square root for. -/// -/// @returns -/// A square root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSquareRootHalf(FfxFloat16x2 c) -{ - return sqrt(c); -} - -/// Compute the square root of a value. -/// -/// @param [in] c The value to compute the square root for. -/// -/// @returns -/// A square root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSquareRootHalf(FfxFloat16x3 c) -{ - return sqrt(c); -} - -/// Compute the cube root of a value. -/// -/// @param [in] c The value to compute the cube root for. -/// -/// @returns -/// A cube root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxCubeRootHalf(FfxFloat16 c) -{ - return pow(c, FFX_BROADCAST_FLOAT16(1.0 / 3.0)); -} - -/// Compute the cube root of a value. -/// -/// @param [in] c The value to compute the cube root for. -/// -/// @returns -/// A cube root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxCubeRootHalf(FfxFloat16x2 c) -{ - return pow(c, FFX_BROADCAST_FLOAT16X2(1.0 / 3.0)); -} - -/// Compute the cube root of a value. -/// -/// @param [in] c The value to compute the cube root for. -/// -/// @returns -/// A cube root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxCubeRootHalf(FfxFloat16x3 c) -{ - return pow(c, FFX_BROADCAST_FLOAT16X3(1.0 / 3.0)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] c The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxLinearFromRec709Half(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.x), c * j.y, pow(c * k.x + k.y, j.z)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] c The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxLinearFromRec709Half(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xx), c * j.yy, pow(c * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] c The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxLinearFromRec709Half(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xxx), c * j.yyy, pow(c * k.xxx + k.yyy, j.zzz)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in gamma space. -/// @param [in] x The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxLinearFromGammaHalf(FfxFloat16 c, FfxFloat16 x) -{ - return pow(c, FFX_BROADCAST_FLOAT16(x)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in gamma space. -/// @param [in] x The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxLinearFromGammaHalf(FfxFloat16x2 c, FfxFloat16 x) -{ - return pow(c, FFX_BROADCAST_FLOAT16X2(x)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in gamma space. -/// @param [in] x The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxLinearFromGammaHalf(FfxFloat16x3 c, FfxFloat16 x) -{ - return pow(c, FFX_BROADCAST_FLOAT16X3(x)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxLinearFromSrgbHalf(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.x), c * j.y, pow(c * k.x + k.y, j.z)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxLinearFromSrgbHalf(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xx), c * j.yy, pow(c * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxLinearFromSrgbHalf(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xxx), c * j.yyy, pow(c * k.xxx + k.yyy, j.zzz)); -} - -/// A remapping of 64x1 to 8x8 imposing rotated 2x2 pixel quads in quad linear. -/// -/// 543210 -/// ====== -/// ..xxx. -/// yy...y -/// -/// @param [in] a The input 1D coordinates to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxRemapForQuadHalf(FfxUInt32 a) -{ - return FfxUInt16x2(bitfieldExtract(a, 1u, 3u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), a, 1u)); -} - -/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. -/// -/// The 64-wide lane indices to 8x8 remapping is performed as follows: -/// -/// 00 01 08 09 10 11 18 19 -/// 02 03 0a 0b 12 13 1a 1b -/// 04 05 0c 0d 14 15 1c 1d -/// 06 07 0e 0f 16 17 1e 1f -/// 20 21 28 29 30 31 38 39 -/// 22 23 2a 2b 32 33 3a 3b -/// 24 25 2c 2d 34 35 3c 3d -/// 26 27 2e 2f 36 37 3e 3f -/// -/// @param [in] a The input 1D coordinate to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxRemapForWaveReductionHalf(FfxUInt32 a) -{ - return FfxUInt16x2(bitfieldInsertMask(bitfieldExtract(a, 2u, 3u), a, 1u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), bitfieldExtract(a, 1u, 2u), 2u)); -} - -#endif // FFX_HALF diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta deleted file mode 100644 index 234aa90..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 1bdb323791a91a5438ee8e1e63187840 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h deleted file mode 100644 index 337eb06..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h +++ /dev/null @@ -1,1651 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup HLSLCore HLSL Core -/// HLSL core defines and functions -/// -/// @ingroup FfxHLSL - -#define DECLARE_SRV_REGISTER(regIndex) t##regIndex -#define DECLARE_UAV_REGISTER(regIndex) u##regIndex -#define DECLARE_CB_REGISTER(regIndex) b##regIndex -#define FFX_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) -#define FFX_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) -#define FFX_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) - -/// A define for abstracting shared memory between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_GROUPSHARED groupshared - -/// A define for abstracting compute memory barriers between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_GROUP_MEMORY_BARRIER GroupMemoryBarrierWithGroupSync - -/// A define for abstracting compute atomic additions between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_ATOMIC_ADD(x, y) InterlockedAdd(x, y) - -/// A define added to accept static markup on functions to aid CPU/GPU portability of code. -/// -/// @ingroup HLSLCore -#define FFX_STATIC static - -/// A define for abstracting loop unrolling between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_UNROLL [unroll] - -/// A define for abstracting a 'greater than' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_GREATER_THAN(x, y) x > y - -/// A define for abstracting a 'greater than or equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_GREATER_THAN_EQUAL(x, y) x >= y - -/// A define for abstracting a 'less than' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_LESS_THAN(x, y) x < y - -/// A define for abstracting a 'less than or equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_LESS_THAN_EQUAL(x, y) x <= y - -/// A define for abstracting an 'equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_EQUAL(x, y) x == y - -/// A define for abstracting a 'not equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_NOT_EQUAL(x, y) x != y - -/// A define for abstracting matrix multiply operations between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_MATRIX_MULTIPLY(a, b) mul(a, b) - -/// A define for abstracting vector transformations between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_TRANSFORM_VECTOR(a, b) mul(a, b) - -/// A define for abstracting modulo operations between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_MODULO(a, b) (fmod(a, b)) - -/// Broadcast a scalar value to a 1-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 2-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32X2(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 3-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32X3(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 4-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32X4(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 1-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 2-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32X2(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32X3(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32X4(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 1-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32(x) FfxInt32(x) - -/// Broadcast a scalar value to a 2-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32X2(x) FfxInt32(x) - -/// Broadcast a scalar value to a 3-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32X3(x) FfxInt32(x) - -/// Broadcast a scalar value to a 4-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32X4(x) FfxInt32(x) - -/// Broadcast a scalar value to a 1-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 2-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16X2(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 3-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16X3(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 4-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16X4(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 1-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 2-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16X2(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 3-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16X3(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 4-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16X4(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 1-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16(a) FFX_MIN16_I(a) - -/// Broadcast a scalar value to a 2-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16X2(a) FFX_MIN16_I(a) - -/// Broadcast a scalar value to a 3-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16X3(a) FFX_MIN16_I(a) - -/// Broadcast a scalar value to a 4-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16X4(a) FFX_MIN16_I(a) - -/// Pack 2x32-bit floating point values in a single 32bit value. -/// -/// This function first converts each component of value into their nearest 16-bit floating -/// point representation, and then stores the X and Y components in the lower and upper 16 bits of the -/// 32bit unsigned integer respectively. -/// -/// @param [in] value A 2-dimensional floating point value to convert and pack. -/// -/// @returns -/// A packed 32bit value containing 2 16bit floating point values. -/// -/// @ingroup HLSLCore -FfxUInt32 packHalf2x16(FfxFloat32x2 value) -{ - return f32tof16(value.x) | (f32tof16(value.y) << 16); -} - -/// Broadcast a scalar value to a 2-dimensional floating point vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 2-dimensional floating point vector with value in each component. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxBroadcast2(FfxFloat32 value) -{ - return FfxFloat32x2(value, value); -} - -/// Broadcast a scalar value to a 3-dimensional floating point vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 3-dimensional floating point vector with value in each component. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxBroadcast3(FfxFloat32 value) -{ - return FfxFloat32x3(value, value, value); -} - -/// Broadcast a scalar value to a 4-dimensional floating point vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 4-dimensional floating point vector with value in each component. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxBroadcast4(FfxFloat32 value) -{ - return FfxFloat32x4(value, value, value, value); -} - -/// Broadcast a scalar value to a 2-dimensional signed integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 2-dimensional signed integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxInt32x2 ffxBroadcast2(FfxInt32 value) -{ - return FfxInt32x2(value, value); -} - -/// Broadcast a scalar value to a 3-dimensional signed integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 3-dimensional signed integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxBroadcast3(FfxInt32 value) -{ - return FfxUInt32x3(value, value, value); -} - -/// Broadcast a scalar value to a 4-dimensional signed integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 4-dimensional signed integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxInt32x4 ffxBroadcast4(FfxInt32 value) -{ - return FfxInt32x4(value, value, value, value); -} - -/// Broadcast a scalar value to a 2-dimensional unsigned integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 2-dimensional unsigned integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxBroadcast2(FfxUInt32 value) -{ - return FfxUInt32x2(value, value); -} - -/// Broadcast a scalar value to a 3-dimensional unsigned integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 3-dimensional unsigned integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxBroadcast3(FfxUInt32 value) -{ - return FfxUInt32x3(value, value, value); -} - -/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 4-dimensional unsigned integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxBroadcast4(FfxUInt32 value) -{ - return FfxUInt32x4(value, value, value, value); -} - -FfxUInt32 bitfieldExtract(FfxUInt32 src, FfxUInt32 off, FfxUInt32 bits) -{ - FfxUInt32 mask = (1u << bits) - 1; - return (src >> off) & mask; -} - -FfxUInt32 bitfieldInsert(FfxUInt32 src, FfxUInt32 ins, FfxUInt32 mask) -{ - return (ins & mask) | (src & (~mask)); -} - -FfxUInt32 bitfieldInsertMask(FfxUInt32 src, FfxUInt32 ins, FfxUInt32 bits) -{ - FfxUInt32 mask = (1u << bits) - 1; - return (ins & mask) | (src & (~mask)); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32 ffxAsUInt32(FfxFloat32 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxAsUInt32(FfxFloat32x2 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxAsUInt32(FfxFloat32x3 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxAsUInt32(FfxFloat32x4 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxAsFloat(FfxUInt32 x) -{ - return asfloat(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxAsFloat(FfxUInt32x2 x) -{ - return asfloat(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxAsFloat(FfxUInt32x3 x) -{ - return asfloat(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxAsFloat(FfxUInt32x4 x) -{ - return asfloat(x); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxLerp(FfxFloat32 x, FfxFloat32 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxLerp(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxLerp(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxLerp(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxLerp(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxLerp(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxLerp(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 t) -{ - return lerp(x, y, t); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxSaturate(FfxFloat32 x) -{ - return saturate(x); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxSaturate(FfxFloat32x2 x) -{ - return saturate(x); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxSaturate(FfxFloat32x3 x) -{ - return saturate(x); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxSaturate(FfxFloat32x4 x) -{ - return saturate(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxFract(FfxFloat32 x) -{ - return x - floor(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxFract(FfxFloat32x2 x) -{ - return x - floor(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxFract(FfxFloat32x3 x) -{ - return x - floor(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxFract(FfxFloat32x4 x) -{ - return x - floor(x); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxMax3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxMax3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxMax3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxMax3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32 ffxMax3(FfxUInt32 x, FfxUInt32 y, FfxUInt32 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxMax3(FfxUInt32x2 x, FfxUInt32x2 y, FfxUInt32x2 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxMax3(FfxUInt32x3 x, FfxUInt32x3 y, FfxUInt32x3 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxMax3(FfxUInt32x4 x, FfxUInt32x4 y, FfxUInt32x4 z) -{ - return max(x, max(y, z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxMed3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxMed3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxMed3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxMed3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32 ffxMed3(FfxInt32 x, FfxInt32 y, FfxInt32 z) -{ - return max(min(x, y), min(max(x, y), z)); - // return min(max(min(y, z), x), max(y, z)); - // return max(max(x, y), z) == x ? max(y, z) : (max(max(x, y), z) == y ? max(x, z) : max(x, y)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32x2 ffxMed3(FfxInt32x2 x, FfxInt32x2 y, FfxInt32x2 z) -{ - return max(min(x, y), min(max(x, y), z)); - // return min(max(min(y, z), x), max(y, z)); - // return max(max(x, y), z) == x ? max(y, z) : (max(max(x, y), z) == y ? max(x, z) : max(x, y)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32x3 ffxMed3(FfxInt32x3 x, FfxInt32x3 y, FfxInt32x3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_I32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32x4 ffxMed3(FfxInt32x4 x, FfxInt32x4 y, FfxInt32x4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxMin3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxMin3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxMin3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxMin3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32 ffxMin3(FfxUInt32 x, FfxUInt32 y, FfxUInt32 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxMin3(FfxUInt32x2 x, FfxUInt32x2 y, FfxUInt32x2 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calculation. -/// @param [in] z The third value to include in the min calculation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxMin3(FfxUInt32x3 x, FfxUInt32x3 y, FfxUInt32x3 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxMin3(FfxUInt32x4 x, FfxUInt32x4 y, FfxUInt32x4 z) -{ - return min(x, min(y, z)); -} - - -FfxUInt32 AShrSU1(FfxUInt32 a, FfxUInt32 b) -{ - return FfxUInt32(FfxInt32(a) >> FfxInt32(b)); -} - -FfxUInt32 ffxPackF32(FfxFloat32x2 v){ - FfxUInt32x2 p = FfxUInt32x2(f32tof16(FfxFloat32x2(v).x), f32tof16(FfxFloat32x2(v).y)); - return p.x | (p.y << 16); -} - -FfxFloat32x2 ffxUnpackF32(FfxUInt32 a){ - return f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16)); -} - -//============================================================================================================================== -// HLSL HALF -//============================================================================================================================== -//============================================================================================================================== -// Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). -// Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ -FFX_MIN16_F2 ffxUint32ToFloat16x2(FfxUInt32 x) -{ - FfxFloat32x2 t = f16tof32(FfxUInt32x2(x & 0xFFFF, x >> 16)); - return FFX_MIN16_F2(t); -} -FFX_MIN16_F4 ffxUint32x2ToFloat16x4(FfxUInt32x2 x) -{ - return FFX_MIN16_F4(ffxUint32ToFloat16x2(x.x), ffxUint32ToFloat16x2(x.y)); -} -FFX_MIN16_U2 ffxUint32ToUint16x2(FfxUInt32 x) -{ - FfxUInt32x2 t = FfxUInt32x2(x & 0xFFFF, x >> 16); - return FFX_MIN16_U2(t); -} -FFX_MIN16_U4 ffxUint32x2ToUint16x4(FfxUInt32x2 x) -{ - return FFX_MIN16_U4(ffxUint32ToUint16x2(x.x), ffxUint32ToUint16x2(x.y)); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32 ffxInvertSafe(FfxFloat32 v){ - FfxFloat32 s = sign(v); - FfxFloat32 s2 = s*s; - return s2/(v + s2 - 1.0); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32x2 ffxInvertSafe(FfxFloat32x2 v){ - FfxFloat32x2 s = sign(v); - FfxFloat32x2 s2 = s*s; - return s2/(v + s2 - FfxFloat32x2(1.0, 1.0)); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32x3 ffxInvertSafe(FfxFloat32x3 v){ - FfxFloat32x3 s = sign(v); - FfxFloat32x3 s2 = s*s; - return s2/(v + s2 - FfxFloat32x3(1.0, 1.0, 1.0)); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32x4 ffxInvertSafe(FfxFloat32x4 v){ - FfxFloat32x4 s = sign(v); - FfxFloat32x4 s2 = s*s; - return s2/(v + s2 - FfxFloat32x4(1.0, 1.0, 1.0, 1.0)); -} - -#define FFX_UINT32_TO_FLOAT16X2(x) ffxUint32ToFloat16x2(FfxUInt32(x)) -#if FFX_HALF - -#define FFX_UINT32X2_TO_FLOAT16X4(x) ffxUint32x2ToFloat16x4(FfxUInt32x2(x)) -#define FFX_UINT32_TO_UINT16X2(x) ffxUint32ToUint16x2(FfxUInt32(x)) -#define FFX_UINT32X2_TO_UINT16X4(x) ffxUint32x2ToUint16x4(FfxUInt32x2(x)) - -FfxUInt32 ffxPackF16(FfxFloat16x2 v){ - FfxUInt32x2 p = FfxUInt32x2(f32tof16(FfxFloat32x2(v).x), f32tof16(FfxFloat32x2(v).y)); - return p.x | (p.y << 16); -} - -FfxFloat16x2 ffxUnpackF16(FfxUInt32 a){ - return FfxFloat16x2(f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16))); -} - -//------------------------------------------------------------------------------------------------------------------------------ -FfxUInt32 FFX_MIN16_F2ToUint32(FFX_MIN16_F2 x) -{ - return f32tof16(x.x) + (f32tof16(x.y) << 16); -} -FfxUInt32x2 FFX_MIN16_F4ToUint32x2(FFX_MIN16_F4 x) -{ - return FfxUInt32x2(FFX_MIN16_F2ToUint32(x.xy), FFX_MIN16_F2ToUint32(x.zw)); -} -FfxUInt32 FFX_MIN16_U2ToUint32(FFX_MIN16_U2 x) -{ - return FfxUInt32(x.x) + (FfxUInt32(x.y) << 16); -} -FfxUInt32x2 FFX_MIN16_U4ToUint32x2(FFX_MIN16_U4 x) -{ - return FfxUInt32x2(FFX_MIN16_U2ToUint32(x.xy), FFX_MIN16_U2ToUint32(x.zw)); -} -#define FFX_FLOAT16X2_TO_UINT32(x) FFX_MIN16_F2ToUint32(FFX_MIN16_F2(x)) -#define FFX_FLOAT16X4_TO_UINT32X2(x) FFX_MIN16_F4ToUint32x2(FFX_MIN16_F4(x)) -#define FFX_UINT16X2_TO_UINT32(x) FFX_MIN16_U2ToUint32(FFX_MIN16_U2(x)) -#define FFX_UINT16X4_TO_UINT32X2(x) FFX_MIN16_U4ToUint32x2(FFX_MIN16_U4(x)) - -#if (FFX_HLSL_SM >= 62) && !defined(FFX_NO_16_BIT_CAST) -#define FFX_TO_UINT16(x) asuint16(x) -#define FFX_TO_UINT16X2(x) asuint16(x) -#define FFX_TO_UINT16X3(x) asuint16(x) -#define FFX_TO_UINT16X4(x) asuint16(x) -#else -#define FFX_TO_UINT16(a) FFX_MIN16_U(f32tof16(FfxFloat32(a))) -#define FFX_TO_UINT16X2(a) FFX_MIN16_U2(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y)) -#define FFX_TO_UINT16X3(a) FFX_MIN16_U3(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y), FFX_TO_UINT16((a).z)) -#define FFX_TO_UINT16X4(a) FFX_MIN16_U4(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y), FFX_TO_UINT16((a).z), FFX_TO_UINT16((a).w)) -#endif // #if (FFX_HLSL_SM>=62) && !defined(FFX_NO_16_BIT_CAST) - -#if (FFX_HLSL_SM >= 62) && !defined(FFX_NO_16_BIT_CAST) -#define FFX_TO_FLOAT16(x) asfloat16(x) -#define FFX_TO_FLOAT16X2(x) asfloat16(x) -#define FFX_TO_FLOAT16X3(x) asfloat16(x) -#define FFX_TO_FLOAT16X4(x) asfloat16(x) -#else -#define FFX_TO_FLOAT16(a) FFX_MIN16_F(f16tof32(FfxUInt32(a))) -#define FFX_TO_FLOAT16X2(a) FFX_MIN16_F2(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y)) -#define FFX_TO_FLOAT16X3(a) FFX_MIN16_F3(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y), FFX_TO_FLOAT16((a).z)) -#define FFX_TO_FLOAT16X4(a) FFX_MIN16_F4(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y), FFX_TO_FLOAT16((a).z), FFX_TO_FLOAT16((a).w)) -#endif // #if (FFX_HLSL_SM>=62) && !defined(FFX_NO_16_BIT_CAST) - -//============================================================================================================================== -#define FFX_BROADCAST_FLOAT16(a) FFX_MIN16_F(a) -#define FFX_BROADCAST_FLOAT16X2(a) FFX_MIN16_F(a) -#define FFX_BROADCAST_FLOAT16X3(a) FFX_MIN16_F(a) -#define FFX_BROADCAST_FLOAT16X4(a) FFX_MIN16_F(a) - -//------------------------------------------------------------------------------------------------------------------------------ -#define FFX_BROADCAST_INT16(a) FFX_MIN16_I(a) -#define FFX_BROADCAST_INT16X2(a) FFX_MIN16_I(a) -#define FFX_BROADCAST_INT16X3(a) FFX_MIN16_I(a) -#define FFX_BROADCAST_INT16X4(a) FFX_MIN16_I(a) - -//------------------------------------------------------------------------------------------------------------------------------ -#define FFX_BROADCAST_UINT16(a) FFX_MIN16_U(a) -#define FFX_BROADCAST_UINT16X2(a) FFX_MIN16_U(a) -#define FFX_BROADCAST_UINT16X3(a) FFX_MIN16_U(a) -#define FFX_BROADCAST_UINT16X4(a) FFX_MIN16_U(a) - -//============================================================================================================================== -FFX_MIN16_U ffxAbsHalf(FFX_MIN16_U a) -{ - return FFX_MIN16_U(abs(FFX_MIN16_I(a))); -} -FFX_MIN16_U2 ffxAbsHalf(FFX_MIN16_U2 a) -{ - return FFX_MIN16_U2(abs(FFX_MIN16_I2(a))); -} -FFX_MIN16_U3 ffxAbsHalf(FFX_MIN16_U3 a) -{ - return FFX_MIN16_U3(abs(FFX_MIN16_I3(a))); -} -FFX_MIN16_U4 ffxAbsHalf(FFX_MIN16_U4 a) -{ - return FFX_MIN16_U4(abs(FFX_MIN16_I4(a))); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxClampHalf(FFX_MIN16_F x, FFX_MIN16_F n, FFX_MIN16_F m) -{ - return max(n, min(x, m)); -} -FFX_MIN16_F2 ffxClampHalf(FFX_MIN16_F2 x, FFX_MIN16_F2 n, FFX_MIN16_F2 m) -{ - return max(n, min(x, m)); -} -FFX_MIN16_F3 ffxClampHalf(FFX_MIN16_F3 x, FFX_MIN16_F3 n, FFX_MIN16_F3 m) -{ - return max(n, min(x, m)); -} -FFX_MIN16_F4 ffxClampHalf(FFX_MIN16_F4 x, FFX_MIN16_F4 n, FFX_MIN16_F4 m) -{ - return max(n, min(x, m)); -} -//------------------------------------------------------------------------------------------------------------------------------ -// V_FRACT_F16 (note DX frac() is different). -FFX_MIN16_F ffxFract(FFX_MIN16_F x) -{ - return x - floor(x); -} -FFX_MIN16_F2 ffxFract(FFX_MIN16_F2 x) -{ - return x - floor(x); -} -FFX_MIN16_F3 ffxFract(FFX_MIN16_F3 x) -{ - return x - floor(x); -} -FFX_MIN16_F4 ffxFract(FFX_MIN16_F4 x) -{ - return x - floor(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxLerp(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F2 ffxLerp(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F2 ffxLerp(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F3 ffxLerp(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F3 ffxLerp(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F4 ffxLerp(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F4 ffxLerp(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 a) -{ - return lerp(x, y, a); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxMax3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) -{ - return max(x, max(y, z)); -} -FFX_MIN16_F2 ffxMax3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) -{ - return max(x, max(y, z)); -} -FFX_MIN16_F3 ffxMax3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) -{ - return max(x, max(y, z)); -} -FFX_MIN16_F4 ffxMax3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) -{ - return max(x, max(y, z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxMin3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) -{ - return min(x, min(y, z)); -} -FFX_MIN16_F2 ffxMin3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) -{ - return min(x, min(y, z)); -} -FFX_MIN16_F3 ffxMin3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) -{ - return min(x, min(y, z)); -} -FFX_MIN16_F4 ffxMin3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) -{ - return min(x, min(y, z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxMed3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_F2 ffxMed3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_F3 ffxMed3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_F4 ffxMed3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_I ffxMed3Half(FFX_MIN16_I x, FFX_MIN16_I y, FFX_MIN16_I z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_I2 ffxMed3Half(FFX_MIN16_I2 x, FFX_MIN16_I2 y, FFX_MIN16_I2 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_I3 ffxMed3Half(FFX_MIN16_I3 x, FFX_MIN16_I3 y, FFX_MIN16_I3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_I4 ffxMed3Half(FFX_MIN16_I4 x, FFX_MIN16_I4 y, FFX_MIN16_I4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxReciprocalHalf(FFX_MIN16_F x) -{ - return rcp(x); -} -FFX_MIN16_F2 ffxReciprocalHalf(FFX_MIN16_F2 x) -{ - return rcp(x); -} -FFX_MIN16_F3 ffxReciprocalHalf(FFX_MIN16_F3 x) -{ - return rcp(x); -} -FFX_MIN16_F4 ffxReciprocalHalf(FFX_MIN16_F4 x) -{ - return rcp(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxReciprocalSquareRootHalf(FFX_MIN16_F x) -{ - return rsqrt(x); -} -FFX_MIN16_F2 ffxReciprocalSquareRootHalf(FFX_MIN16_F2 x) -{ - return rsqrt(x); -} -FFX_MIN16_F3 ffxReciprocalSquareRootHalf(FFX_MIN16_F3 x) -{ - return rsqrt(x); -} -FFX_MIN16_F4 ffxReciprocalSquareRootHalf(FFX_MIN16_F4 x) -{ - return rsqrt(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxSaturate(FFX_MIN16_F x) -{ - return saturate(x); -} -FFX_MIN16_F2 ffxSaturate(FFX_MIN16_F2 x) -{ - return saturate(x); -} -FFX_MIN16_F3 ffxSaturate(FFX_MIN16_F3 x) -{ - return saturate(x); -} -FFX_MIN16_F4 ffxSaturate(FFX_MIN16_F4 x) -{ - return saturate(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_U ffxBitShiftRightHalf(FFX_MIN16_U a, FFX_MIN16_U b) -{ - return FFX_MIN16_U(FFX_MIN16_I(a) >> FFX_MIN16_I(b)); -} -FFX_MIN16_U2 ffxBitShiftRightHalf(FFX_MIN16_U2 a, FFX_MIN16_U2 b) -{ - return FFX_MIN16_U2(FFX_MIN16_I2(a) >> FFX_MIN16_I2(b)); -} -FFX_MIN16_U3 ffxBitShiftRightHalf(FFX_MIN16_U3 a, FFX_MIN16_U3 b) -{ - return FFX_MIN16_U3(FFX_MIN16_I3(a) >> FFX_MIN16_I3(b)); -} -FFX_MIN16_U4 ffxBitShiftRightHalf(FFX_MIN16_U4 a, FFX_MIN16_U4 b) -{ - return FFX_MIN16_U4(FFX_MIN16_I4(a) >> FFX_MIN16_I4(b)); -} -#endif // FFX_HALF - -//============================================================================================================================== -// HLSL WAVE -//============================================================================================================================== -#if defined(FFX_WAVE) -// Where 'x' must be a compile time literal. -FfxFloat32 AWaveXorF1(FfxFloat32 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxFloat32x2 AWaveXorF2(FfxFloat32x2 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxFloat32x3 AWaveXorF3(FfxFloat32x3 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxFloat32x4 AWaveXorF4(FfxFloat32x4 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32 AWaveXorU1(FfxUInt32 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32x2 AWaveXorU1(FfxUInt32x2 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32x3 AWaveXorU1(FfxUInt32x3 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32x4 AWaveXorU1(FfxUInt32x4 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxBoolean AWaveIsFirstLane() -{ - return WaveIsFirstLane(); -} -FfxUInt32 AWaveLaneIndex() -{ - return WaveGetLaneIndex(); -} -FfxBoolean AWaveReadAtLaneIndexB1(FfxBoolean v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, x); -} -FfxUInt32 AWavePrefixCountBits(FfxBoolean v) -{ - return WavePrefixCountBits(v); -} -FfxUInt32 AWaveActiveCountBits(FfxBoolean v) -{ - return WaveActiveCountBits(v); -} -FfxUInt32 AWaveReadLaneFirstU1(FfxUInt32 v) -{ - return WaveReadLaneFirst(v); -} -FfxUInt32 WaveOr(FfxUInt32 a) -{ - return WaveActiveBitOr(a); -} -FfxFloat32 WaveMin(FfxFloat32 a) -{ - return WaveActiveMin(a); -} -FfxFloat32 WaveMax(FfxFloat32 a) -{ - return WaveActiveMax(a); -} -FfxUInt32 WaveLaneCount() -{ - return WaveGetLaneCount(); -} -FfxBoolean WaveAllTrue(FfxBoolean v) -{ - return WaveActiveAllTrue(v); -} -FfxFloat32 QuadReadX(FfxFloat32 v) -{ - return QuadReadAcrossX(v); -} -FfxFloat32x2 QuadReadX(FfxFloat32x2 v) -{ - return QuadReadAcrossX(v); -} -FfxFloat32 QuadReadY(FfxFloat32 v) -{ - return QuadReadAcrossY(v); -} -FfxFloat32x2 QuadReadY(FfxFloat32x2 v) -{ - return QuadReadAcrossY(v); -} - -#if FFX_HALF -FfxFloat16x2 ffxWaveXorFloat16x2(FfxFloat16x2 v, FfxUInt32 x) -{ - return FFX_UINT32_TO_FLOAT16X2(WaveReadLaneAt(FFX_FLOAT16X2_TO_UINT32(v), WaveGetLaneIndex() ^ x)); -} -FfxFloat16x4 ffxWaveXorFloat16x4(FfxFloat16x4 v, FfxUInt32 x) -{ - return FFX_UINT32X2_TO_FLOAT16X4(WaveReadLaneAt(FFX_FLOAT16X4_TO_UINT32X2(v), WaveGetLaneIndex() ^ x)); -} -FfxUInt16x2 ffxWaveXorUint16x2(FfxUInt16x2 v, FfxUInt32 x) -{ - return FFX_UINT32_TO_UINT16X2(WaveReadLaneAt(FFX_UINT16X2_TO_UINT32(v), WaveGetLaneIndex() ^ x)); -} -FfxUInt16x4 ffxWaveXorUint16x4(FfxUInt16x4 v, FfxUInt32 x) -{ - return FFX_UINT32X2_TO_UINT16X4(WaveReadLaneAt(FFX_UINT16X4_TO_UINT32X2(v), WaveGetLaneIndex() ^ x)); -} -#endif // FFX_HALF -#endif // #if defined(FFX_WAVE) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta deleted file mode 100644 index 2aa0691..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 299f67e8b7e1d1a48a577bf8b328ac92 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h deleted file mode 100644 index 84a62d6..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h +++ /dev/null @@ -1,51 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -FfxFloat32x3 opAAddOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d = a + ffxBroadcast3(b); - return d; -} - -FfxFloat32x3 opACpyF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d = a; - return d; -} - -FfxFloat32x3 opAMulF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32x3 b) -{ - d = a * b; - return d; -} - -FfxFloat32x3 opAMulOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d = a * ffxBroadcast3(b); - return d; -} - -FfxFloat32x3 opARcpF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d = rcp(a); - return d; -} diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta deleted file mode 100644 index b333bf0..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 8d2ace0bd52e0e1438e08ddaccd3ba24 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h deleted file mode 100644 index c425de7..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h +++ /dev/null @@ -1,288 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_ACCUMULATE_H -#define FFX_FSR3UPSCALER_ACCUMULATE_H - -FfxFloat32 GetPxHrVelocity(FfxFloat32x2 fMotionVector) -{ - return length(fMotionVector * DisplaySize()); -} -#if FFX_HALF -FFX_MIN16_F GetPxHrVelocity(FFX_MIN16_F2 fMotionVector) -{ - return length(fMotionVector * FFX_MIN16_F2(DisplaySize())); -} -#endif - -void Accumulate(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT FfxFloat32x3 fHistoryColor, FfxFloat32x3 fAccumulation, FFX_PARAMETER_IN FfxFloat32x4 fUpsampledColorAndWeight) -{ - // Aviod invalid values when accumulation and upsampled weight is 0 - fAccumulation = ffxMax(FSR3UPSCALER_EPSILON.xxx, fAccumulation + fUpsampledColorAndWeight.www); - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation) - fUpsampledColorAndWeight.xyz = RGBToYCoCg(Tonemap(YCoCgToRGB(fUpsampledColorAndWeight.xyz))); - fHistoryColor = RGBToYCoCg(Tonemap(YCoCgToRGB(fHistoryColor))); -#endif - - const FfxFloat32x3 fAlpha = fUpsampledColorAndWeight.www / fAccumulation; - fHistoryColor = ffxLerp(fHistoryColor, fUpsampledColorAndWeight.xyz, fAlpha); - - fHistoryColor = YCoCgToRGB(fHistoryColor); - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - fHistoryColor = InverseTonemap(fHistoryColor); -#endif -} - -void RectifyHistory( - const AccumulationPassCommonParams params, - RectificationBox clippingBox, - FFX_PARAMETER_INOUT FfxFloat32x3 fHistoryColor, - FFX_PARAMETER_INOUT FfxFloat32x3 fAccumulation, - FfxFloat32 fLockContributionThisFrame, - FfxFloat32 fTemporalReactiveFactor, - FfxFloat32 fLumaInstabilityFactor) -{ - const FfxFloat32 fVecolityFactor = ffxSaturate(params.fHrVelocity / 20.0f); - const FfxFloat32 fBoxScaleT = ffxMax(params.fDepthClipFactor, ffxMax(params.fAccumulationMask, fVecolityFactor)); - const FfxFloat32 fBoxScale = ffxLerp(3.0f, 1.0f, fBoxScaleT); - - const FfxFloat32x3 fScaledBoxVec = clippingBox.boxVec * fBoxScale; - const FfxFloat32x3 boxMin = clippingBox.boxCenter - fScaledBoxVec; - const FfxFloat32x3 boxMax = clippingBox.boxCenter + fScaledBoxVec; - - if (any(FFX_GREATER_THAN(boxMin, fHistoryColor)) || any(FFX_GREATER_THAN(fHistoryColor, boxMax))) { - - const FfxFloat32x3 fClampedHistoryColor = clamp(fHistoryColor, boxMin, boxMax); - - FfxFloat32x3 fHistoryContribution = ffxMax(fLumaInstabilityFactor, fLockContributionThisFrame).xxx; - - const FfxFloat32 fReactiveFactor = params.fDilatedReactiveFactor; - const FfxFloat32 fReactiveContribution = 1.0f - ffxPow(fReactiveFactor, 1.0f / 2.0f); - fHistoryContribution *= fReactiveContribution; - - // Scale history color using rectification info, also using accumulation mask to avoid potential invalid color protection - fHistoryColor = ffxLerp(fClampedHistoryColor, fHistoryColor, ffxSaturate(fHistoryContribution)); - - // Scale accumulation using rectification info - const FfxFloat32x3 fAccumulationMin = ffxMin(fAccumulation, FFX_BROADCAST_FLOAT32X3(0.1f)); - fAccumulation = ffxLerp(fAccumulationMin, fAccumulation, ffxSaturate(fHistoryContribution)); - } -} - -void WriteUpscaledOutput(FfxInt32x2 iPxHrPos, FfxFloat32x3 fUpscaledColor) -{ - StoreUpscaledOutput(iPxHrPos, fUpscaledColor); -} - -void FinalizeLockStatus(const AccumulationPassCommonParams params, FfxFloat32x2 fLockStatus, FfxFloat32 fUpsampledWeight) -{ - // we expect similar motion for next frame - // kill lock if that location is outside screen, avoid locks to be clamped to screen borders - FfxFloat32x2 fEstimatedUvNextFrame = params.fHrUv - params.fMotionVector; - if (IsUvInside(fEstimatedUvNextFrame) == false) { - KillLock(fLockStatus); - } - else { - // Decrease lock lifetime - const FfxFloat32 fLifetimeDecreaseLanczosMax = FfxFloat32(JitterSequenceLength()) * FfxFloat32(fAverageLanczosWeightPerFrame); - const FfxFloat32 fLifetimeDecrease = FfxFloat32(fUpsampledWeight / fLifetimeDecreaseLanczosMax); - fLockStatus[LOCK_LIFETIME_REMAINING] = ffxMax(FfxFloat32(0), fLockStatus[LOCK_LIFETIME_REMAINING] - fLifetimeDecrease); - } - - StoreLockStatus(params.iPxHrPos, fLockStatus); -} - - -FfxFloat32x3 ComputeBaseAccumulationWeight(const AccumulationPassCommonParams params, FfxFloat32 fThisFrameReactiveFactor, FfxBoolean bInMotionLastFrame, FfxFloat32 fUpsampledWeight, LockState lockState) -{ - // Always assume max accumulation was reached - FfxFloat32 fBaseAccumulation = fMaxAccumulationLanczosWeight * FfxFloat32(params.bIsExistingSample) * (1.0f - fThisFrameReactiveFactor) * (1.0f - params.fDepthClipFactor); - - fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, fUpsampledWeight * 10.0f, ffxMax(FfxFloat32(bInMotionLastFrame), ffxSaturate(params.fHrVelocity * FfxFloat32(10))))); - - fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, fUpsampledWeight, ffxSaturate(params.fHrVelocity / FfxFloat32(20)))); - - return fBaseAccumulation.xxx; -} - -FfxFloat32 ComputeLumaInstabilityFactor(const AccumulationPassCommonParams params, RectificationBox clippingBox, FfxFloat32 fThisFrameReactiveFactor, FfxFloat32 fLuminanceDiff) -{ - const FfxFloat32 fUnormThreshold = 1.0f / 255.0f; - const FfxInt32 N_MINUS_1 = 0; - const FfxInt32 N_MINUS_2 = 1; - const FfxInt32 N_MINUS_3 = 2; - const FfxInt32 N_MINUS_4 = 3; - - FfxFloat32 fCurrentFrameLuma = clippingBox.boxCenter.x; - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - fCurrentFrameLuma = fCurrentFrameLuma / (1.0f + ffxMax(0.0f, fCurrentFrameLuma)); -#endif - - fCurrentFrameLuma = round(fCurrentFrameLuma * 255.0f) / 255.0f; - - const FfxBoolean bSampleLumaHistory = (ffxMax(ffxMax(params.fDepthClipFactor, params.fAccumulationMask), fLuminanceDiff) < 0.1f) && (params.bIsNewSample == false); - FfxFloat32x4 fCurrentFrameLumaHistory = bSampleLumaHistory ? SampleLumaHistory(params.fReprojectedHrUv) : FFX_BROADCAST_FLOAT32X4(0.0f); - - FfxFloat32 fLumaInstability = 0.0f; - FfxFloat32 fDiffs0 = (fCurrentFrameLuma - fCurrentFrameLumaHistory[N_MINUS_1]); - - FfxFloat32 fMin = abs(fDiffs0); - - if (fMin >= fUnormThreshold) { - for (int i = N_MINUS_2; i <= N_MINUS_4; i++) { - FfxFloat32 fDiffs1 = (fCurrentFrameLuma - fCurrentFrameLumaHistory[i]); - - if (sign(fDiffs0) == sign(fDiffs1)) { - - // Scale difference to protect historically similar values - const FfxFloat32 fMinBias = 1.0f; - fMin = ffxMin(fMin, abs(fDiffs1) * fMinBias); - } - } - - const FfxFloat32 fBoxSize = clippingBox.boxVec.x; - const FfxFloat32 fBoxSizeFactor = ffxPow(ffxSaturate(fBoxSize / 0.1f), 6.0f); - - fLumaInstability = FfxFloat32(fMin != abs(fDiffs0)) * fBoxSizeFactor; - fLumaInstability = FfxFloat32(fLumaInstability > fUnormThreshold); - - fLumaInstability *= 1.0f - ffxMax(params.fAccumulationMask, ffxPow(fThisFrameReactiveFactor, 1.0f / 6.0f)); - } - - //shift history - fCurrentFrameLumaHistory[N_MINUS_4] = fCurrentFrameLumaHistory[N_MINUS_3]; - fCurrentFrameLumaHistory[N_MINUS_3] = fCurrentFrameLumaHistory[N_MINUS_2]; - fCurrentFrameLumaHistory[N_MINUS_2] = fCurrentFrameLumaHistory[N_MINUS_1]; - fCurrentFrameLumaHistory[N_MINUS_1] = fCurrentFrameLuma; - - StoreLumaHistory(params.iPxHrPos, fCurrentFrameLumaHistory); - - return fLumaInstability * FfxFloat32(fCurrentFrameLumaHistory[N_MINUS_4] != 0); -} - -FfxFloat32 ComputeTemporalReactiveFactor(const AccumulationPassCommonParams params, FfxFloat32 fTemporalReactiveFactor) -{ - FfxFloat32 fNewFactor = ffxMin(0.99f, fTemporalReactiveFactor); - - fNewFactor = ffxMax(fNewFactor, ffxLerp(fNewFactor, 0.4f, ffxSaturate(params.fHrVelocity))); - - fNewFactor = ffxMax(fNewFactor * fNewFactor, ffxMax(params.fDepthClipFactor * 0.1f, params.fDilatedReactiveFactor)); - - // Force reactive factor for new samples - fNewFactor = params.bIsNewSample ? 1.0f : fNewFactor; - - if (ffxSaturate(params.fHrVelocity * 10.0f) >= 1.0f) { - fNewFactor = ffxMax(FSR3UPSCALER_EPSILON, fNewFactor) * -1.0f; - } - - return fNewFactor; -} - -AccumulationPassCommonParams InitParams(FfxInt32x2 iPxHrPos) -{ - AccumulationPassCommonParams params; - - params.iPxHrPos = iPxHrPos; - const FfxFloat32x2 fHrUv = (iPxHrPos + 0.5f) / DisplaySize(); - params.fHrUv = fHrUv; - - const FfxFloat32x2 fLrUvJittered = fHrUv + Jitter() / RenderSize(); - params.fLrUv_HwSampler = ClampUv(fLrUvJittered, RenderSize(), MaxRenderSize()); - - params.fMotionVector = GetMotionVector(iPxHrPos, fHrUv); - params.fHrVelocity = GetPxHrVelocity(params.fMotionVector); - - ComputeReprojectedUVs(params, params.fReprojectedHrUv, params.bIsExistingSample); - - params.fDepthClipFactor = ffxSaturate(SampleDepthClip(params.fLrUv_HwSampler)); - - const FfxFloat32x2 fDilatedReactiveMasks = SampleDilatedReactiveMasks(params.fLrUv_HwSampler); - params.fDilatedReactiveFactor = fDilatedReactiveMasks.x; - params.fAccumulationMask = fDilatedReactiveMasks.y; - params.bIsResetFrame = (0 == FrameIndex()); - - params.bIsNewSample = (params.bIsExistingSample == false || params.bIsResetFrame); - - return params; -} - -void Accumulate(FfxInt32x2 iPxHrPos) -{ - const AccumulationPassCommonParams params = InitParams(iPxHrPos); - - FfxFloat32x3 fHistoryColor = FfxFloat32x3(0, 0, 0); - FfxFloat32x2 fLockStatus; - InitializeNewLockSample(fLockStatus); - - FfxFloat32 fTemporalReactiveFactor = 0.0f; - FfxBoolean bInMotionLastFrame = FFX_FALSE; - LockState lockState = { FFX_FALSE , FFX_FALSE }; - if (params.bIsExistingSample && !params.bIsResetFrame) { - ReprojectHistoryColor(params, fHistoryColor, fTemporalReactiveFactor, bInMotionLastFrame); - lockState = ReprojectHistoryLockStatus(params, fLockStatus); - } - - FfxFloat32 fThisFrameReactiveFactor = ffxMax(params.fDilatedReactiveFactor, fTemporalReactiveFactor); - - FfxFloat32 fLuminanceDiff = 0.0f; - FfxFloat32 fLockContributionThisFrame = 0.0f; - UpdateLockStatus(params, fThisFrameReactiveFactor, lockState, fLockStatus, fLockContributionThisFrame, fLuminanceDiff); - - // Load upsampled input color - RectificationBox clippingBox; - FfxFloat32x4 fUpsampledColorAndWeight = ComputeUpsampledColorAndWeight(params, clippingBox, fThisFrameReactiveFactor); - - const FfxFloat32 fLumaInstabilityFactor = ComputeLumaInstabilityFactor(params, clippingBox, fThisFrameReactiveFactor, fLuminanceDiff); - - - FfxFloat32x3 fAccumulation = ComputeBaseAccumulationWeight(params, fThisFrameReactiveFactor, bInMotionLastFrame, fUpsampledColorAndWeight.w, lockState); - - if (params.bIsNewSample) { - fHistoryColor = YCoCgToRGB(fUpsampledColorAndWeight.xyz); - } - else { - RectifyHistory(params, clippingBox, fHistoryColor, fAccumulation, fLockContributionThisFrame, fThisFrameReactiveFactor, fLumaInstabilityFactor); - - Accumulate(params, fHistoryColor, fAccumulation, fUpsampledColorAndWeight); - } - - fHistoryColor = UnprepareRgb(fHistoryColor, Exposure()); - - FinalizeLockStatus(params, fLockStatus, fUpsampledColorAndWeight.w); - - // Get new temporal reactive factor - fTemporalReactiveFactor = ComputeTemporalReactiveFactor(params, fThisFrameReactiveFactor); - - StoreInternalColorAndWeight(iPxHrPos, FfxFloat32x4(fHistoryColor, fTemporalReactiveFactor)); - - // Output final color when RCAS is disabled -#if FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING == 0 - WriteUpscaledOutput(iPxHrPos, fHistoryColor); -#endif - StoreNewLocks(iPxHrPos, 0); -} - -#endif // FFX_FSR3UPSCALER_ACCUMULATE_H diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta deleted file mode 100644 index 79fa7a3..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 3fc2f7a2c8c31324a949e1761bf599cc -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h deleted file mode 100644 index 13b317a..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h +++ /dev/null @@ -1,928 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#include "ffx_fsr3upscaler_resources.h" - -#if defined(FFX_GPU) -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic push -#pragma dxc diagnostic ignored "-Wambig-lit-shift" -#endif //__hlsl_dx_compiler -#include "ffx_core.h" -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic pop -#endif //__hlsl_dx_compiler -#endif // #if defined(FFX_GPU) - -#if defined(FFX_GPU) -#ifndef FFX_PREFER_WAVE64 -#define FFX_PREFER_WAVE64 -#endif // FFX_PREFER_WAVE64 - -#if defined(FFX_GPU) -#pragma warning(disable: 3205) // conversion from larger type to smaller -#endif // #if defined(FFX_GPU) - -#define DECLARE_SRV_REGISTER(regIndex) t##regIndex -#define DECLARE_UAV_REGISTER(regIndex) u##regIndex -#define DECLARE_CB_REGISTER(regIndex) b##regIndex -#define FFX_FSR3UPSCALER_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) -#define FFX_FSR3UPSCALER_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) -#define FFX_FSR3UPSCALER_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) - -#if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) - cbuffer cbFSR3Upscaler : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) - { - FfxInt32x2 iRenderSize; - FfxInt32x2 iMaxRenderSize; - FfxInt32x2 iDisplaySize; - FfxInt32x2 iInputColorResourceDimensions; - FfxInt32x2 iLumaMipDimensions; - FfxInt32 iLumaMipLevelToUse; - FfxInt32 iFrameIndex; - - FfxFloat32x4 fDeviceToViewDepth; - FfxFloat32x2 fJitter; - FfxFloat32x2 fMotionVectorScale; - FfxFloat32x2 fDownscaleFactor; - FfxFloat32x2 fMotionVectorJitterCancellation; - FfxFloat32 fPreExposure; - FfxFloat32 fPreviousFramePreExposure; - FfxFloat32 fTanHalfFOV; - FfxFloat32 fJitterSequenceLength; - FfxFloat32 fDeltaTime; - FfxFloat32 fDynamicResChangeFactor; - FfxFloat32 fViewSpaceToMetersFactor; - - FfxInt32 iDummy; - }; - -#define FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE (sizeof(cbFSR3Upscaler) / 4) // Number of 32-bit values. This must be kept in sync with the cbFSR3Upscaler size. - -/* Define getter functions in the order they are defined in the CB! */ -FfxInt32x2 RenderSize() -{ - return iRenderSize; -} - -FfxInt32x2 MaxRenderSize() -{ - return iMaxRenderSize; -} - -FfxInt32x2 DisplaySize() -{ - return iDisplaySize; -} - -FfxInt32x2 InputColorResourceDimensions() -{ - return iInputColorResourceDimensions; -} - -FfxInt32x2 LumaMipDimensions() -{ - return iLumaMipDimensions; -} - -FfxInt32 LumaMipLevelToUse() -{ - return iLumaMipLevelToUse; -} - -FfxInt32 FrameIndex() -{ - return iFrameIndex; -} - -FfxFloat32x2 Jitter() -{ - return fJitter; -} - -FfxFloat32x4 DeviceToViewSpaceTransformFactors() -{ - return fDeviceToViewDepth; -} - -FfxFloat32x2 MotionVectorScale() -{ - return fMotionVectorScale; -} - -FfxFloat32x2 DownscaleFactor() -{ - return fDownscaleFactor; -} - -FfxFloat32x2 MotionVectorJitterCancellation() -{ - return fMotionVectorJitterCancellation; -} - -FfxFloat32 PreExposure() -{ - return fPreExposure; -} - -FfxFloat32 PreviousFramePreExposure() -{ - return fPreviousFramePreExposure; -} - -FfxFloat32 TanHalfFoV() -{ - return fTanHalfFOV; -} - -FfxFloat32 JitterSequenceLength() -{ - return fJitterSequenceLength; -} - -FfxFloat32 DeltaTime() -{ - return fDeltaTime; -} - -FfxFloat32 DynamicResChangeFactor() -{ - return fDynamicResChangeFactor; -} - -FfxFloat32 ViewSpaceToMetersFactor() -{ - return fViewSpaceToMetersFactor; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) - -#define FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(p) FFX_FSR3UPSCALER_ROOTSIG_STR(p) -#define FFX_FSR3UPSCALER_ROOTSIG_STR(p) #p -#define FFX_FSR3UPSCALER_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "RootConstants(num32BitConstants=" FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE) ", b0), " \ - "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ - "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] - -#define FFX_FSR3UPSCALER_CONSTANT_BUFFER_2_SIZE 6 // Number of 32-bit values. This must be kept in sync with max( cbRCAS , cbSPD) size. - -#define FFX_FSR3UPSCALER_CB2_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "RootConstants(num32BitConstants=" FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE) ", b0), " \ - "RootConstants(num32BitConstants=" FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_CONSTANT_BUFFER_2_SIZE) ", b1), " \ - "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ - "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] -#if defined(FFX_FSR3UPSCALER_EMBED_ROOTSIG) -#define FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT FFX_FSR3UPSCALER_ROOTSIG -#define FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT FFX_FSR3UPSCALER_CB2_ROOTSIG -#else -#define FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -#define FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT -#endif // #if FFX_FSR3UPSCALER_EMBED_ROOTSIG - -#if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) -cbuffer cbGenerateReactive : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) -{ - FfxFloat32 fTcThreshold; // 0.1 is a good starting value, lower will result in more TC pixels - FfxFloat32 fTcScale; - FfxFloat32 fReactiveScale; - FfxFloat32 fReactiveMax; -}; - -FfxFloat32 TcThreshold() -{ - return fTcThreshold; -} - -FfxFloat32 TcScale() -{ - return fTcScale; -} - -FfxFloat32 ReactiveScale() -{ - return fReactiveScale; -} - -FfxFloat32 ReactiveMax() -{ - return fReactiveMax; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) - -#if defined(FSR3UPSCALER_BIND_CB_RCAS) -cbuffer cbRCAS : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_RCAS) -{ - FfxUInt32x4 rcasConfig; -}; - -FfxUInt32x4 RCASConfig() -{ - return rcasConfig; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_RCAS) - - -#if defined(FSR3UPSCALER_BIND_CB_REACTIVE) -cbuffer cbGenerateReactive : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_REACTIVE) -{ - FfxFloat32 gen_reactive_scale; - FfxFloat32 gen_reactive_threshold; - FfxFloat32 gen_reactive_binaryValue; - FfxUInt32 gen_reactive_flags; -}; - -FfxFloat32 GenReactiveScale() -{ - return gen_reactive_scale; -} - -FfxFloat32 GenReactiveThreshold() -{ - return gen_reactive_threshold; -} - -FfxFloat32 GenReactiveBinaryValue() -{ - return gen_reactive_binaryValue; -} - -FfxUInt32 GenReactiveFlags() -{ - return gen_reactive_flags; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_REACTIVE) - -#if defined(FSR3UPSCALER_BIND_CB_SPD) -cbuffer cbSPD : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_SPD) { - - FfxUInt32 mips; - FfxUInt32 numWorkGroups; - FfxUInt32x2 workGroupOffset; - FfxUInt32x2 renderSize; -}; - -FfxUInt32 MipCount() -{ - return mips; -} - -FfxUInt32 NumWorkGroups() -{ - return numWorkGroups; -} - -FfxUInt32x2 WorkGroupOffset() -{ - return workGroupOffset; -} - -FfxUInt32x2 SPD_RenderSize() -{ - return renderSize; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_SPD) - -// Declare and sample camera buffers as regular textures, unless overridden -#if !defined(UNITY_FSR3_TEX2D) -#define UNITY_FSR3_TEX2D(type) Texture2D -#endif -#if !defined(UNITY_FSR3_RWTEX2D) -#define UNITY_FSR3_RWTEX2D(type) RWTexture2D -#endif -#if !defined(UNITY_FSR3_POS) -#define UNITY_FSR3_POS(pxPos) (pxPos) -#endif -#if !defined(UNITY_FSR3_UV) -#define UNITY_FSR3_UV(uv) (uv) -#endif - -SamplerState s_PointClamp : register(s0); -SamplerState s_LinearClamp : register(s1); - - // SRVs - #if defined FSR3UPSCALER_BIND_SRV_INPUT_COLOR - UNITY_FSR3_TEX2D(FfxFloat32x4) r_input_color_jittered : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY - UNITY_FSR3_TEX2D(FfxFloat32x4) r_input_opaque_only : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS - UNITY_FSR3_TEX2D(FfxFloat32x4) r_input_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_DEPTH - UNITY_FSR3_TEX2D(FfxFloat32) r_input_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE - Texture2D r_input_exposure : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE - Texture2D r_auto_exposure : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_SRV_REACTIVE_MASK - UNITY_FSR3_TEX2D(FfxFloat32) r_reactive_mask : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK); - #endif - #if defined FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK - UNITY_FSR3_TEX2D(FfxFloat32) r_transparency_and_composition_mask : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK); - #endif - #if defined FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH - Texture2D r_reconstructed_previous_nearest_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS - Texture2D r_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS - Texture2D r_previous_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_DILATED_DEPTH - Texture2D r_dilated_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED - Texture2D r_internal_upscaled_color : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LOCK_STATUS - Texture2D r_lock_status : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LOCK_STATUS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA - Texture2D r_lock_input_luma : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA); - #endif - #if defined FSR3UPSCALER_BIND_SRV_NEW_LOCKS - Texture2D r_new_locks : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_NEW_LOCKS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR - Texture2D r_prepared_input_color : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LUMA_HISTORY - Texture2D r_luma_history : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY); - #endif - #if defined FSR3UPSCALER_BIND_SRV_RCAS_INPUT - Texture2D r_rcas_input : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_RCAS_INPUT); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LANCZOS_LUT - Texture2D r_lanczos_lut : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT); - #endif - #if defined FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS - Texture2D r_imgMips : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT - Texture2D r_upsample_maximum_bias_lut : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT); - #endif - #if defined FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS - Texture2D r_dilated_reactive_masks : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS); - #endif - - #if defined FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR - Texture2D r_input_prev_color_pre_alpha : FFX_FSR3UPSCALER_DECLARE_SRV(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR - Texture2D r_input_prev_color_post_alpha : FFX_FSR3UPSCALER_DECLARE_SRV(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR); - #endif - - // UAV declarations - #if defined FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH - RWTexture2D rw_reconstructed_previous_nearest_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS - RWTexture2D rw_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_DILATED_DEPTH - RWTexture2D rw_dilated_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED - RWTexture2D rw_internal_upscaled_color : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED); - #endif - #if defined FSR3UPSCALER_BIND_UAV_LOCK_STATUS - RWTexture2D rw_lock_status : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LOCK_STATUS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA - RWTexture2D rw_lock_input_luma : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA); - #endif - #if defined FSR3UPSCALER_BIND_UAV_NEW_LOCKS - RWTexture2D rw_new_locks : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_NEW_LOCKS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR - RWTexture2D rw_prepared_input_color : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_UAV_LUMA_HISTORY - RWTexture2D rw_luma_history : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY); - #endif - #if defined FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT - UNITY_FSR3_RWTEX2D(FfxFloat32x4) rw_upscaled_output : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT); - #endif - #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE - globallycoherent RWTexture2D rw_img_mip_shading_change : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - globallycoherent RWTexture2D rw_img_mip_5 : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5); - #endif - #if defined FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS - RWTexture2D rw_dilated_reactive_masks : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE - RWTexture2D rw_exposure : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE - RWTexture2D rw_auto_exposure : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC - globallycoherent RWTexture2D rw_spd_global_atomic : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC); - #endif - - #if defined FSR3UPSCALER_BIND_UAV_AUTOREACTIVE - RWTexture2D rw_output_autoreactive : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION - RWTexture2D rw_output_autocomposition : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION); - #endif - #if defined FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR - RWTexture2D rw_output_prev_color_pre_alpha : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR - RWTexture2D rw_output_prev_color_post_alpha : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR); - #endif - -#if defined(FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS) -FfxFloat32 LoadMipLuma(FfxUInt32x2 iPxPos, FfxUInt32 mipLevel) -{ - return r_imgMips.mips[mipLevel][iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS) -FfxFloat32 SampleMipLuma(FfxFloat32x2 fUV, FfxUInt32 mipLevel) -{ - return r_imgMips.SampleLevel(s_LinearClamp, fUV, mipLevel); -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH) -FfxFloat32 LoadInputDepth(FfxUInt32x2 iPxPos) -{ - return r_input_depth[UNITY_FSR3_POS(iPxPos)]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH) -FfxFloat32 SampleInputDepth(FfxFloat32x2 fUV) -{ - return r_input_depth.SampleLevel(s_LinearClamp, UNITY_FSR3_UV(fUV), 0).x; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK) -FfxFloat32 LoadReactiveMask(FfxUInt32x2 iPxPos) -{ - return r_reactive_mask[UNITY_FSR3_POS(iPxPos)]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK) -FfxFloat32 LoadTransparencyAndCompositionMask(FfxUInt32x2 iPxPos) -{ - return r_transparency_and_composition_mask[UNITY_FSR3_POS(iPxPos)]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_COLOR) -FfxFloat32x3 LoadInputColor(FfxUInt32x2 iPxPos) -{ - return r_input_color_jittered[UNITY_FSR3_POS(iPxPos)].rgb; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_COLOR) -FfxFloat32x3 SampleInputColor(FfxFloat32x2 fUV) -{ - return r_input_color_jittered.SampleLevel(s_LinearClamp, UNITY_FSR3_UV(fUV), 0).rgb; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR) -FfxFloat32x3 LoadPreparedInputColor(FfxUInt32x2 iPxPos) -{ - return r_prepared_input_color[iPxPos].xyz; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS) -FfxFloat32x2 LoadInputMotionVector(FfxUInt32x2 iPxDilatedMotionVectorPos) -{ - FfxFloat32x2 fSrcMotionVector = r_input_motion_vectors[UNITY_FSR3_POS(iPxDilatedMotionVectorPos)].xy; - - FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale(); - -#if FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS - fUvMotionVector -= MotionVectorJitterCancellation(); -#endif - - return fUvMotionVector; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED) -FfxFloat32x4 LoadHistory(FfxUInt32x2 iPxHistory) -{ - return r_internal_upscaled_color[iPxHistory]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY) -void StoreLumaHistory(FfxUInt32x2 iPxPos, FfxFloat32x4 fLumaHistory) -{ - rw_luma_history[iPxPos] = fLumaHistory; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY) -FfxFloat32x4 SampleLumaHistory(FfxFloat32x2 fUV) -{ - return r_luma_history.SampleLevel(s_LinearClamp, fUV, 0); -} -#endif - -FfxFloat32x4 LoadRCAS_Input(FfxInt32x2 iPxPos) -{ -#if defined(FSR3UPSCALER_BIND_SRV_RCAS_INPUT) - return r_rcas_input[iPxPos]; -#else - return 0.0; -#endif -} - -#if defined(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED) -void StoreReprojectedHistory(FfxUInt32x2 iPxHistory, FfxFloat32x4 fHistory) -{ - rw_internal_upscaled_color[iPxHistory] = fHistory; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED) -void StoreInternalColorAndWeight(FfxUInt32x2 iPxPos, FfxFloat32x4 fColorAndWeight) -{ - rw_internal_upscaled_color[iPxPos] = fColorAndWeight; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT) -void StoreUpscaledOutput(FfxUInt32x2 iPxPos, FfxFloat32x3 fColor) -{ - rw_upscaled_output[UNITY_FSR3_POS(iPxPos)] = FfxFloat32x4(fColor, 1.f); -} -#endif - -//LOCK_LIFETIME_REMAINING == 0 -//Should make LockInitialLifetime() return a const 1.0f later -#if defined(FSR3UPSCALER_BIND_SRV_LOCK_STATUS) -FfxFloat32x2 LoadLockStatus(FfxUInt32x2 iPxPos) -{ - return r_lock_status[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_LOCK_STATUS) -void StoreLockStatus(FfxUInt32x2 iPxPos, FfxFloat32x2 fLockStatus) -{ - rw_lock_status[iPxPos] = fLockStatus; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA) -FfxFloat32 LoadLockInputLuma(FfxUInt32x2 iPxPos) -{ - return r_lock_input_luma[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA) -void StoreLockInputLuma(FfxUInt32x2 iPxPos, FfxFloat32 fLuma) -{ - rw_lock_input_luma[iPxPos] = fLuma; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_NEW_LOCKS) -FfxFloat32 LoadNewLocks(FfxUInt32x2 iPxPos) -{ - return r_new_locks[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_NEW_LOCKS) -FfxFloat32 LoadRwNewLocks(FfxUInt32x2 iPxPos) -{ - return rw_new_locks[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_NEW_LOCKS) -void StoreNewLocks(FfxUInt32x2 iPxPos, FfxFloat32 newLock) -{ - rw_new_locks[iPxPos] = newLock; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR) -void StorePreparedInputColor(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x4 fTonemapped) -{ - rw_prepared_input_color[iPxPos] = fTonemapped; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR) -FfxFloat32 SampleDepthClip(FfxFloat32x2 fUV) -{ - return r_prepared_input_color.SampleLevel(s_LinearClamp, fUV, 0).w; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_LOCK_STATUS) -FfxFloat32x2 SampleLockStatus(FfxFloat32x2 fUV) -{ - FfxFloat32x2 fLockStatus = r_lock_status.SampleLevel(s_LinearClamp, fUV, 0); - return fLockStatus; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH) -FfxFloat32 LoadReconstructedPrevDepth(FfxUInt32x2 iPxPos) -{ - return asfloat(r_reconstructed_previous_nearest_depth[iPxPos]); -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) -void StoreReconstructedDepth(FfxUInt32x2 iPxSample, FfxFloat32 fDepth) -{ - FfxUInt32 uDepth = asuint(fDepth); - - #if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - InterlockedMax(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); - #else - InterlockedMin(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); // min for standard, max for inverted depth - #endif -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) -void SetReconstructedDepth(FfxUInt32x2 iPxSample, const FfxUInt32 uValue) -{ - rw_reconstructed_previous_nearest_depth[iPxSample] = uValue; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH) -void StoreDilatedDepth(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32 fDepth) -{ - rw_dilated_depth[iPxPos] = fDepth; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS) -void StoreDilatedMotionVector(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fMotionVector) -{ - rw_dilated_motion_vectors[iPxPos] = fMotionVector; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS) -FfxFloat32x2 LoadDilatedMotionVector(FfxUInt32x2 iPxInput) -{ - return r_dilated_motion_vectors[iPxInput].xy; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS) -FfxFloat32x2 LoadPreviousDilatedMotionVector(FfxUInt32x2 iPxInput) -{ - return r_previous_dilated_motion_vectors[iPxInput].xy; -} - -FfxFloat32x2 SamplePreviousDilatedMotionVector(FfxFloat32x2 uv) -{ - return r_previous_dilated_motion_vectors.SampleLevel(s_LinearClamp, uv, 0).xy; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH) -FfxFloat32 LoadDilatedDepth(FfxUInt32x2 iPxInput) -{ - return r_dilated_depth[iPxInput]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE) -FfxFloat32 Exposure() -{ - FfxFloat32 exposure = r_input_exposure[FfxUInt32x2(0, 0)].x; - - if (exposure == 0.0f) { - exposure = 1.0f; - } - - return exposure; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE) -FfxFloat32 AutoExposure() -{ - FfxFloat32 exposure = r_auto_exposure[FfxUInt32x2(0, 0)].x; - - if (exposure == 0.0f) { - exposure = 1.0f; - } - - return exposure; -} -#endif - -FfxFloat32 SampleLanczos2Weight(FfxFloat32 x) -{ -#if defined(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT) - return r_lanczos_lut.SampleLevel(s_LinearClamp, FfxFloat32x2(x / 2, 0.5f), 0); -#else - return 0.f; -#endif -} - -#if defined(FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT) -FfxFloat32 SampleUpsampleMaximumBias(FfxFloat32x2 uv) -{ - // Stored as a SNORM, so make sure to multiply by 2 to retrieve the actual expected range. - return FfxFloat32(2.0) * r_upsample_maximum_bias_lut.SampleLevel(s_LinearClamp, abs(uv) * 2.0, 0); -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS) -FfxFloat32x2 SampleDilatedReactiveMasks(FfxFloat32x2 fUV) -{ - return r_dilated_reactive_masks.SampleLevel(s_LinearClamp, fUV, 0); -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS) -FfxFloat32x2 LoadDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos) -{ - return r_dilated_reactive_masks[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS) -void StoreDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fDilatedReactiveMasks) -{ - rw_dilated_reactive_masks[iPxPos] = fDilatedReactiveMasks; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY) -FfxFloat32x3 LoadOpaqueOnly(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) -{ - return r_input_opaque_only[UNITY_FSR3_POS(iPxPos)].xyz; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR) -FfxFloat32x3 LoadPrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) -{ - return r_input_prev_color_pre_alpha[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR) -FfxFloat32x3 LoadPrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) -{ - return r_input_prev_color_post_alpha[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE) -#if defined(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION) -void StoreAutoReactive(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F2 fReactive) -{ - rw_output_autoreactive[iPxPos] = fReactive.x; - - rw_output_autocomposition[iPxPos] = fReactive.y; -} -#endif -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR) -void StorePrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) -{ - rw_output_prev_color_pre_alpha[iPxPos] = color; - -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR) -void StorePrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) -{ - rw_output_prev_color_post_alpha[iPxPos] = color; -} -#endif - -FfxFloat32x2 SPD_LoadExposureBuffer() -{ -#if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE - return rw_auto_exposure[FfxInt32x2(0, 0)]; -#else - return FfxFloat32x2(0.f, 0.f); -#endif // #if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE -} - -void SPD_SetExposureBuffer(FfxFloat32x2 value) -{ -#if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE - rw_auto_exposure[FfxInt32x2(0, 0)] = value; -#endif // #if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE -} - -FfxFloat32x4 SPD_LoadMipmap5(FfxInt32x2 iPxPos) -{ -#if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - return FfxFloat32x4(rw_img_mip_5[iPxPos], 0, 0, 0); -#else - return FfxFloat32x4(0.f, 0.f, 0.f, 0.f); -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 -} - -void SPD_SetMipmap(FfxInt32x2 iPxPos, FfxUInt32 slice, FfxFloat32 value) -{ - switch (slice) - { - case FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL: -#if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE - rw_img_mip_shading_change[iPxPos] = value; -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE - break; - case 5: -#if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - rw_img_mip_5[iPxPos] = value; -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - break; - default: - - // avoid flattened side effect -#if defined(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE) - rw_img_mip_shading_change[iPxPos] = rw_img_mip_shading_change[iPxPos]; -#elif defined(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5) - rw_img_mip_5[iPxPos] = rw_img_mip_5[iPxPos]; -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - break; - } -} - -void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter) -{ -#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC - InterlockedAdd(rw_spd_global_atomic[FfxInt32x2(0, 0)], 1, spdCounter); -#endif // #if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC -} - -void SPD_ResetAtomicCounter() -{ -#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC - rw_spd_global_atomic[FfxInt32x2(0, 0)] = 0; -#endif // #if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC -} - -#endif // #if defined(FFX_GPU) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta deleted file mode 100644 index e78e6a1..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: ba849fdeb042e7f458c81408414db834 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h deleted file mode 100644 index 1f78a29..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h +++ /dev/null @@ -1,566 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#if !defined(FFX_FSR3UPSCALER_COMMON_H) -#define FFX_FSR3UPSCALER_COMMON_H - -#if defined(FFX_CPU) || defined(FFX_GPU) -//Locks -#define LOCK_LIFETIME_REMAINING 0 -#define LOCK_TEMPORAL_LUMA 1 -#endif // #if defined(FFX_CPU) || defined(FFX_GPU) - -#if defined(FFX_GPU) -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MIN = 6.10e-05f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MAX = 65504.0f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_EPSILON = 1e-03f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_TONEMAP_EPSILON = 1.0f / FSR3UPSCALER_FP16_MAX; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FLT_MAX = 3.402823466e+38f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FLT_MIN = 1.175494351e-38f; - -// treat vector truncation warnings as errors -#pragma warning(error: 3206) - -// suppress warnings -#pragma warning(disable: 3205) // conversion from larger type to smaller -#pragma warning(disable: 3571) // in ffxPow(f, e), f could be negative - -// Reconstructed depth usage -FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = 0.01f; - -// Accumulation -FFX_STATIC const FfxFloat32 fUpsampleLanczosWeightScale = 1.0f / 12.0f; -FFX_STATIC const FfxFloat32 fMaxAccumulationLanczosWeight = 1.0f; -FFX_STATIC const FfxFloat32 fAverageLanczosWeightPerFrame = 0.74f * fUpsampleLanczosWeightScale; // Average lanczos weight for jitter accumulated samples -FFX_STATIC const FfxFloat32 fAccumulationMaxOnMotion = 3.0f * fUpsampleLanczosWeightScale; - -// Auto exposure -FFX_STATIC const FfxFloat32 resetAutoExposureAverageSmoothing = 1e8f; - -struct AccumulationPassCommonParams -{ - FfxInt32x2 iPxHrPos; - FfxFloat32x2 fHrUv; - FfxFloat32x2 fLrUv_HwSampler; - FfxFloat32x2 fMotionVector; - FfxFloat32x2 fReprojectedHrUv; - FfxFloat32 fHrVelocity; - FfxFloat32 fDepthClipFactor; - FfxFloat32 fDilatedReactiveFactor; - FfxFloat32 fAccumulationMask; - - FfxBoolean bIsResetFrame; - FfxBoolean bIsExistingSample; - FfxBoolean bIsNewSample; -}; - -struct LockState -{ - FfxBoolean NewLock; //Set for both unique new and re-locked new - FfxBoolean WasLockedPrevFrame; //Set to identify if the pixel was already locked (relock) -}; - -void InitializeNewLockSample(FFX_PARAMETER_OUT FfxFloat32x2 fLockStatus) -{ - fLockStatus = FfxFloat32x2(0, 0); -} - -#if FFX_HALF -void InitializeNewLockSample(FFX_PARAMETER_OUT FFX_MIN16_F2 fLockStatus) -{ - fLockStatus = FFX_MIN16_F2(0, 0); -} -#endif - - -void KillLock(FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus) -{ - fLockStatus[LOCK_LIFETIME_REMAINING] = 0; -} - -#if FFX_HALF -void KillLock(FFX_PARAMETER_INOUT FFX_MIN16_F2 fLockStatus) -{ - fLockStatus[LOCK_LIFETIME_REMAINING] = FFX_MIN16_F(0); -} -#endif - -struct RectificationBox -{ - FfxFloat32x3 boxCenter; - FfxFloat32x3 boxVec; - FfxFloat32x3 aabbMin; - FfxFloat32x3 aabbMax; - FfxFloat32 fBoxCenterWeight; -}; -#if FFX_HALF -struct RectificationBoxMin16 -{ - FFX_MIN16_F3 boxCenter; - FFX_MIN16_F3 boxVec; - FFX_MIN16_F3 aabbMin; - FFX_MIN16_F3 aabbMax; - FFX_MIN16_F fBoxCenterWeight; -}; -#endif - -void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBox rectificationBox) -{ - rectificationBox.fBoxCenterWeight = FfxFloat32(0); - - rectificationBox.boxCenter = FfxFloat32x3(0, 0, 0); - rectificationBox.boxVec = FfxFloat32x3(0, 0, 0); - rectificationBox.aabbMin = FfxFloat32x3(FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX); - rectificationBox.aabbMax = -FfxFloat32x3(FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX); -} -#if FFX_HALF -void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) -{ - rectificationBox.fBoxCenterWeight = FFX_MIN16_F(0); - - rectificationBox.boxCenter = FFX_MIN16_F3(0, 0, 0); - rectificationBox.boxVec = FFX_MIN16_F3(0, 0, 0); - rectificationBox.aabbMin = FFX_MIN16_F3(FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX); - rectificationBox.aabbMax = -FFX_MIN16_F3(FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX); -} -#endif - -void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) -{ - rectificationBox.aabbMin = colorSample; - rectificationBox.aabbMax = colorSample; - - FfxFloat32x3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter = weightedSample; - rectificationBox.boxVec = colorSample * weightedSample; - rectificationBox.fBoxCenterWeight = fSampleWeight; -} - -void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) -{ - if (bInitialSample) { - RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); - } else { - rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); - rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); - - FfxFloat32x3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter += weightedSample; - rectificationBox.boxVec += colorSample * weightedSample; - rectificationBox.fBoxCenterWeight += fSampleWeight; - } -} -#if FFX_HALF -void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) -{ - rectificationBox.aabbMin = colorSample; - rectificationBox.aabbMax = colorSample; - - FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter = weightedSample; - rectificationBox.boxVec = colorSample * weightedSample; - rectificationBox.fBoxCenterWeight = fSampleWeight; -} - -void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) -{ - if (bInitialSample) { - RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); - } else { - rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); - rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); - - FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter += weightedSample; - rectificationBox.boxVec += colorSample * weightedSample; - rectificationBox.fBoxCenterWeight += fSampleWeight; - } -} -#endif - -void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBox rectificationBox) -{ - rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FfxFloat32(FSR3UPSCALER_EPSILON) ? rectificationBox.fBoxCenterWeight : FfxFloat32(1.f)); - rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; - rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; - FfxFloat32x3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); - rectificationBox.boxVec = stdDev; -} -#if FFX_HALF -void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) -{ - rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FFX_MIN16_F(FSR3UPSCALER_EPSILON) ? rectificationBox.fBoxCenterWeight : FFX_MIN16_F(1.f)); - rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; - rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; - FFX_MIN16_F3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); - rectificationBox.boxVec = stdDev; -} -#endif - -FfxFloat32x3 SafeRcp3(FfxFloat32x3 v) -{ - return (all(FFX_NOT_EQUAL(v, FfxFloat32x3(0, 0, 0)))) ? (FfxFloat32x3(1, 1, 1) / v) : FfxFloat32x3(0, 0, 0); -} -#if FFX_HALF -FFX_MIN16_F3 SafeRcp3(FFX_MIN16_F3 v) -{ - return (all(FFX_NOT_EQUAL(v, FFX_MIN16_F3(0, 0, 0)))) ? (FFX_MIN16_F3(1, 1, 1) / v) : FFX_MIN16_F3(0, 0, 0); -} -#endif - -FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1) -{ - const FfxFloat32 m = ffxMax(v0, v1); - return m != 0 ? ffxMin(v0, v1) / m : 0; -} - -#if FFX_HALF -FFX_MIN16_F MinDividedByMax(const FFX_MIN16_F v0, const FFX_MIN16_F v1) -{ - const FFX_MIN16_F m = ffxMax(v0, v1); - return m != FFX_MIN16_F(0) ? ffxMin(v0, v1) / m : FFX_MIN16_F(0); -} -#endif - -FfxFloat32x3 YCoCgToRGB(FfxFloat32x3 fYCoCg) -{ - FfxFloat32x3 fRgb; - - fRgb = FfxFloat32x3( - fYCoCg.x + fYCoCg.y - fYCoCg.z, - fYCoCg.x + fYCoCg.z, - fYCoCg.x - fYCoCg.y - fYCoCg.z); - - return fRgb; -} -#if FFX_HALF -FFX_MIN16_F3 YCoCgToRGB(FFX_MIN16_F3 fYCoCg) -{ - FFX_MIN16_F3 fRgb; - - fRgb = FFX_MIN16_F3( - fYCoCg.x + fYCoCg.y - fYCoCg.z, - fYCoCg.x + fYCoCg.z, - fYCoCg.x - fYCoCg.y - fYCoCg.z); - - return fRgb; -} -#endif - -FfxFloat32x3 RGBToYCoCg(FfxFloat32x3 fRgb) -{ - FfxFloat32x3 fYCoCg; - - fYCoCg = FfxFloat32x3( - 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b, - 0.5f * fRgb.r - 0.5f * fRgb.b, - -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b); - - return fYCoCg; -} -#if FFX_HALF -FFX_MIN16_F3 RGBToYCoCg(FFX_MIN16_F3 fRgb) -{ - FFX_MIN16_F3 fYCoCg; - - fYCoCg = FFX_MIN16_F3( - 0.25 * fRgb.r + 0.5 * fRgb.g + 0.25 * fRgb.b, - 0.5 * fRgb.r - 0.5 * fRgb.b, - -0.25 * fRgb.r + 0.5 * fRgb.g - 0.25 * fRgb.b); - - return fYCoCg; -} -#endif - -FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb) -{ - return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f)); -} -#if FFX_HALF -FFX_MIN16_F RGBToLuma(FFX_MIN16_F3 fLinearRgb) -{ - return dot(fLinearRgb, FFX_MIN16_F3(0.2126f, 0.7152f, 0.0722f)); -} -#endif - -FfxFloat32 RGBToPerceivedLuma(FfxFloat32x3 fLinearRgb) -{ - FfxFloat32 fLuminance = RGBToLuma(fLinearRgb); - - FfxFloat32 fPercievedLuminance = 0; - if (fLuminance <= 216.0f / 24389.0f) { - fPercievedLuminance = fLuminance * (24389.0f / 27.0f); - } - else { - fPercievedLuminance = ffxPow(fLuminance, 1.0f / 3.0f) * 116.0f - 16.0f; - } - - return fPercievedLuminance * 0.01f; -} -#if FFX_HALF -FFX_MIN16_F RGBToPerceivedLuma(FFX_MIN16_F3 fLinearRgb) -{ - FFX_MIN16_F fLuminance = RGBToLuma(fLinearRgb); - - FFX_MIN16_F fPercievedLuminance = FFX_MIN16_F(0); - if (fLuminance <= FFX_MIN16_F(216.0f / 24389.0f)) { - fPercievedLuminance = fLuminance * FFX_MIN16_F(24389.0f / 27.0f); - } - else { - fPercievedLuminance = ffxPow(fLuminance, FFX_MIN16_F(1.0f / 3.0f)) * FFX_MIN16_F(116.0f) - FFX_MIN16_F(16.0f); - } - - return fPercievedLuminance * FFX_MIN16_F(0.01f); -} -#endif - -FfxFloat32x3 Tonemap(FfxFloat32x3 fRgb) -{ - return fRgb / (ffxMax(ffxMax(0.f, fRgb.r), ffxMax(fRgb.g, fRgb.b)) + 1.f).xxx; -} - -FfxFloat32x3 InverseTonemap(FfxFloat32x3 fRgb) -{ - return fRgb / ffxMax(FSR3UPSCALER_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; -} - -#if FFX_HALF -FFX_MIN16_F3 Tonemap(FFX_MIN16_F3 fRgb) -{ - return fRgb / (ffxMax(ffxMax(FFX_MIN16_F(0.f), fRgb.r), ffxMax(fRgb.g, fRgb.b)) + FFX_MIN16_F(1.f)).xxx; -} - -FFX_MIN16_F3 InverseTonemap(FFX_MIN16_F3 fRgb) -{ - return fRgb / ffxMax(FFX_MIN16_F(FSR3UPSCALER_TONEMAP_EPSILON), FFX_MIN16_F(1.f) - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; -} -#endif - -FfxInt32x2 ClampLoad(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) -{ - FfxInt32x2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; - result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; - result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; - result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; - return result; - - // return ffxMed3(iPxSample + iPxOffset, FfxInt32x2(0, 0), iTextureSize - FfxInt32x2(1, 1)); -} -#if FFX_HALF -FFX_MIN16_I2 ClampLoad(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) -{ - FFX_MIN16_I2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < 0) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; - result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; - result.y = (iPxOffset.y < 0) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; - result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; - return result; - - // return ffxMed3Half(iPxSample + iPxOffset, FFX_MIN16_I2(0, 0), iTextureSize - FFX_MIN16_I2(1, 1)); -} -#endif - -FfxFloat32x2 ClampUv(FfxFloat32x2 fUv, FfxInt32x2 iTextureSize, FfxInt32x2 iResourceSize) -{ - const FfxFloat32x2 fSampleLocation = fUv * iTextureSize; - const FfxFloat32x2 fClampedLocation = ffxMax(FfxFloat32x2(0.5f, 0.5f), ffxMin(fSampleLocation, FfxFloat32x2(iTextureSize) - FfxFloat32x2(0.5f, 0.5f))); - const FfxFloat32x2 fClampedUv = fClampedLocation / FfxFloat32x2(iResourceSize); - - return fClampedUv; -} - -FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size) -{ - return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size))); -} -#if FFX_HALF -FfxBoolean IsOnScreen(FFX_MIN16_I2 pos, FFX_MIN16_I2 size) -{ - return all(FFX_LESS_THAN(FFX_MIN16_U2(pos), FFX_MIN16_U2(size))); -} -#endif - -FfxFloat32 ComputeAutoExposureFromLavg(FfxFloat32 Lavg) -{ - Lavg = exp(Lavg); - - const FfxFloat32 S = 100.0f; //ISO arithmetic speed - const FfxFloat32 K = 12.5f; - FfxFloat32 ExposureISO100 = log2((Lavg * S) / K); - - const FfxFloat32 q = 0.65f; - FfxFloat32 Lmax = (78.0f / (q * S)) * ffxPow(2.0f, ExposureISO100); - - return 1 / Lmax; -} -#if FFX_HALF -FFX_MIN16_F ComputeAutoExposureFromLavg(FFX_MIN16_F Lavg) -{ - Lavg = exp(Lavg); - - const FFX_MIN16_F S = FFX_MIN16_F(100.0f); //ISO arithmetic speed - const FFX_MIN16_F K = FFX_MIN16_F(12.5f); - const FFX_MIN16_F ExposureISO100 = log2((Lavg * S) / K); - - const FFX_MIN16_F q = FFX_MIN16_F(0.65f); - const FFX_MIN16_F Lmax = (FFX_MIN16_F(78.0f) / (q * S)) * ffxPow(FFX_MIN16_F(2.0f), ExposureISO100); - - return FFX_MIN16_F(1) / Lmax; -} -#endif - -FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos) -{ - FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter(); - FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * DisplaySize(); - FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr)); - return iPxHrPos; -} -#if FFX_HALF -FFX_MIN16_I2 ComputeHrPosFromLrPos(FFX_MIN16_I2 iPxLrPos) -{ - FFX_MIN16_F2 fSrcJitteredPos = FFX_MIN16_F2(iPxLrPos) + FFX_MIN16_F(0.5f) - FFX_MIN16_F2(Jitter()); - FFX_MIN16_F2 fLrPosInHr = (fSrcJitteredPos / FFX_MIN16_F2(RenderSize())) * FFX_MIN16_F2(DisplaySize()); - FFX_MIN16_I2 iPxHrPos = FFX_MIN16_I2(floor(fLrPosInHr)); - return iPxHrPos; -} -#endif - -FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize) -{ - return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f); -} - -FfxFloat32 GetViewSpaceDepth(FfxFloat32 fDeviceDepth) -{ - const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); - - // fDeviceToViewDepth details found in ffx_fsr3upscaler.cpp - return (fDeviceToViewDepth[1] / (fDeviceDepth - fDeviceToViewDepth[0])); -} - -FfxFloat32 GetViewSpaceDepthInMeters(FfxFloat32 fDeviceDepth) -{ - return GetViewSpaceDepth(fDeviceDepth) * ViewSpaceToMetersFactor(); -} - -FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) -{ - const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); - - const FfxFloat32 Z = GetViewSpaceDepth(fDeviceDepth); - - const FfxFloat32x2 fNdcPos = ComputeNdc(iViewportPos, iViewportSize); - const FfxFloat32 X = fDeviceToViewDepth[2] * fNdcPos.x * Z; - const FfxFloat32 Y = fDeviceToViewDepth[3] * fNdcPos.y * Z; - - return FfxFloat32x3(X, Y, Z); -} - -FfxFloat32x3 GetViewSpacePositionInMeters(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) -{ - return GetViewSpacePosition(iViewportPos, iViewportSize, fDeviceDepth) * ViewSpaceToMetersFactor(); -} - -FfxFloat32 GetMaxDistanceInMeters() -{ -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - return GetViewSpaceDepth(0.0f) * ViewSpaceToMetersFactor(); -#else - return GetViewSpaceDepth(1.0f) * ViewSpaceToMetersFactor(); -#endif -} - -FfxFloat32x3 PrepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure, FfxFloat32 fPreExposure) -{ - fRgb /= fPreExposure; - fRgb *= fExposure; - - fRgb = clamp(fRgb, 0.0f, FSR3UPSCALER_FP16_MAX); - - return fRgb; -} - -FfxFloat32x3 UnprepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure) -{ - fRgb /= fExposure; - fRgb *= PreExposure(); - - return fRgb; -} - - -struct BilinearSamplingData -{ - FfxInt32x2 iOffsets[4]; - FfxFloat32 fWeights[4]; - FfxInt32x2 iBasePos; -}; - -BilinearSamplingData GetBilinearSamplingData(FfxFloat32x2 fUv, FfxInt32x2 iSize) -{ - BilinearSamplingData data; - - FfxFloat32x2 fPxSample = (fUv * iSize) - FfxFloat32x2(0.5f, 0.5f); - data.iBasePos = FfxInt32x2(floor(fPxSample)); - FfxFloat32x2 fPxFrac = ffxFract(fPxSample); - - data.iOffsets[0] = FfxInt32x2(0, 0); - data.iOffsets[1] = FfxInt32x2(1, 0); - data.iOffsets[2] = FfxInt32x2(0, 1); - data.iOffsets[3] = FfxInt32x2(1, 1); - - data.fWeights[0] = (1 - fPxFrac.x) * (1 - fPxFrac.y); - data.fWeights[1] = (fPxFrac.x) * (1 - fPxFrac.y); - data.fWeights[2] = (1 - fPxFrac.x) * (fPxFrac.y); - data.fWeights[3] = (fPxFrac.x) * (fPxFrac.y); - - return data; -} - -struct PlaneData -{ - FfxFloat32x3 fNormal; - FfxFloat32 fDistanceFromOrigin; -}; - -PlaneData GetPlaneFromPoints(FfxFloat32x3 fP0, FfxFloat32x3 fP1, FfxFloat32x3 fP2) -{ - PlaneData plane; - - FfxFloat32x3 v0 = fP0 - fP1; - FfxFloat32x3 v1 = fP0 - fP2; - plane.fNormal = normalize(cross(v0, v1)); - plane.fDistanceFromOrigin = -dot(fP0, plane.fNormal); - - return plane; -} - -FfxFloat32 PointToPlaneDistance(PlaneData plane, FfxFloat32x3 fPoint) -{ - return abs(dot(plane.fNormal, fPoint) + plane.fDistanceFromOrigin); -} - -#endif // #if defined(FFX_GPU) - -#endif //!defined(FFX_FSR3UPSCALER_COMMON_H) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta deleted file mode 100644 index 08b2046..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 6a638bec681caac4fa8e2ca198726694 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h deleted file mode 100644 index d26cf23..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h +++ /dev/null @@ -1,176 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -FFX_GROUPSHARED FfxUInt32 spdCounter; - -void SpdIncreaseAtomicCounter(FfxUInt32 slice) -{ - SPD_IncreaseAtomicCounter(spdCounter); -} - -FfxUInt32 SpdGetAtomicCounter() -{ - return spdCounter; -} - -void SpdResetAtomicCounter(FfxUInt32 slice) -{ - SPD_ResetAtomicCounter(); -} - -#ifndef SPD_PACKED_ONLY -FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16]; -FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16]; -FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16]; -FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16]; - -FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 tex, FfxUInt32 slice) -{ - FfxFloat32x2 fUv = (tex + 0.5f + Jitter()) / RenderSize(); - fUv = ClampUv(fUv, RenderSize(), InputColorResourceDimensions()); - FfxFloat32x3 fRgb = SampleInputColor(fUv); - - fRgb /= PreExposure(); - - //compute log luma - const FfxFloat32 fLogLuma = log(ffxMax(FSR3UPSCALER_EPSILON, RGBToLuma(fRgb))); - - // Make sure out of screen pixels contribute no value to the end result - const FfxFloat32 result = all(FFX_LESS_THAN(tex, RenderSize())) ? fLogLuma : 0.0f; - - return FfxFloat32x4(result, 0, 0, 0); -} - -FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice) -{ - return SPD_LoadMipmap5(tex); -} - -void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice) -{ - if (index == LumaMipLevelToUse() || index == 5) - { - SPD_SetMipmap(pix, index, outValue.r); - } - - if (index == MipCount() - 1) { //accumulate on 1x1 level - - if (all(FFX_EQUAL(pix, FfxInt32x2(0, 0)))) - { - FfxFloat32 prev = SPD_LoadExposureBuffer().y; - FfxFloat32 result = outValue.r; - - if (prev < resetAutoExposureAverageSmoothing) // Compare Lavg, so small or negative values - { - FfxFloat32 rate = 1.0f; - result = prev + (result - prev) * (1 - exp(-DeltaTime() * rate)); - } - FfxFloat32x2 spdOutput = FfxFloat32x2(ComputeAutoExposureFromLavg(result), result); - SPD_SetExposureBuffer(spdOutput); - } - } -} - -FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) -{ - return FfxFloat32x4( - spdIntermediateR[x][y], - spdIntermediateG[x][y], - spdIntermediateB[x][y], - spdIntermediateA[x][y]); -} -void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) -{ - spdIntermediateR[x][y] = value.x; - spdIntermediateG[x][y] = value.y; - spdIntermediateB[x][y] = value.z; - spdIntermediateA[x][y] = value.w; -} -FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) -{ - return (v0 + v1 + v2 + v3) * 0.25f; -} -#endif - -// define fetch and store functions Packed -#if FFX_HALF - -FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16]; -FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16]; - -FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice) -{ - return FfxFloat16x4(0, 0, 0, 0); -} - -FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice) -{ - return FfxFloat16x4(0, 0, 0, 0); -} - -void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice) -{ -} - -FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y) -{ - return FfxFloat16x4( - spdIntermediateRG[x][y].x, - spdIntermediateRG[x][y].y, - spdIntermediateBA[x][y].x, - spdIntermediateBA[x][y].y); -} - -void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value) -{ - spdIntermediateRG[x][y] = value.xy; - spdIntermediateBA[x][y] = value.zw; -} - -FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3) -{ - return (v0 + v1 + v2 + v3) * FfxFloat16(0.25); -} -#endif - -#include "spd/ffx_spd.h" - -void ComputeAutoExposure(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex) -{ -#if FFX_HALF - SpdDownsampleH( - FfxUInt32x2(WorkGroupId.xy), - FfxUInt32(LocalThreadIndex), - FfxUInt32(MipCount()), - FfxUInt32(NumWorkGroups()), - FfxUInt32(WorkGroupId.z), - FfxUInt32x2(WorkGroupOffset())); -#else - SpdDownsample( - FfxUInt32x2(WorkGroupId.xy), - FfxUInt32(LocalThreadIndex), - FfxUInt32(MipCount()), - FfxUInt32(NumWorkGroups()), - FfxUInt32(WorkGroupId.z), - FfxUInt32x2(WorkGroupOffset())); -#endif -} diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta deleted file mode 100644 index 9fb7653..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 61bd10363d44ee2478461c9e9efbcb67 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h deleted file mode 100644 index 53763c8..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h +++ /dev/null @@ -1,259 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_DEPTH_CLIP_H -#define FFX_FSR3UPSCALER_DEPTH_CLIP_H - -FFX_STATIC const FfxFloat32 DepthClipBaseScale = 4.0f; - -FfxFloat32 ComputeDepthClip(FfxFloat32x2 fUvSample, FfxFloat32 fCurrentDepthSample) -{ - FfxFloat32 fCurrentDepthViewSpace = GetViewSpaceDepth(fCurrentDepthSample); - BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fUvSample, RenderSize()); - - FfxFloat32 fDilatedSum = 0.0f; - FfxFloat32 fDepth = 0.0f; - FfxFloat32 fWeightSum = 0.0f; - for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { - - const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; - const FfxInt32x2 iSamplePos = bilinearInfo.iBasePos + iOffset; - - if (IsOnScreen(iSamplePos, RenderSize())) { - const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; - if (fWeight > fReconstructedDepthBilinearWeightThreshold) { - - const FfxFloat32 fPrevDepthSample = LoadReconstructedPrevDepth(iSamplePos); - const FfxFloat32 fPrevNearestDepthViewSpace = GetViewSpaceDepth(fPrevDepthSample); - - const FfxFloat32 fDepthDiff = fCurrentDepthViewSpace - fPrevNearestDepthViewSpace; - - if (fDepthDiff > 0.0f) { - -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - const FfxFloat32 fPlaneDepth = ffxMin(fPrevDepthSample, fCurrentDepthSample); -#else - const FfxFloat32 fPlaneDepth = ffxMax(fPrevDepthSample, fCurrentDepthSample); -#endif - - const FfxFloat32x3 fCenter = GetViewSpacePosition(FfxInt32x2(RenderSize() * 0.5f), RenderSize(), fPlaneDepth); - const FfxFloat32x3 fCorner = GetViewSpacePosition(FfxInt32x2(0, 0), RenderSize(), fPlaneDepth); - - const FfxFloat32 fHalfViewportWidth = length(FfxFloat32x2(RenderSize())); - const FfxFloat32 fDepthThreshold = ffxMax(fCurrentDepthViewSpace, fPrevNearestDepthViewSpace); - - const FfxFloat32 Ksep = 1.37e-05f; - const FfxFloat32 Kfov = length(fCorner) / length(fCenter); - const FfxFloat32 fRequiredDepthSeparation = Ksep * Kfov * fHalfViewportWidth * fDepthThreshold; - - const FfxFloat32 fResolutionFactor = ffxSaturate(length(FfxFloat32x2(RenderSize())) / length(FfxFloat32x2(1920.0f, 1080.0f))); - const FfxFloat32 fPower = ffxLerp(1.0f, 3.0f, fResolutionFactor); - fDepth += ffxPow(ffxSaturate(FfxFloat32(fRequiredDepthSeparation / fDepthDiff)), fPower) * fWeight; - fWeightSum += fWeight; - } - } - } - } - - return (fWeightSum > 0) ? ffxSaturate(1.0f - fDepth / fWeightSum) : 0.0f; -} - -FfxFloat32 ComputeMotionDivergence(FfxInt32x2 iPxPos, FfxInt32x2 iPxInputMotionVectorSize) -{ - FfxFloat32 minconvergence = 1.0f; - - FfxFloat32x2 fMotionVectorNucleus = LoadInputMotionVector(iPxPos); - FfxFloat32 fNucleusVelocityLr = length(fMotionVectorNucleus * RenderSize()); - FfxFloat32 fMaxVelocityUv = length(fMotionVectorNucleus); - - const FfxFloat32 MotionVectorVelocityEpsilon = 1e-02f; - - if (fNucleusVelocityLr > MotionVectorVelocityEpsilon) { - for (FfxInt32 y = -1; y <= 1; ++y) { - for (FfxInt32 x = -1; x <= 1; ++x) { - - FfxInt32x2 sp = ClampLoad(iPxPos, FfxInt32x2(x, y), iPxInputMotionVectorSize); - - FfxFloat32x2 fMotionVector = LoadInputMotionVector(sp); - FfxFloat32 fVelocityUv = length(fMotionVector); - - fMaxVelocityUv = ffxMax(fVelocityUv, fMaxVelocityUv); - fVelocityUv = ffxMax(fVelocityUv, fMaxVelocityUv); - minconvergence = ffxMin(minconvergence, dot(fMotionVector / fVelocityUv, fMotionVectorNucleus / fVelocityUv)); - } - } - } - - return ffxSaturate(1.0f - minconvergence) * ffxSaturate(fMaxVelocityUv / 0.01f); -} - -FfxFloat32 ComputeDepthDivergence(FfxInt32x2 iPxPos) -{ - const FfxFloat32 fMaxDistInMeters = GetMaxDistanceInMeters(); - FfxFloat32 fDepthMax = 0.0f; - FfxFloat32 fDepthMin = fMaxDistInMeters; - - FfxInt32 iMaxDistFound = 0; - - for (FfxInt32 y = -1; y < 2; y++) { - for (FfxInt32 x = -1; x < 2; x++) { - - const FfxInt32x2 iOffset = FfxInt32x2(x, y); - const FfxInt32x2 iSamplePos = iPxPos + iOffset; - - const FfxFloat32 fOnScreenFactor = IsOnScreen(iSamplePos, RenderSize()) ? 1.0f : 0.0f; - FfxFloat32 fDepth = GetViewSpaceDepthInMeters(LoadDilatedDepth(iSamplePos)) * fOnScreenFactor; - - iMaxDistFound |= FfxInt32(fMaxDistInMeters == fDepth); - - fDepthMin = ffxMin(fDepthMin, fDepth); - fDepthMax = ffxMax(fDepthMax, fDepth); - } - } - - return (1.0f - fDepthMin / fDepthMax) * (FfxBoolean(iMaxDistFound) ? 0.0f : 1.0f); -} - -FfxFloat32 ComputeTemporalMotionDivergence(FfxInt32x2 iPxPos) -{ - const FfxFloat32x2 fUv = FfxFloat32x2(iPxPos + 0.5f) / RenderSize(); - - FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); - FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; - fReprojectedUv = ClampUv(fReprojectedUv, RenderSize(), MaxRenderSize()); - FfxFloat32x2 fPrevMotionVector = SamplePreviousDilatedMotionVector(fReprojectedUv); - - float fPxDistance = length(fMotionVector * DisplaySize()); - return fPxDistance > 1.0f ? ffxLerp(0.0f, 1.0f - ffxSaturate(length(fPrevMotionVector) / length(fMotionVector)), ffxSaturate(ffxPow(fPxDistance / 20.0f, 3.0f))) : 0; -} - -void PreProcessReactiveMasks(FfxInt32x2 iPxLrPos, FfxFloat32 fMotionDivergence) -{ - // Compensate for bilinear sampling in accumulation pass - - FfxFloat32x3 fReferenceColor = LoadInputColor(iPxLrPos).xyz; - FfxFloat32x2 fReactiveFactor = FfxFloat32x2(0.0f, fMotionDivergence); - - float fMasksSum = 0.0f; - - FfxFloat32x3 fColorSamples[9]; - FfxFloat32 fReactiveSamples[9]; - FfxFloat32 fTransparencyAndCompositionSamples[9]; - - FFX_UNROLL - for (FfxInt32 y = -1; y < 2; y++) { - FFX_UNROLL - for (FfxInt32 x = -1; x < 2; x++) { - - const FfxInt32x2 sampleCoord = ClampLoad(iPxLrPos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); - - FfxInt32 sampleIdx = (y + 1) * 3 + x + 1; - - FfxFloat32x3 fColorSample = LoadInputColor(sampleCoord).xyz; - FfxFloat32 fReactiveSample = LoadReactiveMask(sampleCoord); - FfxFloat32 fTransparencyAndCompositionSample = LoadTransparencyAndCompositionMask(sampleCoord); - - fColorSamples[sampleIdx] = fColorSample; - fReactiveSamples[sampleIdx] = fReactiveSample; - fTransparencyAndCompositionSamples[sampleIdx] = fTransparencyAndCompositionSample; - - fMasksSum += (fReactiveSample + fTransparencyAndCompositionSample); - } - } - - if (fMasksSum > 0) - { - for (FfxInt32 sampleIdx = 0; sampleIdx < 9; sampleIdx++) - { - FfxFloat32x3 fColorSample = fColorSamples[sampleIdx]; - FfxFloat32 fReactiveSample = fReactiveSamples[sampleIdx]; - FfxFloat32 fTransparencyAndCompositionSample = fTransparencyAndCompositionSamples[sampleIdx]; - - const FfxFloat32 fMaxLenSq = ffxMax(dot(fReferenceColor, fReferenceColor), dot(fColorSample, fColorSample)); - const FfxFloat32 fSimilarity = dot(fReferenceColor, fColorSample) / fMaxLenSq; - - // Increase power for non-similar samples - const FfxFloat32 fPowerBiasMax = 6.0f; - const FfxFloat32 fSimilarityPower = 1.0f + (fPowerBiasMax - fSimilarity * fPowerBiasMax); - const FfxFloat32 fWeightedReactiveSample = ffxPow(fReactiveSample, fSimilarityPower); - const FfxFloat32 fWeightedTransparencyAndCompositionSample = ffxPow(fTransparencyAndCompositionSample, fSimilarityPower); - - fReactiveFactor = ffxMax(fReactiveFactor, FfxFloat32x2(fWeightedReactiveSample, fWeightedTransparencyAndCompositionSample)); - } - } - - StoreDilatedReactiveMasks(iPxLrPos, fReactiveFactor); -} - -FfxFloat32x3 ComputePreparedInputColor(FfxInt32x2 iPxLrPos) -{ - //We assume linear data. if non-linear input (sRGB, ...), - //then we should convert to linear first and back to sRGB on output. - FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxLrPos)); - - fRgb = PrepareRgb(fRgb, Exposure(), PreExposure()); - - const FfxFloat32x3 fPreparedYCoCg = RGBToYCoCg(fRgb); - - return fPreparedYCoCg; -} - -FfxFloat32 EvaluateSurface(FfxInt32x2 iPxPos, FfxFloat32x2 fMotionVector) -{ - FfxFloat32 d0 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, -1))); - FfxFloat32 d1 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, 0))); - FfxFloat32 d2 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, 1))); - - return 1.0f - FfxFloat32(((d0 - d1) > (d1 * 0.01f)) && ((d1 - d2) > (d2 * 0.01f))); -} - -void DepthClip(FfxInt32x2 iPxPos) -{ - FfxFloat32x2 fDepthUv = (iPxPos + 0.5f) / RenderSize(); - FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); - - // Discard tiny mvs - fMotionVector *= FfxFloat32(length(fMotionVector * DisplaySize()) > 0.01f); - - const FfxFloat32x2 fDilatedUv = fDepthUv + fMotionVector; - const FfxFloat32 fDilatedDepth = LoadDilatedDepth(iPxPos); - const FfxFloat32 fCurrentDepthViewSpace = GetViewSpaceDepth(LoadInputDepth(iPxPos)); - - // Compute prepared input color and depth clip - FfxFloat32 fDepthClip = ComputeDepthClip(fDilatedUv, fDilatedDepth) * EvaluateSurface(iPxPos, fMotionVector); - FfxFloat32x3 fPreparedYCoCg = ComputePreparedInputColor(iPxPos); - StorePreparedInputColor(iPxPos, FfxFloat32x4(fPreparedYCoCg, fDepthClip)); - - // Compute dilated reactive mask -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS - FfxInt32x2 iSamplePos = iPxPos; -#else - FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxPos); -#endif - - FfxFloat32 fMotionDivergence = ComputeMotionDivergence(iSamplePos, RenderSize()); - FfxFloat32 fTemporalMotionDifference = ffxSaturate(ComputeTemporalMotionDivergence(iPxPos) - ComputeDepthDivergence(iPxPos)); - - PreProcessReactiveMasks(iPxPos, ffxMax(fTemporalMotionDifference, fMotionDivergence)); -} - -#endif //!defined( FFX_FSR3UPSCALER_DEPTH_CLIPH ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta deleted file mode 100644 index 891d3d1..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 7c662249d70c4434da4f2da00e432c38 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h deleted file mode 100644 index e1a0d06..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h +++ /dev/null @@ -1,116 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_LOCK_H -#define FFX_FSR3UPSCALER_LOCK_H - -void ClearResourcesForNextFrame(in FfxInt32x2 iPxHrPos) -{ - if (all(FFX_LESS_THAN(iPxHrPos, FfxInt32x2(RenderSize())))) - { -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - const FfxUInt32 farZ = 0x0; -#else - const FfxUInt32 farZ = 0x3f800000; -#endif - SetReconstructedDepth(iPxHrPos, farZ); - } -} - -FfxBoolean ComputeThinFeatureConfidence(FfxInt32x2 pos) -{ - const FfxInt32 RADIUS = 1; - - FfxFloat32 fNucleus = LoadLockInputLuma(pos); - - FfxFloat32 similar_threshold = 1.05f; - FfxFloat32 dissimilarLumaMin = FSR3UPSCALER_FLT_MAX; - FfxFloat32 dissimilarLumaMax = 0; - - /* - 0 1 2 - 3 4 5 - 6 7 8 - */ - - #define SETBIT(x) (1U << x) - - FfxUInt32 mask = SETBIT(4); //flag fNucleus as similar - - const FfxUInt32 uNumRejectionMasks = 4; - const FfxUInt32 uRejectionMasks[uNumRejectionMasks] = { - SETBIT(0) | SETBIT(1) | SETBIT(3) | SETBIT(4), //Upper left - SETBIT(1) | SETBIT(2) | SETBIT(4) | SETBIT(5), //Upper right - SETBIT(3) | SETBIT(4) | SETBIT(6) | SETBIT(7), //Lower left - SETBIT(4) | SETBIT(5) | SETBIT(7) | SETBIT(8), //Lower right - }; - - FfxInt32 idx = 0; - FFX_UNROLL - for (FfxInt32 y = -RADIUS; y <= RADIUS; y++) { - FFX_UNROLL - for (FfxInt32 x = -RADIUS; x <= RADIUS; x++, idx++) { - if (x == 0 && y == 0) continue; - - FfxInt32x2 samplePos = ClampLoad(pos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); - - FfxFloat32 sampleLuma = LoadLockInputLuma(samplePos); - FfxFloat32 difference = ffxMax(sampleLuma, fNucleus) / ffxMin(sampleLuma, fNucleus); - - if (difference > 0 && (difference < similar_threshold)) { - mask |= SETBIT(idx); - } else { - dissimilarLumaMin = ffxMin(dissimilarLumaMin, sampleLuma); - dissimilarLumaMax = ffxMax(dissimilarLumaMax, sampleLuma); - } - } - } - - FfxBoolean isRidge = fNucleus > dissimilarLumaMax || fNucleus < dissimilarLumaMin; - - if (FFX_FALSE == isRidge) { - - return false; - } - - FFX_UNROLL - for (FfxInt32 i = 0; i < 4; i++) { - - if ((mask & uRejectionMasks[i]) == uRejectionMasks[i]) { - return false; - } - } - - return true; -} - -void ComputeLock(FfxInt32x2 iPxLrPos) -{ - if (ComputeThinFeatureConfidence(iPxLrPos)) - { - StoreNewLocks(ComputeHrPosFromLrPos(iPxLrPos), 1.f); - } - - // ClearResourcesForNextFrame(iPxLrPos); -} - -#endif // FFX_FSR3UPSCALER_LOCK_H diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta deleted file mode 100644 index 4013169..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: c7e9f53dd040b2645af5ccd936a94b0e -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h deleted file mode 100644 index 3709113..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h +++ /dev/null @@ -1,107 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_POSTPROCESS_LOCK_STATUS_H -#define FFX_FSR3UPSCALER_POSTPROCESS_LOCK_STATUS_H - -FfxFloat32x4 WrapShadingChangeLuma(FfxInt32x2 iPxSample) -{ - return FfxFloat32x4(LoadMipLuma(iPxSample, LumaMipLevelToUse()), 0, 0, 0); -} - -#if FFX_HALF -FFX_MIN16_F4 WrapShadingChangeLuma(FFX_MIN16_I2 iPxSample) -{ - return FFX_MIN16_F4(LoadMipLuma(iPxSample, LumaMipLevelToUse()), 0, 0, 0); -} -#endif - -#if FFX_FSR3UPSCALER_OPTION_POSTPROCESSLOCKSTATUS_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBilinearSamplesMin16(FetchShadingChangeLumaSamples, WrapShadingChangeLuma) -#else -DeclareCustomFetchBicubicSamples(FetchShadingChangeLumaSamples, WrapShadingChangeLuma) -#endif -DeclareCustomTextureSample(ShadingChangeLumaSample, Lanczos2, FetchShadingChangeLumaSamples) - -FfxFloat32 GetShadingChangeLuma(FfxInt32x2 iPxHrPos, FfxFloat32x2 fUvCoord) -{ - FfxFloat32 fShadingChangeLuma = 0; - -#if 0 - fShadingChangeLuma = Exposure() * exp(ShadingChangeLumaSample(fUvCoord, LumaMipDimensions()).x); -#else - - const FfxFloat32 fDiv = FfxFloat32(2u << LumaMipLevelToUse()); - FfxInt32x2 iMipRenderSize = FfxInt32x2(RenderSize() / fDiv); - - fUvCoord = ClampUv(fUvCoord, iMipRenderSize, LumaMipDimensions()); - fShadingChangeLuma = Exposure() * exp(FfxFloat32(SampleMipLuma(fUvCoord, LumaMipLevelToUse()))); -#endif - - fShadingChangeLuma = ffxPow(fShadingChangeLuma, 1.0f / 6.0f); - - return fShadingChangeLuma; -} - -void UpdateLockStatus(AccumulationPassCommonParams params, - FFX_PARAMETER_INOUT FfxFloat32 fReactiveFactor, LockState state, - FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus, - FFX_PARAMETER_OUT FfxFloat32 fLockContributionThisFrame, - FFX_PARAMETER_OUT FfxFloat32 fLuminanceDiff) { - - const FfxFloat32 fShadingChangeLuma = GetShadingChangeLuma(params.iPxHrPos, params.fHrUv); - - //init temporal shading change factor, init to -1 or so in reproject to know if "true new"? - fLockStatus[LOCK_TEMPORAL_LUMA] = (fLockStatus[LOCK_TEMPORAL_LUMA] == FfxFloat32(0.0f)) ? fShadingChangeLuma : fLockStatus[LOCK_TEMPORAL_LUMA]; - - FfxFloat32 fPreviousShadingChangeLuma = fLockStatus[LOCK_TEMPORAL_LUMA]; - - fLuminanceDiff = 1.0f - MinDividedByMax(fPreviousShadingChangeLuma, fShadingChangeLuma); - - if (state.NewLock) { - fLockStatus[LOCK_TEMPORAL_LUMA] = fShadingChangeLuma; - - fLockStatus[LOCK_LIFETIME_REMAINING] = (fLockStatus[LOCK_LIFETIME_REMAINING] != 0.0f) ? 2.0f : 1.0f; - } - else if(fLockStatus[LOCK_LIFETIME_REMAINING] <= 1.0f) { - fLockStatus[LOCK_TEMPORAL_LUMA] = ffxLerp(fLockStatus[LOCK_TEMPORAL_LUMA], FfxFloat32(fShadingChangeLuma), 0.5f); - } - else { - if (fLuminanceDiff > 0.1f) { - KillLock(fLockStatus); - } - } - - fReactiveFactor = ffxMax(fReactiveFactor, ffxSaturate((fLuminanceDiff - 0.1f) * 10.0f)); - fLockStatus[LOCK_LIFETIME_REMAINING] *= (1.0f - fReactiveFactor); - - fLockStatus[LOCK_LIFETIME_REMAINING] *= ffxSaturate(1.0f - params.fAccumulationMask); - fLockStatus[LOCK_LIFETIME_REMAINING] *= FfxFloat32(params.fDepthClipFactor < 0.1f); - - // Compute this frame lock contribution - const FfxFloat32 fLifetimeContribution = ffxSaturate(fLockStatus[LOCK_LIFETIME_REMAINING] - 1.0f); - const FfxFloat32 fShadingChangeContribution = ffxSaturate(MinDividedByMax(fLockStatus[LOCK_TEMPORAL_LUMA], fShadingChangeLuma)); - - fLockContributionThisFrame = ffxSaturate(ffxSaturate(fLifetimeContribution * 4.0f) * fShadingChangeContribution); -} - -#endif //!defined( FFX_FSR3UPSCALER_POSTPROCESS_LOCK_STATUS_H ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta deleted file mode 100644 index 8c8bf49..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 67a8b72ceb93d634f883b086fdccb348 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h deleted file mode 100644 index 77619a5..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define GROUP_SIZE 8 -#define FSR_RCAS_DENOISE 1 - -#include "ffx_core.h" - -void WriteUpscaledOutput(FFX_MIN16_U2 iPxHrPos, FfxFloat32x3 fUpscaledColor) -{ - StoreUpscaledOutput(FFX_MIN16_I2(iPxHrPos), fUpscaledColor); -} - -#define FSR_RCAS_F 1 -FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p) -{ - FfxFloat32x4 fColor = LoadRCAS_Input(p); - - fColor.rgb = PrepareRgb(fColor.rgb, Exposure(), PreExposure()); - - return fColor; -} -void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {} - -#include "fsr1/ffx_fsr1.h" - -void CurrFilter(FFX_MIN16_U2 pos) -{ - FfxFloat32x3 c; - FsrRcasF(c.r, c.g, c.b, pos, RCASConfig()); - - c = UnprepareRgb(c, Exposure()); - - WriteUpscaledOutput(pos, c); -} - -void RCAS(FfxUInt32x3 LocalThreadId, FfxUInt32x3 WorkGroupId, FfxUInt32x3 Dtid) -{ - // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. - FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u); - CurrFilter(FFX_MIN16_U2(gxy)); - gxy.x += 8u; - CurrFilter(FFX_MIN16_U2(gxy)); - gxy.y += 8u; - CurrFilter(FFX_MIN16_U2(gxy)); - gxy.x -= 8u; - CurrFilter(FFX_MIN16_U2(gxy)); -} diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta deleted file mode 100644 index a315002..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 692efb7cec0df67408a583a7ff34146a -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h deleted file mode 100644 index a822dfc..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h +++ /dev/null @@ -1,146 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H -#define FFX_FSR3UPSCALER_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H - -void ReconstructPrevDepth(FfxInt32x2 iPxPos, FfxFloat32 fDepth, FfxFloat32x2 fMotionVector, FfxInt32x2 iPxDepthSize) -{ - fMotionVector *= FfxFloat32(length(fMotionVector * DisplaySize()) > 0.1f); - - FfxFloat32x2 fUv = (iPxPos + FfxFloat32(0.5)) / iPxDepthSize; - FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; - - BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize()); - - // Project current depth into previous frame locations. - // Push to all pixels having some contribution if reprojection is using bilinear logic. - for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { - - const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; - FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; - - if (fWeight > fReconstructedDepthBilinearWeightThreshold) { - - FfxInt32x2 iStorePos = bilinearInfo.iBasePos + iOffset; - if (IsOnScreen(iStorePos, iPxDepthSize)) { - StoreReconstructedDepth(iStorePos, fDepth); - } - } - } -} - -void FindNearestDepth(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxInt32x2 iPxSize, FFX_PARAMETER_OUT FfxFloat32 fNearestDepth, FFX_PARAMETER_OUT FfxInt32x2 fNearestDepthCoord) -{ - const FfxInt32 iSampleCount = 9; - const FfxInt32x2 iSampleOffsets[iSampleCount] = { - FfxInt32x2(+0, +0), - FfxInt32x2(+1, +0), - FfxInt32x2(+0, +1), - FfxInt32x2(+0, -1), - FfxInt32x2(-1, +0), - FfxInt32x2(-1, +1), - FfxInt32x2(+1, +1), - FfxInt32x2(-1, -1), - FfxInt32x2(+1, -1), - }; - - // pull out the depth loads to allow SC to batch them - FfxFloat32 depth[9]; - FfxInt32 iSampleIndex = 0; - FFX_UNROLL - for (iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) { - - FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; - depth[iSampleIndex] = LoadInputDepth(iPos); - } - - // find closest depth - fNearestDepthCoord = iPxPos; - fNearestDepth = depth[0]; - FFX_UNROLL - for (iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex) { - - FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; - if (IsOnScreen(iPos, iPxSize)) { - - FfxFloat32 fNdDepth = depth[iSampleIndex]; -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - if (fNdDepth > fNearestDepth) { -#else - if (fNdDepth < fNearestDepth) { -#endif - fNearestDepthCoord = iPos; - fNearestDepth = fNdDepth; - } - } - } -} - -FfxFloat32 ComputeLockInputLuma(FfxInt32x2 iPxLrPos) -{ - //We assume linear data. if non-linear input (sRGB, ...), - //then we should convert to linear first and back to sRGB on output. - FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxLrPos)); - - // Use internal auto exposure for locking logic - fRgb /= PreExposure(); - fRgb *= Exposure(); - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - fRgb = Tonemap(fRgb); -#endif - - //compute luma used to lock pixels, if used elsewhere the ffxPow must be moved! - const FfxFloat32 fLockInputLuma = ffxPow(RGBToPerceivedLuma(fRgb), FfxFloat32(1.0 / 6.0)); - - return fLockInputLuma; -} - -void ReconstructAndDilate(FfxInt32x2 iPxLrPos) -{ - FfxFloat32 fDilatedDepth; - FfxInt32x2 iNearestDepthCoord; - - FindNearestDepth(iPxLrPos, RenderSize(), fDilatedDepth, iNearestDepthCoord); - -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS - FfxInt32x2 iSamplePos = iPxLrPos; - FfxInt32x2 iMotionVectorPos = iNearestDepthCoord; -#else - FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxLrPos); - FfxInt32x2 iMotionVectorPos = ComputeHrPosFromLrPos(iNearestDepthCoord); -#endif - - FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iMotionVectorPos); - - StoreDilatedDepth(iPxLrPos, fDilatedDepth); - StoreDilatedMotionVector(iPxLrPos, fDilatedMotionVector); - - ReconstructPrevDepth(iPxLrPos, fDilatedDepth, fDilatedMotionVector, RenderSize()); - - FfxFloat32 fLockInputLuma = ComputeLockInputLuma(iPxLrPos); - StoreLockInputLuma(iPxLrPos, fLockInputLuma); -} - - -#endif //!defined( FFX_FSR3UPSCALER_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta deleted file mode 100644 index a1fd018..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: c8b3854bad30a8b40babc5a9805f294e -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h deleted file mode 100644 index 29b7584..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h +++ /dev/null @@ -1,137 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_REPROJECT_H -#define FFX_FSR3UPSCALER_REPROJECT_H - -#ifndef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 0 // Reference -#endif - -FfxFloat32x4 WrapHistory(FfxInt32x2 iPxSample) -{ - return LoadHistory(iPxSample); -} - -#if FFX_HALF -FFX_MIN16_F4 WrapHistory(FFX_MIN16_I2 iPxSample) -{ - return FFX_MIN16_F4(LoadHistory(iPxSample)); -} -#endif - - -#if FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBicubicSamplesMin16(FetchHistorySamples, WrapHistory) -DeclareCustomTextureSampleMin16(HistorySample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) -#else -DeclareCustomFetchBicubicSamples(FetchHistorySamples, WrapHistory) -DeclareCustomTextureSample(HistorySample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) -#endif - -FfxFloat32x4 WrapLockStatus(FfxInt32x2 iPxSample) -{ - FfxFloat32x4 fSample = FfxFloat32x4(LoadLockStatus(iPxSample), 0.0f, 0.0f); - return fSample; -} - -#if FFX_HALF -FFX_MIN16_F4 WrapLockStatus(FFX_MIN16_I2 iPxSample) -{ - FFX_MIN16_F4 fSample = FFX_MIN16_F4(LoadLockStatus(iPxSample), 0.0, 0.0); - - return fSample; -} -#endif - -#if 1 -#if FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBilinearSamplesMin16(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSampleMin16(LockStatusSample, Bilinear, FetchLockStatusSamples) -#else -DeclareCustomFetchBilinearSamples(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSample(LockStatusSample, Bilinear, FetchLockStatusSamples) -#endif -#else -#if FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBicubicSamplesMin16(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSampleMin16(LockStatusSample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchLockStatusSamples) -#else -DeclareCustomFetchBicubicSamples(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSample(LockStatusSample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchLockStatusSamples) -#endif -#endif - -FfxFloat32x2 GetMotionVector(FfxInt32x2 iPxHrPos, FfxFloat32x2 fHrUv) -{ -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS - FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(FFX_MIN16_I2(fHrUv * RenderSize())); -#else - FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iPxHrPos); -#endif - - return fDilatedMotionVector; -} - -FfxBoolean IsUvInside(FfxFloat32x2 fUv) -{ - return (fUv.x >= 0.0f && fUv.x <= 1.0f) && (fUv.y >= 0.0f && fUv.y <= 1.0f); -} - -void ComputeReprojectedUVs(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedHrUv, FFX_PARAMETER_OUT FfxBoolean bIsExistingSample) -{ - fReprojectedHrUv = params.fHrUv + params.fMotionVector; - - bIsExistingSample = IsUvInside(fReprojectedHrUv); -} - -void ReprojectHistoryColor(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x3 fHistoryColor, FFX_PARAMETER_OUT FfxFloat32 fTemporalReactiveFactor, FFX_PARAMETER_OUT FfxBoolean bInMotionLastFrame) -{ - FfxFloat32x4 fHistory = HistorySample(params.fReprojectedHrUv, DisplaySize()); - - fHistoryColor = PrepareRgb(fHistory.rgb, Exposure(), PreviousFramePreExposure()); - - fHistoryColor = RGBToYCoCg(fHistoryColor); - - //Compute temporal reactivity info - fTemporalReactiveFactor = ffxSaturate(abs(fHistory.w)); - bInMotionLastFrame = (fHistory.w < 0.0f); -} - -LockState ReprojectHistoryLockStatus(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedLockStatus) -{ - LockState state = { FFX_FALSE, FFX_FALSE }; - const FfxFloat32 fNewLockIntensity = LoadRwNewLocks(params.iPxHrPos); - state.NewLock = fNewLockIntensity > (127.0f / 255.0f); - - FfxFloat32 fInPlaceLockLifetime = state.NewLock ? fNewLockIntensity : 0; - - fReprojectedLockStatus = SampleLockStatus(params.fReprojectedHrUv); - - if (fReprojectedLockStatus[LOCK_LIFETIME_REMAINING] != FfxFloat32(0.0f)) { - state.WasLockedPrevFrame = true; - } - - return state; -} - -#endif //!defined( FFX_FSR3UPSCALER_REPROJECT_H ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta deleted file mode 100644 index 3fa39db..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 9d893016eebb2564f9a66b80afb0849f -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h deleted file mode 100644 index d98cfcc..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h +++ /dev/null @@ -1,104 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_RESOURCES_H -#define FFX_FSR3UPSCALER_RESOURCES_H - -#if defined(FFX_CPU) || defined(FFX_GPU) -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NULL 0 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_OPAQUE_ONLY 1 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_COLOR 2 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 3 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_DEPTH 4 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_EXPOSURE 5 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_REACTIVE_MASK 6 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_TRANSPARENCY_AND_COMPOSITION_MASK 7 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RECONSTRUCTED_PREVIOUS_NEAREST_DEPTH 8 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_MOTION_VECTORS 9 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_DEPTH 10 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR 11 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_STATUS 12 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NEW_LOCKS 13 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREPARED_INPUT_COLOR 14 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY 15 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DEBUG_OUTPUT 16 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LANCZOS_LUT 17 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_ATOMIC_COUNT 18 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_UPSCALED_OUTPUT 19 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RCAS_INPUT 20 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_STATUS_1 21 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_STATUS_2 22 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_1 23 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_2 24 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_REACTIVITY 25 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_TRANSPARENCY_AND_COMPOSITION 26 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTITIER_UPSAMPLE_MAXIMUM_BIAS_LUT 27 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_REACTIVE_MASKS 28 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE 29 // same as FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 29 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_1 30 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_2 31 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_3 32 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 33 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_5 34 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_6 35 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_7 36 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_8 37 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_9 38 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_10 39 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_11 40 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12 41 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_EXPOSURE 42 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTO_EXPOSURE 43 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOREACTIVE 44 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOCOMPOSITION_DEPRECATED 45 - -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR 46 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR 47 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_1 48 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_1 49 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_2 50 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_2 51 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREVIOUS_DILATED_MOTION_VECTORS 52 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_1 53 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_2 54 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_INPUT_LUMA 55 - -// Shading change detection mip level setting, value must be in the range [FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0, FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12] -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 -#define FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL (FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE - FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE) - -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT 56 - -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_FSR3UPSCALER 0 -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_SPD 1 -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_RCAS 2 -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_GENREACTIVE 3 - -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP 1 -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP 2 -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD 4 -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX 8 - -#endif // #if defined(FFX_CPU) || defined(FFX_GPU) - -#endif //!defined( FFX_FSR3UPSCALER_RESOURCES_H ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta deleted file mode 100644 index 8e862df..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: b5a95a38dcfaf3946a5095bbbc42939a -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h deleted file mode 100644 index d33f70c..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h +++ /dev/null @@ -1,606 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_SAMPLE_H -#define FFX_FSR3UPSCALER_SAMPLE_H - -// suppress warnings -#ifdef FFX_HLSL -#pragma warning(disable: 4008) // potentially divide by zero -#endif //FFX_HLSL - -struct FetchedBilinearSamples { - - FfxFloat32x4 fColor00; - FfxFloat32x4 fColor10; - - FfxFloat32x4 fColor01; - FfxFloat32x4 fColor11; -}; - -struct FetchedBicubicSamples { - - FfxFloat32x4 fColor00; - FfxFloat32x4 fColor10; - FfxFloat32x4 fColor20; - FfxFloat32x4 fColor30; - - FfxFloat32x4 fColor01; - FfxFloat32x4 fColor11; - FfxFloat32x4 fColor21; - FfxFloat32x4 fColor31; - - FfxFloat32x4 fColor02; - FfxFloat32x4 fColor12; - FfxFloat32x4 fColor22; - FfxFloat32x4 fColor32; - - FfxFloat32x4 fColor03; - FfxFloat32x4 fColor13; - FfxFloat32x4 fColor23; - FfxFloat32x4 fColor33; -}; - -#if FFX_HALF -struct FetchedBilinearSamplesMin16 { - - FFX_MIN16_F4 fColor00; - FFX_MIN16_F4 fColor10; - - FFX_MIN16_F4 fColor01; - FFX_MIN16_F4 fColor11; -}; - -struct FetchedBicubicSamplesMin16 { - - FFX_MIN16_F4 fColor00; - FFX_MIN16_F4 fColor10; - FFX_MIN16_F4 fColor20; - FFX_MIN16_F4 fColor30; - - FFX_MIN16_F4 fColor01; - FFX_MIN16_F4 fColor11; - FFX_MIN16_F4 fColor21; - FFX_MIN16_F4 fColor31; - - FFX_MIN16_F4 fColor02; - FFX_MIN16_F4 fColor12; - FFX_MIN16_F4 fColor22; - FFX_MIN16_F4 fColor32; - - FFX_MIN16_F4 fColor03; - FFX_MIN16_F4 fColor13; - FFX_MIN16_F4 fColor23; - FFX_MIN16_F4 fColor33; -}; -#else //FFX_HALF -#define FetchedBicubicSamplesMin16 FetchedBicubicSamples -#define FetchedBilinearSamplesMin16 FetchedBilinearSamples -#endif //FFX_HALF - -FfxFloat32x4 Linear(FfxFloat32x4 A, FfxFloat32x4 B, FfxFloat32 t) -{ - return A + (B - A) * t; -} - -FfxFloat32x4 Bilinear(FetchedBilinearSamples BilinearSamples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); - FfxFloat32x4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); - FfxFloat32x4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Linear(FFX_MIN16_F4 A, FFX_MIN16_F4 B, FFX_MIN16_F t) -{ - return A + (B - A) * t; -} - -FFX_MIN16_F4 Bilinear(FetchedBilinearSamplesMin16 BilinearSamples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); - return fColorXY; -} -#endif - -FfxFloat32 Lanczos2NoClamp(FfxFloat32 x) -{ - const FfxFloat32 PI = 3.141592653589793f; // TODO: share SDK constants - return abs(x) < FSR3UPSCALER_EPSILON ? 1.f : (sin(PI * x) / (PI * x)) * (sin(0.5f * PI * x) / (0.5f * PI * x)); -} - -FfxFloat32 Lanczos2(FfxFloat32 x) -{ - x = ffxMin(abs(x), 2.0f); - return Lanczos2NoClamp(x); -} - -#if FFX_HALF - -#if 0 -FFX_MIN16_F Lanczos2NoClamp(FFX_MIN16_F x) -{ - const FFX_MIN16_F PI = FFX_MIN16_F(3.141592653589793f); // TODO: share SDK constants - return abs(x) < FFX_MIN16_F(FSR3UPSCALER_EPSILON) ? FFX_MIN16_F(1.f) : (sin(PI * x) / (PI * x)) * (sin(FFX_MIN16_F(0.5f) * PI * x) / (FFX_MIN16_F(0.5f) * PI * x)); -} -#endif - -FFX_MIN16_F Lanczos2(FFX_MIN16_F x) -{ - x = ffxMin(abs(x), FFX_MIN16_F(2.0f)); - return FFX_MIN16_F(Lanczos2NoClamp(x)); -} -#endif //FFX_HALF - -// FSR1 lanczos approximation. Input is x*x and must be <= 4. -FfxFloat32 Lanczos2ApproxSqNoClamp(FfxFloat32 x2) -{ - FfxFloat32 a = (2.0f / 5.0f) * x2 - 1; - FfxFloat32 b = (1.0f / 4.0f) * x2 - 1; - return ((25.0f / 16.0f) * a * a - (25.0f / 16.0f - 1)) * (b * b); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2ApproxSqNoClamp(FFX_MIN16_F x2) -{ - FFX_MIN16_F a = FFX_MIN16_F(2.0f / 5.0f) * x2 - FFX_MIN16_F(1); - FFX_MIN16_F b = FFX_MIN16_F(1.0f / 4.0f) * x2 - FFX_MIN16_F(1); - return (FFX_MIN16_F(25.0f / 16.0f) * a * a - FFX_MIN16_F(25.0f / 16.0f - 1)) * (b * b); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2ApproxSq(FfxFloat32 x2) -{ - x2 = ffxMin(x2, 4.0f); - return Lanczos2ApproxSqNoClamp(x2); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2ApproxSq(FFX_MIN16_F x2) -{ - x2 = ffxMin(x2, FFX_MIN16_F(4.0f)); - return Lanczos2ApproxSqNoClamp(x2); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2ApproxNoClamp(FfxFloat32 x) -{ - return Lanczos2ApproxSqNoClamp(x * x); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2ApproxNoClamp(FFX_MIN16_F x) -{ - return Lanczos2ApproxSqNoClamp(x * x); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2Approx(FfxFloat32 x) -{ - return Lanczos2ApproxSq(x * x); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2Approx(FFX_MIN16_F x) -{ - return Lanczos2ApproxSq(x * x); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2_UseLUT(FfxFloat32 x) -{ - return SampleLanczos2Weight(abs(x)); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2_UseLUT(FFX_MIN16_F x) -{ - return FFX_MIN16_F(SampleLanczos2Weight(abs(x))); -} -#endif //FFX_HALF - -FfxFloat32x4 Lanczos2_UseLUT(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) -{ - FfxFloat32 fWeight0 = Lanczos2_UseLUT(-1.f - t); - FfxFloat32 fWeight1 = Lanczos2_UseLUT(-0.f - t); - FfxFloat32 fWeight2 = Lanczos2_UseLUT(+1.f - t); - FfxFloat32 fWeight3 = Lanczos2_UseLUT(+2.f - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} -#if FFX_HALF -FFX_MIN16_F4 Lanczos2_UseLUT(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) -{ - FFX_MIN16_F fWeight0 = Lanczos2_UseLUT(FFX_MIN16_F(-1.f) - t); - FFX_MIN16_F fWeight1 = Lanczos2_UseLUT(FFX_MIN16_F(-0.f) - t); - FFX_MIN16_F fWeight2 = Lanczos2_UseLUT(FFX_MIN16_F(+1.f) - t); - FFX_MIN16_F fWeight3 = Lanczos2_UseLUT(FFX_MIN16_F(+2.f) - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} -#endif - -FfxFloat32x4 Lanczos2(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) -{ - FfxFloat32 fWeight0 = Lanczos2(-1.f - t); - FfxFloat32 fWeight1 = Lanczos2(-0.f - t); - FfxFloat32 fWeight2 = Lanczos2(+1.f - t); - FfxFloat32 fWeight3 = Lanczos2(+2.f - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} - -FfxFloat32x4 Lanczos2(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FfxFloat32x4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FfxFloat32x4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FfxFloat32x4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FfxFloat32x4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FfxFloat32x4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; - FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { - - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) -{ - FFX_MIN16_F fWeight0 = Lanczos2(FFX_MIN16_F(-1.f) - t); - FFX_MIN16_F fWeight1 = Lanczos2(FFX_MIN16_F(-0.f) - t); - FFX_MIN16_F fWeight2 = Lanczos2(FFX_MIN16_F(+1.f) - t); - FFX_MIN16_F fWeight3 = Lanczos2(FFX_MIN16_F(+2.f) - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} - -FFX_MIN16_F4 Lanczos2(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FFX_MIN16_F4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FFX_MIN16_F4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FFX_MIN16_F4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; - FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} -#endif //FFX_HALF - - -FfxFloat32x4 Lanczos2LUT(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FfxFloat32x4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FfxFloat32x4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FfxFloat32x4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FfxFloat32x4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FfxFloat32x4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; - FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { - - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2LUT(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FFX_MIN16_F4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FFX_MIN16_F4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FFX_MIN16_F4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; - FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} -#endif //FFX_HALF - - - -FfxFloat32x4 Lanczos2Approx(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) -{ - FfxFloat32 fWeight0 = Lanczos2ApproxNoClamp(-1.f - t); - FfxFloat32 fWeight1 = Lanczos2ApproxNoClamp(-0.f - t); - FfxFloat32 fWeight2 = Lanczos2ApproxNoClamp(+1.f - t); - FfxFloat32 fWeight3 = Lanczos2ApproxNoClamp(+2.f - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2Approx(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) -{ - FFX_MIN16_F fWeight0 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-1.f) - t); - FFX_MIN16_F fWeight1 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-0.f) - t); - FFX_MIN16_F fWeight2 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+1.f) - t); - FFX_MIN16_F fWeight3 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+2.f) - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} -#endif //FFX_HALF - -FfxFloat32x4 Lanczos2Approx(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FfxFloat32x4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FfxFloat32x4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FfxFloat32x4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FfxFloat32x4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FfxFloat32x4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; - FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2Approx(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FFX_MIN16_F4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FFX_MIN16_F4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FFX_MIN16_F4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; - FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} -#endif - -// Clamp by offset direction. Assuming iPxSample is already in range and iPxOffset is compile time constant. -FfxInt32x2 ClampCoord(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) -{ - FfxInt32x2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; - result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; - result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; - result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; - return result; -} -#if FFX_HALF -FFX_MIN16_I2 ClampCoord(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) -{ - FFX_MIN16_I2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < FFX_MIN16_I(0)) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; - result.x = (iPxOffset.x > FFX_MIN16_I(0)) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; - result.y = (iPxOffset.y < FFX_MIN16_I(0)) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; - result.y = (iPxOffset.y > FFX_MIN16_I(0)) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; - return result; -} -#endif //FFX_HALF - - -#define DeclareCustomFetchBicubicSamplesWithType(SampleType, TextureType, AddrType, Name, LoadTexture) \ - SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ - { \ - SampleType Samples; \ - \ - Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, -1), iTextureSize))); \ - Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, -1), iTextureSize))); \ - Samples.fColor20 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, -1), iTextureSize))); \ - Samples.fColor30 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, -1), iTextureSize))); \ - \ - Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +0), iTextureSize))); \ - Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ - Samples.fColor21 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ - Samples.fColor31 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +0), iTextureSize))); \ - \ - Samples.fColor02 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +1), iTextureSize))); \ - Samples.fColor12 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ - Samples.fColor22 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ - Samples.fColor32 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +1), iTextureSize))); \ - \ - Samples.fColor03 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +2), iTextureSize))); \ - Samples.fColor13 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +2), iTextureSize))); \ - Samples.fColor23 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +2), iTextureSize))); \ - Samples.fColor33 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +2), iTextureSize))); \ - \ - return Samples; \ - } - -#define DeclareCustomFetchBicubicSamples(Name, LoadTexture) \ - DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) - -#define DeclareCustomFetchBicubicSamplesMin16(Name, LoadTexture) \ - DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) - -#define DeclareCustomFetchBilinearSamplesWithType(SampleType, TextureType,AddrType, Name, LoadTexture) \ - SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ - { \ - SampleType Samples; \ - Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ - Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ - Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ - Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ - return Samples; \ - } - -#define DeclareCustomFetchBilinearSamples(Name, LoadTexture) \ - DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) - -#define DeclareCustomFetchBilinearSamplesMin16(Name, LoadTexture) \ - DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) - -// BE CAREFUL: there is some precision issues and (3253, 125) leading to (3252.9989778, 125.001102) -// is common, so iPxSample can "jitter" -#define DeclareCustomTextureSample(Name, InterpolateSamples, FetchSamples) \ - FfxFloat32x4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ - { \ - FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ - /* Clamp base coords */ \ - fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ - fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ - /* */ \ - FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ - FfxFloat32x2 fPxFrac = ffxFract(fPxSample); \ - FfxFloat32x4 fColorXY = FfxFloat32x4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ - return fColorXY; \ - } - -#define DeclareCustomTextureSampleMin16(Name, InterpolateSamples, FetchSamples) \ - FFX_MIN16_F4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ - { \ - FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ - /* Clamp base coords */ \ - fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ - fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ - /* */ \ - FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ - FFX_MIN16_F2 fPxFrac = FFX_MIN16_F2(ffxFract(fPxSample)); \ - FFX_MIN16_F4 fColorXY = FFX_MIN16_F4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ - return fColorXY; \ - } - -#define FFX_FSR3UPSCALER_CONCAT_ID(x, y) x ## y -#define FFX_FSR3UPSCALER_CONCAT(x, y) FFX_FSR3UPSCALER_CONCAT_ID(x, y) -#define FFX_FSR3UPSCALER_SAMPLER_1D_0 Lanczos2 -#define FFX_FSR3UPSCALER_SAMPLER_1D_1 Lanczos2LUT -#define FFX_FSR3UPSCALER_SAMPLER_1D_2 Lanczos2Approx - -#define FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(x) FFX_FSR3UPSCALER_CONCAT(FFX_FSR3UPSCALER_SAMPLER_1D_, x) - -#endif //!defined( FFX_FSR3UPSCALER_SAMPLE_H ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta deleted file mode 100644 index 8e3cf72..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: dcb900c9deecd06419a8a4c10c305890 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta deleted file mode 100644 index bdeb970..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: f01d5a8fbd1f34a4ea8d971755a21b6c -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h deleted file mode 100644 index 47e7ccf..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h +++ /dev/null @@ -1,195 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_UPSAMPLE_H -#define FFX_FSR3UPSCALER_UPSAMPLE_H - -FFX_STATIC const FfxUInt32 iLanczos2SampleCount = 16; - -void Deringing(RectificationBox clippingBox, FFX_PARAMETER_INOUT FfxFloat32x3 fColor) -{ - fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); -} -#if FFX_HALF -void Deringing(RectificationBoxMin16 clippingBox, FFX_PARAMETER_INOUT FFX_MIN16_F3 fColor) -{ - fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); -} -#endif - -#ifndef FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE -#define FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE 2 // Approximate -#endif - -FfxFloat32 GetUpsampleLanczosWeight(FfxFloat32x2 fSrcSampleOffset, FfxFloat32 fKernelWeight) -{ - FfxFloat32x2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; -#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE - FfxFloat32 fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT - FfxFloat32 fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE - FfxFloat32 fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); -#else -#error "Invalid Lanczos type" -#endif - return fSampleWeight; -} - -#if FFX_HALF -FFX_MIN16_F GetUpsampleLanczosWeight(FFX_MIN16_F2 fSrcSampleOffset, FFX_MIN16_F fKernelWeight) -{ - FFX_MIN16_F2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; -#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE - FFX_MIN16_F fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT - FFX_MIN16_F fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE - FFX_MIN16_F fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); - - // To Test: Save reciproqual sqrt compute - // FfxFloat32 fSampleWeight = Lanczos2Sq_UseLUT(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); -#else -#error "Invalid Lanczos type" -#endif - return fSampleWeight; -} -#endif - -FfxFloat32 ComputeMaxKernelWeight() { - const FfxFloat32 fKernelSizeBias = 1.0f; - - FfxFloat32 fKernelWeight = FfxFloat32(1) + (FfxFloat32(1.0f) / FfxFloat32x2(DownscaleFactor()) - FfxFloat32(1)).x * FfxFloat32(fKernelSizeBias); - - return ffxMin(FfxFloat32(1.99f), fKernelWeight); -} - -FfxFloat32x4 ComputeUpsampledColorAndWeight(const AccumulationPassCommonParams params, - FFX_PARAMETER_INOUT RectificationBox clippingBox, FfxFloat32 fReactiveFactor) -{ - #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF - #include "ffx_fsr3upscaler_force16_begin.h" - #endif - // We compute a sliced lanczos filter with 2 lobes (other slices are accumulated temporaly) - FfxFloat32x2 fDstOutputPos = FfxFloat32x2(params.iPxHrPos) + FFX_BROADCAST_FLOAT32X2(0.5f); // Destination resolution output pixel center position - FfxFloat32x2 fSrcOutputPos = fDstOutputPos * DownscaleFactor(); // Source resolution output pixel center position - FfxInt32x2 iSrcInputPos = FfxInt32x2(floor(fSrcOutputPos)); // TODO: what about weird upscale factors... - - #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF - #include "ffx_fsr3upscaler_force16_end.h" - #endif - - FfxFloat32x3 fSamples[iLanczos2SampleCount]; - - FfxFloat32x2 fSrcUnjitteredPos = (FfxFloat32x2(iSrcInputPos) + FfxFloat32x2(0.5f, 0.5f)) - Jitter(); // This is the un-jittered position of the sample at offset 0,0 - - FfxInt32x2 offsetTL; - offsetTL.x = (fSrcUnjitteredPos.x > fSrcOutputPos.x) ? FfxInt32(-2) : FfxInt32(-1); - offsetTL.y = (fSrcUnjitteredPos.y > fSrcOutputPos.y) ? FfxInt32(-2) : FfxInt32(-1); - - //Load samples - // If fSrcUnjitteredPos.y > fSrcOutputPos.y, indicates offsetTL.y = -2, sample offset Y will be [-2, 1], clipbox will be rows [1, 3]. - // Flip row# for sampling offset in this case, so first 0~2 rows in the sampled array can always be used for computing the clipbox. - // This reduces branch or cmove on sampled colors, but moving this overhead to sample position / weight calculation time which apply to less values. - const FfxBoolean bFlipRow = fSrcUnjitteredPos.y > fSrcOutputPos.y; - const FfxBoolean bFlipCol = fSrcUnjitteredPos.x > fSrcOutputPos.x; - - FfxFloat32x2 fOffsetTL = FfxFloat32x2(offsetTL); - - FFX_UNROLL - for (FfxInt32 row = 0; row < 3; row++) { - - FFX_UNROLL - for (FfxInt32 col = 0; col < 3; col++) { - FfxInt32 iSampleIndex = col + (row << 2); - - FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); - FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + offsetTL + sampleColRow; - - const FfxInt32x2 sampleCoord = ClampLoad(iSrcSamplePos, FfxInt32x2(0, 0), FfxInt32x2(RenderSize())); - - fSamples[iSampleIndex] = LoadPreparedInputColor(FfxInt32x2(sampleCoord)); - } - } - - FfxFloat32x4 fColorAndWeight = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f); - - FfxFloat32x2 fBaseSampleOffset = FfxFloat32x2(fSrcUnjitteredPos - fSrcOutputPos); - - // Identify how much of each upsampled color to be used for this frame - const FfxFloat32 fKernelReactiveFactor = ffxMax(fReactiveFactor, FfxFloat32(params.bIsNewSample)); - const FfxFloat32 fKernelBiasMax = ComputeMaxKernelWeight() * (1.0f - fKernelReactiveFactor); - - const FfxFloat32 fKernelBiasMin = ffxMax(1.0f, ((1.0f + fKernelBiasMax) * 0.3f)); - const FfxFloat32 fKernelBiasFactor = ffxMax(0.0f, ffxMax(0.25f * params.fDepthClipFactor, fKernelReactiveFactor)); - const FfxFloat32 fKernelBias = ffxLerp(fKernelBiasMax, fKernelBiasMin, fKernelBiasFactor); - - const FfxFloat32 fRectificationCurveBias = ffxLerp(-2.0f, -3.0f, ffxSaturate(params.fHrVelocity / 50.0f)); - - FFX_UNROLL - for (FfxInt32 row = 0; row < 3; row++) { - FFX_UNROLL - for (FfxInt32 col = 0; col < 3; col++) { - FfxInt32 iSampleIndex = col + (row << 2); - - const FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); - const FfxFloat32x2 fOffset = fOffsetTL + FfxFloat32x2(sampleColRow); - FfxFloat32x2 fSrcSampleOffset = fBaseSampleOffset + fOffset; - - FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + FfxInt32x2(offsetTL) + sampleColRow; - - const FfxFloat32 fOnScreenFactor = FfxFloat32(IsOnScreen(FfxInt32x2(iSrcSamplePos), FfxInt32x2(RenderSize()))); - FfxFloat32 fSampleWeight = fOnScreenFactor * FfxFloat32(GetUpsampleLanczosWeight(fSrcSampleOffset, fKernelBias)); - - fColorAndWeight += FfxFloat32x4(fSamples[iSampleIndex] * fSampleWeight, fSampleWeight); - - // Update rectification box - { - const FfxFloat32 fSrcSampleOffsetSq = dot(fSrcSampleOffset, fSrcSampleOffset); - const FfxFloat32 fBoxSampleWeight = exp(fRectificationCurveBias * fSrcSampleOffsetSq); - - const FfxBoolean bInitialSample = (row == 0) && (col == 0); - RectificationBoxAddSample(bInitialSample, clippingBox, fSamples[iSampleIndex], fBoxSampleWeight); - } - } - } - - RectificationBoxComputeVarianceBoxData(clippingBox); - - fColorAndWeight.w *= FfxFloat32(fColorAndWeight.w > FSR3UPSCALER_EPSILON); - - if (fColorAndWeight.w > FSR3UPSCALER_EPSILON) { - // Normalize for deringing (we need to compare colors) - fColorAndWeight.xyz = fColorAndWeight.xyz / fColorAndWeight.w; - fColorAndWeight.w *= fUpsampleLanczosWeightScale; - - Deringing(clippingBox, fColorAndWeight.xyz); - } - - #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF - #include "ffx_fsr3upscaler_force16_end.h" - #endif - - return fColorAndWeight; -} - -#endif //!defined( FFX_FSR3UPSCALER_UPSAMPLE_H ) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta deleted file mode 100644 index dc0a729..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 3e7832c4a9154414f9eaa125acfe6cd5 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta deleted file mode 100644 index 731c94f..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 09438bc445e66204f970dc99ca8dae5a -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h deleted file mode 100644 index e780995..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h +++ /dev/null @@ -1,1252 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup FfxGPUFsr1 FidelityFX FSR1 -/// FidelityFX Super Resolution 1 GPU documentation -/// -/// @ingroup FfxGPUEffects - -/// Setup required constant values for EASU (works on CPU or GPU). -/// -/// @param [out] con0 -/// @param [out] con1 -/// @param [out] con2 -/// @param [out] con3 -/// @param [in] inputViewportInPixelsX The rendered image resolution being upscaled in X dimension. -/// @param [in] inputViewportInPixelsY The rendered image resolution being upscaled in Y dimension. -/// @param [in] inputSizeInPixelsX The resolution of the resource containing the input image (useful for dynamic resolution) in X dimension. -/// @param [in] inputSizeInPixelsY The resolution of the resource containing the input image (useful for dynamic resolution) in Y dimension. -/// @param [in] outputSizeInPixelsX The display resolution which the input image gets upscaled to in X dimension. -/// @param [in] outputSizeInPixelsY The display resolution which the input image gets upscaled to in Y dimension. -/// -/// @ingroup FfxGPUFsr1 -FFX_STATIC void ffxFsrPopulateEasuConstants( - FFX_PARAMETER_INOUT FfxUInt32x4 con0, - FFX_PARAMETER_INOUT FfxUInt32x4 con1, - FFX_PARAMETER_INOUT FfxUInt32x4 con2, - FFX_PARAMETER_INOUT FfxUInt32x4 con3, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsY, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsY, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsY) -{ - // Output integer position to a pixel position in viewport. - con0[0] = ffxAsUInt32(inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX)); - con0[1] = ffxAsUInt32(inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY)); - con0[2] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX) - FfxFloat32(0.5)); - con0[3] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY) - FfxFloat32(0.5)); - - // Viewport pixel position to normalized image space. - // This is used to get upper-left of 'F' tap. - con1[0] = ffxAsUInt32(ffxReciprocal(inputSizeInPixelsX)); - con1[1] = ffxAsUInt32(ffxReciprocal(inputSizeInPixelsY)); - - // Centers of gather4, first offset from upper-left of 'F'. - // +---+---+ - // | | | - // +--(0)--+ - // | b | c | - // +---F---+---+---+ - // | e | f | g | h | - // +--(1)--+--(2)--+ - // | i | j | k | l | - // +---+---+---+---+ - // | n | o | - // +--(3)--+ - // | | | - // +---+---+ - con1[2] = ffxAsUInt32(FfxFloat32(1.0) * ffxReciprocal(inputSizeInPixelsX)); - con1[3] = ffxAsUInt32(FfxFloat32(-1.0) * ffxReciprocal(inputSizeInPixelsY)); - - // These are from (0) instead of 'F'. - con2[0] = ffxAsUInt32(FfxFloat32(-1.0) * ffxReciprocal(inputSizeInPixelsX)); - con2[1] = ffxAsUInt32(FfxFloat32(2.0) * ffxReciprocal(inputSizeInPixelsY)); - con2[2] = ffxAsUInt32(FfxFloat32(1.0) * ffxReciprocal(inputSizeInPixelsX)); - con2[3] = ffxAsUInt32(FfxFloat32(2.0) * ffxReciprocal(inputSizeInPixelsY)); - con3[0] = ffxAsUInt32(FfxFloat32(0.0) * ffxReciprocal(inputSizeInPixelsX)); - con3[1] = ffxAsUInt32(FfxFloat32(4.0) * ffxReciprocal(inputSizeInPixelsY)); - con3[2] = con3[3] = 0; -} - -/// Setup required constant values for EASU (works on CPU or GPU). -/// -/// @param [out] con0 -/// @param [out] con1 -/// @param [out] con2 -/// @param [out] con3 -/// @param [in] inputViewportInPixelsX The resolution of the input in the X dimension. -/// @param [in] inputViewportInPixelsY The resolution of the input in the Y dimension. -/// @param [in] inputSizeInPixelsX The input size in pixels in the X dimension. -/// @param [in] inputSizeInPixelsY The input size in pixels in the Y dimension. -/// @param [in] outputSizeInPixelsX The output size in pixels in the X dimension. -/// @param [in] outputSizeInPixelsY The output size in pixels in the Y dimension. -/// @param [in] inputOffsetInPixelsX The input image offset in the X dimension into the resource containing it (useful for dynamic resolution). -/// @param [in] inputOffsetInPixelsY The input image offset in the Y dimension into the resource containing it (useful for dynamic resolution). -/// -/// @ingroup FfxGPUFsr1 -FFX_STATIC void ffxFsrPopulateEasuConstantsOffset( - FFX_PARAMETER_INOUT FfxUInt32x4 con0, - FFX_PARAMETER_INOUT FfxUInt32x4 con1, - FFX_PARAMETER_INOUT FfxUInt32x4 con2, - FFX_PARAMETER_INOUT FfxUInt32x4 con3, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsY, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsY, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsY, - FFX_PARAMETER_IN FfxFloat32 inputOffsetInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputOffsetInPixelsY) -{ - ffxFsrPopulateEasuConstants( - con0, - con1, - con2, - con3, - inputViewportInPixelsX, - inputViewportInPixelsY, - inputSizeInPixelsX, - inputSizeInPixelsY, - outputSizeInPixelsX, - outputSizeInPixelsY); - - // override - con0[2] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX) - FfxFloat32(0.5) + inputOffsetInPixelsX); - con0[3] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY) - FfxFloat32(0.5) + inputOffsetInPixelsY); -} - -#if defined(FFX_GPU) && defined(FFX_FSR_EASU_FLOAT) -// Input callback prototypes, need to be implemented by calling shader -FfxFloat32x4 FsrEasuRF(FfxFloat32x2 p); -FfxFloat32x4 FsrEasuGF(FfxFloat32x2 p); -FfxFloat32x4 FsrEasuBF(FfxFloat32x2 p); - -// Filtering for a given tap for the scalar. -void fsrEasuTapFloat( - FFX_PARAMETER_INOUT FfxFloat32x3 accumulatedColor, // Accumulated color, with negative lobe. - FFX_PARAMETER_INOUT FfxFloat32 accumulatedWeight, // Accumulated weight. - FFX_PARAMETER_IN FfxFloat32x2 pixelOffset, // Pixel offset from resolve position to tap. - FFX_PARAMETER_IN FfxFloat32x2 gradientDirection, // Gradient direction. - FFX_PARAMETER_IN FfxFloat32x2 length, // Length. - FFX_PARAMETER_IN FfxFloat32 negativeLobeStrength, // Negative lobe strength. - FFX_PARAMETER_IN FfxFloat32 clippingPoint, // Clipping point. - FFX_PARAMETER_IN FfxFloat32x3 color) // Tap color. -{ - // Rotate offset by direction. - FfxFloat32x2 rotatedOffset; - rotatedOffset.x = (pixelOffset.x * (gradientDirection.x)) + (pixelOffset.y * gradientDirection.y); - rotatedOffset.y = (pixelOffset.x * (-gradientDirection.y)) + (pixelOffset.y * gradientDirection.x); - - // Anisotropy. - rotatedOffset *= length; - - // Compute distance^2. - FfxFloat32 distanceSquared = rotatedOffset.x * rotatedOffset.x + rotatedOffset.y * rotatedOffset.y; - - // Limit to the window as at corner, 2 taps can easily be outside. - distanceSquared = ffxMin(distanceSquared, clippingPoint); - - // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. - // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 - // |_______________________________________| |_______________| - // base window - // The general form of the 'base' is, - // (a*(b*x^2-1)^2-(a-1)) - // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. - FfxFloat32 weightB = FfxFloat32(2.0 / 5.0) * distanceSquared + FfxFloat32(-1.0); - FfxFloat32 weightA = negativeLobeStrength * distanceSquared + FfxFloat32(-1.0); - weightB *= weightB; - weightA *= weightA; - weightB = FfxFloat32(25.0 / 16.0) * weightB + FfxFloat32(-(25.0 / 16.0 - 1.0)); - FfxFloat32 weight = weightB * weightA; - - // Do weighted average. - accumulatedColor += color * weight; - accumulatedWeight += weight; -} - -// Accumulate direction and length. -void fsrEasuSetFloat( - FFX_PARAMETER_INOUT FfxFloat32x2 direction, - FFX_PARAMETER_INOUT FfxFloat32 length, - FFX_PARAMETER_IN FfxFloat32x2 pp, - FFX_PARAMETER_IN FfxBoolean biS, - FFX_PARAMETER_IN FfxBoolean biT, - FFX_PARAMETER_IN FfxBoolean biU, - FFX_PARAMETER_IN FfxBoolean biV, - FFX_PARAMETER_IN FfxFloat32 lA, - FFX_PARAMETER_IN FfxFloat32 lB, - FFX_PARAMETER_IN FfxFloat32 lC, - FFX_PARAMETER_IN FfxFloat32 lD, - FFX_PARAMETER_IN FfxFloat32 lE) -{ - // Compute bilinear weight, branches factor out as predicates are compiler time immediates. - // s t - // u v - FfxFloat32 weight = FfxFloat32(0.0); - if (biS) - weight = (FfxFloat32(1.0) - pp.x) * (FfxFloat32(1.0) - pp.y); - if (biT) - weight = pp.x * (FfxFloat32(1.0) - pp.y); - if (biU) - weight = (FfxFloat32(1.0) - pp.x) * pp.y; - if (biV) - weight = pp.x * pp.y; - - // Direction is the '+' diff. - // a - // b c d - // e - // Then takes magnitude from abs average of both sides of 'c'. - // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. - FfxFloat32 dc = lD - lC; - FfxFloat32 cb = lC - lB; - FfxFloat32 lengthX = max(abs(dc), abs(cb)); - lengthX = ffxApproximateReciprocal(lengthX); - FfxFloat32 directionX = lD - lB; - direction.x += directionX * weight; - lengthX = ffxSaturate(abs(directionX) * lengthX); - lengthX *= lengthX; - length += lengthX * weight; - - // Repeat for the y axis. - FfxFloat32 ec = lE - lC; - FfxFloat32 ca = lC - lA; - FfxFloat32 lengthY = max(abs(ec), abs(ca)); - lengthY = ffxApproximateReciprocal(lengthY); - FfxFloat32 directionY = lE - lA; - direction.y += directionY * weight; - lengthY = ffxSaturate(abs(directionY) * lengthY); - lengthY *= lengthY; - length += lengthY * weight; -} - -/// Apply edge-aware spatial upsampling using 32bit floating point precision calculations. -/// -/// @param [out] outPixel The computed color of a pixel. -/// @param [in] integerPosition Integer pixel position within the output. -/// @param [in] con0 The first constant value generated by ffxFsrPopulateEasuConstants. -/// @param [in] con1 The second constant value generated by ffxFsrPopulateEasuConstants. -/// @param [in] con2 The third constant value generated by ffxFsrPopulateEasuConstants. -/// @param [in] con3 The fourth constant value generated by ffxFsrPopulateEasuConstants. -/// -/// @ingroup FSR -void ffxFsrEasuFloat( - FFX_PARAMETER_OUT FfxFloat32x3 pix, - FFX_PARAMETER_IN FfxUInt32x2 ip, - FFX_PARAMETER_IN FfxUInt32x4 con0, - FFX_PARAMETER_IN FfxUInt32x4 con1, - FFX_PARAMETER_IN FfxUInt32x4 con2, - FFX_PARAMETER_IN FfxUInt32x4 con3) -{ - // Get position of 'f'. - FfxFloat32x2 pp = FfxFloat32x2(ip) * ffxAsFloat(con0.xy) + ffxAsFloat(con0.zw); - FfxFloat32x2 fp = floor(pp); - pp -= fp; - - // 12-tap kernel. - // b c - // e f g h - // i j k l - // n o - // Gather 4 ordering. - // a b - // r g - // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, - // a b <- unused (z) - // r g - // a b a b - // r g r g - // a b - // r g <- unused (z) - // Allowing dead-code removal to remove the 'z's. - FfxFloat32x2 p0 = fp * ffxAsFloat(con1.xy) + ffxAsFloat(con1.zw); - - // These are from p0 to avoid pulling two constants on pre-Navi hardware. - FfxFloat32x2 p1 = p0 + ffxAsFloat(con2.xy); - FfxFloat32x2 p2 = p0 + ffxAsFloat(con2.zw); - FfxFloat32x2 p3 = p0 + ffxAsFloat(con3.xy); - FfxFloat32x4 bczzR = FsrEasuRF(p0); - FfxFloat32x4 bczzG = FsrEasuGF(p0); - FfxFloat32x4 bczzB = FsrEasuBF(p0); - FfxFloat32x4 ijfeR = FsrEasuRF(p1); - FfxFloat32x4 ijfeG = FsrEasuGF(p1); - FfxFloat32x4 ijfeB = FsrEasuBF(p1); - FfxFloat32x4 klhgR = FsrEasuRF(p2); - FfxFloat32x4 klhgG = FsrEasuGF(p2); - FfxFloat32x4 klhgB = FsrEasuBF(p2); - FfxFloat32x4 zzonR = FsrEasuRF(p3); - FfxFloat32x4 zzonG = FsrEasuGF(p3); - FfxFloat32x4 zzonB = FsrEasuBF(p3); - - // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). - FfxFloat32x4 bczzL = bczzB * ffxBroadcast4(0.5) + (bczzR * ffxBroadcast4(0.5) + bczzG); - FfxFloat32x4 ijfeL = ijfeB * ffxBroadcast4(0.5) + (ijfeR * ffxBroadcast4(0.5) + ijfeG); - FfxFloat32x4 klhgL = klhgB * ffxBroadcast4(0.5) + (klhgR * ffxBroadcast4(0.5) + klhgG); - FfxFloat32x4 zzonL = zzonB * ffxBroadcast4(0.5) + (zzonR * ffxBroadcast4(0.5) + zzonG); - - // Rename. - FfxFloat32 bL = bczzL.x; - FfxFloat32 cL = bczzL.y; - FfxFloat32 iL = ijfeL.x; - FfxFloat32 jL = ijfeL.y; - FfxFloat32 fL = ijfeL.z; - FfxFloat32 eL = ijfeL.w; - FfxFloat32 kL = klhgL.x; - FfxFloat32 lL = klhgL.y; - FfxFloat32 hL = klhgL.z; - FfxFloat32 gL = klhgL.w; - FfxFloat32 oL = zzonL.z; - FfxFloat32 nL = zzonL.w; - - // Accumulate for bilinear interpolation. - FfxFloat32x2 dir = ffxBroadcast2(0.0); - FfxFloat32 len = FfxFloat32(0.0); - fsrEasuSetFloat(dir, len, pp, FFX_TRUE, FFX_FALSE, FFX_FALSE, FFX_FALSE, bL, eL, fL, gL, jL); - fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_TRUE, FFX_FALSE, FFX_FALSE, cL, fL, gL, hL, kL); - fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_FALSE, FFX_TRUE, FFX_FALSE, fL, iL, jL, kL, nL); - fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_FALSE, FFX_FALSE, FFX_TRUE, gL, jL, kL, lL, oL); - - // Normalize with approximation, and cleanup close to zero. - FfxFloat32x2 dir2 = dir * dir; - FfxFloat32 dirR = dir2.x + dir2.y; - FfxBoolean zro = dirR < FfxFloat32(1.0 / 32768.0); - dirR = ffxApproximateReciprocalSquareRoot(dirR); - dirR = zro ? FfxFloat32(1.0) : dirR; - dir.x = zro ? FfxFloat32(1.0) : dir.x; - dir *= ffxBroadcast2(dirR); - - // Transform from {0 to 2} to {0 to 1} range, and shape with square. - len = len * FfxFloat32(0.5); - len *= len; - - // Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}. - FfxFloat32 stretch = (dir.x * dir.x + dir.y * dir.y) * ffxApproximateReciprocal(max(abs(dir.x), abs(dir.y))); - - // Anisotropic length after rotation, - // x := 1.0 lerp to 'stretch' on edges - // y := 1.0 lerp to 2x on edges - FfxFloat32x2 len2 = FfxFloat32x2(FfxFloat32(1.0) + (stretch - FfxFloat32(1.0)) * len, FfxFloat32(1.0) + FfxFloat32(-0.5) * len); - - // Based on the amount of 'edge', - // the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}. - FfxFloat32 lob = FfxFloat32(0.5) + FfxFloat32((1.0 / 4.0 - 0.04) - 0.5) * len; - - // Set distance^2 clipping point to the end of the adjustable window. - FfxFloat32 clp = ffxApproximateReciprocal(lob); - - // Accumulation mixed with min/max of 4 nearest. - // b c - // e f g h - // i j k l - // n o - FfxFloat32x3 min4 = - ffxMin(ffxMin3(FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z), FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w), FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)), - FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); - FfxFloat32x3 max4 = - max(ffxMax3(FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z), FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w), FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)), FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); - - // Accumulation. - FfxFloat32x3 aC = ffxBroadcast3(0.0); - FfxFloat32 aW = FfxFloat32(0.0); - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, -1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(bczzR.x, bczzG.x, bczzB.x)); // b - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, -1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(bczzR.y, bczzG.y, bczzB.y)); // c - fsrEasuTapFloat(aC, aW, FfxFloat32x2(-1.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.x, ijfeG.x, ijfeB.x)); // i - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)); // j - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z)); // f - fsrEasuTapFloat(aC, aW, FfxFloat32x2(-1.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.w, ijfeG.w, ijfeB.w)); // e - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); // k - fsrEasuTapFloat(aC, aW, FfxFloat32x2(2.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.y, klhgG.y, klhgB.y)); // l - fsrEasuTapFloat(aC, aW, FfxFloat32x2(2.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.z, klhgG.z, klhgB.z)); // h - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w)); // g - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 2.0) - pp, dir, len2, lob, clp, FfxFloat32x3(zzonR.z, zzonG.z, zzonB.z)); // o - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 2.0) - pp, dir, len2, lob, clp, FfxFloat32x3(zzonR.w, zzonG.w, zzonB.w)); // n - - // Normalize and dering. - pix = ffxMin(max4, max(min4, aC * ffxBroadcast3(rcp(aW)))); -} -#endif // #if defined(FFX_GPU) && defined(FFX_FSR_EASU_FLOAT) - -#if defined(FFX_GPU) && FFX_HALF == 1 && defined(FFX_FSR_EASU_HALF) -// Input callback prototypes, need to be implemented by calling shader -FfxFloat16x4 FsrEasuRH(FfxFloat32x2 p); -FfxFloat16x4 FsrEasuGH(FfxFloat32x2 p); -FfxFloat16x4 FsrEasuBH(FfxFloat32x2 p); - -// This runs 2 taps in parallel. -void FsrEasuTapH( - FFX_PARAMETER_INOUT FfxFloat16x2 aCR, - FFX_PARAMETER_INOUT FfxFloat16x2 aCG, - FFX_PARAMETER_INOUT FfxFloat16x2 aCB, - FFX_PARAMETER_INOUT FfxFloat16x2 aW, - FFX_PARAMETER_IN FfxFloat16x2 offX, - FFX_PARAMETER_IN FfxFloat16x2 offY, - FFX_PARAMETER_IN FfxFloat16x2 dir, - FFX_PARAMETER_IN FfxFloat16x2 len, - FFX_PARAMETER_IN FfxFloat16 lob, - FFX_PARAMETER_IN FfxFloat16 clp, - FFX_PARAMETER_IN FfxFloat16x2 cR, - FFX_PARAMETER_IN FfxFloat16x2 cG, - FFX_PARAMETER_IN FfxFloat16x2 cB) -{ - FfxFloat16x2 vX, vY; - vX = offX * dir.xx + offY * dir.yy; - vY = offX * (-dir.yy) + offY * dir.xx; - vX *= len.x; - vY *= len.y; - FfxFloat16x2 d2 = vX * vX + vY * vY; - d2 = min(d2, FFX_BROADCAST_FLOAT16X2(clp)); - FfxFloat16x2 wB = FFX_BROADCAST_FLOAT16X2(2.0 / 5.0) * d2 + FFX_BROADCAST_FLOAT16X2(-1.0); - FfxFloat16x2 wA = FFX_BROADCAST_FLOAT16X2(lob) * d2 + FFX_BROADCAST_FLOAT16X2(-1.0); - wB *= wB; - wA *= wA; - wB = FFX_BROADCAST_FLOAT16X2(25.0 / 16.0) * wB + FFX_BROADCAST_FLOAT16X2(-(25.0 / 16.0 - 1.0)); - FfxFloat16x2 w = wB * wA; - aCR += cR * w; - aCG += cG * w; - aCB += cB * w; - aW += w; -} - -// This runs 2 taps in parallel. -void FsrEasuSetH( - FFX_PARAMETER_INOUT FfxFloat16x2 dirPX, - FFX_PARAMETER_INOUT FfxFloat16x2 dirPY, - FFX_PARAMETER_INOUT FfxFloat16x2 lenP, - FFX_PARAMETER_IN FfxFloat16x2 pp, - FFX_PARAMETER_IN FfxBoolean biST, - FFX_PARAMETER_IN FfxBoolean biUV, - FFX_PARAMETER_IN FfxFloat16x2 lA, - FFX_PARAMETER_IN FfxFloat16x2 lB, - FFX_PARAMETER_IN FfxFloat16x2 lC, - FFX_PARAMETER_IN FfxFloat16x2 lD, - FFX_PARAMETER_IN FfxFloat16x2 lE) -{ - FfxFloat16x2 w = FFX_BROADCAST_FLOAT16X2(0.0); - - if (biST) - w = (FfxFloat16x2(1.0, 0.0) + FfxFloat16x2(-pp.x, pp.x)) * FFX_BROADCAST_FLOAT16X2(FFX_BROADCAST_FLOAT16(1.0) - pp.y); - - if (biUV) - w = (FfxFloat16x2(1.0, 0.0) + FfxFloat16x2(-pp.x, pp.x)) * FFX_BROADCAST_FLOAT16X2(pp.y); - - // ABS is not free in the packed FP16 path. - FfxFloat16x2 dc = lD - lC; - FfxFloat16x2 cb = lC - lB; - FfxFloat16x2 lenX = max(abs(dc), abs(cb)); - lenX = ffxReciprocalHalf(lenX); - - FfxFloat16x2 dirX = lD - lB; - dirPX += dirX * w; - lenX = FfxFloat16x2(ffxSaturate(abs(dirX) * lenX)); - lenX *= lenX; - lenP += lenX * w; - FfxFloat16x2 ec = lE - lC; - FfxFloat16x2 ca = lC - lA; - FfxFloat16x2 lenY = max(abs(ec), abs(ca)); - lenY = ffxReciprocalHalf(lenY); - FfxFloat16x2 dirY = lE - lA; - dirPY += dirY * w; - lenY = FfxFloat16x2(ffxSaturate(abs(dirY) * lenY)); - lenY *= lenY; - lenP += lenY * w; -} - -void FsrEasuH( - FFX_PARAMETER_OUT FfxFloat16x3 pix, - FFX_PARAMETER_IN FfxUInt32x2 ip, - FFX_PARAMETER_IN FfxUInt32x4 con0, - FFX_PARAMETER_IN FfxUInt32x4 con1, - FFX_PARAMETER_IN FfxUInt32x4 con2, - FFX_PARAMETER_IN FfxUInt32x4 con3) -{ - FfxFloat32x2 pp = FfxFloat32x2(ip) * ffxAsFloat(con0.xy) + ffxAsFloat(con0.zw); - FfxFloat32x2 fp = floor(pp); - pp -= fp; - FfxFloat16x2 ppp = FfxFloat16x2(pp); - - FfxFloat32x2 p0 = fp * ffxAsFloat(con1.xy) + ffxAsFloat(con1.zw); - FfxFloat32x2 p1 = p0 + ffxAsFloat(con2.xy); - FfxFloat32x2 p2 = p0 + ffxAsFloat(con2.zw); - FfxFloat32x2 p3 = p0 + ffxAsFloat(con3.xy); - FfxFloat16x4 bczzR = FsrEasuRH(p0); - FfxFloat16x4 bczzG = FsrEasuGH(p0); - FfxFloat16x4 bczzB = FsrEasuBH(p0); - FfxFloat16x4 ijfeR = FsrEasuRH(p1); - FfxFloat16x4 ijfeG = FsrEasuGH(p1); - FfxFloat16x4 ijfeB = FsrEasuBH(p1); - FfxFloat16x4 klhgR = FsrEasuRH(p2); - FfxFloat16x4 klhgG = FsrEasuGH(p2); - FfxFloat16x4 klhgB = FsrEasuBH(p2); - FfxFloat16x4 zzonR = FsrEasuRH(p3); - FfxFloat16x4 zzonG = FsrEasuGH(p3); - FfxFloat16x4 zzonB = FsrEasuBH(p3); - - FfxFloat16x4 bczzL = bczzB * FFX_BROADCAST_FLOAT16X4(0.5) + (bczzR * FFX_BROADCAST_FLOAT16X4(0.5) + bczzG); - FfxFloat16x4 ijfeL = ijfeB * FFX_BROADCAST_FLOAT16X4(0.5) + (ijfeR * FFX_BROADCAST_FLOAT16X4(0.5) + ijfeG); - FfxFloat16x4 klhgL = klhgB * FFX_BROADCAST_FLOAT16X4(0.5) + (klhgR * FFX_BROADCAST_FLOAT16X4(0.5) + klhgG); - FfxFloat16x4 zzonL = zzonB * FFX_BROADCAST_FLOAT16X4(0.5) + (zzonR * FFX_BROADCAST_FLOAT16X4(0.5) + zzonG); - FfxFloat16 bL = bczzL.x; - FfxFloat16 cL = bczzL.y; - FfxFloat16 iL = ijfeL.x; - FfxFloat16 jL = ijfeL.y; - FfxFloat16 fL = ijfeL.z; - FfxFloat16 eL = ijfeL.w; - FfxFloat16 kL = klhgL.x; - FfxFloat16 lL = klhgL.y; - FfxFloat16 hL = klhgL.z; - FfxFloat16 gL = klhgL.w; - FfxFloat16 oL = zzonL.z; - FfxFloat16 nL = zzonL.w; - - // This part is different, accumulating 2 taps in parallel. - FfxFloat16x2 dirPX = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 dirPY = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 lenP = FFX_BROADCAST_FLOAT16X2(0.0); - FsrEasuSetH(dirPX, - dirPY, - lenP, - ppp, - FfxBoolean(true), - FfxBoolean(false), - FfxFloat16x2(bL, cL), - FfxFloat16x2(eL, fL), - FfxFloat16x2(fL, gL), - FfxFloat16x2(gL, hL), - FfxFloat16x2(jL, kL)); - FsrEasuSetH(dirPX, - dirPY, - lenP, - ppp, - FfxBoolean(false), - FfxBoolean(true), - FfxFloat16x2(fL, gL), - FfxFloat16x2(iL, jL), - FfxFloat16x2(jL, kL), - FfxFloat16x2(kL, lL), - FfxFloat16x2(nL, oL)); - FfxFloat16x2 dir = FfxFloat16x2(dirPX.r + dirPX.g, dirPY.r + dirPY.g); - FfxFloat16 len = lenP.r + lenP.g; - - FfxFloat16x2 dir2 = dir * dir; - FfxFloat16 dirR = dir2.x + dir2.y; - FfxUInt32 zro = FfxUInt32(dirR < FFX_BROADCAST_FLOAT16(1.0 / 32768.0)); - dirR = ffxApproximateReciprocalSquareRootHalf(dirR); - dirR = (zro > 0) ? FFX_BROADCAST_FLOAT16(1.0) : dirR; - dir.x = (zro > 0) ? FFX_BROADCAST_FLOAT16(1.0) : dir.x; - dir *= FFX_BROADCAST_FLOAT16X2(dirR); - len = len * FFX_BROADCAST_FLOAT16(0.5); - len *= len; - FfxFloat16 stretch = (dir.x * dir.x + dir.y * dir.y) * ffxApproximateReciprocalHalf(max(abs(dir.x), abs(dir.y))); - FfxFloat16x2 len2 = - FfxFloat16x2(FFX_BROADCAST_FLOAT16(1.0) + (stretch - FFX_BROADCAST_FLOAT16(1.0)) * len, FFX_BROADCAST_FLOAT16(1.0) + FFX_BROADCAST_FLOAT16(-0.5) * len); - FfxFloat16 lob = FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16((1.0 / 4.0 - 0.04) - 0.5) * len; - FfxFloat16 clp = ffxApproximateReciprocalHalf(lob); - - // FP16 is different, using packed trick to do min and max in same operation. - FfxFloat16x2 bothR = - max(max(FfxFloat16x2(-ijfeR.z, ijfeR.z), FfxFloat16x2(-klhgR.w, klhgR.w)), max(FfxFloat16x2(-ijfeR.y, ijfeR.y), FfxFloat16x2(-klhgR.x, klhgR.x))); - FfxFloat16x2 bothG = - max(max(FfxFloat16x2(-ijfeG.z, ijfeG.z), FfxFloat16x2(-klhgG.w, klhgG.w)), max(FfxFloat16x2(-ijfeG.y, ijfeG.y), FfxFloat16x2(-klhgG.x, klhgG.x))); - FfxFloat16x2 bothB = - max(max(FfxFloat16x2(-ijfeB.z, ijfeB.z), FfxFloat16x2(-klhgB.w, klhgB.w)), max(FfxFloat16x2(-ijfeB.y, ijfeB.y), FfxFloat16x2(-klhgB.x, klhgB.x))); - - // This part is different for FP16, working pairs of taps at a time. - FfxFloat16x2 pR = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 pG = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 pB = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 pW = FFX_BROADCAST_FLOAT16X2(0.0); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(0.0, 1.0) - ppp.xx, FfxFloat16x2(-1.0, -1.0) - ppp.yy, dir, len2, lob, clp, bczzR.xy, bczzG.xy, bczzB.xy); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(-1.0, 0.0) - ppp.xx, FfxFloat16x2(1.0, 1.0) - ppp.yy, dir, len2, lob, clp, ijfeR.xy, ijfeG.xy, ijfeB.xy); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(0.0, -1.0) - ppp.xx, FfxFloat16x2(0.0, 0.0) - ppp.yy, dir, len2, lob, clp, ijfeR.zw, ijfeG.zw, ijfeB.zw); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(1.0, 2.0) - ppp.xx, FfxFloat16x2(1.0, 1.0) - ppp.yy, dir, len2, lob, clp, klhgR.xy, klhgG.xy, klhgB.xy); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(2.0, 1.0) - ppp.xx, FfxFloat16x2(0.0, 0.0) - ppp.yy, dir, len2, lob, clp, klhgR.zw, klhgG.zw, klhgB.zw); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(1.0, 0.0) - ppp.xx, FfxFloat16x2(2.0, 2.0) - ppp.yy, dir, len2, lob, clp, zzonR.zw, zzonG.zw, zzonB.zw); - FfxFloat16x3 aC = FfxFloat16x3(pR.x + pR.y, pG.x + pG.y, pB.x + pB.y); - FfxFloat16 aW = pW.x + pW.y; - - // Slightly different for FP16 version due to combined min and max. - pix = min(FfxFloat16x3(bothR.y, bothG.y, bothB.y), max(-FfxFloat16x3(bothR.x, bothG.x, bothB.x), aC * FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(aW)))); -} -#endif // #if defined(FFX_GPU) && defined(FFX_HALF) && defined(FFX_FSR_EASU_HALF) - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING -// -//------------------------------------------------------------------------------------------------------------------------------ -// CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness. -// RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. -// RCAS also has a built in process to limit sharpening of what it detects as possible noise. -// RCAS sharper does not support scaling, as it should be applied after EASU scaling. -// Pass EASU output straight into RCAS, no color conversions necessary. -//------------------------------------------------------------------------------------------------------------------------------ -// RCAS is based on the following logic. -// RCAS uses a 5 tap filter in a cross pattern (same as CAS), -// w n -// w 1 w for taps w m e -// w s -// Where 'w' is the negative lobe weight. -// output = (w*(n+e+w+s)+m)/(4*w+1) -// RCAS solves for 'w' by seeing where the signal might clip out of the {0 to 1} input range, -// 0 == (w*(n+e+w+s)+m)/(4*w+1) -> w = -m/(n+e+w+s) -// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) -// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. -// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. -// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. -// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. -// This stabilizes RCAS. -// RCAS does a simple highpass which is normalized against the local contrast then shaped, -// 0.25 -// 0.25 -1 0.25 -// 0.25 -// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. -// -// GLSL example for the required callbacks : -// -// FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p){return FfxFloat16x4(imageLoad(imgSrc,FfxInt32x2(p)));} -// void FsrRcasInputH(inout FfxFloat16 r,inout FfxFloat16 g,inout FfxFloat16 b) -// { -// //do any simple input color conversions here or leave empty if none needed -// } -// -// FsrRcasCon need to be called from the CPU or GPU to set up constants. -// Including a GPU example here, the 'con' value would be stored out to a constant buffer. -// -// FfxUInt32x4 con; -// FsrRcasCon(con, -// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. -// --------------- -// RCAS sharpening supports a CAS-like pass-through alpha via, -// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 -// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. -// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, -// #define FSR_RCAS_DENOISE 1 -//============================================================================================================================== -// This is set at the limit of providing unnatural results for sharpening. -#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// CONSTANT SETUP -//============================================================================================================================== -// Call to setup required constant values (works on CPU or GPU). - FFX_STATIC void FsrRcasCon(FfxUInt32x4 con, - // The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. - FfxFloat32 sharpness) - { - // Transform from stops to linear value. - sharpness = exp2(-sharpness); - FfxFloat32x2 hSharp = {sharpness, sharpness}; - con[0] = ffxAsUInt32(sharpness); - con[1] = packHalf2x16(hSharp); - con[2] = 0; - con[3] = 0; - } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// NON-PACKED 32-BIT VERSION -//============================================================================================================================== -#if defined(FFX_GPU)&&defined(FSR_RCAS_F) - // Input callback prototypes that need to be implemented by calling shader - FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p); - void FsrRcasInputF(inout FfxFloat32 r,inout FfxFloat32 g,inout FfxFloat32 b); -//------------------------------------------------------------------------------------------------------------------------------ - void FsrRcasF(out FfxFloat32 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. - out FfxFloat32 pixG, - out FfxFloat32 pixB, -#ifdef FSR_RCAS_PASSTHROUGH_ALPHA - out FfxFloat32 pixA, -#endif - FfxUInt32x2 ip, // Integer pixel position in output. - FfxUInt32x4 con) - { // Constant generated by RcasSetup(). - // Algorithm uses minimal 3x3 pixel neighborhood. - // b - // d e f - // h - FfxInt32x2 sp = FfxInt32x2(ip); - FfxFloat32x3 b = FsrRcasLoadF(sp + FfxInt32x2(0, -1)).rgb; - FfxFloat32x3 d = FsrRcasLoadF(sp + FfxInt32x2(-1, 0)).rgb; -#ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat32x4 ee = FsrRcasLoadF(sp); - FfxFloat32x3 e = ee.rgb; - pixA = ee.a; -#else - FfxFloat32x3 e = FsrRcasLoadF(sp).rgb; -#endif - FfxFloat32x3 f = FsrRcasLoadF(sp + FfxInt32x2(1, 0)).rgb; - FfxFloat32x3 h = FsrRcasLoadF(sp + FfxInt32x2(0, 1)).rgb; - // Rename (32-bit) or regroup (16-bit). - FfxFloat32 bR = b.r; - FfxFloat32 bG = b.g; - FfxFloat32 bB = b.b; - FfxFloat32 dR = d.r; - FfxFloat32 dG = d.g; - FfxFloat32 dB = d.b; - FfxFloat32 eR = e.r; - FfxFloat32 eG = e.g; - FfxFloat32 eB = e.b; - FfxFloat32 fR = f.r; - FfxFloat32 fG = f.g; - FfxFloat32 fB = f.b; - FfxFloat32 hR = h.r; - FfxFloat32 hG = h.g; - FfxFloat32 hB = h.b; - // Run optional input transform. - FsrRcasInputF(bR, bG, bB); - FsrRcasInputF(dR, dG, dB); - FsrRcasInputF(eR, eG, eB); - FsrRcasInputF(fR, fG, fB); - FsrRcasInputF(hR, hG, hB); - // Luma times 2. - FfxFloat32 bL = bB * FfxFloat32(0.5) + (bR * FfxFloat32(0.5) + bG); - FfxFloat32 dL = dB * FfxFloat32(0.5) + (dR * FfxFloat32(0.5) + dG); - FfxFloat32 eL = eB * FfxFloat32(0.5) + (eR * FfxFloat32(0.5) + eG); - FfxFloat32 fL = fB * FfxFloat32(0.5) + (fR * FfxFloat32(0.5) + fG); - FfxFloat32 hL = hB * FfxFloat32(0.5) + (hR * FfxFloat32(0.5) + hG); - // Noise detection. - FfxFloat32 nz = FfxFloat32(0.25) * bL + FfxFloat32(0.25) * dL + FfxFloat32(0.25) * fL + FfxFloat32(0.25) * hL - eL; - nz = ffxSaturate(abs(nz) * ffxApproximateReciprocalMedium(ffxMax3(ffxMax3(bL, dL, eL), fL, hL) - ffxMin3(ffxMin3(bL, dL, eL), fL, hL))); - nz = FfxFloat32(-0.5) * nz + FfxFloat32(1.0); - // Min and max of ring. - FfxFloat32 mn4R = ffxMin(ffxMin3(bR, dR, fR), hR); - FfxFloat32 mn4G = ffxMin(ffxMin3(bG, dG, fG), hG); - FfxFloat32 mn4B = ffxMin(ffxMin3(bB, dB, fB), hB); - FfxFloat32 mx4R = max(ffxMax3(bR, dR, fR), hR); - FfxFloat32 mx4G = max(ffxMax3(bG, dG, fG), hG); - FfxFloat32 mx4B = max(ffxMax3(bB, dB, fB), hB); - // Immediate constants for peak range. - FfxFloat32x2 peakC = FfxFloat32x2(1.0, -1.0 * 4.0); - // Limiters, these need to be high precision RCPs. - FfxFloat32 hitMinR = mn4R * rcp(FfxFloat32(4.0) * mx4R); - FfxFloat32 hitMinG = mn4G * rcp(FfxFloat32(4.0) * mx4G); - FfxFloat32 hitMinB = mn4B * rcp(FfxFloat32(4.0) * mx4B); - FfxFloat32 hitMaxR = (peakC.x - mx4R) * rcp(FfxFloat32(4.0) * mn4R + peakC.y); - FfxFloat32 hitMaxG = (peakC.x - mx4G) * rcp(FfxFloat32(4.0) * mn4G + peakC.y); - FfxFloat32 hitMaxB = (peakC.x - mx4B) * rcp(FfxFloat32(4.0) * mn4B + peakC.y); - FfxFloat32 lobeR = max(-hitMinR, hitMaxR); - FfxFloat32 lobeG = max(-hitMinG, hitMaxG); - FfxFloat32 lobeB = max(-hitMinB, hitMaxB); - FfxFloat32 lobe = max(FfxFloat32(-FSR_RCAS_LIMIT), ffxMin(ffxMax3(lobeR, lobeG, lobeB), FfxFloat32(0.0))) * ffxAsFloat - (con.x); - // Apply noise removal. -#ifdef FSR_RCAS_DENOISE - lobe *= nz; -#endif - // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. - FfxFloat32 rcpL = ffxApproximateReciprocalMedium(FfxFloat32(4.0) * lobe + FfxFloat32(1.0)); - pixR = (lobe * bR + lobe * dR + lobe * hR + lobe * fR + eR) * rcpL; - pixG = (lobe * bG + lobe * dG + lobe * hG + lobe * fG + eG) * rcpL; - pixB = (lobe * bB + lobe * dB + lobe * hB + lobe * fB + eB) * rcpL; - return; - } -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// NON-PACKED 16-BIT VERSION -//============================================================================================================================== -#if defined(FFX_GPU) && FFX_HALF == 1 && defined(FSR_RCAS_H) - // Input callback prototypes that need to be implemented by calling shader - FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p); - void FsrRcasInputH(inout FfxFloat16 r,inout FfxFloat16 g,inout FfxFloat16 b); -//------------------------------------------------------------------------------------------------------------------------------ - void FsrRcasH( - out FfxFloat16 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. - out FfxFloat16 pixG, - out FfxFloat16 pixB, - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - out FfxFloat16 pixA, - #endif - FfxUInt32x2 ip, // Integer pixel position in output. - FfxUInt32x4 con){ // Constant generated by RcasSetup(). - // Sharpening algorithm uses minimal 3x3 pixel neighborhood. - // b - // d e f - // h - FfxInt16x2 sp=FfxInt16x2(ip); - FfxFloat16x3 b=FsrRcasLoadH(sp+FfxInt16x2( 0,-1)).rgb; - FfxFloat16x3 d=FsrRcasLoadH(sp+FfxInt16x2(-1, 0)).rgb; - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat16x4 ee=FsrRcasLoadH(sp); - FfxFloat16x3 e=ee.rgb;pixA=ee.a; - #else - FfxFloat16x3 e=FsrRcasLoadH(sp).rgb; - #endif - FfxFloat16x3 f=FsrRcasLoadH(sp+FfxInt16x2( 1, 0)).rgb; - FfxFloat16x3 h=FsrRcasLoadH(sp+FfxInt16x2( 0, 1)).rgb; - // Rename (32-bit) or regroup (16-bit). - FfxFloat16 bR=b.r; - FfxFloat16 bG=b.g; - FfxFloat16 bB=b.b; - FfxFloat16 dR=d.r; - FfxFloat16 dG=d.g; - FfxFloat16 dB=d.b; - FfxFloat16 eR=e.r; - FfxFloat16 eG=e.g; - FfxFloat16 eB=e.b; - FfxFloat16 fR=f.r; - FfxFloat16 fG=f.g; - FfxFloat16 fB=f.b; - FfxFloat16 hR=h.r; - FfxFloat16 hG=h.g; - FfxFloat16 hB=h.b; - // Run optional input transform. - FsrRcasInputH(bR,bG,bB); - FsrRcasInputH(dR,dG,dB); - FsrRcasInputH(eR,eG,eB); - FsrRcasInputH(fR,fG,fB); - FsrRcasInputH(hR,hG,hB); - // Luma times 2. - FfxFloat16 bL=bB*FFX_BROADCAST_FLOAT16(0.5)+(bR*FFX_BROADCAST_FLOAT16(0.5)+bG); - FfxFloat16 dL=dB*FFX_BROADCAST_FLOAT16(0.5)+(dR*FFX_BROADCAST_FLOAT16(0.5)+dG); - FfxFloat16 eL=eB*FFX_BROADCAST_FLOAT16(0.5)+(eR*FFX_BROADCAST_FLOAT16(0.5)+eG); - FfxFloat16 fL=fB*FFX_BROADCAST_FLOAT16(0.5)+(fR*FFX_BROADCAST_FLOAT16(0.5)+fG); - FfxFloat16 hL=hB*FFX_BROADCAST_FLOAT16(0.5)+(hR*FFX_BROADCAST_FLOAT16(0.5)+hG); - // Noise detection. - FfxFloat16 nz=FFX_BROADCAST_FLOAT16(0.25)*bL+FFX_BROADCAST_FLOAT16(0.25)*dL+FFX_BROADCAST_FLOAT16(0.25)*fL+FFX_BROADCAST_FLOAT16(0.25)*hL-eL; - nz=FfxFloat16(ffxSaturate(abs(nz)*ffxApproximateReciprocalMediumHalf(ffxMax3Half(ffxMax3Half(bL,dL,eL),fL,hL)-ffxMin3Half(ffxMin3Half(bL,dL,eL),fL,hL)))); - nz=FFX_BROADCAST_FLOAT16(-0.5)*nz+FFX_BROADCAST_FLOAT16(1.0); - // Min and max of ring. - FfxFloat16 mn4R=min(ffxMin3Half(bR,dR,fR),hR); - FfxFloat16 mn4G=min(ffxMin3Half(bG,dG,fG),hG); - FfxFloat16 mn4B=min(ffxMin3Half(bB,dB,fB),hB); - FfxFloat16 mx4R=max(ffxMax3Half(bR,dR,fR),hR); - FfxFloat16 mx4G=max(ffxMax3Half(bG,dG,fG),hG); - FfxFloat16 mx4B=max(ffxMax3Half(bB,dB,fB),hB); - // Immediate constants for peak range. - FfxFloat16x2 peakC=FfxFloat16x2(1.0,-1.0*4.0); - // Limiters, these need to be high precision RCPs. - FfxFloat16 hitMinR=mn4R*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4R); - FfxFloat16 hitMinG=mn4G*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4G); - FfxFloat16 hitMinB=mn4B*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4B); - FfxFloat16 hitMaxR=(peakC.x-mx4R)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4R+peakC.y); - FfxFloat16 hitMaxG=(peakC.x-mx4G)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4G+peakC.y); - FfxFloat16 hitMaxB=(peakC.x-mx4B)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4B+peakC.y); - FfxFloat16 lobeR=max(-hitMinR,hitMaxR); - FfxFloat16 lobeG=max(-hitMinG,hitMaxG); - FfxFloat16 lobeB=max(-hitMinB,hitMaxB); - FfxFloat16 lobe=max(FFX_BROADCAST_FLOAT16(-FSR_RCAS_LIMIT),min(ffxMax3Half(lobeR,lobeG,lobeB),FFX_BROADCAST_FLOAT16(0.0)))*FFX_UINT32_TO_FLOAT16X2(con.y).x; - // Apply noise removal. - #ifdef FSR_RCAS_DENOISE - lobe*=nz; - #endif - // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. - FfxFloat16 rcpL=ffxApproximateReciprocalMediumHalf(FFX_BROADCAST_FLOAT16(4.0)*lobe+FFX_BROADCAST_FLOAT16(1.0)); - pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; - pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; - pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; -} -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// PACKED 16-BIT VERSION -//============================================================================================================================== -#if defined(FFX_GPU)&& FFX_HALF == 1 && defined(FSR_RCAS_HX2) - // Input callback prototypes that need to be implemented by the calling shader - FfxFloat16x4 FsrRcasLoadHx2(FfxInt16x2 p); - void FsrRcasInputHx2(inout FfxFloat16x2 r,inout FfxFloat16x2 g,inout FfxFloat16x2 b); -//------------------------------------------------------------------------------------------------------------------------------ - // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. - void FsrRcasDepackHx2(out FfxFloat16x4 pix0,out FfxFloat16x4 pix1,FfxFloat16x2 pixR,FfxFloat16x2 pixG,FfxFloat16x2 pixB){ - #ifdef FFX_HLSL - // Invoke a slower path for DX only, since it won't allow uninitialized values. - pix0.a=pix1.a=0.0; - #endif - pix0.rgb=FfxFloat16x3(pixR.x,pixG.x,pixB.x); - pix1.rgb=FfxFloat16x3(pixR.y,pixG.y,pixB.y);} -//------------------------------------------------------------------------------------------------------------------------------ - void FsrRcasHx2( - // Output values are for 2 8x8 tiles in a 16x8 region. - // pix.x = left 8x8 tile - // pix.y = right 8x8 tile - // This enables later processing to easily be packed as well. - out FfxFloat16x2 pixR, - out FfxFloat16x2 pixG, - out FfxFloat16x2 pixB, - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - out FfxFloat16x2 pixA, - #endif - FfxUInt32x2 ip, // Integer pixel position in output. - FfxUInt32x4 con){ // Constant generated by RcasSetup(). - // No scaling algorithm uses minimal 3x3 pixel neighborhood. - FfxInt16x2 sp0=FfxInt16x2(ip); - FfxFloat16x3 b0=FsrRcasLoadHx2(sp0+FfxInt16x2( 0,-1)).rgb; - FfxFloat16x3 d0=FsrRcasLoadHx2(sp0+FfxInt16x2(-1, 0)).rgb; - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat16x4 ee0=FsrRcasLoadHx2(sp0); - FfxFloat16x3 e0=ee0.rgb;pixA.r=ee0.a; - #else - FfxFloat16x3 e0=FsrRcasLoadHx2(sp0).rgb; - #endif - FfxFloat16x3 f0=FsrRcasLoadHx2(sp0+FfxInt16x2( 1, 0)).rgb; - FfxFloat16x3 h0=FsrRcasLoadHx2(sp0+FfxInt16x2( 0, 1)).rgb; - FfxInt16x2 sp1=sp0+FfxInt16x2(8,0); - FfxFloat16x3 b1=FsrRcasLoadHx2(sp1+FfxInt16x2( 0,-1)).rgb; - FfxFloat16x3 d1=FsrRcasLoadHx2(sp1+FfxInt16x2(-1, 0)).rgb; - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat16x4 ee1=FsrRcasLoadHx2(sp1); - FfxFloat16x3 e1=ee1.rgb;pixA.g=ee1.a; - #else - FfxFloat16x3 e1=FsrRcasLoadHx2(sp1).rgb; - #endif - FfxFloat16x3 f1=FsrRcasLoadHx2(sp1+FfxInt16x2( 1, 0)).rgb; - FfxFloat16x3 h1=FsrRcasLoadHx2(sp1+FfxInt16x2( 0, 1)).rgb; - // Arrays of Structures to Structures of Arrays conversion. - FfxFloat16x2 bR=FfxFloat16x2(b0.r,b1.r); - FfxFloat16x2 bG=FfxFloat16x2(b0.g,b1.g); - FfxFloat16x2 bB=FfxFloat16x2(b0.b,b1.b); - FfxFloat16x2 dR=FfxFloat16x2(d0.r,d1.r); - FfxFloat16x2 dG=FfxFloat16x2(d0.g,d1.g); - FfxFloat16x2 dB=FfxFloat16x2(d0.b,d1.b); - FfxFloat16x2 eR=FfxFloat16x2(e0.r,e1.r); - FfxFloat16x2 eG=FfxFloat16x2(e0.g,e1.g); - FfxFloat16x2 eB=FfxFloat16x2(e0.b,e1.b); - FfxFloat16x2 fR=FfxFloat16x2(f0.r,f1.r); - FfxFloat16x2 fG=FfxFloat16x2(f0.g,f1.g); - FfxFloat16x2 fB=FfxFloat16x2(f0.b,f1.b); - FfxFloat16x2 hR=FfxFloat16x2(h0.r,h1.r); - FfxFloat16x2 hG=FfxFloat16x2(h0.g,h1.g); - FfxFloat16x2 hB=FfxFloat16x2(h0.b,h1.b); - // Run optional input transform. - FsrRcasInputHx2(bR,bG,bB); - FsrRcasInputHx2(dR,dG,dB); - FsrRcasInputHx2(eR,eG,eB); - FsrRcasInputHx2(fR,fG,fB); - FsrRcasInputHx2(hR,hG,hB); - // Luma times 2. - FfxFloat16x2 bL=bB*FFX_BROADCAST_FLOAT16X2(0.5)+(bR*FFX_BROADCAST_FLOAT16X2(0.5)+bG); - FfxFloat16x2 dL=dB*FFX_BROADCAST_FLOAT16X2(0.5)+(dR*FFX_BROADCAST_FLOAT16X2(0.5)+dG); - FfxFloat16x2 eL=eB*FFX_BROADCAST_FLOAT16X2(0.5)+(eR*FFX_BROADCAST_FLOAT16X2(0.5)+eG); - FfxFloat16x2 fL=fB*FFX_BROADCAST_FLOAT16X2(0.5)+(fR*FFX_BROADCAST_FLOAT16X2(0.5)+fG); - FfxFloat16x2 hL=hB*FFX_BROADCAST_FLOAT16X2(0.5)+(hR*FFX_BROADCAST_FLOAT16X2(0.5)+hG); - // Noise detection. - FfxFloat16x2 nz=FFX_BROADCAST_FLOAT16X2(0.25)*bL+FFX_BROADCAST_FLOAT16X2(0.25)*dL+FFX_BROADCAST_FLOAT16X2(0.25)*fL+FFX_BROADCAST_FLOAT16X2(0.25)*hL-eL; - nz=ffxSaturate(abs(nz)*ffxApproximateReciprocalMediumHalf(ffxMax3Half(ffxMax3Half(bL,dL,eL),fL,hL)-ffxMin3Half(ffxMin3Half(bL,dL,eL),fL,hL))); - nz=FFX_BROADCAST_FLOAT16X2(-0.5)*nz+FFX_BROADCAST_FLOAT16X2(1.0); - // Min and max of ring. - FfxFloat16x2 mn4R=min(ffxMin3Half(bR,dR,fR),hR); - FfxFloat16x2 mn4G=min(ffxMin3Half(bG,dG,fG),hG); - FfxFloat16x2 mn4B=min(ffxMin3Half(bB,dB,fB),hB); - FfxFloat16x2 mx4R=max(ffxMax3Half(bR,dR,fR),hR); - FfxFloat16x2 mx4G=max(ffxMax3Half(bG,dG,fG),hG); - FfxFloat16x2 mx4B=max(ffxMax3Half(bB,dB,fB),hB); - // Immediate constants for peak range. - FfxFloat16x2 peakC=FfxFloat16x2(1.0,-1.0*4.0); - // Limiters, these need to be high precision RCPs. - FfxFloat16x2 hitMinR=mn4R*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4R); - FfxFloat16x2 hitMinG=mn4G*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4G); - FfxFloat16x2 hitMinB=mn4B*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4B); - FfxFloat16x2 hitMaxR=(peakC.x-mx4R)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4R+peakC.y); - FfxFloat16x2 hitMaxG=(peakC.x-mx4G)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4G+peakC.y); - FfxFloat16x2 hitMaxB=(peakC.x-mx4B)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4B+peakC.y); - FfxFloat16x2 lobeR=max(-hitMinR,hitMaxR); - FfxFloat16x2 lobeG=max(-hitMinG,hitMaxG); - FfxFloat16x2 lobeB=max(-hitMinB,hitMaxB); - FfxFloat16x2 lobe=max(FFX_BROADCAST_FLOAT16X2(-FSR_RCAS_LIMIT),min(ffxMax3Half(lobeR,lobeG,lobeB),FFX_BROADCAST_FLOAT16X2(0.0)))*FFX_BROADCAST_FLOAT16X2(FFX_UINT32_TO_FLOAT16X2(con.y).x); - // Apply noise removal. - #ifdef FSR_RCAS_DENOISE - lobe*=nz; - #endif - // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. - FfxFloat16x2 rcpL=ffxApproximateReciprocalMediumHalf(FFX_BROADCAST_FLOAT16X2(4.0)*lobe+FFX_BROADCAST_FLOAT16X2(1.0)); - pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; - pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; - pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR -// -//------------------------------------------------------------------------------------------------------------------------------ -// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. -// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. -// The 'Lfga*()' functions provide a convenient way to introduce grain. -// These functions limit grain based on distance to signal limits. -// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. -// Grain application should be done in a linear colorspace. -// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). -//------------------------------------------------------------------------------------------------------------------------------ -// Usage, -// FsrLfga*( -// color, // In/out linear colorspace color {0 to 1} ranged. -// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. -// amount); // Amount of grain (0 to 1} ranged. -//------------------------------------------------------------------------------------------------------------------------------ -// Example if grain texture is monochrome: 'FsrLfgaF(color,ffxBroadcast3(grain),amount)' -//============================================================================================================================== -#if defined(FFX_GPU) - // Maximum grain is the minimum distance to the signal limit. - void FsrLfgaF(inout FfxFloat32x3 c, FfxFloat32x3 t, FfxFloat32 a) - { - c += (t * ffxBroadcast3(a)) * ffxMin(ffxBroadcast3(1.0) - c, c); - } -#endif -//============================================================================================================================== -#if defined(FFX_GPU)&& FFX_HALF == 1 - // Half precision version (slower). - void FsrLfgaH(inout FfxFloat16x3 c, FfxFloat16x3 t, FfxFloat16 a) - { - c += (t * FFX_BROADCAST_FLOAT16X3(a)) * min(FFX_BROADCAST_FLOAT16X3(1.0) - c, c); - } - //------------------------------------------------------------------------------------------------------------------------------ - // Packed half precision version (faster). - void FsrLfgaHx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB,FfxFloat16x2 tR,FfxFloat16x2 tG,FfxFloat16x2 tB,FfxFloat16 a){ - cR+=(tR*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cR,cR);cG+=(tG*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cG,cG);cB+=(tB*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cB,cB);} -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER -// -//------------------------------------------------------------------------------------------------------------------------------ -// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. -// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. -//------------------------------------------------------------------------------------------------------------------------------ -// Reversible tonemapper usage, -// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. -// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. -//============================================================================================================================== -#if defined(FFX_GPU) - void FsrSrtmF(inout FfxFloat32x3 c) - { - c *= ffxBroadcast3(rcp(ffxMax3(c.r, c.g, c.b) + FfxFloat32(1.0))); - } - // The extra max solves the c=1.0 case (which is a /0). - void FsrSrtmInvF(inout FfxFloat32x3 c){c*=ffxBroadcast3(rcp(max(FfxFloat32(1.0/32768.0),FfxFloat32(1.0)-ffxMax3(c.r,c.g,c.b))));} -#endif -//============================================================================================================================== -#if defined(FFX_GPU )&& FFX_HALF == 1 - void FsrSrtmH(inout FfxFloat16x3 c) - { - c *= FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(ffxMax3Half(c.r, c.g, c.b) + FFX_BROADCAST_FLOAT16(1.0))); - } - void FsrSrtmInvH(inout FfxFloat16x3 c) - { - c *= FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(max(FFX_BROADCAST_FLOAT16(1.0 / 32768.0), FFX_BROADCAST_FLOAT16(1.0) - ffxMax3Half(c.r, c.g, c.b)))); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrSrtmHx2(inout FfxFloat16x2 cR, inout FfxFloat16x2 cG, inout FfxFloat16x2 cB) - { - FfxFloat16x2 rcp = ffxReciprocalHalf(ffxMax3Half(cR, cG, cB) + FFX_BROADCAST_FLOAT16X2(1.0)); - cR *= rcp; - cG *= rcp; - cB *= rcp; - } - void FsrSrtmInvHx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB) - { - FfxFloat16x2 rcp=ffxReciprocalHalf(max(FFX_BROADCAST_FLOAT16X2(1.0/32768.0),FFX_BROADCAST_FLOAT16X2(1.0)-ffxMax3Half(cR,cG,cB))); - cR*=rcp; - cG*=rcp; - cB*=rcp; - } -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER -// -//------------------------------------------------------------------------------------------------------------------------------ -// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. -// Gamma 2.0 is used so that the conversion back to linear is just to square the color. -// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. -// Given good non-biased temporal blue noise as dither input, -// the output dither will temporally conserve energy. -// This is done by choosing the linear nearest step point instead of perceptual nearest. -// See code below for details. -//------------------------------------------------------------------------------------------------------------------------------ -// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION -// =============================================== -// - Output is 'FfxUInt32(floor(saturate(n)*255.0+0.5))'. -// - Thus rounding is to nearest. -// - NaN gets converted to zero. -// - INF is clamped to {0.0 to 1.0}. -//============================================================================================================================== -#if defined(FFX_GPU) - // Hand tuned integer position to dither value, with more values than simple checkerboard. - // Only 32-bit has enough precision for this compddation. - // Output is {0 to <1}. - FfxFloat32 FsrTepdDitF(FfxUInt32x2 p, FfxUInt32 f) - { - FfxFloat32 x = FfxFloat32(p.x + f); - FfxFloat32 y = FfxFloat32(p.y); - // The 1.61803 golden ratio. - FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); - // Number designed to provide a good visual pattern. - FfxFloat32 b = FfxFloat32(1.0 / 3.69); - x = x * a + (y * b); - return ffxFract(x); - } - //------------------------------------------------------------------------------------------------------------------------------ - // This version is 8-bit gamma 2.0. - // The 'c' input is {0 to 1}. - // Output is {0 to 1} ready for image store. - void FsrTepdC8F(inout FfxFloat32x3 c, FfxFloat32 dit) - { - FfxFloat32x3 n = ffxSqrt(c); - n = floor(n * ffxBroadcast3(255.0)) * ffxBroadcast3(1.0 / 255.0); - FfxFloat32x3 a = n * n; - FfxFloat32x3 b = n + ffxBroadcast3(1.0 / 255.0); - b = b * b; - // Ratio of 'a' to 'b' required to produce 'c'. - // ffxApproximateReciprocal() won't work here (at least for very high dynamic ranges). - // ffxApproximateReciprocalMedium() is an IADD,FMA,MUL. - FfxFloat32x3 r = (c - b) * ffxApproximateReciprocalMedium(a - b); - // Use the ratio as a cutoff to choose 'a' or 'b'. - // ffxIsGreaterThanZero() is a MUL. - c = ffxSaturate(n + ffxIsGreaterThanZero(ffxBroadcast3(dit) - r) * ffxBroadcast3(1.0 / 255.0)); - } - //------------------------------------------------------------------------------------------------------------------------------ - // This version is 10-bit gamma 2.0. - // The 'c' input is {0 to 1}. - // Output is {0 to 1} ready for image store. - void FsrTepdC10F(inout FfxFloat32x3 c, FfxFloat32 dit) - { - FfxFloat32x3 n = ffxSqrt(c); - n = floor(n * ffxBroadcast3(1023.0)) * ffxBroadcast3(1.0 / 1023.0); - FfxFloat32x3 a = n * n; - FfxFloat32x3 b = n + ffxBroadcast3(1.0 / 1023.0); - b = b * b; - FfxFloat32x3 r = (c - b) * ffxApproximateReciprocalMedium(a - b); - c = ffxSaturate(n + ffxIsGreaterThanZero(ffxBroadcast3(dit) - r) * ffxBroadcast3(1.0 / 1023.0)); - } -#endif -//============================================================================================================================== -#if defined(FFX_GPU)&& FFX_HALF == 1 - FfxFloat16 FsrTepdDitH(FfxUInt32x2 p, FfxUInt32 f) - { - FfxFloat32 x = FfxFloat32(p.x + f); - FfxFloat32 y = FfxFloat32(p.y); - FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); - FfxFloat32 b = FfxFloat32(1.0 / 3.69); - x = x * a + (y * b); - return FfxFloat16(ffxFract(x)); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC8H(inout FfxFloat16x3 c, FfxFloat16 dit) - { - FfxFloat16x3 n = sqrt(c); - n = floor(n * FFX_BROADCAST_FLOAT16X3(255.0)) * FFX_BROADCAST_FLOAT16X3(1.0 / 255.0); - FfxFloat16x3 a = n * n; - FfxFloat16x3 b = n + FFX_BROADCAST_FLOAT16X3(1.0 / 255.0); - b = b * b; - FfxFloat16x3 r = (c - b) * ffxApproximateReciprocalMediumHalf(a - b); - c = FfxFloat16x3(ffxSaturate(n + ffxIsGreaterThanZeroHalf(FFX_BROADCAST_FLOAT16X3(dit) - r) * FFX_BROADCAST_FLOAT16X3(1.0 / 255.0))); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC10H(inout FfxFloat16x3 c, FfxFloat16 dit) - { - FfxFloat16x3 n = sqrt(c); - n = floor(n * FFX_BROADCAST_FLOAT16X3(1023.0)) * FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0); - FfxFloat16x3 a = n * n; - FfxFloat16x3 b = n + FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0); - b = b * b; - FfxFloat16x3 r = (c - b) * ffxApproximateReciprocalMediumHalf(a - b); - c = FfxFloat16x3(ffxSaturate(n + ffxIsGreaterThanZeroHalf(FFX_BROADCAST_FLOAT16X3(dit) - r) * FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0))); - } - //============================================================================================================================== - // This computes dither for positions 'p' and 'p+{8,0}'. - FfxFloat16x2 FsrTepdDitHx2(FfxUInt32x2 p, FfxUInt32 f) - { - FfxFloat32x2 x; - x.x = FfxFloat32(p.x + f); - x.y = x.x + FfxFloat32(8.0); - FfxFloat32 y = FfxFloat32(p.y); - FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); - FfxFloat32 b = FfxFloat32(1.0 / 3.69); - x = x * ffxBroadcast2(a) + ffxBroadcast2(y * b); - return FfxFloat16x2(ffxFract(x)); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC8Hx2(inout FfxFloat16x2 cR, inout FfxFloat16x2 cG, inout FfxFloat16x2 cB, FfxFloat16x2 dit) - { - FfxFloat16x2 nR = sqrt(cR); - FfxFloat16x2 nG = sqrt(cG); - FfxFloat16x2 nB = sqrt(cB); - nR = floor(nR * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - nG = floor(nG * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - nB = floor(nB * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - FfxFloat16x2 aR = nR * nR; - FfxFloat16x2 aG = nG * nG; - FfxFloat16x2 aB = nB * nB; - FfxFloat16x2 bR = nR + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - bR = bR * bR; - FfxFloat16x2 bG = nG + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - bG = bG * bG; - FfxFloat16x2 bB = nB + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - bB = bB * bB; - FfxFloat16x2 rR = (cR - bR) * ffxApproximateReciprocalMediumHalf(aR - bR); - FfxFloat16x2 rG = (cG - bG) * ffxApproximateReciprocalMediumHalf(aG - bG); - FfxFloat16x2 rB = (cB - bB) * ffxApproximateReciprocalMediumHalf(aB - bB); - cR = FfxFloat16x2(ffxSaturate(nR + ffxIsGreaterThanZeroHalf(dit - rR) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0))); - cG = FfxFloat16x2(ffxSaturate(nG + ffxIsGreaterThanZeroHalf(dit - rG) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0))); - cB = FfxFloat16x2(ffxSaturate(nB + ffxIsGreaterThanZeroHalf(dit - rB) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0))); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC10Hx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB,FfxFloat16x2 dit){ - FfxFloat16x2 nR=sqrt(cR); - FfxFloat16x2 nG=sqrt(cG); - FfxFloat16x2 nB=sqrt(cB); - nR=floor(nR*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); - nG=floor(nG*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); - nB=floor(nB*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); - FfxFloat16x2 aR=nR*nR; - FfxFloat16x2 aG=nG*nG; - FfxFloat16x2 aB=nB*nB; - FfxFloat16x2 bR=nR+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bR=bR*bR; - FfxFloat16x2 bG=nG+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bG=bG*bG; - FfxFloat16x2 bB=nB+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bB=bB*bB; - FfxFloat16x2 rR=(cR-bR)*ffxApproximateReciprocalMediumHalf(aR-bR); - FfxFloat16x2 rG=(cG-bG)*ffxApproximateReciprocalMediumHalf(aG-bG); - FfxFloat16x2 rB=(cB-bB)*ffxApproximateReciprocalMediumHalf(aB-bB); - cR=FfxFloat16x2(ffxSaturate(nR+ffxIsGreaterThanZeroHalf(dit-rR)*FFX_BROADCAST_FLOAT16X2(1.0/1023.0))); - cG=FfxFloat16x2(ffxSaturate(nG+ffxIsGreaterThanZeroHalf(dit-rG)*FFX_BROADCAST_FLOAT16X2(1.0/1023.0))); - cB=FfxFloat16x2(ffxSaturate(nB + ffxIsGreaterThanZeroHalf(dit - rB) * FFX_BROADCAST_FLOAT16X2(1.0 / 1023.0))); -} -#endif diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta deleted file mode 100644 index 3c97f69..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta +++ /dev/null @@ -1,67 +0,0 @@ -fileFormatVersion: 2 -guid: 628e23510f46ef44bbf0035ce9a63be0 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd.meta deleted file mode 100644 index 0b775af..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 0f03de1579ac3294595ae4f40106b7a2 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h b/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h deleted file mode 100644 index 6441419..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h +++ /dev/null @@ -1,1009 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup FfxGPUSpd FidelityFX SPD -/// FidelityFX Single Pass Downsampler 2.0 GPU documentation -/// -/// @ingroup FfxGPUEffects - -/// Setup required constant values for SPD (CPU). -/// -/// @param [out] dispatchThreadGroupCountXY CPU side: dispatch thread group count xy. z is number of slices of the input texture -/// @param [out] workGroupOffset GPU side: pass in as constant -/// @param [out] numWorkGroupsAndMips GPU side: pass in as constant -/// @param [in] rectInfo left, top, width, height -/// @param [in] mips optional: if -1, calculate based on rect width and height -/// -/// @ingroup FfxGPUSpd -#if defined(FFX_CPU) -FFX_STATIC void ffxSpdSetup(FfxUInt32x2 dispatchThreadGroupCountXY, - FfxUInt32x2 workGroupOffset, - FfxUInt32x2 numWorkGroupsAndMips, - FfxUInt32x4 rectInfo, - FfxInt32 mips) -{ - // determines the offset of the first tile to downsample based on - // left (rectInfo[0]) and top (rectInfo[1]) of the subregion. - workGroupOffset[0] = rectInfo[0] / 64; - workGroupOffset[1] = rectInfo[1] / 64; - - FfxUInt32 endIndexX = (rectInfo[0] + rectInfo[2] - 1) / 64; // rectInfo[0] = left, rectInfo[2] = width - FfxUInt32 endIndexY = (rectInfo[1] + rectInfo[3] - 1) / 64; // rectInfo[1] = top, rectInfo[3] = height - - // we only need to dispatch as many thread groups as tiles we need to downsample - // number of tiles per slice depends on the subregion to downsample - dispatchThreadGroupCountXY[0] = endIndexX + 1 - workGroupOffset[0]; - dispatchThreadGroupCountXY[1] = endIndexY + 1 - workGroupOffset[1]; - - // number of thread groups per slice - numWorkGroupsAndMips[0] = (dispatchThreadGroupCountXY[0]) * (dispatchThreadGroupCountXY[1]); - - if (mips >= 0) - { - numWorkGroupsAndMips[1] = FfxUInt32(mips); - } - else - { - // calculate based on rect width and height - FfxUInt32 resolution = ffxMax(rectInfo[2], rectInfo[3]); - numWorkGroupsAndMips[1] = FfxUInt32((ffxMin(floor(log2(FfxFloat32(resolution))), FfxFloat32(12)))); - } -} - -/// Setup required constant values for SPD (CPU). -/// -/// @param [out] dispatchThreadGroupCountXY CPU side: dispatch thread group count xy. z is number of slices of the input texture -/// @param [out] workGroupOffset GPU side: pass in as constant -/// @param [out] numWorkGroupsAndMips GPU side: pass in as constant -/// @param [in] rectInfo left, top, width, height -/// -/// @ingroup FfxGPUSpd -FFX_STATIC void ffxSpdSetup(FfxUInt32x2 dispatchThreadGroupCountXY, - FfxUInt32x2 workGroupOffset, - FfxUInt32x2 numWorkGroupsAndMips, - FfxUInt32x4 rectInfo) -{ - ffxSpdSetup(dispatchThreadGroupCountXY, workGroupOffset, numWorkGroupsAndMips, rectInfo, -1); -} -#endif // #if defined(FFX_CPU) - - -//============================================================================================================================== -// NON-PACKED VERSION -//============================================================================================================================== -#if defined(FFX_GPU) -#if defined(FFX_SPD_PACKED_ONLY) -// Avoid compiler errors by including default implementations of these callbacks. -FfxFloat32x4 SpdLoadSourceImage(FfxInt32x2 p, FfxUInt32 slice) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} - -FfxFloat32x4 SpdLoad(FfxInt32x2 p, FfxUInt32 slice) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} -void SpdStore(FfxInt32x2 p, FfxFloat32x4 value, FfxUInt32 mip, FfxUInt32 slice) -{ -} -FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} -void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) -{ -} -FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} -#endif // #if FFX_SPD_PACKED_ONLY - -//_____________________________________________________________/\_______________________________________________________________ - -void ffxSpdWorkgroupShuffleBarrier() -{ - FFX_GROUP_MEMORY_BARRIER(); -} - -// Only last active workgroup should proceed -bool SpdExitWorkgroup(FfxUInt32 numWorkGroups, FfxUInt32 localInvocationIndex, FfxUInt32 slice) -{ - // global atomic counter - if (localInvocationIndex == 0) - { - SpdIncreaseAtomicCounter(slice); - } - - ffxSpdWorkgroupShuffleBarrier(); - return (SpdGetAtomicCounter() != (numWorkGroups - 1)); -} - -// User defined: FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3); -FfxFloat32x4 SpdReduceQuad(FfxFloat32x4 v) -{ -#if defined(FFX_GLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - - FfxFloat32x4 v0 = v; - FfxFloat32x4 v1 = subgroupQuadSwapHorizontal(v); - FfxFloat32x4 v2 = subgroupQuadSwapVertical(v); - FfxFloat32x4 v3 = subgroupQuadSwapDiagonal(v); - return SpdReduce4(v0, v1, v2, v3); - -#elif defined(FFX_HLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - - // requires SM6.0 - FfxUInt32 quad = WaveGetLaneIndex() & (~0x3); - FfxFloat32x4 v0 = v; - FfxFloat32x4 v1 = WaveReadLaneAt(v, quad | 1); - FfxFloat32x4 v2 = WaveReadLaneAt(v, quad | 2); - FfxFloat32x4 v3 = WaveReadLaneAt(v, quad | 3); - return SpdReduce4(v0, v1, v2, v3); -/* - // if SM6.0 is not available, you can use the AMD shader intrinsics - // the AMD shader intrinsics are available in AMD GPU Services (AGS) library: - // https://gpuopen.com/amd-gpu-services-ags-library/ - // works for DX11 - FfxFloat32x4 v0 = v; - FfxFloat32x4 v1; - v1.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - FfxFloat32x4 v2; - v2.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - FfxFloat32x4 v3; - v3.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - return SpdReduce4(v0, v1, v2, v3); - */ -#endif - return v; -} - -FfxFloat32x4 SpdReduceIntermediate(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3) -{ - FfxFloat32x4 v0 = SpdLoadIntermediate(i0.x, i0.y); - FfxFloat32x4 v1 = SpdLoadIntermediate(i1.x, i1.y); - FfxFloat32x4 v2 = SpdLoadIntermediate(i2.x, i2.y); - FfxFloat32x4 v3 = SpdLoadIntermediate(i3.x, i3.y); - return SpdReduce4(v0, v1, v2, v3); -} - -FfxFloat32x4 SpdReduceLoad4(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat32x4 v0 = SpdLoad(FfxInt32x2(i0), slice); - FfxFloat32x4 v1 = SpdLoad(FfxInt32x2(i1), slice); - FfxFloat32x4 v2 = SpdLoad(FfxInt32x2(i2), slice); - FfxFloat32x4 v3 = SpdLoad(FfxInt32x2(i3), slice); - return SpdReduce4(v0, v1, v2, v3); -} - -FfxFloat32x4 SpdReduceLoad4(FfxUInt32x2 base, FfxUInt32 slice) -{ - return SpdReduceLoad4(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -} - -FfxFloat32x4 SpdReduceLoadSourceImage4(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat32x4 v0 = SpdLoadSourceImage(FfxInt32x2(i0), slice); - FfxFloat32x4 v1 = SpdLoadSourceImage(FfxInt32x2(i1), slice); - FfxFloat32x4 v2 = SpdLoadSourceImage(FfxInt32x2(i2), slice); - FfxFloat32x4 v3 = SpdLoadSourceImage(FfxInt32x2(i3), slice); - return SpdReduce4(v0, v1, v2, v3); -} - -FfxFloat32x4 SpdReduceLoadSourceImage(FfxUInt32x2 base, FfxUInt32 slice) -{ -#if defined(SPD_LINEAR_SAMPLER) - return SpdLoadSourceImage(FfxInt32x2(base), slice); -#else - return SpdReduceLoadSourceImage4(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -#endif -} - -void SpdDownsampleMips_0_1_Intrinsics(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ - FfxFloat32x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[3], 0, slice); - - if (mip <= 1) - return; - - v[0] = SpdReduceQuad(v[0]); - v[1] = SpdReduceQuad(v[1]); - v[2] = SpdReduceQuad(v[2]); - v[3] = SpdReduceQuad(v[3]); - - if ((localInvocationIndex % 4) == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2), v[0], 1, slice); - SpdStoreIntermediate(x / 2, y / 2, v[0]); - - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2), v[1], 1, slice); - SpdStoreIntermediate(x / 2 + 8, y / 2, v[1]); - - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2 + 8), v[2], 1, slice); - SpdStoreIntermediate(x / 2, y / 2 + 8, v[2]); - - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); - SpdStoreIntermediate(x / 2 + 8, y / 2 + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1_LDS(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ - FfxFloat32x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[3], 0, slice); - - if (mip <= 1) - return; - - for (FfxUInt32 i = 0; i < 4; i++) - { - SpdStoreIntermediate(x, y, v[i]); - ffxSpdWorkgroupShuffleBarrier(); - if (localInvocationIndex < 64) - { - v[i] = SpdReduceIntermediate(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); - } - ffxSpdWorkgroupShuffleBarrier(); - } - - if (localInvocationIndex < 64) - { - SpdStoreIntermediate(x + 0, y + 0, v[0]); - SpdStoreIntermediate(x + 8, y + 0, v[1]); - SpdStoreIntermediate(x + 0, y + 8, v[2]); - SpdStoreIntermediate(x + 8, y + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - SpdDownsampleMips_0_1_LDS(x, y, workGroupID, localInvocationIndex, mip, slice); -#else - SpdDownsampleMips_0_1_Intrinsics(x, y, workGroupID, localInvocationIndex, mip, slice); -#endif -} - - -void SpdDownsampleMip_2(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 64) - { - FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStore(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS, try to reduce bank conflicts - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // ... - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - SpdStoreIntermediate(x * 2 + y % 2, y * 2, v); - } -#else - FfxFloat32x4 v = SpdLoadIntermediate(x, y); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediate(x + (y / 2) % 2, y, v); - } -#endif -} - -void SpdDownsampleMip_3(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 16) - { - // x 0 x 0 - // 0 0 0 0 - // 0 x 0 x - // 0 0 0 0 - FfxFloat32x4 v = - SpdReduceIntermediate(FfxUInt32x2(x * 4 + 0 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 2 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 0 + 1, y * 4 + 2), FfxUInt32x2(x * 4 + 2 + 1, y * 4 + 2)); - SpdStore(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 - // ... - // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 - // ... - // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x - // ... - SpdStoreIntermediate(x * 4 + y, y * 4, v); - } -#else - if (localInvocationIndex < 64) - { - FfxFloat32x4 v = SpdLoadIntermediate(x * 2 + y % 2, y * 2); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediate(x * 2 + y / 2, y * 2, v); - } - } -#endif -} - -void SpdDownsampleMip_4(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 4) - { - // x 0 0 0 x 0 0 0 - // ... - // 0 x 0 0 0 x 0 0 - FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), - FfxUInt32x2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); - SpdStore(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x x x x 0 ... - // 0 ... - SpdStoreIntermediate(x + y * 2, 0, v); - } -#else - if (localInvocationIndex < 16) - { - FfxFloat32x4 v = SpdLoadIntermediate(x * 4 + y, y * 4); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediate(x / 2 + y, 0, v); - } - } -#endif -} - -void SpdDownsampleMip_5(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 1) - { - // x x x x 0 ... - // 0 ... - FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(0, 0), FfxUInt32x2(1, 0), FfxUInt32x2(2, 0), FfxUInt32x2(3, 0)); - SpdStore(FfxInt32x2(workGroupID.xy), v, mip, slice); - } -#else - if (localInvocationIndex < 4) - { - FfxFloat32x4 v = SpdLoadIntermediate(localInvocationIndex, 0); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy), v, mip, slice); - } - } -#endif -} - -void SpdDownsampleMips_6_7(FfxUInt32 x, FfxUInt32 y, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxInt32x2 tex = FfxInt32x2(x * 4 + 0, y * 4 + 0); - FfxInt32x2 pix = FfxInt32x2(x * 2 + 0, y * 2 + 0); - FfxFloat32x4 v0 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v0, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 0); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 0); - FfxFloat32x4 v1 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v1, 6, slice); - - tex = FfxInt32x2(x * 4 + 0, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 0, y * 2 + 1); - FfxFloat32x4 v2 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v2, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 1); - FfxFloat32x4 v3 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v3, 6, slice); - - if (mips <= 7) - return; - // no barrier needed, working on values only from the same thread - - FfxFloat32x4 v = SpdReduce4(v0, v1, v2, v3); - SpdStore(FfxInt32x2(x, y), v, 7, slice); - SpdStoreIntermediate(x, y, v); -} - -void SpdDownsampleNextFour(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 baseMip, FfxUInt32 mips, FfxUInt32 slice) -{ - if (mips <= baseMip) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_2(x, y, workGroupID, localInvocationIndex, baseMip, slice); - - if (mips <= baseMip + 1) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_3(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); - - if (mips <= baseMip + 2) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_4(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); - - if (mips <= baseMip + 3) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_5(workGroupID, localInvocationIndex, baseMip + 3, slice); -} - -/// Downsamples a 64x64 tile based on the work group id. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// -/// @ingroup FfxGPUSpd -void SpdDownsample(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice) -{ - // compute MIP level 0 and 1 - FfxUInt32x2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); - FfxUInt32 x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); - FfxUInt32 y = sub_xy.y + 8 * ((localInvocationIndex >> 7)); - SpdDownsampleMips_0_1(x, y, workGroupID, localInvocationIndex, mips, slice); - - // compute MIP level 2, 3, 4, 5 - SpdDownsampleNextFour(x, y, workGroupID, localInvocationIndex, 2, mips, slice); - - if (mips <= 6) - return; - - // increase the global atomic counter for the given slice and check if it's the last remaining thread group: - // terminate if not, continue if yes. - if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) - return; - - // reset the global atomic counter back to 0 for the next spd dispatch - SpdResetAtomicCounter(slice); - - // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. - // compute MIP level 6 and 7 - SpdDownsampleMips_6_7(x, y, mips, slice); - - // compute MIP level 8, 9, 10, 11 - SpdDownsampleNextFour(x, y, FfxUInt32x2(0, 0), localInvocationIndex, 8, mips, slice); -} -/// Downsamples a 64x64 tile based on the work group id and work group offset. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. -/// -/// @ingroup FfxGPUSpd -void SpdDownsample(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice, FfxUInt32x2 workGroupOffset) -{ - SpdDownsample(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//============================================================================================================================== -// PACKED VERSION -//============================================================================================================================== - -#if FFX_HALF - -FfxFloat16x4 SpdReduceQuadH(FfxFloat16x4 v) -{ -#if defined(FFX_GLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - FfxFloat16x4 v0 = v; - FfxFloat16x4 v1 = subgroupQuadSwapHorizontal(v); - FfxFloat16x4 v2 = subgroupQuadSwapVertical(v); - FfxFloat16x4 v3 = subgroupQuadSwapDiagonal(v); - return SpdReduce4H(v0, v1, v2, v3); -#elif defined(FFX_HLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - // requires SM6.0 - FfxUInt32 quad = WaveGetLaneIndex() & (~0x3); - FfxFloat16x4 v0 = v; - FfxFloat16x4 v1 = WaveReadLaneAt(v, quad | 1); - FfxFloat16x4 v2 = WaveReadLaneAt(v, quad | 2); - FfxFloat16x4 v3 = WaveReadLaneAt(v, quad | 3); - return SpdReduce4H(v0, v1, v2, v3); -/* - // if SM6.0 is not available, you can use the AMD shader intrinsics - // the AMD shader intrinsics are available in AMD GPU Services (AGS) library: - // https://gpuopen.com/amd-gpu-services-ags-library/ - // works for DX11 - FfxFloat16x4 v0 = v; - FfxFloat16x4 v1; - v1.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - FfxFloat16x4 v2; - v2.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - FfxFloat16x4 v3; - v3.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - return SpdReduce4H(v0, v1, v2, v3); - */ -#endif - return FfxFloat16x4(0.0, 0.0, 0.0, 0.0); -} - -FfxFloat16x4 SpdReduceIntermediateH(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3) -{ - FfxFloat16x4 v0 = SpdLoadIntermediateH(i0.x, i0.y); - FfxFloat16x4 v1 = SpdLoadIntermediateH(i1.x, i1.y); - FfxFloat16x4 v2 = SpdLoadIntermediateH(i2.x, i2.y); - FfxFloat16x4 v3 = SpdLoadIntermediateH(i3.x, i3.y); - return SpdReduce4H(v0, v1, v2, v3); -} - -FfxFloat16x4 SpdReduceLoad4H(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat16x4 v0 = SpdLoadH(FfxInt32x2(i0), slice); - FfxFloat16x4 v1 = SpdLoadH(FfxInt32x2(i1), slice); - FfxFloat16x4 v2 = SpdLoadH(FfxInt32x2(i2), slice); - FfxFloat16x4 v3 = SpdLoadH(FfxInt32x2(i3), slice); - return SpdReduce4H(v0, v1, v2, v3); -} - -FfxFloat16x4 SpdReduceLoad4H(FfxUInt32x2 base, FfxUInt32 slice) -{ - return SpdReduceLoad4H(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -} - -FfxFloat16x4 SpdReduceLoadSourceImage4H(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat16x4 v0 = SpdLoadSourceImageH(FfxInt32x2(i0), slice); - FfxFloat16x4 v1 = SpdLoadSourceImageH(FfxInt32x2(i1), slice); - FfxFloat16x4 v2 = SpdLoadSourceImageH(FfxInt32x2(i2), slice); - FfxFloat16x4 v3 = SpdLoadSourceImageH(FfxInt32x2(i3), slice); - return SpdReduce4H(v0, v1, v2, v3); -} - -FfxFloat16x4 SpdReduceLoadSourceImageH(FfxUInt32x2 base, FfxUInt32 slice) -{ -#if defined(SPD_LINEAR_SAMPLER) - return SpdLoadSourceImageH(FfxInt32x2(base), slice); -#else - return SpdReduceLoadSourceImage4H(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -#endif -} - -void SpdDownsampleMips_0_1_IntrinsicsH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxFloat16x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[3], 0, slice); - - if (mips <= 1) - return; - - v[0] = SpdReduceQuadH(v[0]); - v[1] = SpdReduceQuadH(v[1]); - v[2] = SpdReduceQuadH(v[2]); - v[3] = SpdReduceQuadH(v[3]); - - if ((localInvocationIndex % 4) == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2), v[0], 1, slice); - SpdStoreIntermediateH(x / 2, y / 2, v[0]); - - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2), v[1], 1, slice); - SpdStoreIntermediateH(x / 2 + 8, y / 2, v[1]); - - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2 + 8), v[2], 1, slice); - SpdStoreIntermediateH(x / 2, y / 2 + 8, v[2]); - - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); - SpdStoreIntermediateH(x / 2 + 8, y / 2 + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1_LDSH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxFloat16x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[3], 0, slice); - - if (mips <= 1) - return; - - for (FfxUInt32 i = 0; i < 4; i++) - { - SpdStoreIntermediateH(x, y, v[i]); - ffxSpdWorkgroupShuffleBarrier(); - if (localInvocationIndex < 64) - { - v[i] = SpdReduceIntermediateH(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); - } - ffxSpdWorkgroupShuffleBarrier(); - } - - if (localInvocationIndex < 64) - { - SpdStoreIntermediateH(x + 0, y + 0, v[0]); - SpdStoreIntermediateH(x + 8, y + 0, v[1]); - SpdStoreIntermediateH(x + 0, y + 8, v[2]); - SpdStoreIntermediateH(x + 8, y + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - SpdDownsampleMips_0_1_LDSH(x, y, workGroupID, localInvocationIndex, mips, slice); -#else - SpdDownsampleMips_0_1_IntrinsicsH(x, y, workGroupID, localInvocationIndex, mips, slice); -#endif -} - - -void SpdDownsampleMip_2H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 64) - { - FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS, try to reduce bank conflicts - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // ... - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - SpdStoreIntermediateH(x * 2 + y % 2, y * 2, v); - } -#else - FfxFloat16x4 v = SpdLoadIntermediateH(x, y); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediateH(x + (y / 2) % 2, y, v); - } -#endif -} - -void SpdDownsampleMip_3H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 16) - { - // x 0 x 0 - // 0 0 0 0 - // 0 x 0 x - // 0 0 0 0 - FfxFloat16x4 v = - SpdReduceIntermediateH(FfxUInt32x2(x * 4 + 0 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 2 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 0 + 1, y * 4 + 2), FfxUInt32x2(x * 4 + 2 + 1, y * 4 + 2)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 - // ... - // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 - // ... - // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x - // ... - SpdStoreIntermediateH(x * 4 + y, y * 4, v); - } -#else - if (localInvocationIndex < 64) - { - FfxFloat16x4 v = SpdLoadIntermediateH(x * 2 + y % 2, y * 2); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediateH(x * 2 + y / 2, y * 2, v); - } - } -#endif -} - -void SpdDownsampleMip_4H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 4) - { - // x 0 0 0 x 0 0 0 - // ... - // 0 x 0 0 0 x 0 0 - FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), - FfxUInt32x2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x x x x 0 ... - // 0 ... - SpdStoreIntermediateH(x + y * 2, 0, v); - } -#else - if (localInvocationIndex < 16) - { - FfxFloat16x4 v = SpdLoadIntermediateH(x * 4 + y, y * 4); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediateH(x / 2 + y, 0, v); - } - } -#endif -} - -void SpdDownsampleMip_5H(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 1) - { - // x x x x 0 ... - // 0 ... - FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(0, 0), FfxUInt32x2(1, 0), FfxUInt32x2(2, 0), FfxUInt32x2(3, 0)); - SpdStoreH(FfxInt32x2(workGroupID.xy), v, mip, slice); - } -#else - if (localInvocationIndex < 4) - { - FfxFloat16x4 v = SpdLoadIntermediateH(localInvocationIndex, 0); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy), v, mip, slice); - } - } -#endif -} - -void SpdDownsampleMips_6_7H(FfxUInt32 x, FfxUInt32 y, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxInt32x2 tex = FfxInt32x2(x * 4 + 0, y * 4 + 0); - FfxInt32x2 pix = FfxInt32x2(x * 2 + 0, y * 2 + 0); - FfxFloat16x4 v0 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v0, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 0); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 0); - FfxFloat16x4 v1 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v1, 6, slice); - - tex = FfxInt32x2(x * 4 + 0, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 0, y * 2 + 1); - FfxFloat16x4 v2 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v2, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 1); - FfxFloat16x4 v3 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v3, 6, slice); - - if (mips < 8) - return; - // no barrier needed, working on values only from the same thread - - FfxFloat16x4 v = SpdReduce4H(v0, v1, v2, v3); - SpdStoreH(FfxInt32x2(x, y), v, 7, slice); - SpdStoreIntermediateH(x, y, v); -} - -void SpdDownsampleNextFourH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 baseMip, FfxUInt32 mips, FfxUInt32 slice) -{ - if (mips <= baseMip) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_2H(x, y, workGroupID, localInvocationIndex, baseMip, slice); - - if (mips <= baseMip + 1) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_3H(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); - - if (mips <= baseMip + 2) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_4H(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); - - if (mips <= baseMip + 3) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_5H(workGroupID, localInvocationIndex, baseMip + 3, slice); -} - -/// Downsamples a 64x64 tile based on the work group id and work group offset. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// Uses half types. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// -/// @ingroup FfxGPUSpd -void SpdDownsampleH(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice) -{ - FfxUInt32x2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); - FfxUInt32 x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); - FfxUInt32 y = sub_xy.y + 8 * ((localInvocationIndex >> 7)); - - // compute MIP level 0 and 1 - SpdDownsampleMips_0_1H(x, y, workGroupID, localInvocationIndex, mips, slice); - - // compute MIP level 2, 3, 4, 5 - SpdDownsampleNextFourH(x, y, workGroupID, localInvocationIndex, 2, mips, slice); - - if (mips < 7) - return; - - // increase the global atomic counter for the given slice and check if it's the last remaining thread group: - // terminate if not, continue if yes. - if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) - return; - - // reset the global atomic counter back to 0 for the next spd dispatch - SpdResetAtomicCounter(slice); - - // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. - // compute MIP level 6 and 7 - SpdDownsampleMips_6_7H(x, y, mips, slice); - - // compute MIP level 8, 9, 10, 11 - SpdDownsampleNextFourH(x, y, FfxUInt32x2(0, 0), localInvocationIndex, 8, mips, slice); -} - -/// Downsamples a 64x64 tile based on the work group id and work group offset. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// Uses half types. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. -/// -/// @ingroup FfxGPUSpd -void SpdDownsampleH(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice, FfxUInt32x2 workGroupOffset) -{ - SpdDownsampleH(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); -} - -#endif // #if FFX_HALF -#endif // #if defined(FFX_GPU) diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta b/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta deleted file mode 100644 index 2741ab1..0000000 --- a/Assets/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta +++ /dev/null @@ -1,67 +0,0 @@ -fileFormatVersion: 2 -guid: face65176ee3b82498bd0b8fed0ddacd -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/LICENSE.txt b/LICENSE.txt index cbe5a70..01b3e51 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,19 +1,19 @@ -// Copyright (c) 2023 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. +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. diff --git a/com.unity.postprocessing/.signature b/Packages/com.unity.postprocessing/.signature similarity index 100% rename from com.unity.postprocessing/.signature rename to Packages/com.unity.postprocessing/.signature diff --git a/com.unity.postprocessing/CHANGELOG.md b/Packages/com.unity.postprocessing/CHANGELOG.md similarity index 100% rename from com.unity.postprocessing/CHANGELOG.md rename to Packages/com.unity.postprocessing/CHANGELOG.md diff --git a/com.unity.postprocessing/CHANGELOG.md.meta b/Packages/com.unity.postprocessing/CHANGELOG.md.meta similarity index 100% rename from com.unity.postprocessing/CHANGELOG.md.meta rename to Packages/com.unity.postprocessing/CHANGELOG.md.meta diff --git a/com.unity.postprocessing/Documentation~/Ambient-Occlusion.md b/Packages/com.unity.postprocessing/Documentation~/Ambient-Occlusion.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Ambient-Occlusion.md rename to Packages/com.unity.postprocessing/Documentation~/Ambient-Occlusion.md diff --git a/com.unity.postprocessing/Documentation~/Anti-aliasing.md b/Packages/com.unity.postprocessing/Documentation~/Anti-aliasing.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Anti-aliasing.md rename to Packages/com.unity.postprocessing/Documentation~/Anti-aliasing.md diff --git a/com.unity.postprocessing/Documentation~/Auto-Exposure.md b/Packages/com.unity.postprocessing/Documentation~/Auto-Exposure.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Auto-Exposure.md rename to Packages/com.unity.postprocessing/Documentation~/Auto-Exposure.md diff --git a/com.unity.postprocessing/Documentation~/Bloom.md b/Packages/com.unity.postprocessing/Documentation~/Bloom.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Bloom.md rename to Packages/com.unity.postprocessing/Documentation~/Bloom.md diff --git a/com.unity.postprocessing/Documentation~/Chromatic-Aberration.md b/Packages/com.unity.postprocessing/Documentation~/Chromatic-Aberration.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Chromatic-Aberration.md rename to Packages/com.unity.postprocessing/Documentation~/Chromatic-Aberration.md diff --git a/com.unity.postprocessing/Documentation~/Color-Grading.md b/Packages/com.unity.postprocessing/Documentation~/Color-Grading.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Color-Grading.md rename to Packages/com.unity.postprocessing/Documentation~/Color-Grading.md diff --git a/com.unity.postprocessing/Documentation~/Debugging-Post-processing-effects.md b/Packages/com.unity.postprocessing/Documentation~/Debugging-Post-processing-effects.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Debugging-Post-processing-effects.md rename to Packages/com.unity.postprocessing/Documentation~/Debugging-Post-processing-effects.md diff --git a/com.unity.postprocessing/Documentation~/Deferred-Fog.md b/Packages/com.unity.postprocessing/Documentation~/Deferred-Fog.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Deferred-Fog.md rename to Packages/com.unity.postprocessing/Documentation~/Deferred-Fog.md diff --git a/com.unity.postprocessing/Documentation~/Depth-of-Field.md b/Packages/com.unity.postprocessing/Documentation~/Depth-of-Field.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Depth-of-Field.md rename to Packages/com.unity.postprocessing/Documentation~/Depth-of-Field.md diff --git a/com.unity.postprocessing/Documentation~/Grain.md b/Packages/com.unity.postprocessing/Documentation~/Grain.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Grain.md rename to Packages/com.unity.postprocessing/Documentation~/Grain.md diff --git a/com.unity.postprocessing/Documentation~/Installation.md b/Packages/com.unity.postprocessing/Documentation~/Installation.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Installation.md rename to Packages/com.unity.postprocessing/Documentation~/Installation.md diff --git a/com.unity.postprocessing/Documentation~/Lens-Distortion.md b/Packages/com.unity.postprocessing/Documentation~/Lens-Distortion.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Lens-Distortion.md rename to Packages/com.unity.postprocessing/Documentation~/Lens-Distortion.md diff --git a/com.unity.postprocessing/Documentation~/Manipulating-the-Stack.md b/Packages/com.unity.postprocessing/Documentation~/Manipulating-the-Stack.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Manipulating-the-Stack.md rename to Packages/com.unity.postprocessing/Documentation~/Manipulating-the-Stack.md diff --git a/com.unity.postprocessing/Documentation~/Motion-Blur.md b/Packages/com.unity.postprocessing/Documentation~/Motion-Blur.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Motion-Blur.md rename to Packages/com.unity.postprocessing/Documentation~/Motion-Blur.md diff --git a/com.unity.postprocessing/Documentation~/Quick-start.md b/Packages/com.unity.postprocessing/Documentation~/Quick-start.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Quick-start.md rename to Packages/com.unity.postprocessing/Documentation~/Quick-start.md diff --git a/com.unity.postprocessing/Documentation~/Screen-Space-Reflections.md b/Packages/com.unity.postprocessing/Documentation~/Screen-Space-Reflections.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Screen-Space-Reflections.md rename to Packages/com.unity.postprocessing/Documentation~/Screen-Space-Reflections.md diff --git a/com.unity.postprocessing/Documentation~/TableOfContents.md b/Packages/com.unity.postprocessing/Documentation~/TableOfContents.md similarity index 100% rename from com.unity.postprocessing/Documentation~/TableOfContents.md rename to Packages/com.unity.postprocessing/Documentation~/TableOfContents.md diff --git a/com.unity.postprocessing/Documentation~/Vignette.md b/Packages/com.unity.postprocessing/Documentation~/Vignette.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Vignette.md rename to Packages/com.unity.postprocessing/Documentation~/Vignette.md diff --git a/com.unity.postprocessing/Documentation~/Writing-Custom-Effects.md b/Packages/com.unity.postprocessing/Documentation~/Writing-Custom-Effects.md similarity index 100% rename from com.unity.postprocessing/Documentation~/Writing-Custom-Effects.md rename to Packages/com.unity.postprocessing/Documentation~/Writing-Custom-Effects.md diff --git a/com.unity.postprocessing/Documentation~/images/Grain_image_0.png b/Packages/com.unity.postprocessing/Documentation~/images/Grain_image_0.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Grain_image_0.png rename to Packages/com.unity.postprocessing/Documentation~/images/Grain_image_0.png diff --git a/com.unity.postprocessing/Documentation~/images/Grain_image_1.png b/Packages/com.unity.postprocessing/Documentation~/images/Grain_image_1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Grain_image_1.png rename to Packages/com.unity.postprocessing/Documentation~/images/Grain_image_1.png diff --git a/com.unity.postprocessing/Documentation~/images/PostProcessing-Bloom-0.png b/Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-Bloom-0.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/PostProcessing-Bloom-0.png rename to Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-Bloom-0.png diff --git a/com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-0.png b/Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-0.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-0.png rename to Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-0.png diff --git a/com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-1.png b/Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-ChromaticAberration-1.png diff --git a/com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-1.png b/Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-1.png diff --git a/com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-2.png b/Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-2.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-2.png rename to Packages/com.unity.postprocessing/Documentation~/images/PostProcessing-Vignette-2.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Light meter_Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Light meter_Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Light meter_Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Light meter_Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Vectorscope_Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Vectorscope_Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Vectorscope_Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2 _ Debugging_Vectorscope_Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Histogram-Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Histogram-Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Histogram-Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Histogram-Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Light-Meter-Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Light-Meter-Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Light-Meter-Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Light-Meter-Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Vectorscope-Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Vectorscope-Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Vectorscope-Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Vectorscope-Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Waveform-Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Waveform-Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Waveform-Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2-Debugging-Waveform-Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Histogram.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Histogram.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Histogram.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Histogram.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Waveform_Graph.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Waveform_Graph.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Waveform_Graph.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Debugging_Waveform_Graph.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2_Light Meter.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Light Meter.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2_Light Meter.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Light Meter.png diff --git a/com.unity.postprocessing/Documentation~/images/Ppv2_Post-processing-Debug.png b/Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Post-processing-Debug.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/Ppv2_Post-processing-Debug.png rename to Packages/com.unity.postprocessing/Documentation~/images/Ppv2_Post-processing-Debug.png diff --git a/com.unity.postprocessing/Documentation~/images/aa-1.png b/Packages/com.unity.postprocessing/Documentation~/images/aa-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/aa-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/aa-1.png diff --git a/com.unity.postprocessing/Documentation~/images/aa-2.png b/Packages/com.unity.postprocessing/Documentation~/images/aa-2.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/aa-2.png rename to Packages/com.unity.postprocessing/Documentation~/images/aa-2.png diff --git a/com.unity.postprocessing/Documentation~/images/aa-3.png b/Packages/com.unity.postprocessing/Documentation~/images/aa-3.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/aa-3.png rename to Packages/com.unity.postprocessing/Documentation~/images/aa-3.png diff --git a/com.unity.postprocessing/Documentation~/images/ao-off.png b/Packages/com.unity.postprocessing/Documentation~/images/ao-off.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/ao-off.png rename to Packages/com.unity.postprocessing/Documentation~/images/ao-off.png diff --git a/com.unity.postprocessing/Documentation~/images/ao-on.png b/Packages/com.unity.postprocessing/Documentation~/images/ao-on.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/ao-on.png rename to Packages/com.unity.postprocessing/Documentation~/images/ao-on.png diff --git a/com.unity.postprocessing/Documentation~/images/auto-exposure-off.png b/Packages/com.unity.postprocessing/Documentation~/images/auto-exposure-off.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/auto-exposure-off.png rename to Packages/com.unity.postprocessing/Documentation~/images/auto-exposure-off.png diff --git a/com.unity.postprocessing/Documentation~/images/auto-exposure-on.png b/Packages/com.unity.postprocessing/Documentation~/images/auto-exposure-on.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/auto-exposure-on.png rename to Packages/com.unity.postprocessing/Documentation~/images/auto-exposure-on.png diff --git a/com.unity.postprocessing/Documentation~/images/autoexposure.png b/Packages/com.unity.postprocessing/Documentation~/images/autoexposure.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/autoexposure.png rename to Packages/com.unity.postprocessing/Documentation~/images/autoexposure.png diff --git a/com.unity.postprocessing/Documentation~/images/bloom.png b/Packages/com.unity.postprocessing/Documentation~/images/bloom.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/bloom.png rename to Packages/com.unity.postprocessing/Documentation~/images/bloom.png diff --git a/com.unity.postprocessing/Documentation~/images/chroma.png b/Packages/com.unity.postprocessing/Documentation~/images/chroma.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/chroma.png rename to Packages/com.unity.postprocessing/Documentation~/images/chroma.png diff --git a/com.unity.postprocessing/Documentation~/images/custom-effect-sorting.png b/Packages/com.unity.postprocessing/Documentation~/images/custom-effect-sorting.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/custom-effect-sorting.png rename to Packages/com.unity.postprocessing/Documentation~/images/custom-effect-sorting.png diff --git a/com.unity.postprocessing/Documentation~/images/deferredfog.png b/Packages/com.unity.postprocessing/Documentation~/images/deferredfog.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/deferredfog.png rename to Packages/com.unity.postprocessing/Documentation~/images/deferredfog.png diff --git a/com.unity.postprocessing/Documentation~/images/dof.png b/Packages/com.unity.postprocessing/Documentation~/images/dof.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/dof.png rename to Packages/com.unity.postprocessing/Documentation~/images/dof.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-1.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-1.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-10.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-10.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-10.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-10.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-11.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-11.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-11.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-11.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-2.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-2.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-2.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-2.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-3.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-3.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-3.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-3.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-4.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-4.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-4.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-4.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-5.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-5.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-5.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-5.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-6.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-6.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-6.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-6.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-7.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-7.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-7.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-7.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-8.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-8.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-8.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-8.png diff --git a/com.unity.postprocessing/Documentation~/images/grading-9.png b/Packages/com.unity.postprocessing/Documentation~/images/grading-9.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grading-9.png rename to Packages/com.unity.postprocessing/Documentation~/images/grading-9.png diff --git a/com.unity.postprocessing/Documentation~/images/grain.png b/Packages/com.unity.postprocessing/Documentation~/images/grain.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/grain.png rename to Packages/com.unity.postprocessing/Documentation~/images/grain.png diff --git a/com.unity.postprocessing/Documentation~/images/home-after.png b/Packages/com.unity.postprocessing/Documentation~/images/home-after.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/home-after.png rename to Packages/com.unity.postprocessing/Documentation~/images/home-after.png diff --git a/com.unity.postprocessing/Documentation~/images/home-before.png b/Packages/com.unity.postprocessing/Documentation~/images/home-before.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/home-before.png rename to Packages/com.unity.postprocessing/Documentation~/images/home-before.png diff --git a/com.unity.postprocessing/Documentation~/images/hue-vs-hue.png b/Packages/com.unity.postprocessing/Documentation~/images/hue-vs-hue.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/hue-vs-hue.png rename to Packages/com.unity.postprocessing/Documentation~/images/hue-vs-hue.png diff --git a/com.unity.postprocessing/Documentation~/images/hue-vs-sat.png b/Packages/com.unity.postprocessing/Documentation~/images/hue-vs-sat.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/hue-vs-sat.png rename to Packages/com.unity.postprocessing/Documentation~/images/hue-vs-sat.png diff --git a/com.unity.postprocessing/Documentation~/images/lens-distortion.png b/Packages/com.unity.postprocessing/Documentation~/images/lens-distortion.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/lens-distortion.png rename to Packages/com.unity.postprocessing/Documentation~/images/lens-distortion.png diff --git a/com.unity.postprocessing/Documentation~/images/lensdistortion.png b/Packages/com.unity.postprocessing/Documentation~/images/lensdistortion.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/lensdistortion.png rename to Packages/com.unity.postprocessing/Documentation~/images/lensdistortion.png diff --git a/com.unity.postprocessing/Documentation~/images/lum-vs-sat.png b/Packages/com.unity.postprocessing/Documentation~/images/lum-vs-sat.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/lum-vs-sat.png rename to Packages/com.unity.postprocessing/Documentation~/images/lum-vs-sat.png diff --git a/com.unity.postprocessing/Documentation~/images/motionblur.png b/Packages/com.unity.postprocessing/Documentation~/images/motionblur.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/motionblur.png rename to Packages/com.unity.postprocessing/Documentation~/images/motionblur.png diff --git a/com.unity.postprocessing/Documentation~/images/no-lens-distortion.png b/Packages/com.unity.postprocessing/Documentation~/images/no-lens-distortion.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/no-lens-distortion.png rename to Packages/com.unity.postprocessing/Documentation~/images/no-lens-distortion.png diff --git a/com.unity.postprocessing/Documentation~/images/quickstart-1.png b/Packages/com.unity.postprocessing/Documentation~/images/quickstart-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/quickstart-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/quickstart-1.png diff --git a/com.unity.postprocessing/Documentation~/images/quickstart-2.png b/Packages/com.unity.postprocessing/Documentation~/images/quickstart-2.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/quickstart-2.png rename to Packages/com.unity.postprocessing/Documentation~/images/quickstart-2.png diff --git a/com.unity.postprocessing/Documentation~/images/quickstart-3.png b/Packages/com.unity.postprocessing/Documentation~/images/quickstart-3.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/quickstart-3.png rename to Packages/com.unity.postprocessing/Documentation~/images/quickstart-3.png diff --git a/com.unity.postprocessing/Documentation~/images/sat-vs-sat.png b/Packages/com.unity.postprocessing/Documentation~/images/sat-vs-sat.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/sat-vs-sat.png rename to Packages/com.unity.postprocessing/Documentation~/images/sat-vs-sat.png diff --git a/com.unity.postprocessing/Documentation~/images/ssao-1.png b/Packages/com.unity.postprocessing/Documentation~/images/ssao-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/ssao-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/ssao-1.png diff --git a/com.unity.postprocessing/Documentation~/images/ssao-2.png b/Packages/com.unity.postprocessing/Documentation~/images/ssao-2.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/ssao-2.png rename to Packages/com.unity.postprocessing/Documentation~/images/ssao-2.png diff --git a/com.unity.postprocessing/Documentation~/images/ssr.png b/Packages/com.unity.postprocessing/Documentation~/images/ssr.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/ssr.png rename to Packages/com.unity.postprocessing/Documentation~/images/ssr.png diff --git a/com.unity.postprocessing/Documentation~/images/tonemapping.png b/Packages/com.unity.postprocessing/Documentation~/images/tonemapping.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/tonemapping.png rename to Packages/com.unity.postprocessing/Documentation~/images/tonemapping.png diff --git a/com.unity.postprocessing/Documentation~/images/trackballs.png b/Packages/com.unity.postprocessing/Documentation~/images/trackballs.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/trackballs.png rename to Packages/com.unity.postprocessing/Documentation~/images/trackballs.png diff --git a/com.unity.postprocessing/Documentation~/images/vignette-1.png b/Packages/com.unity.postprocessing/Documentation~/images/vignette-1.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/vignette-1.png rename to Packages/com.unity.postprocessing/Documentation~/images/vignette-1.png diff --git a/com.unity.postprocessing/Documentation~/images/vignette-2.png b/Packages/com.unity.postprocessing/Documentation~/images/vignette-2.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/vignette-2.png rename to Packages/com.unity.postprocessing/Documentation~/images/vignette-2.png diff --git a/com.unity.postprocessing/Documentation~/images/yrgb-curves.png b/Packages/com.unity.postprocessing/Documentation~/images/yrgb-curves.png similarity index 100% rename from com.unity.postprocessing/Documentation~/images/yrgb-curves.png rename to Packages/com.unity.postprocessing/Documentation~/images/yrgb-curves.png diff --git a/com.unity.postprocessing/Documentation~/index.md b/Packages/com.unity.postprocessing/Documentation~/index.md similarity index 100% rename from com.unity.postprocessing/Documentation~/index.md rename to Packages/com.unity.postprocessing/Documentation~/index.md diff --git a/com.unity.postprocessing/Documentation~/known-issues.md b/Packages/com.unity.postprocessing/Documentation~/known-issues.md similarity index 100% rename from com.unity.postprocessing/Documentation~/known-issues.md rename to Packages/com.unity.postprocessing/Documentation~/known-issues.md diff --git a/com.unity.postprocessing/Documentation~/requirements.md b/Packages/com.unity.postprocessing/Documentation~/requirements.md similarity index 100% rename from com.unity.postprocessing/Documentation~/requirements.md rename to Packages/com.unity.postprocessing/Documentation~/requirements.md diff --git a/com.unity.postprocessing/LICENSE.md b/Packages/com.unity.postprocessing/LICENSE.md similarity index 100% rename from com.unity.postprocessing/LICENSE.md rename to Packages/com.unity.postprocessing/LICENSE.md diff --git a/com.unity.postprocessing/LICENSE.md.meta b/Packages/com.unity.postprocessing/LICENSE.md.meta similarity index 100% rename from com.unity.postprocessing/LICENSE.md.meta rename to Packages/com.unity.postprocessing/LICENSE.md.meta diff --git a/com.unity.postprocessing/PostProcessing.meta b/Packages/com.unity.postprocessing/PostProcessing.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing.meta rename to Packages/com.unity.postprocessing/PostProcessing.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Attributes.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Attributes.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/DecoratorAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Attributes/PostProcessEditorAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/BaseEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/AttributeDecorator.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/Decorators.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Decorators/TrackballDecorator.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/EffectListEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AmbientOcclusionEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/AutoExposureEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/BloomEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ChromaticAberrationEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ColorGradingEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DefaultPostProcessEffectEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/DepthOfFieldEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/LensDistortionEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/MotionBlurEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/ScreenSpaceReflectionsEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Effects/VignetteEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessDebugEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectBaseEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessEffectEditor.cs.meta diff --git a/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs new file mode 100644 index 0000000..6fdf634 --- /dev/null +++ b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Rendering.PostProcessing; +using UnityEditorInternal; +using System.IO; + +namespace UnityEditor.Rendering.PostProcessing +{ + using SerializedBundleRef = PostProcessLayer.SerializedBundleRef; + using EXRFlags = Texture2D.EXRFlags; + + [CanEditMultipleObjects, CustomEditor(typeof(PostProcessLayer))] + sealed class PostProcessLayerEditor : BaseEditor + { + SerializedProperty m_StopNaNPropagation; +#pragma warning disable 414 + SerializedProperty m_DirectToCameraTarget; +#pragma warning restore 414 + SerializedProperty m_VolumeTrigger; + SerializedProperty m_VolumeLayer; + + SerializedProperty m_AntialiasingMode; + SerializedProperty m_TaaJitterSpread; + SerializedProperty m_TaaSharpness; + SerializedProperty m_TaaStationaryBlending; + SerializedProperty m_TaaMotionBlending; + SerializedProperty m_SmaaQuality; + SerializedProperty m_FxaaFastMode; + SerializedProperty m_FxaaKeepAlpha; + + SerializedProperty m_FsrQualityMode; + SerializedProperty m_FsrPerformSharpen; + SerializedProperty m_FsrSharpness; + SerializedProperty m_FsrEnableFP16; + SerializedProperty m_FsrExposureSource; + SerializedProperty m_FsrExposureTexture; + SerializedProperty m_FsrPreExposure; + SerializedProperty m_FsrDebugView; + SerializedProperty m_FsrAutoReactive; + SerializedProperty m_FsrAutoReactiveParams; + SerializedProperty m_FsrReactiveMaskTexture; + SerializedProperty m_FsrAutoTcr; + SerializedProperty m_FsrAutoTcrParams; + SerializedProperty m_FsrTcrMaskTexture; + + SerializedProperty m_FogEnabled; + SerializedProperty m_FogExcludeSkybox; + + SerializedProperty m_ShowToolkit; + SerializedProperty m_ShowCustomSorter; + + Dictionary m_CustomLists; + +#if UNITY_2017_3_OR_NEWER + Camera m_TargetCameraComponent; +#endif + + static GUIContent[] s_AntialiasingMethodNames = + { + new GUIContent("No Anti-aliasing"), + new GUIContent("Fast Approximate Anti-aliasing (FXAA)"), + new GUIContent("Subpixel Morphological Anti-aliasing (SMAA)"), + new GUIContent("Temporal Anti-aliasing (TAA)"), + new GUIContent("FidelityFX Super Resolution 3 (FSR3) Upscaler") + }; + + enum ExportMode + { + FullFrame, + DisablePost, + BreakBeforeColorGradingLinear, + BreakBeforeColorGradingLog + } + + void OnEnable() + { + m_StopNaNPropagation = FindProperty(x => x.stopNaNPropagation); + m_DirectToCameraTarget = FindProperty(x => x.finalBlitToCameraTarget); + m_VolumeTrigger = FindProperty(x => x.volumeTrigger); + m_VolumeLayer = FindProperty(x => x.volumeLayer); + + m_AntialiasingMode = FindProperty(x => x.antialiasingMode); + m_TaaJitterSpread = FindProperty(x => x.temporalAntialiasing.jitterSpread); + m_TaaSharpness = FindProperty(x => x.temporalAntialiasing.sharpness); + m_TaaStationaryBlending = FindProperty(x => x.temporalAntialiasing.stationaryBlending); + m_TaaMotionBlending = FindProperty(x => x.temporalAntialiasing.motionBlending); + m_SmaaQuality = FindProperty(x => x.subpixelMorphologicalAntialiasing.quality); + m_FxaaFastMode = FindProperty(x => x.fastApproximateAntialiasing.fastMode); + m_FxaaKeepAlpha = FindProperty(x => x.fastApproximateAntialiasing.keepAlpha); + + m_FsrQualityMode = FindProperty(x => x.superResolution.qualityMode); + m_FsrPerformSharpen = FindProperty(x => x.superResolution.performSharpenPass); + m_FsrSharpness = FindProperty(x => x.superResolution.sharpness); + m_FsrEnableFP16 = FindProperty(x => x.superResolution.enableFP16); + m_FsrExposureSource = FindProperty(x => x.superResolution.exposureSource); + m_FsrExposureTexture = FindProperty(x => x.superResolution.exposure); + m_FsrPreExposure = FindProperty(x => x.superResolution.preExposure); + m_FsrDebugView = FindProperty(x => x.superResolution.enableDebugView); + m_FsrAutoReactive = FindProperty(x => x.superResolution.autoGenerateReactiveMask); + m_FsrAutoReactiveParams = FindProperty(x => x.superResolution.generateReactiveParameters); + m_FsrReactiveMaskTexture = FindProperty(x => x.superResolution.reactiveMask); + m_FsrAutoTcr = FindProperty(x => x.superResolution.autoGenerateTransparencyAndComposition); + m_FsrAutoTcrParams = FindProperty(x => x.superResolution.generateTransparencyAndCompositionParameters); + m_FsrTcrMaskTexture = FindProperty(x => x.superResolution.transparencyAndCompositionMask); + + m_FogEnabled = FindProperty(x => x.fog.enabled); + m_FogExcludeSkybox = FindProperty(x => x.fog.excludeSkybox); + + m_ShowToolkit = serializedObject.FindProperty("m_ShowToolkit"); + m_ShowCustomSorter = serializedObject.FindProperty("m_ShowCustomSorter"); + +#if UNITY_2017_3_OR_NEWER + m_TargetCameraComponent = m_Target.GetComponent(); +#endif + } + + void OnDisable() + { + m_CustomLists = null; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + var camera = m_Target.GetComponent(); + + DoVolumeBlending(); + DoAntialiasing(); + DoFog(camera); + + EditorGUILayout.PropertyField(m_StopNaNPropagation, EditorUtilities.GetContent("Stop NaN Propagation|Automatically replaces NaN/Inf in shaders by a black pixel to avoid breaking some effects. This will slightly affect performances and should only be used if you experience NaN issues that you can't fix. Has no effect on GLES2 platforms.")); + +#if UNITY_2019_1_OR_NEWER + if (!RuntimeUtilities.scriptableRenderPipelineActive) + EditorGUILayout.PropertyField(m_DirectToCameraTarget, EditorUtilities.GetContent("Directly to Camera Target|Use the final blit to the camera render target for postprocessing. This has less overhead but breaks compatibility with legacy image effect that use OnRenderImage.")); +#endif + + EditorGUILayout.Space(); + + DoToolkit(); + DoCustomEffectSorter(); + + EditorUtilities.DrawSplitter(); + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + + void DoVolumeBlending() + { + EditorGUILayout.LabelField(EditorUtilities.GetContent("Volume blending"), EditorStyles.boldLabel); + EditorGUI.indentLevel++; + { + // The layout system sort of break alignement when mixing inspector fields with + // custom layouted fields, do the layout manually instead + var indentOffset = EditorGUI.indentLevel * 15f; + var lineRect = GUILayoutUtility.GetRect(1, EditorGUIUtility.singleLineHeight); + var labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth - indentOffset, lineRect.height); + var fieldRect = new Rect(labelRect.xMax, lineRect.y, lineRect.width - labelRect.width - 60f, lineRect.height); + var buttonRect = new Rect(fieldRect.xMax, lineRect.y, 60f, lineRect.height); + + EditorGUI.PrefixLabel(labelRect, EditorUtilities.GetContent("Trigger|A transform that will act as a trigger for volume blending.")); + m_VolumeTrigger.objectReferenceValue = (Transform)EditorGUI.ObjectField(fieldRect, m_VolumeTrigger.objectReferenceValue, typeof(Transform), true); + if (GUI.Button(buttonRect, EditorUtilities.GetContent("This|Assigns the current GameObject as a trigger."), EditorStyles.miniButton)) + m_VolumeTrigger.objectReferenceValue = m_Target.transform; + + if (m_VolumeTrigger.objectReferenceValue == null) + EditorGUILayout.HelpBox("No trigger has been set, the camera will only be affected by global volumes.", MessageType.Info); + + EditorGUILayout.PropertyField(m_VolumeLayer, EditorUtilities.GetContent("Layer|This camera will only be affected by volumes in the selected scene-layers.")); + + int mask = m_VolumeLayer.intValue; + if (mask == 0) + EditorGUILayout.HelpBox("No layer has been set, the trigger will never be affected by volumes.", MessageType.Warning); + else if (mask == -1 || ((mask & 1) != 0)) + EditorGUILayout.HelpBox("Do not use \"Everything\" or \"Default\" as a layer mask as it will slow down the volume blending process! Put post-processing volumes in their own dedicated layer for best performances.", MessageType.Warning); + } + EditorGUI.indentLevel--; + + EditorGUILayout.Space(); + } + + void DoAntialiasing() + { + EditorGUILayout.LabelField(EditorUtilities.GetContent("Anti-aliasing"), EditorStyles.boldLabel); + EditorGUI.indentLevel++; + { + m_AntialiasingMode.intValue = EditorGUILayout.Popup(EditorUtilities.GetContent("Mode|The anti-aliasing method to use. FXAA is fast but low quality. SMAA works well for non-HDR scenes. TAA is a bit slower but higher quality and works well with HDR."), m_AntialiasingMode.intValue, s_AntialiasingMethodNames); + + if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.TemporalAntialiasing) + { +#if !UNITY_2017_3_OR_NEWER + if (RuntimeUtilities.isSinglePassStereoSelected) + EditorGUILayout.HelpBox("TAA requires Unity 2017.3+ for Single-pass stereo rendering support.", MessageType.Warning); +#endif +#if UNITY_2017_3_OR_NEWER + if (m_TargetCameraComponent != null && RuntimeUtilities.IsDynamicResolutionEnabled(m_TargetCameraComponent)) + EditorGUILayout.HelpBox("TAA is not supported with Dynamic Resolution.", MessageType.Warning); +#endif + + EditorGUILayout.PropertyField(m_TaaJitterSpread); + EditorGUILayout.PropertyField(m_TaaStationaryBlending); + EditorGUILayout.PropertyField(m_TaaMotionBlending); + EditorGUILayout.PropertyField(m_TaaSharpness); + } + else if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing) + { + if (RuntimeUtilities.isSinglePassStereoSelected) + EditorGUILayout.HelpBox("SMAA doesn't work with Single-pass stereo rendering.", MessageType.Warning); + + EditorGUILayout.PropertyField(m_SmaaQuality); + + if (m_SmaaQuality.intValue != (int)SubpixelMorphologicalAntialiasing.Quality.Low && EditorUtilities.isTargetingConsolesOrMobiles) + EditorGUILayout.HelpBox("For performance reasons it is recommended to use Low Quality on mobile and console platforms.", MessageType.Warning); + } + else if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.FastApproximateAntialiasing) + { + EditorGUILayout.PropertyField(m_FxaaFastMode); + EditorGUILayout.PropertyField(m_FxaaKeepAlpha); + + if (!m_FxaaFastMode.boolValue && EditorUtilities.isTargetingConsolesOrMobiles) + EditorGUILayout.HelpBox("For performance reasons it is recommended to use Fast Mode on mobile and console platforms.", MessageType.Warning); + } + else if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.SuperResolution) + { + EditorGUILayout.PropertyField(m_FsrQualityMode); + EditorGUILayout.PropertyField(m_FsrPerformSharpen); + EditorGUILayout.PropertyField(m_FsrSharpness); + EditorGUILayout.PropertyField(m_FsrEnableFP16); + EditorGUILayout.PropertyField(m_FsrExposureSource); + if (m_FsrExposureSource.intValue == (int)SuperResolution.ExposureSource.Manual) EditorGUILayout.PropertyField(m_FsrExposureTexture); + EditorGUILayout.PropertyField(m_FsrPreExposure); + EditorGUILayout.PropertyField(m_FsrDebugView); + EditorGUILayout.PropertyField(m_FsrAutoReactive); + EditorGUILayout.PropertyField(m_FsrAutoReactive.boolValue ? m_FsrAutoReactiveParams : m_FsrReactiveMaskTexture); + EditorGUILayout.PropertyField(m_FsrAutoTcr); + EditorGUILayout.PropertyField(m_FsrAutoTcr.boolValue ? m_FsrAutoTcrParams : m_FsrTcrMaskTexture); + } + } + EditorGUI.indentLevel--; + + EditorGUILayout.Space(); + } + + void DoFog(Camera camera) + { + if (camera == null || camera.actualRenderingPath != RenderingPath.DeferredShading) + return; + + EditorGUILayout.LabelField(EditorUtilities.GetContent("Deferred Fog"), EditorStyles.boldLabel); + EditorGUI.indentLevel++; + { + EditorGUILayout.PropertyField(m_FogEnabled); + + if (m_FogEnabled.boolValue) + { + EditorGUILayout.PropertyField(m_FogExcludeSkybox); + EditorGUILayout.HelpBox("This adds fog compatibility to the deferred rendering path; actual fog settings should be set in the Lighting panel.", MessageType.Info); + } + } + EditorGUI.indentLevel--; + + EditorGUILayout.Space(); + } + + void DoToolkit() + { + EditorUtilities.DrawSplitter(); + m_ShowToolkit.boolValue = EditorUtilities.DrawHeader("Toolkit", m_ShowToolkit.boolValue); + + if (m_ShowToolkit.boolValue) + { + GUILayout.Space(2); + + if (GUILayout.Button(EditorUtilities.GetContent("Export frame to EXR..."), EditorStyles.miniButton)) + { + var menu = new GenericMenu(); + menu.AddItem(EditorUtilities.GetContent("Full Frame (as displayed)"), false, () => ExportFrameToExr(ExportMode.FullFrame)); + menu.AddItem(EditorUtilities.GetContent("Disable post-processing"), false, () => ExportFrameToExr(ExportMode.DisablePost)); + menu.AddItem(EditorUtilities.GetContent("Break before Color Grading (Linear)"), false, () => ExportFrameToExr(ExportMode.BreakBeforeColorGradingLinear)); + menu.AddItem(EditorUtilities.GetContent("Break before Color Grading (Log)"), false, () => ExportFrameToExr(ExportMode.BreakBeforeColorGradingLog)); + menu.ShowAsContext(); + } + + if (GUILayout.Button(EditorUtilities.GetContent("Select all layer volumes|Selects all the volumes that will influence this layer."), EditorStyles.miniButton)) + { + var volumes = RuntimeUtilities.GetAllSceneObjects() + .Where(x => (m_VolumeLayer.intValue & (1 << x.gameObject.layer)) != 0) + .Select(x => x.gameObject) + .Cast() + .ToArray(); + + if (volumes.Length > 0) + Selection.objects = volumes; + } + + if (GUILayout.Button(EditorUtilities.GetContent("Select all active volumes|Selects all volumes currently affecting the layer."), EditorStyles.miniButton)) + { + var volumes = new List(); + PostProcessManager.instance.GetActiveVolumes(m_Target, volumes); + + if (volumes.Count > 0) + { + Selection.objects = volumes + .Select(x => x.gameObject) + .Cast() + .ToArray(); + } + } + + GUILayout.Space(3); + } + } + + void DoCustomEffectSorter() + { + EditorUtilities.DrawSplitter(); + m_ShowCustomSorter.boolValue = EditorUtilities.DrawHeader("Custom Effect Sorting", m_ShowCustomSorter.boolValue); + + if (m_ShowCustomSorter.boolValue) + { + bool isInPrefab = false; + + // Init lists if needed + if (m_CustomLists == null) + { + // In some cases the editor will refresh before components which means + // components might not have been fully initialized yet. In this case we also + // need to make sure that we're not in a prefab as sorteBundles isn't a + // serializable object and won't exist until put on a scene. + if (m_Target.sortedBundles == null) + { + isInPrefab = string.IsNullOrEmpty(m_Target.gameObject.scene.name); + + if (!isInPrefab) + { + // sortedBundles will be initialized and ready to use on the next frame + Repaint(); + } + } + else + { + // Create a reorderable list for each injection event + m_CustomLists = new Dictionary(); + foreach (var evt in Enum.GetValues(typeof(PostProcessEvent)).Cast()) + { + var bundles = m_Target.sortedBundles[evt]; + var listName = ObjectNames.NicifyVariableName(evt.ToString()); + + var list = new ReorderableList(bundles, typeof(SerializedBundleRef), true, true, false, false); + + list.drawHeaderCallback = (rect) => + { + EditorGUI.LabelField(rect, listName); + }; + + list.drawElementCallback = (rect, index, isActive, isFocused) => + { + var sbr = (SerializedBundleRef)list.list[index]; + EditorGUI.LabelField(rect, sbr.bundle.attribute.menuItem); + }; + + list.onReorderCallback = (l) => + { + EditorUtility.SetDirty(m_Target); + }; + + m_CustomLists.Add(evt, list); + } + } + } + + GUILayout.Space(5); + + if (isInPrefab) + { + EditorGUILayout.HelpBox("Not supported in prefabs.", MessageType.Info); + GUILayout.Space(3); + return; + } + + bool anyList = false; + if (m_CustomLists != null) + { + foreach (var kvp in m_CustomLists) + { + var list = kvp.Value; + + // Skip empty lists to avoid polluting the inspector + if (list.count == 0) + continue; + + list.DoLayoutList(); + anyList = true; + } + } + + if (!anyList) + { + EditorGUILayout.HelpBox("No custom effect loaded.", MessageType.Info); + GUILayout.Space(3); + } + } + } + + void ExportFrameToExr(ExportMode mode) + { + string path = EditorUtility.SaveFilePanel("Export EXR...", "", "Frame", "exr"); + + if (string.IsNullOrEmpty(path)) + return; + + EditorUtility.DisplayProgressBar("Export EXR", "Rendering...", 0f); + + var camera = m_Target.GetComponent(); + var w = camera.pixelWidth; + var h = camera.pixelHeight; + + var texOut = new Texture2D(w, h, TextureFormat.RGBAFloat, false, true); + var target = RenderTexture.GetTemporary(w, h, 24, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); + + var lastActive = RenderTexture.active; + var lastTargetSet = camera.targetTexture; + var lastPostFXState = m_Target.enabled; + var lastBreakColorGradingState = m_Target.breakBeforeColorGrading; + + if (mode == ExportMode.DisablePost) + m_Target.enabled = false; + else if (mode == ExportMode.BreakBeforeColorGradingLinear || mode == ExportMode.BreakBeforeColorGradingLog) + m_Target.breakBeforeColorGrading = true; + + camera.targetTexture = target; + camera.Render(); + camera.targetTexture = lastTargetSet; + + EditorUtility.DisplayProgressBar("Export EXR", "Reading...", 0.25f); + + m_Target.enabled = lastPostFXState; + m_Target.breakBeforeColorGrading = lastBreakColorGradingState; + + if (mode == ExportMode.BreakBeforeColorGradingLog) + { + // Convert to log + var material = new Material(Shader.Find("Hidden/PostProcessing/Editor/ConvertToLog")); + var newTarget = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); + Graphics.Blit(target, newTarget, material, 0); + RenderTexture.ReleaseTemporary(target); + DestroyImmediate(material); + target = newTarget; + } + + RenderTexture.active = target; + texOut.ReadPixels(new Rect(0, 0, w, h), 0, 0); + texOut.Apply(); + RenderTexture.active = lastActive; + + EditorUtility.DisplayProgressBar("Export EXR", "Encoding...", 0.5f); + + var bytes = texOut.EncodeToEXR(EXRFlags.OutputAsFloat | EXRFlags.CompressZIP); + + EditorUtility.DisplayProgressBar("Export EXR", "Saving...", 0.75f); + + File.WriteAllBytes(path, bytes); + + EditorUtility.ClearProgressBar(); + AssetDatabase.Refresh(); + + RenderTexture.ReleaseTemporary(target); + DestroyImmediate(texOut); + } + } +} diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessProfileEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/PostProcessVolumeEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetFactory.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/CubeLutAssetImporter.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/DefineSetter.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ProfileFactory.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/ResourceAssetFactory.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Tools/VolumeFactory.cs.meta diff --git a/Packages/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef b/Packages/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef new file mode 100644 index 0000000..f90cfb2 --- /dev/null +++ b/Packages/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef @@ -0,0 +1,37 @@ +{ + "name": "Unity.Postprocessing.Editor", + "rootNamespace": "", + "references": [ + "Unity.Postprocessing.Runtime", + "Unity.XR.Management", + "Unity.XR.Management.Editor", + "FidelityFX.FSR" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.xr.management", + "expression": "4.0.1", + "define": "XR_MANAGEMENT_4_0_1_OR_NEWER" + }, + { + "name": "com.unity.modules.vr", + "expression": "1.0.0", + "define": "ENABLE_VR_MODULE" + }, + { + "name": "com.unity.modules.xr", + "expression": "1.0.0", + "define": "ENABLE_XR_MODULE" + } + ], + "noEngineReferences": false +} diff --git a/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/CurveEditor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/EditorUtilities.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/GlobalSettings.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/SerializedParameterOverride.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs diff --git a/com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Editor/Utils/Styling.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Gizmos.meta b/Packages/com.unity.postprocessing/PostProcessing/Gizmos.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Gizmos.meta rename to Packages/com.unity.postprocessing/PostProcessing/Gizmos.meta diff --git a/com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png b/Packages/com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png rename to Packages/com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png diff --git a/com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Gizmos/PostProcessLayer.png.meta diff --git a/Packages/com.unity.postprocessing/PostProcessing/PostProcessResources.asset b/Packages/com.unity.postprocessing/PostProcessing/PostProcessResources.asset new file mode 100644 index 0000000..c9f5acb --- /dev/null +++ b/Packages/com.unity.postprocessing/PostProcessing/PostProcessResources.asset @@ -0,0 +1,141 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30f4b897495c7ad40b2d47143e02aaba, type: 3} + m_Name: PostProcessResources + m_EditorClassIdentifier: + blueNoise64: + - {fileID: 2800000, guid: 50b54341495978843a6f85583ed4417d, type: 3} + - {fileID: 2800000, guid: 3c2f1fb7e4b66e74191b7c328ada52d9, type: 3} + - {fileID: 2800000, guid: a469f920b21fc7c4fb5b950917ce2fb2, type: 3} + - {fileID: 2800000, guid: 373f9bf6b0841af4ebf26d25e4a3f4e2, type: 3} + - {fileID: 2800000, guid: 6fa5cf178eaaa5f42b820f636bb6e0bd, type: 3} + - {fileID: 2800000, guid: a1ae041906217ae44a774d4ca139af50, type: 3} + - {fileID: 2800000, guid: 79b86f3419b87f3429164a956da8cfab, type: 3} + - {fileID: 2800000, guid: 3ac02e7e783571c468f9c086d2384ba7, type: 3} + - {fileID: 2800000, guid: c55042318a938344ab23cd7f09dd0076, type: 3} + - {fileID: 2800000, guid: 71583cfd8899717428d5b1a95fa39cda, type: 3} + - {fileID: 2800000, guid: afe1e502240079342a0a980484b6da8b, type: 3} + - {fileID: 2800000, guid: 771903fe7b4674445829e52e91cff019, type: 3} + - {fileID: 2800000, guid: 980acadb960f8424c94307ec0e585b4e, type: 3} + - {fileID: 2800000, guid: 68613e6a221be1a4b9f31d7fa1c2d1bf, type: 3} + - {fileID: 2800000, guid: f6439b54b28f3884eb67579dec0b6f21, type: 3} + - {fileID: 2800000, guid: 2ee161d8945169243b5698fec114e1b7, type: 3} + - {fileID: 2800000, guid: 153f7d6dfbe713d4884df0f1e243ba92, type: 3} + - {fileID: 2800000, guid: bf95b6fdc179b0e4f890c841406193fc, type: 3} + - {fileID: 2800000, guid: 74aca53eb7273624baffc2bf5e5cc173, type: 3} + - {fileID: 2800000, guid: 729a3ae164bcb3b4380459386adcf331, type: 3} + - {fileID: 2800000, guid: 6dda07f1420a968449cf4c6620c44d9f, type: 3} + - {fileID: 2800000, guid: b7f000750830ddb4bbc80065b9314ce9, type: 3} + - {fileID: 2800000, guid: df01d03f056c6f445b4b8a0ae054207c, type: 3} + - {fileID: 2800000, guid: bfe953600e8fb1849a804ee08ace7b4c, type: 3} + - {fileID: 2800000, guid: 32c6a5f7143b86c44bd5cdee2ff3f8ad, type: 3} + - {fileID: 2800000, guid: f4b8ab78b57749d4e96d36f6d8a395d0, type: 3} + - {fileID: 2800000, guid: 09f6c01f98a3ded4daf1afc52a3c260f, type: 3} + - {fileID: 2800000, guid: bdd06fb88ef36ed4a85dd506352c2d80, type: 3} + - {fileID: 2800000, guid: 02c0a84bd64c6f044954d8bde9b46ec8, type: 3} + - {fileID: 2800000, guid: aa80dc44aa4fe4c43bb9d51d90cf2958, type: 3} + - {fileID: 2800000, guid: 0fa10b21877c61b4db40ba5708815f81, type: 3} + - {fileID: 2800000, guid: 6b0a189df0bd4d5448eaefb4e673ace8, type: 3} + - {fileID: 2800000, guid: 87a5e40cc271ea648b583616f6ebe7fe, type: 3} + - {fileID: 2800000, guid: b71bb466b71fd13449dd736f63caeb67, type: 3} + - {fileID: 2800000, guid: 319b8e66db3faa4438cf6982e9c89b2f, type: 3} + - {fileID: 2800000, guid: 0a79c155edf9b2d429d4736abee5acdb, type: 3} + - {fileID: 2800000, guid: 351e95d0e20a54849bd4ce5f9b498934, type: 3} + - {fileID: 2800000, guid: 1d6958e30e40a254dbe5a54c573eeb3c, type: 3} + - {fileID: 2800000, guid: 9660a4ca1ca8425408ac25c641932977, type: 3} + - {fileID: 2800000, guid: 547dbd5f858c74047ba3f213e4408307, type: 3} + - {fileID: 2800000, guid: 1a9ce5640cde5934aae0022f020464a6, type: 3} + - {fileID: 2800000, guid: cd9006dc442cc244e89b3f492384d46a, type: 3} + - {fileID: 2800000, guid: b266511438fae724f9d3ce6bd26583e8, type: 3} + - {fileID: 2800000, guid: 71bc1b6b66e8b784b972199b7e90204e, type: 3} + - {fileID: 2800000, guid: 15e54aa23a938444389469d53765d741, type: 3} + - {fileID: 2800000, guid: b9960364038cbfa4aa49d7b2032d3110, type: 3} + - {fileID: 2800000, guid: 8ecbbcae4cc747a4abbc4adce795d25e, type: 3} + - {fileID: 2800000, guid: 1378a33cdd085d64c9da863d2484ff21, type: 3} + - {fileID: 2800000, guid: aff59c63d25d43f4c938f248837c30fb, type: 3} + - {fileID: 2800000, guid: 3f7c3687170b90e4a8d2ee6b142670f4, type: 3} + - {fileID: 2800000, guid: d8c290e38ff0425409d0ae6a98c95e41, type: 3} + - {fileID: 2800000, guid: d5a51525b27e3ee4aadbeb39cbcf0750, type: 3} + - {fileID: 2800000, guid: d2e8e90fac2e6a341a38e1c3963c218d, type: 3} + - {fileID: 2800000, guid: c94b57b5a32a22d43ade66e09f6a4bd2, type: 3} + - {fileID: 2800000, guid: 936dea238abb0864ab3985a995e16a29, type: 3} + - {fileID: 2800000, guid: 5e542d0126a2c7848b66bffc428905fd, type: 3} + - {fileID: 2800000, guid: 70f23eaf7d8ae9147aa542d20e93733b, type: 3} + - {fileID: 2800000, guid: e138166e7a7c70f49943be7edda35d35, type: 3} + - {fileID: 2800000, guid: 85a45a6d8b2ffb84987d2b028ecfb220, type: 3} + - {fileID: 2800000, guid: d96974690c77f50489eb60ec84bd8dac, type: 3} + - {fileID: 2800000, guid: 404fa8def46b1c447817e1ebdaa7144e, type: 3} + - {fileID: 2800000, guid: 119591e0bb084e848835d237546b3882, type: 3} + - {fileID: 2800000, guid: a03c400b0e3959f428ee99dfc6cfc263, type: 3} + - {fileID: 2800000, guid: 4a11d65ce13d5f542a0ff136cc2f3fba, type: 3} + blueNoise256: + - {fileID: 2800000, guid: 6017f374382d64245a0a4aab668e6f38, type: 3} + - {fileID: 2800000, guid: 0f8fa14b3731cda4e947062e734d5e1e, type: 3} + - {fileID: 2800000, guid: 1abfe0e165ca1e9428b455ffc9a2d9ef, type: 3} + - {fileID: 2800000, guid: c072b653e98a06e40857d76ca8c7eecd, type: 3} + - {fileID: 2800000, guid: b52d5033b68309943a2386c270a90f44, type: 3} + - {fileID: 2800000, guid: acde5141d5f4f7a4188394bd52c4dc38, type: 3} + - {fileID: 2800000, guid: 999434725cbc2be4eb54043b36efd4a8, type: 3} + - {fileID: 2800000, guid: 70d0a1182b29d6347ac70374c3593bba, type: 3} + smaaLuts: + area: {fileID: 2800000, guid: 73ec4ae984a0a0f44a2be737e41a6f2f, type: 3} + search: {fileID: 2800000, guid: d99701099481a2f489610e977df6dcbc, type: 3} + shaders: + bloom: {fileID: 4800000, guid: c1e1d3119c6fd4646aea0b4b74cacc1a, type: 3} + copy: {fileID: 4800000, guid: cdbdb71de5f9c454b980f6d0e87f0afb, type: 3} + copyStd: {fileID: 4800000, guid: 4bf4cff0d0bac3d43894e2e8839feb40, type: 3} + copyStdFromTexArray: {fileID: 4800000, guid: 02d2da9bc88d25c4d878c1ed4e0b3854, type: 3} + copyStdFromDoubleWide: {fileID: 4800000, guid: e8ce9961912f3214586fe8709b9012c1, type: 3} + discardAlpha: {fileID: 4800000, guid: 5ab0816423f0dfe45841cab3b05ec9ef, type: 3} + depthOfField: {fileID: 4800000, guid: 0ef78d24e85a44f4da9d5b5eaa00e50b, type: 3} + finalPass: {fileID: 4800000, guid: f75014305794b3948a3c6d5ccd550e05, type: 3} + grainBaker: {fileID: 4800000, guid: 0d8afcb51cc9f0349a6d190da929b838, type: 3} + motionBlur: {fileID: 4800000, guid: 2c459b89a7c8b1a4fbefe0d81341651c, type: 3} + temporalAntialiasing: {fileID: 4800000, guid: 51bcf79c50dc92e47ba87821b61100c3, type: 3} + subpixelMorphologicalAntialiasing: {fileID: 4800000, guid: 81af42a93ade3dd46a9b583d4eec76d6, type: 3} + texture2dLerp: {fileID: 4800000, guid: 34a819c9e33402547a81619693adc8d5, type: 3} + uber: {fileID: 4800000, guid: 382151503e2a43a4ebb7366d1632731d, type: 3} + lut2DBaker: {fileID: 4800000, guid: 7ad194cbe7d006f4bace915156972026, type: 3} + lightMeter: {fileID: 4800000, guid: b34a29e523cb9d545881e193a079f2df, type: 3} + gammaHistogram: {fileID: 4800000, guid: f7ea35cfb33fcad4ab8f2429ec103bef, type: 3} + waveform: {fileID: 4800000, guid: 3020ac7ece79a7f4eb789a236f8bd6c5, type: 3} + vectorscope: {fileID: 4800000, guid: a71093f2a4fe26a40805c22739e10e4a, type: 3} + debugOverlays: {fileID: 4800000, guid: b958ad1c92bd3d64c9e61318b8681dab, type: 3} + deferredFog: {fileID: 4800000, guid: 4117fce9491711c4094d33a048e36e73, type: 3} + scalableAO: {fileID: 4800000, guid: d7640629310e79646af0f46eb55ae466, type: 3} + multiScaleAO: {fileID: 4800000, guid: 67f9497810829eb4791ec19e95781e51, type: 3} + screenSpaceReflections: {fileID: 4800000, guid: f997a3dc9254c44459323cced085150c, type: 3} + computeShaders: + autoExposure: {fileID: 7200000, guid: 34845e0ca016b7448842e965db5890a5, type: 3} + exposureHistogram: {fileID: 7200000, guid: 8c2fcbdf9bc58664f89917f7b9d79501, type: 3} + lut3DBaker: {fileID: 7200000, guid: 42496b74c071f5749950ca1abe33e945, type: 3} + texture3dLerp: {fileID: 7200000, guid: 31e9175024adfd44aba2530ff9b77494, type: 3} + gammaHistogram: {fileID: 7200000, guid: 18183ebfeeab97749b43e38b928604a7, type: 3} + waveform: {fileID: 7200000, guid: 92c63830cd50c0b4fbb8233613839958, type: 3} + vectorscope: {fileID: 7200000, guid: e1efca7c36fd01840aae0dd10378de5c, type: 3} + multiScaleAODownsample1: {fileID: 7200000, guid: 4c63bc487e6c29a4a99f85a6c47b292b, type: 3} + multiScaleAODownsample2: {fileID: 7200000, guid: e4d3e4779e48a374f91d48d4c0aedb7b, type: 3} + multiScaleAORender: {fileID: 7200000, guid: 34a460e8a2e66c243a9c12024e5a798d, type: 3} + multiScaleAOUpsample: {fileID: 7200000, guid: 600d6212b59bb40409d19d750b5fd1e9, type: 3} + gaussianDownsample: {fileID: 7200000, guid: 6dba4103d23a7904fbc49099355aff3e, type: 3} + superResolution: + prepareInputsPass: {fileID: 7200000, guid: 4f59e5b9179d74844ae06a30ae1e0629, type: 3} + lumaPyramidPass: {fileID: 7200000, guid: d253be05abcdc80428503d3e4cce3a36, type: 3} + shadingChangePyramidPass: {fileID: 7200000, guid: 251e663738905fa4d8817001682d802f, type: 3} + shadingChangePass: {fileID: 7200000, guid: 9a2bff2f97619ed4989d9b0577ba0641, type: 3} + prepareReactivityPass: {fileID: 7200000, guid: 20e44016ed34b0d4b8de499d1b566c69, type: 3} + lumaInstabilityPass: {fileID: 7200000, guid: a135306e6d1857e43a86ef20db2a47fe, type: 3} + accumulatePass: {fileID: 7200000, guid: c9b45f0ae7673694ba57a4aadfe212e9, type: 3} + sharpenPass: {fileID: 7200000, guid: 7aaf5cfff022de2499e9b0412f947f6c, type: 3} + autoGenReactivePass: {fileID: 7200000, guid: 5716b91fdaa4e9e439df6b96a796fe6e, type: 3} + tcrAutoGenPass: {fileID: 7200000, guid: 75cdc6ef23f08ed498d4da511923fcea, type: 3} + debugViewPass: {fileID: 7200000, guid: cb24a71d54164c54eb5e86839acd48c5, type: 3} diff --git a/com.unity.postprocessing/PostProcessing/PostProcessResources.asset.meta b/Packages/com.unity.postprocessing/PostProcessing/PostProcessResources.asset.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/PostProcessResources.asset.meta rename to Packages/com.unity.postprocessing/PostProcessing/PostProcessResources.asset.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/DisplayNameAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MaxAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/MinMaxAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/PostProcessAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Attributes/TrackballAttribute.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AmbientOcclusion.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/AutoExposure.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Bloom.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ChromaticAberration.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ColorGrading.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/DepthOfField.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Dithering.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/FastApproximateAntialiasing.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Fog.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Grain.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/LensDistortion.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MotionBlur.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/MultiScaleVO.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScalableAO.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/ScreenSpaceReflections.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SubpixelMorphologicalAntialiasing.cs.meta diff --git a/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs new file mode 100644 index 0000000..e997b3f --- /dev/null +++ b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs @@ -0,0 +1,339 @@ +// 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 System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Experimental.Rendering; +using FidelityFX; +using FidelityFX.FSR3; + +namespace UnityEngine.Rendering.PostProcessing +{ + [UnityEngine.Scripting.Preserve] + [Serializable] + public class SuperResolution + { + public Func callbacksFactory { get; set; } = (context) => new Fsr3UpscalerCallbacksBase(); + + [Tooltip("Standard scaling ratio presets.")] + public Fsr3Upscaler.QualityMode qualityMode = Fsr3Upscaler.QualityMode.Quality; + + [Tooltip("Apply RCAS sharpening to the image after upscaling.")] + public bool performSharpenPass = true; + [Tooltip("Strength of the sharpening effect.")] + [Range(0, 1)] public float sharpness = 0.8f; + + [Tooltip("Allow the use of half precision compute operations, potentially improving performance if the platform supports it.")] + public bool enableFP16 = false; + + [Tooltip("Choose where to get the exposure value from. Use auto-exposure from either FSR3 or Unity, provide a manual exposure texture, or use a default value.")] + public ExposureSource exposureSource = ExposureSource.Auto; + [Tooltip("Value by which the input signal will be divided, to get back to the original signal produced by the game.")] + public float preExposure = 1.0f; + [Tooltip("Optional 1x1 texture containing the exposure value for the current frame.")] + public Texture exposure = null; + + public enum ExposureSource + { + Default, + Auto, + Unity, + Manual, + } + + [Tooltip("Enable a debug view to analyze the upscaling process.")] + public bool enableDebugView = false; + + [Tooltip("Optional texture to control the influence of the current frame on the reconstructed output. If unset, either an auto-generated or a default cleared reactive mask will be used.")] + public Texture reactiveMask = null; + [Tooltip("Optional texture for marking areas of specialist rendering which should be accounted for during the upscaling process. If unset, a default cleared mask will be used.")] + public Texture transparencyAndCompositionMask = null; + [Tooltip("Automatically generate a reactive mask based on the difference between opaque-only render output and the final render output including alpha transparencies.")] + public bool autoGenerateReactiveMask = true; + [Tooltip("Parameters to control the process of auto-generating a reactive mask.")] + public GenerateReactiveParameters generateReactiveParameters = new GenerateReactiveParameters(); + + [Serializable] + public class GenerateReactiveParameters + { + [Tooltip("A value to scale the output")] + [Range(0, 2)] public float scale = 0.5f; + [Tooltip("A threshold value to generate a binary reactive mask")] + [Range(0, 1)] public float cutoffThreshold = 0.2f; + [Tooltip("A value to set for the binary reactive mask")] + [Range(0, 1)] public float binaryValue = 0.9f; + [Tooltip("Flags to determine how to generate the reactive mask")] + public Fsr3Upscaler.GenerateReactiveFlags flags = Fsr3Upscaler.GenerateReactiveFlags.ApplyTonemap | Fsr3Upscaler.GenerateReactiveFlags.ApplyThreshold | Fsr3Upscaler.GenerateReactiveFlags.UseComponentsMax; + } + + [Tooltip("(Experimental) Automatically generate and use Reactive mask and Transparency & composition mask internally.")] + public bool autoGenerateTransparencyAndComposition = false; + [Tooltip("Parameters to control the process of auto-generating transparency and composition masks.")] + public GenerateTcrParameters generateTransparencyAndCompositionParameters = new GenerateTcrParameters(); + + [Serializable] + public class GenerateTcrParameters + { + [Tooltip("Setting this value too small will cause visual instability. Larger values can cause ghosting.")] + [Range(0, 1)] public float autoTcThreshold = 0.05f; + [Tooltip("Smaller values will increase stability at hard edges of translucent objects.")] + [Range(0, 2)] public float autoTcScale = 1.0f; + [Tooltip("Larger values result in more reactive pixels.")] + [Range(0, 10)] public float autoReactiveScale = 5.0f; + [Tooltip("Maximum value reactivity can reach.")] + [Range(0, 1)] public float autoReactiveMax = 0.9f; + } + + public Vector2 jitter { get; private set; } + public Vector2Int renderSize => _maxRenderSize; + public Vector2Int displaySize => _displaySize; + public RenderTargetIdentifier colorOpaqueOnly { get; set; } + + private Fsr3UpscalerContext _fsrContext; + private Vector2Int _maxRenderSize; + private Vector2Int _displaySize; + private bool _resetHistory; + + private IFsr3UpscalerCallbacks _callbacks; + + private readonly Fsr3Upscaler.DispatchDescription _dispatchDescription = new Fsr3Upscaler.DispatchDescription(); + private readonly Fsr3Upscaler.GenerateReactiveDescription _genReactiveDescription = new Fsr3Upscaler.GenerateReactiveDescription(); + + private Fsr3Upscaler.QualityMode _prevQualityMode; + private ExposureSource _prevExposureSource; + private Vector2Int _prevDisplaySize; + + private Rect _originalRect; + + public bool IsSupported() + { + return SystemInfo.supportsComputeShaders && SystemInfo.supportsMotionVectors; + } + + public DepthTextureMode GetCameraFlags() + { + return DepthTextureMode.Depth | DepthTextureMode.MotionVectors; + } + + public void Release() + { + DestroyFsrContext(); + } + + public void ResetHistory() + { + _resetHistory = true; + } + + public void ConfigureJitteredProjectionMatrix(PostProcessRenderContext context) + { + ApplyJitter(context.camera); + } + + public void ConfigureCameraViewport(PostProcessRenderContext context) + { + var camera = context.camera; + _originalRect = camera.rect; + + // Determine the desired rendering and display resolutions + _displaySize = new Vector2Int(camera.pixelWidth, camera.pixelHeight); + Fsr3Upscaler.GetRenderResolutionFromQualityMode(out int maxRenderWidth, out int maxRenderHeight, _displaySize.x, _displaySize.y, qualityMode); + _maxRenderSize = new Vector2Int(maxRenderWidth, maxRenderHeight); + + // Render to a smaller portion of the screen by manipulating the camera's viewport rect + camera.aspect = (float)_displaySize.x / _displaySize.y; + camera.rect = new Rect(0, 0, _originalRect.width * _maxRenderSize.x / _displaySize.x, _originalRect.height * _maxRenderSize.y / _displaySize.y); + } + + public void ResetCameraViewport(PostProcessRenderContext context) + { + context.camera.rect = _originalRect; + } + + public void Render(PostProcessRenderContext context) + { + var cmd = context.command; + cmd.BeginSample("FSR3 Upscaler"); + + // Monitor for any resolution changes and recreate the FSR3 Upscaler context if necessary + // We can't create an FSR3 Upscaler context without info from the post-processing context, so delay the initial setup until here + if (_fsrContext == null || _displaySize.x != _prevDisplaySize.x || _displaySize.y != _prevDisplaySize.y || qualityMode != _prevQualityMode || exposureSource != _prevExposureSource) + { + DestroyFsrContext(); + CreateFsrContext(context); + } + + SetupDispatchDescription(context); + + if (autoGenerateReactiveMask) + { + SetupAutoReactiveDescription(context); + + var scaledRenderSize = _genReactiveDescription.RenderSize; + cmd.GetTemporaryRT(Fsr3ShaderIDs.UavAutoReactive, scaledRenderSize.x, scaledRenderSize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); + _fsrContext.GenerateReactiveMask(_genReactiveDescription, cmd); + _dispatchDescription.Reactive = new ResourceView(Fsr3ShaderIDs.UavAutoReactive); + } + + _fsrContext.Dispatch(_dispatchDescription, cmd); + + cmd.EndSample("FSR3 Upscaler"); + + _resetHistory = false; + } + + private void CreateFsrContext(PostProcessRenderContext context) + { + _prevQualityMode = qualityMode; + _prevExposureSource = exposureSource; + _prevDisplaySize = _displaySize; + + // Initialize FSR3 Upscaler context + Fsr3Upscaler.InitializationFlags flags = 0; + if (context.camera.allowHDR) flags |= Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange; + if (enableFP16) flags |= Fsr3Upscaler.InitializationFlags.EnableFP16Usage; + if (exposureSource == ExposureSource.Auto) flags |= Fsr3Upscaler.InitializationFlags.EnableAutoExposure; + if (RuntimeUtilities.IsDynamicResolutionEnabled(context.camera)) flags |= Fsr3Upscaler.InitializationFlags.EnableDynamicResolution; + + _callbacks = callbacksFactory(context); + _fsrContext = Fsr3Upscaler.CreateContext(_displaySize, _maxRenderSize, context.resources.computeShaders.superResolution, flags); + + // Apply a mipmap bias so that textures retain their sharpness + float biasOffset = Fsr3Upscaler.GetMipmapBiasOffset(_maxRenderSize.x, _displaySize.x); + if (!float.IsNaN(biasOffset) && !float.IsInfinity(biasOffset)) + { + _callbacks.ApplyMipmapBias(biasOffset); + } + } + + private void DestroyFsrContext() + { + if (_fsrContext != null) + { + _fsrContext.Destroy(); + _fsrContext = null; + } + + if (_callbacks != null) + { + // Undo the current mipmap bias offset + _callbacks.UndoMipmapBias(); + _callbacks = null; + } + } + + private void ApplyJitter(Camera camera) + { + var scaledRenderSize = GetScaledRenderSize(camera); + + // Perform custom jittering of the camera's projection matrix according to FSR3's recipe + int jitterPhaseCount = Fsr3Upscaler.GetJitterPhaseCount(scaledRenderSize.x, _displaySize.x); + Fsr3Upscaler.GetJitterOffset(out float jitterX, out float jitterY, Time.frameCount, jitterPhaseCount); + + _dispatchDescription.JitterOffset = new Vector2(jitterX, jitterY); + + jitterX = 2.0f * jitterX / scaledRenderSize.x; + jitterY = 2.0f * jitterY / scaledRenderSize.y; + + var jitterTranslationMatrix = Matrix4x4.Translate(new Vector3(jitterX, jitterY, 0)); + camera.nonJitteredProjectionMatrix = camera.projectionMatrix; + camera.projectionMatrix = jitterTranslationMatrix * camera.nonJitteredProjectionMatrix; + camera.useJitteredProjectionMatrixForTransparentRendering = true; + + jitter = new Vector2(jitterX, jitterY); + } + + private void SetupDispatchDescription(PostProcessRenderContext context) + { + var camera = context.camera; + + // Set up the main FSR3 Upscaler dispatch parameters + _dispatchDescription.Color = new ResourceView(context.source); + _dispatchDescription.Depth = new ResourceView(BuiltinRenderTextureType.CameraTarget, RenderTextureSubElement.Depth); + _dispatchDescription.MotionVectors = new ResourceView(BuiltinRenderTextureType.MotionVectors); + _dispatchDescription.Exposure = ResourceView.Unassigned; + _dispatchDescription.Reactive = ResourceView.Unassigned; + _dispatchDescription.TransparencyAndComposition = ResourceView.Unassigned; + + if (exposureSource == ExposureSource.Manual && exposure != null) _dispatchDescription.Exposure = new ResourceView(exposure); + if (exposureSource == ExposureSource.Unity) _dispatchDescription.Exposure = new ResourceView(context.autoExposureTexture); + if (reactiveMask != null) _dispatchDescription.Reactive = new ResourceView(reactiveMask); + if (transparencyAndCompositionMask != null) _dispatchDescription.TransparencyAndComposition = new ResourceView(transparencyAndCompositionMask); + + var scaledRenderSize = GetScaledRenderSize(context.camera); + + _dispatchDescription.Output = new ResourceView(context.destination); + _dispatchDescription.PreExposure = preExposure; + _dispatchDescription.EnableSharpening = performSharpenPass; + _dispatchDescription.Sharpness = sharpness; + _dispatchDescription.MotionVectorScale.x = -scaledRenderSize.x; + _dispatchDescription.MotionVectorScale.y = -scaledRenderSize.y; + _dispatchDescription.RenderSize = scaledRenderSize; + _dispatchDescription.UpscaleSize = _displaySize; + _dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime; + _dispatchDescription.CameraNear = camera.nearClipPlane; + _dispatchDescription.CameraFar = camera.farClipPlane; + _dispatchDescription.CameraFovAngleVertical = camera.fieldOfView * Mathf.Deg2Rad; + _dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit is 1 meter in Unity + _dispatchDescription.Reset = _resetHistory; + _dispatchDescription.Flags = enableDebugView ? Fsr3Upscaler.DispatchFlags.DrawDebugView : 0; + + // Set up the parameters for the optional experimental auto-TCR feature + _dispatchDescription.EnableAutoReactive = autoGenerateTransparencyAndComposition; + if (autoGenerateTransparencyAndComposition) + { + _dispatchDescription.ColorOpaqueOnly = new ResourceView(colorOpaqueOnly); + _dispatchDescription.AutoTcThreshold = generateTransparencyAndCompositionParameters.autoTcThreshold; + _dispatchDescription.AutoTcScale = generateTransparencyAndCompositionParameters.autoTcScale; + _dispatchDescription.AutoReactiveScale = generateTransparencyAndCompositionParameters.autoReactiveScale; + _dispatchDescription.AutoReactiveMax = generateTransparencyAndCompositionParameters.autoReactiveMax; + } + + if (SystemInfo.usesReversedZBuffer) + { + // Swap the near and far clip plane distances as FSR3 expects this when using inverted depth + (_dispatchDescription.CameraNear, _dispatchDescription.CameraFar) = (_dispatchDescription.CameraFar, _dispatchDescription.CameraNear); + } + } + + private void SetupAutoReactiveDescription(PostProcessRenderContext context) + { + // Set up the parameters to auto-generate a reactive mask + _genReactiveDescription.ColorOpaqueOnly = new ResourceView(colorOpaqueOnly); + _genReactiveDescription.ColorPreUpscale = new ResourceView(context.source); + _genReactiveDescription.OutReactive = new ResourceView(Fsr3ShaderIDs.UavAutoReactive); + _genReactiveDescription.RenderSize = GetScaledRenderSize(context.camera); + _genReactiveDescription.Scale = generateReactiveParameters.scale; + _genReactiveDescription.CutoffThreshold = generateReactiveParameters.cutoffThreshold; + _genReactiveDescription.BinaryValue = generateReactiveParameters.binaryValue; + _genReactiveDescription.Flags = generateReactiveParameters.flags; + } + + internal Vector2Int GetScaledRenderSize(Camera camera) + { + if (!RuntimeUtilities.IsDynamicResolutionEnabled(camera)) + return _maxRenderSize; + + return new Vector2Int(Mathf.CeilToInt(_maxRenderSize.x * ScalableBufferManager.widthScaleFactor), Mathf.CeilToInt(_maxRenderSize.y * ScalableBufferManager.heightScaleFactor)); + } + } +} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/TemporalAntialiasing.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Effects/Vignette.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/HistogramMonitor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/LightMeterMonitor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/Monitor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/VectorscopeMonitor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Monitors/WaveformMonitor.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/ParameterOverride.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessBundle.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebug.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessDebugLayer.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectRenderer.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEffectSettings.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessEvent.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessLayer.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessManager.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessProfile.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessRenderContext.cs.meta diff --git a/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs new file mode 100644 index 0000000..e745fa6 --- /dev/null +++ b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs @@ -0,0 +1,293 @@ +using System; +using FidelityFX.FSR3; + +namespace UnityEngine.Rendering.PostProcessing +{ + /// + /// This asset is used to store references to shaders and other resources we might need at + /// runtime without having to use a `Resources` folder. This allows for better memory management, + /// better dependency tracking and better interoperability with asset bundles. + /// + public sealed class PostProcessResources : ScriptableObject + { + /// + /// All the shaders used by post-processing. + /// + [Serializable] + public sealed class Shaders + { + /// + /// The shader used for the bloom effect. + /// + public Shader bloom; + + /// + /// The shader used for internal blit copies. + /// + public Shader copy; + + /// + /// The shader used for built-in blit copies in the built-in pipeline. + /// + public Shader copyStd; + + /// + /// The shader used for built-in blit copies in the built-in pipeline when using a + /// texture array (for stereo rendering). + /// + public Shader copyStdFromTexArray; + + /// + /// The shader used for built-in blit copies in the built-in pipeline when using a + /// double-wide texture (for stereo rendering). + /// + public Shader copyStdFromDoubleWide; + + /// + /// The shader used to kill the alpha. + /// + public Shader discardAlpha; + + /// + /// The shader used for the depth of field effect. + /// + public Shader depthOfField; + + /// + /// The shader used for the final pass. + /// + public Shader finalPass; + + /// + /// The shader used to generate the grain texture. + /// + public Shader grainBaker; + + /// + /// The shader used for the motion blur effect. + /// + public Shader motionBlur; + + /// + /// The shader used for the temporal anti-aliasing effect. + /// + public Shader temporalAntialiasing; + + /// + /// The shader used for the sub-pixel morphological anti-aliasing effect. + /// + public Shader subpixelMorphologicalAntialiasing; + + /// + /// The shader use by the volume manager to interpolate between two 2D textures. + /// + public Shader texture2dLerp; + + /// + /// The uber shader that combine several effects into one. + /// + public Shader uber; + + /// + /// The shader used to bake the 2D lookup table for color grading. + /// + public Shader lut2DBaker; + + /// + /// The shader used to draw the light meter monitor. + /// + public Shader lightMeter; + + /// + /// The shader used to draw the histogram monitor. + /// + public Shader gammaHistogram; + + /// + /// The shader used to draw the waveform monitor. + /// + public Shader waveform; + + /// + /// The shader used to draw the vectorscope monitor. + /// + public Shader vectorscope; + + /// + /// The shader used to draw debug overlays. + /// + public Shader debugOverlays; + + /// + /// The shader used for the deferred fog effect. + /// + public Shader deferredFog; + + /// + /// The shader used for the scalable ambient occlusion effect. + /// + public Shader scalableAO; + + /// + /// The shader used for the multi-scale ambient occlusion effect. + /// + public Shader multiScaleAO; + + /// + /// The shader used for the screen-space reflection effect. + /// + public Shader screenSpaceReflections; + + /// + /// Returns a copy of this class and its content. + /// + /// A copy of this class and its content. + public Shaders Clone() + { + return (Shaders)MemberwiseClone(); + } + } + + /// + /// All the compute shaders used by post-processing. + /// + [Serializable] + public sealed class ComputeShaders + { + /// + /// The compute shader used for the auto-exposure effect. + /// + public ComputeShader autoExposure; + + /// + /// The compute shader used to compute an histogram of the current frame. + /// + public ComputeShader exposureHistogram; + + /// + /// The compute shader used to bake the 3D lookup table for color grading. + /// + public ComputeShader lut3DBaker; + + /// + /// The compute shader used by the volume manager to interpolate between two 3D textures. + /// + public ComputeShader texture3dLerp; + + /// + /// The compute shader used to compute the histogram monitor. + /// + public ComputeShader gammaHistogram; + + /// + /// The compute shader used to compute the waveform monitor. + /// + public ComputeShader waveform; + + /// + /// The compute shader used to compute the vectorscope monitor. + /// + public ComputeShader vectorscope; + + /// + /// The compute shader used for the first downsampling pass of MSVO. + /// + public ComputeShader multiScaleAODownsample1; + + /// + /// The compute shader used for the second downsampling pass of MSVO. + /// + public ComputeShader multiScaleAODownsample2; + + /// + /// The compute shader used for the render pass of MSVO. + /// + public ComputeShader multiScaleAORender; + + /// + /// The compute shader used for the upsampling pass of MSVO. + /// + public ComputeShader multiScaleAOUpsample; + + /// + /// The compute shader used to a fast gaussian downsample. + /// + public ComputeShader gaussianDownsample; + + /// + /// Compute shaders used by the FidelityFX Super Resolution 3 (FSR3) Upscaler. + /// + public Fsr3UpscalerShaders superResolution; + + /// + /// Returns a copy of this class and its content. + /// + /// A copy of this class and its content. + public ComputeShaders Clone() + { + return (ComputeShaders)MemberwiseClone(); + } + } + + /// + /// A set of textures needed by the sub-pixel morphological anti-aliasing effect. + /// + [Serializable] + public sealed class SMAALuts + { + /// + /// The area lookup table. + /// + public Texture2D area; + + /// + /// The search lookup table. + /// + public Texture2D search; + } + + /// + /// A set of 64x64, single-channel blue noise textures. + /// + public Texture2D[] blueNoise64; + + /// + /// A set of 256x256, single-channel blue noise textures. + /// + public Texture2D[] blueNoise256; + + /// + /// Lookup tables used by the sub-pixel morphological anti-aliasing effect. + /// + public SMAALuts smaaLuts; + + /// + /// All the shaders used by post-processing. + /// + public Shaders shaders; + + /// + /// All the compute shaders used by post-processing. + /// + public ComputeShaders computeShaders; + +#if UNITY_EDITOR + /// + /// A delegate used to track resource changes. + /// + public delegate void ChangeHandler(); + + /// + /// Set this callback to be notified of resource changes. + /// + public ChangeHandler changeHandler; + + void OnValidate() + { + if (changeHandler != null) + changeHandler(); + } + +#endif + } +} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/PostProcessVolume.cs.meta diff --git a/Packages/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef new file mode 100644 index 0000000..8220b4a --- /dev/null +++ b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef @@ -0,0 +1,37 @@ +{ + "name": "Unity.Postprocessing.Runtime", + "rootNamespace": "", + "references": [ + "FidelityFX.FSR" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.render-pipelines.lightweight", + "expression": "1.0.0", + "define": "LWRP_1_0_0_OR_NEWER" + }, + { + "name": "com.unity.render-pipelines.universal", + "expression": "1.0.0", + "define": "UNIVERSAL_1_0_0_OR_NEWER" + }, + { + "name": "com.unity.modules.vr", + "expression": "1.0.0", + "define": "ENABLE_VR_MODULE" + }, + { + "name": "com.unity.modules.xr", + "expression": "1.0.0", + "define": "ENABLE_XR_MODULE" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ColorUtilities.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HableCurve.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/HaltonSeq.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/LogHistogram.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/MeshUtilities.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PostProcessEffectRendererExtensions.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheet.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/PropertySheetFactory.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/RuntimeUtilities.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/ShaderIDs.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/Spline.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TargetPool.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureFormatUtilities.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/TextureLerper.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs.meta b/Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Runtime/Utils/XRSettings.cs.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/ACES.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D11.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D12.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/D3D9.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Metal.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/OpenGL.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSP2.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/PSSL.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Switch.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/Vulkan.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/WebGPU.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/API/XboxOne.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/AutoExposure.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Bloom.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Copy.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStd.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromDoubleWide.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/CopyStdFromTexArray.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DeferredFog.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DepthOfField.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiscardAlpha.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/DiskKernels.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Distortion.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Dithering.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ExposureHistogram.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FastApproximateAntialiasing.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/FinalPass.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Fog.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GaussianDownsample.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/GrainBaker.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut2DBaker.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Lut3DBaker.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MotionBlur.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVO.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample1.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVODownsample2.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVORender.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/MultiScaleVOUpsample.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScalableAO.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/ScreenSpaceReflections.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasing.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/SubpixelMorphologicalAntialiasingBridge.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/TemporalAntialiasing.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture2DLerp.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Texture3DLerp.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Builtins/Uber.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Histogram.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/LightMeter.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Overlays.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Vectorscope.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.compute.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Debug/Waveform.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/ConvertToLog.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/CurveGrid.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Editor/Trackball.shader.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl b/Packages/com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl diff --git a/com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl.meta b/Packages/com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl.meta rename to Packages/com.unity.postprocessing/PostProcessing/Shaders/xRLib.hlsl.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_0.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_1.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_2.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_3.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_4.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_5.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_6.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise256px/LDR_LLL1_7.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_0.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_1.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_10.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_11.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_12.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_13.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_14.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_15.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_16.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_17.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_18.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_19.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_2.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_20.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_21.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_22.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_23.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_24.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_25.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_26.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_27.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_28.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_29.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_3.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_30.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_31.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_32.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_33.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_34.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_35.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_36.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_37.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_38.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_39.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_4.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_40.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_41.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_42.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_43.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_44.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_45.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_46.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_47.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_48.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_49.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_5.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_50.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_51.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_52.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_53.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_54.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_55.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_56.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_57.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_58.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_59.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_6.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_60.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_61.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_62.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_63.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_7.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_8.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/BlueNoise64px/LDR_LLL1_9.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_Unity_Log_r1.cube.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Linear_to_sRGB_r1.cube.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_Linear_r1.cube.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/Unity_Log_to_sRGB_r1.cube.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Linear_r1.cube.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube diff --git a/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/Cubes/sRGB_to_Unity_Log_r1.cube.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LUTs.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LUTs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LUTs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LUTs.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LUTs/NeutralLdrLut.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt00.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt01.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt02.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png diff --git a/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/LensDirt/LensDirt03.png.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SMAA.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SMAA.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga b/Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga diff --git a/com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/AreaTex.tga.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga b/Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga diff --git a/com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SMAA/SearchTex.tga.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_BlueRed.tga.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_GreenPurple.tga.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_PurpleGreen.tga.meta diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga diff --git a/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga.meta b/Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga.meta similarity index 100% rename from com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga.meta rename to Packages/com.unity.postprocessing/PostProcessing/Textures/SpectralLUTs/SpectralLut_RedBlue.tga.meta diff --git a/com.unity.postprocessing/Tests.meta b/Packages/com.unity.postprocessing/Tests.meta similarity index 100% rename from com.unity.postprocessing/Tests.meta rename to Packages/com.unity.postprocessing/Tests.meta diff --git a/com.unity.postprocessing/Tests/.tests.json b/Packages/com.unity.postprocessing/Tests/.tests.json similarity index 100% rename from com.unity.postprocessing/Tests/.tests.json rename to Packages/com.unity.postprocessing/Tests/.tests.json diff --git a/com.unity.postprocessing/Tests/Editor.meta b/Packages/com.unity.postprocessing/Tests/Editor.meta similarity index 100% rename from com.unity.postprocessing/Tests/Editor.meta rename to Packages/com.unity.postprocessing/Tests/Editor.meta diff --git a/com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs b/Packages/com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs similarity index 100% rename from com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs rename to Packages/com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs diff --git a/com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs.meta b/Packages/com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs.meta similarity index 100% rename from com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs.meta rename to Packages/com.unity.postprocessing/Tests/Editor/PostProcessEditorTests.cs.meta diff --git a/com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef b/Packages/com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef similarity index 100% rename from com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef rename to Packages/com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef diff --git a/com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef.meta b/Packages/com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef.meta similarity index 100% rename from com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef.meta rename to Packages/com.unity.postprocessing/Tests/Editor/Unity.Postprocessing.Editor.Tests.asmdef.meta diff --git a/com.unity.postprocessing/Tests/Runtime.meta b/Packages/com.unity.postprocessing/Tests/Runtime.meta similarity index 100% rename from com.unity.postprocessing/Tests/Runtime.meta rename to Packages/com.unity.postprocessing/Tests/Runtime.meta diff --git a/com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs b/Packages/com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs similarity index 100% rename from com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs rename to Packages/com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs diff --git a/com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs.meta b/Packages/com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs.meta similarity index 100% rename from com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs.meta rename to Packages/com.unity.postprocessing/Tests/Runtime/PostProcessingRuntimeTests.cs.meta diff --git a/com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef b/Packages/com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef similarity index 100% rename from com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef rename to Packages/com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef diff --git a/com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef.meta b/Packages/com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef.meta similarity index 100% rename from com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef.meta rename to Packages/com.unity.postprocessing/Tests/Runtime/Unity.Postprocessing.Runtime.Tests.asmdef.meta diff --git a/Packages/com.unity.postprocessing/package.json b/Packages/com.unity.postprocessing/package.json new file mode 100644 index 0000000..2cb7183 --- /dev/null +++ b/Packages/com.unity.postprocessing/package.json @@ -0,0 +1,11 @@ +{ + "name": "com.unity.postprocessing", + "version": "3.4.0", + "displayName": "Post Processing", + "unity": "2020.1", + "description": "The post-processing stack (v2) comes with a collection of effects and image filters you can apply to your cameras to improve the visuals of your games.", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "fidelityfx.fsr": "1.0.0" + } +} \ No newline at end of file diff --git a/com.unity.postprocessing/package.json.meta b/Packages/com.unity.postprocessing/package.json.meta similarity index 100% rename from com.unity.postprocessing/package.json.meta rename to Packages/com.unity.postprocessing/package.json.meta diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler.meta b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler.meta new file mode 100644 index 0000000..d1de5ce --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 73e35266d635ae64f9b765169e998d8c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs new file mode 100644 index 0000000..012cf05 --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using FidelityFX; +using FidelityFX.FSR3; + +// We use an old C# trick here to override the UnityEngine.AMD namespace and force FSR2Pass to use code from this namespace instead. +// By exactly mimicking the interface of the AMDUnityPlugin module, we can replace Unity's implementation of FSR2 with one of our own. +// Here we redirect all calls to the open source FSR3 Upscaler port with a bit of glue logic, making the entire thing cross-platform compatible. +namespace UnityEngine.Rendering.HighDefinition.AMD +{ + public static class AMDUnityPlugin + { + internal static Fsr3UpscalerAssets Assets; + + static AMDUnityPlugin() + { + _ = Load(); + } + + public static bool Load() + { + Unload(); + + Assets = Resources.Load("FSR3 Upscaler Assets"); + return Assets != null; + } + + public static bool IsLoaded() => Assets != null; + + internal static void Unload() + { + if (Assets != null) + { + Resources.UnloadAsset(Assets); + Assets = null; + } + } + } + + public class GraphicsDevice + { + private static GraphicsDevice sGraphicsDevice; + + public static GraphicsDevice device => sGraphicsDevice; + + public static int version => 0x00; + + private readonly List _contexts = new(); + + public static GraphicsDevice CreateGraphicsDevice() + { + if (sGraphicsDevice != null) + { + sGraphicsDevice.Destroy(); + sGraphicsDevice = null; + } + + var graphicsDevice = new GraphicsDevice(); + if (graphicsDevice.Initialize()) + { + sGraphicsDevice = graphicsDevice; + return graphicsDevice; + } + + Debug.LogWarning("Failed to initialize FSR3 Upscaler Graphics Device"); + return null; + } + +#if UNITY_EDITOR + private GraphicsDevice() + { + // Ensure resources are properly cleaned up during an in-editor domain reload + UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += Destroy; + } +#endif + + private bool Initialize() + { + return AMDUnityPlugin.Load(); + } + + private void Destroy() + { +#if UNITY_EDITOR + UnityEditor.AssemblyReloadEvents.beforeAssemblyReload -= Destroy; +#endif + + foreach (var context in _contexts) + { + context.Destroy(); + } + + _contexts.Clear(); + + AMDUnityPlugin.Unload(); + } + + public FSR2Context CreateFeature(CommandBuffer cmd, in FSR2CommandInitializationData initSettings) + { + var context = new FSR2Context(); + if (!context.Initialize(initSettings)) + return null; + + _contexts.Add(context); + return context; + } + + public void DestroyFeature(CommandBuffer cmd, FSR2Context fsrContext) + { + if (fsrContext == null) + return; + + fsrContext.Destroy(); + _contexts.Remove(fsrContext); + } + + public void ExecuteFSR2(CommandBuffer cmd, FSR2Context fsrContext, in FSR2TextureTable textures) + { + if (fsrContext == null) + return; + + fsrContext.Execute(cmd, textures); + } + + public bool GetRenderResolutionFromQualityMode(FSR2Quality qualityMode, uint displayWidth, uint displayHeight, out uint renderWidth, out uint renderHeight) + { + Fsr3Upscaler.GetRenderResolutionFromQualityMode(out int rw, out int rh, (int)displayWidth, (int)displayHeight, ConvertQualityMode(qualityMode)); + renderWidth = (uint)rw; + renderHeight = (uint)rh; + return true; + } + + public float GetUpscaleRatioFromQualityMode(FSR2Quality qualityMode) + { + return Fsr3Upscaler.GetUpscaleRatioFromQualityMode(ConvertQualityMode(qualityMode)); + } + + private static Fsr3Upscaler.QualityMode ConvertQualityMode(FSR2Quality qualityMode) + { + // FSR3 offers two more quality modes (Native AA and Ultra Quality) than standard FSR2, so a conversion is needed + const int diff = (int)Fsr3Upscaler.QualityMode.Quality - (int)FSR2Quality.Quality; + return (Fsr3Upscaler.QualityMode)((int)qualityMode + diff); + } + } + + public class FSR2Context + { + public static bool DrawDebugView { get; set; } = false; + + private FSR2CommandInitializationData _initData; + public ref readonly FSR2CommandInitializationData initData => ref _initData; + + private FSR2CommandExecutionData _executeData; + public ref FSR2CommandExecutionData executeData => ref _executeData; + + private readonly Fsr3UpscalerContext _context = new(); + private readonly Fsr3Upscaler.DispatchDescription _dispatchDescription = new(); + + internal bool Initialize(in FSR2CommandInitializationData initSettings) + { + _initData = initSettings; + _executeData = new FSR2CommandExecutionData(); + + if (AMDUnityPlugin.Assets == null || AMDUnityPlugin.Assets.shaders == null) + return false; + + Fsr3Upscaler.InitializationFlags flags = 0; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableHighDynamicRange)) flags |= Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDisplayResolutionMotionVectors)) flags |= Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableMotionVectorsJitterCancellation)) flags |= Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.DepthInverted)) flags |= Fsr3Upscaler.InitializationFlags.EnableDepthInverted; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDepthInfinite)) flags |= Fsr3Upscaler.InitializationFlags.EnableDepthInfinite; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableAutoExposure)) flags |= Fsr3Upscaler.InitializationFlags.EnableAutoExposure; + if (initSettings.GetFlag(FfxFsr2InitializationFlags.EnableDynamicResolution)) flags |= Fsr3Upscaler.InitializationFlags.EnableDynamicResolution; + + var contextDescription = new Fsr3Upscaler.ContextDescription + { + Flags = flags, + MaxRenderSize = new Vector2Int((int)initSettings.maxRenderSizeWidth, (int)initSettings.maxRenderSizeHeight), + MaxUpscaleSize = new Vector2Int((int)initSettings.displaySizeWidth, (int)initSettings.displaySizeHeight), + Shaders = AMDUnityPlugin.Assets.shaders, + }; + + _context.Create(contextDescription); + return true; + } + + internal void Destroy() + { + _context.Destroy(); + } + + internal void Execute(CommandBuffer cmd, in FSR2TextureTable textures) + { + _dispatchDescription.Color = new ResourceView(textures.colorInput); + _dispatchDescription.Depth = new ResourceView(textures.depth); + _dispatchDescription.MotionVectors = new ResourceView(textures.motionVectors); + _dispatchDescription.Exposure = new ResourceView(textures.exposureTexture); + _dispatchDescription.Reactive = new ResourceView(textures.biasColorMask); + _dispatchDescription.TransparencyAndComposition = new ResourceView(textures.transparencyMask); + _dispatchDescription.Output = new ResourceView(textures.colorOutput); + _dispatchDescription.JitterOffset = new Vector2(_executeData.jitterOffsetX, _executeData.jitterOffsetY); + _dispatchDescription.MotionVectorScale = new Vector2(_executeData.MVScaleX, _executeData.MVScaleY); + _dispatchDescription.RenderSize = new Vector2Int((int)_executeData.renderSizeWidth, (int)_executeData.renderSizeHeight); + _dispatchDescription.UpscaleSize = new Vector2Int((int)_initData.displaySizeWidth, (int)_initData.displaySizeHeight); + _dispatchDescription.EnableSharpening = _executeData.enableSharpening != 0; + _dispatchDescription.Sharpness = _executeData.sharpness; + _dispatchDescription.FrameTimeDelta = _executeData.frameTimeDelta / 1000f; + _dispatchDescription.PreExposure = _executeData.preExposure; + _dispatchDescription.Reset = _executeData.reset != 0; + _dispatchDescription.CameraNear = _executeData.cameraNear; + _dispatchDescription.CameraFar = _executeData.cameraFar; + _dispatchDescription.CameraFovAngleVertical = _executeData.cameraFovAngleVertical; + _dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit == 1 meter in Unity + _dispatchDescription.Flags = DrawDebugView ? Fsr3Upscaler.DispatchFlags.DrawDebugView : 0; + _dispatchDescription.UseTextureArrays = TextureXR.useTexArray && textures.colorInput.dimension == TextureDimension.Tex2DArray; + + _context.Dispatch(_dispatchDescription, cmd); + } + } + + public struct FSR2CommandInitializationData + { + public uint displaySizeHeight; + public uint displaySizeWidth; + public FfxFsr2InitializationFlags ffxFsrFlags; + public uint maxRenderSizeHeight; + public uint maxRenderSizeWidth; + + public readonly bool GetFlag(FfxFsr2InitializationFlags flag) + { + return (ffxFsrFlags & flag) == flag; + } + + public void SetFlag(FfxFsr2InitializationFlags flag, bool value) + { + if (value) + ffxFsrFlags |= flag; + else + ffxFsrFlags &= ~flag; + } + } + + public struct FSR2CommandExecutionData + { + public float cameraFar; + public float cameraFovAngleVertical; + public float cameraNear; + public int enableSharpening; + public float frameTimeDelta; + public float jitterOffsetX; + public float jitterOffsetY; + public float MVScaleX; + public float MVScaleY; + public float preExposure; + public uint renderSizeHeight; + public uint renderSizeWidth; + public int reset; + public float sharpness; + } + + public struct FSR2TextureTable + { + public Texture biasColorMask; + public Texture colorInput; + public Texture colorOutput; + public Texture depth; + public Texture exposureTexture; + public Texture motionVectors; + public Texture reactiveMask; // Note: reactiveMask does not seem to be used at all by HDRP, instead we get a biasColorMask + public Texture transparencyMask; + } + + [Flags] + public enum FfxFsr2InitializationFlags + { + EnableHighDynamicRange = 1 << 0, + EnableDisplayResolutionMotionVectors = 1 << 1, + EnableMotionVectorsJitterCancellation = 1 << 2, + DepthInverted = 1 << 3, + EnableDepthInfinite = 1 << 4, + EnableAutoExposure = 1 << 5, + EnableDynamicResolution = 1 << 6, + EnableTexture1DUsage = 1 << 7, + } + + public enum FSR2Quality + { + Quality, + Balanced, + Performance, + UltraPerformance, + } +} diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs.meta b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs.meta new file mode 100644 index 0000000..d5d7074 --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Fsr3UpscalerPlugin.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 774d82037ae83c74baac61a4c4e8bce5 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources.meta b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources.meta new file mode 100644 index 0000000..d0892b2 --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0599fe7b8fac94a4b81d1cac815f887f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset new file mode 100644 index 0000000..0b741dc --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset @@ -0,0 +1,30 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2dbcc608a4754d049a14a0bcce2eb40b, type: 3} + m_Name: FSR3 Upscaler Assets + m_EditorClassIdentifier: + shaders: + prepareInputsPass: {fileID: 7200000, guid: 4f59e5b9179d74844ae06a30ae1e0629, type: 3} + lumaPyramidPass: {fileID: 7200000, guid: d253be05abcdc80428503d3e4cce3a36, type: 3} + shadingChangePyramidPass: {fileID: 7200000, guid: 251e663738905fa4d8817001682d802f, + type: 3} + shadingChangePass: {fileID: 7200000, guid: 9a2bff2f97619ed4989d9b0577ba0641, type: 3} + prepareReactivityPass: {fileID: 7200000, guid: 20e44016ed34b0d4b8de499d1b566c69, + type: 3} + lumaInstabilityPass: {fileID: 7200000, guid: a135306e6d1857e43a86ef20db2a47fe, + type: 3} + accumulatePass: {fileID: 7200000, guid: c9b45f0ae7673694ba57a4aadfe212e9, type: 3} + sharpenPass: {fileID: 7200000, guid: 7aaf5cfff022de2499e9b0412f947f6c, type: 3} + autoGenReactivePass: {fileID: 7200000, guid: 5716b91fdaa4e9e439df6b96a796fe6e, + type: 3} + tcrAutoGenPass: {fileID: 7200000, guid: 75cdc6ef23f08ed498d4da511923fcea, type: 3} + debugViewPass: {fileID: 7200000, guid: cb24a71d54164c54eb5e86839acd48c5, type: 3} diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset.meta b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset.meta new file mode 100644 index 0000000..4c6e899 --- /dev/null +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Fsr3Upscaler/Resources/FSR3 Upscaler Assets.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 73e17b6fa270e2f4c90963a8d2fe5394 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/LICENSE.md b/Packages/fidelityfx.fsr/LICENSE.md new file mode 100644 index 0000000..01b3e51 --- /dev/null +++ b/Packages/fidelityfx.fsr/LICENSE.md @@ -0,0 +1,19 @@ +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. diff --git a/Packages/fidelityfx.fsr/LICENSE.md.meta b/Packages/fidelityfx.fsr/LICENSE.md.meta new file mode 100644 index 0000000..880be8e --- /dev/null +++ b/Packages/fidelityfx.fsr/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 09b65756f4c90b942b60816c53887dfa +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime.meta b/Packages/fidelityfx.fsr/Runtime.meta new file mode 100644 index 0000000..a57d834 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b64c5aa2a705139438e2bcc551a02b97 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/Common.meta b/Packages/fidelityfx.fsr/Runtime/Common.meta new file mode 100644 index 0000000..7ac2081 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e87a0efd0d1d4644abdef16b5a28bd0f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs b/Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs new file mode 100644 index 0000000..e5946a3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs @@ -0,0 +1,55 @@ +// 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.Rendering; + +namespace FidelityFX +{ + /// + /// An immutable structure wrapping all of the necessary information to bind a specific buffer or attachment of a render target to a compute shader. + /// + public readonly struct ResourceView + { + /// + /// This value is the equivalent of not setting any value at all; all struct fields will have their default values. + /// It does not refer to a valid texture, therefore any variable set to this value should be checked for IsValid and reassigned before being bound to a shader. + /// + public static readonly ResourceView Unassigned = new ResourceView(default); + + /// + /// This value contains a valid texture reference that can be bound to a shader, however it is just an empty placeholder texture. + /// Binding this to a shader can be seen as setting the texture variable inside the shader to null. + /// + public static readonly ResourceView None = new ResourceView(BuiltinRenderTextureType.None); + + public ResourceView(in RenderTargetIdentifier renderTarget, RenderTextureSubElement subElement = RenderTextureSubElement.Default, int mipLevel = 0) + { + RenderTarget = renderTarget; + SubElement = subElement; + MipLevel = mipLevel; + } + + public bool IsValid => !RenderTarget.Equals(default); + + public readonly RenderTargetIdentifier RenderTarget; + public readonly RenderTextureSubElement SubElement; + public readonly int MipLevel; + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs.meta b/Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs.meta new file mode 100644 index 0000000..901157b --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/Common/ResourceView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb9fdfac33a070740b66520d88f43ab7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2.meta b/Packages/fidelityfx.fsr/Runtime/FSR2.meta new file mode 100644 index 0000000..696ee85 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cce3e495c6ece2145b041d0a6e43bb26 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs new file mode 100644 index 0000000..4632b96 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs @@ -0,0 +1,302 @@ +// 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 System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR2 +{ + /// + /// A collection of helper functions and data structures required by the FSR2 process. + /// + public static class Fsr2 + { + /// + /// Creates a new FSR2 context with standard parameters that are appropriate for the current platform. + /// + public static Fsr2Context CreateContext(Vector2Int displaySize, Vector2Int maxRenderSize, Fsr2Shaders shaders, InitializationFlags flags = 0) + { + if (SystemInfo.usesReversedZBuffer) + flags |= InitializationFlags.EnableDepthInverted; + else + flags &= ~InitializationFlags.EnableDepthInverted; + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + flags |= InitializationFlags.EnableDebugChecking; +#endif + + Debug.Log($"Setting up FSR2 with render size: {maxRenderSize.x}x{maxRenderSize.y}, display size: {displaySize.x}x{displaySize.y}, flags: {flags}"); + + var contextDescription = new ContextDescription + { + Flags = flags, + DisplaySize = displaySize, + MaxRenderSize = maxRenderSize, + Shaders = shaders, + }; + + var context = new Fsr2Context(); + context.Create(contextDescription); + return context; + } + + public static float GetUpscaleRatioFromQualityMode(QualityMode qualityMode) + { + switch (qualityMode) + { + case QualityMode.NativeAA: + return 1.0f; + case QualityMode.UltraQuality: + return 1.2f; + case QualityMode.Quality: + return 1.5f; + case QualityMode.Balanced: + return 1.7f; + case QualityMode.Performance: + return 2.0f; + case QualityMode.UltraPerformance: + return 3.0f; + default: + return 1.0f; + } + } + + public static void GetRenderResolutionFromQualityMode( + out int renderWidth, out int renderHeight, + int displayWidth, int displayHeight, QualityMode qualityMode) + { + float ratio = GetUpscaleRatioFromQualityMode(qualityMode); + renderWidth = Mathf.RoundToInt(displayWidth / ratio); + renderHeight = Mathf.RoundToInt(displayHeight / ratio); + } + + public static float GetMipmapBiasOffset(int renderWidth, int displayWidth) + { + return Mathf.Log((float)renderWidth / displayWidth, 2.0f) - 1.0f; + } + + public static int GetJitterPhaseCount(int renderWidth, int displayWidth) + { + const float basePhaseCount = 8.0f; + int jitterPhaseCount = (int)(basePhaseCount * Mathf.Pow((float)displayWidth / renderWidth, 2.0f)); + return jitterPhaseCount; + } + + public static void GetJitterOffset(out float outX, out float outY, int index, int phaseCount) + { + outX = Halton((index % phaseCount) + 1, 2) - 0.5f; + outY = Halton((index % phaseCount) + 1, 3) - 0.5f; + } + + // Calculate halton number for index and base. + private static float Halton(int index, int @base) + { + float f = 1.0f, result = 0.0f; + + for (int currentIndex = index; currentIndex > 0;) { + + f /= @base; + result += f * (currentIndex % @base); + currentIndex = (int)Mathf.Floor((float)currentIndex / @base); + } + + return result; + } + + public static float Lanczos2(float value) + { + return Mathf.Abs(value) < Mathf.Epsilon ? 1.0f : Mathf.Sin(Mathf.PI * value) / (Mathf.PI * value) * (Mathf.Sin(0.5f * Mathf.PI * value) / (0.5f * Mathf.PI * value)); + } + +#if !UNITY_2021_1_OR_NEWER + internal static void SetBufferData(this CommandBuffer commandBuffer, ComputeBuffer computeBuffer, Array data) + { + commandBuffer.SetComputeBufferData(computeBuffer, data); + } +#endif + + public enum QualityMode + { + NativeAA = 0, + UltraQuality = 1, + Quality = 2, + Balanced = 3, + Performance = 4, + UltraPerformance = 5, + } + + [Flags] + public enum InitializationFlags + { + EnableHighDynamicRange = 1 << 0, + EnableDisplayResolutionMotionVectors = 1 << 1, + EnableMotionVectorsJitterCancellation = 1 << 2, + EnableDepthInverted = 1 << 3, + EnableDepthInfinite = 1 << 4, + EnableAutoExposure = 1 << 5, + EnableDynamicResolution = 1 << 6, + EnableFP16Usage = 1 << 7, + EnableDebugChecking = 1 << 8, + } + + /// + /// A structure encapsulating the parameters required to initialize FidelityFX Super Resolution 2 upscaling. + /// + public struct ContextDescription + { + public InitializationFlags Flags; + public Vector2Int MaxRenderSize; + public Vector2Int DisplaySize; + public Fsr2Shaders Shaders; + } + + /// + /// A structure encapsulating the parameters for dispatching the various passes of FidelityFX Super Resolution 2. + /// + public class DispatchDescription + { + public ResourceView Color; + public ResourceView Depth; + public ResourceView MotionVectors; + public ResourceView Exposure; // optional + public ResourceView Reactive; // optional + public ResourceView TransparencyAndComposition; // optional + public ResourceView Output; + public Vector2 JitterOffset; + public Vector2 MotionVectorScale; + public Vector2Int RenderSize; + public Vector2Int InputResourceSize; + public bool EnableSharpening; + public float Sharpness; + public float FrameTimeDelta; // in seconds + public float PreExposure; + public bool Reset; + public float CameraNear; + public float CameraFar; + public float CameraFovAngleVertical; + public float ViewSpaceToMetersFactor; + public bool UseTextureArrays; // Enable texture array bindings, primarily used for HDRP and XR + + // EXPERIMENTAL reactive mask generation parameters + public bool EnableAutoReactive; + public ResourceView ColorOpaqueOnly; + public float AutoTcThreshold = 0.05f; + public float AutoTcScale = 1.0f; + public float AutoReactiveScale = 5.0f; + public float AutoReactiveMax = 0.9f; + } + + /// + /// A structure encapsulating the parameters for automatic generation of a reactive mask. + /// The default values for Scale, CutoffThreshold, BinaryValue and Flags were taken from the FSR2 demo project. + /// + public class GenerateReactiveDescription + { + public ResourceView ColorOpaqueOnly; + public ResourceView ColorPreUpscale; + public ResourceView OutReactive; + public Vector2Int RenderSize; + public float Scale = 0.5f; + public float CutoffThreshold = 0.2f; + public float BinaryValue = 0.9f; + public GenerateReactiveFlags Flags = GenerateReactiveFlags.ApplyTonemap | GenerateReactiveFlags.ApplyThreshold | GenerateReactiveFlags.UseComponentsMax; + } + + [Flags] + public enum GenerateReactiveFlags + { + ApplyTonemap = 1 << 0, + ApplyInverseTonemap = 1 << 1, + ApplyThreshold = 1 << 2, + UseComponentsMax = 1 << 3, + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct UpscalerConstants + { + public Vector2Int renderSize; + public Vector2Int maxRenderSize; + public Vector2Int displaySize; + public Vector2Int inputColorResourceDimensions; + public Vector2Int lumaMipDimensions; + public int lumaMipLevelToUse; + public int frameIndex; + + public Vector4 deviceToViewDepth; + public Vector2 jitterOffset; + public Vector2 motionVectorScale; + public Vector2 downscaleFactor; + public Vector2 motionVectorJitterCancellation; + public float preExposure; + public float previousFramePreExposure; + public float tanHalfFOV; + public float jitterPhaseCount; + public float deltaTime; + public float dynamicResChangeFactor; + public float viewSpaceToMetersFactor; + public float padding; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct SpdConstants + { + public uint mips; + public uint numWorkGroups; + public uint workGroupOffsetX, workGroupOffsetY; + public uint renderSizeX, renderSizeY; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct GenerateReactiveConstants + { + public float scale; + public float threshold; + public float binaryValue; + public uint flags; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct GenerateReactiveConstants2 + { + public float autoTcThreshold; + public float autoTcScale; + public float autoReactiveScale; + public float autoReactiveMax; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct RcasConstants + { + public RcasConstants(uint sharpness, uint halfSharp) + { + this.sharpness = sharpness; + this.halfSharp = halfSharp; + dummy0 = dummy1 = 0; + } + + public readonly uint sharpness; + public readonly uint halfSharp; + public readonly uint dummy0; + public readonly uint dummy1; + } + } +} diff --git a/Assets/Scripts/Core/Fsr3Upscaler.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3Upscaler.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs new file mode 100644 index 0000000..c30a8f6 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs @@ -0,0 +1,152 @@ +// 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; + +namespace FidelityFX.FSR2 +{ + /// + /// Scriptable object containing all shader resources required by FidelityFX Super Resolution 2 (FSR2). + /// These can be stored in an asset file and referenced from a scene or prefab, avoiding the need to load the shaders from a Resources folder. + /// + [CreateAssetMenu(fileName = "FSR2 Assets", menuName = "FidelityFX/FSR2 Assets", order = 1102)] + public class Fsr2Assets : ScriptableObject + { + public Fsr2Shaders shaders; + +#if UNITY_EDITOR + private void Reset() + { + shaders = new Fsr2Shaders + { + computeLuminancePyramidPass = FindComputeShader("ffx_fsr2_compute_luminance_pyramid_pass"), + reconstructPreviousDepthPass = FindComputeShader("ffx_fsr2_reconstruct_previous_depth_pass"), + depthClipPass = FindComputeShader("ffx_fsr2_depth_clip_pass"), + lockPass = FindComputeShader("ffx_fsr2_lock_pass"), + accumulatePass = FindComputeShader("ffx_fsr2_accumulate_pass"), + sharpenPass = FindComputeShader("ffx_fsr2_rcas_pass"), + autoGenReactivePass = FindComputeShader("ffx_fsr2_autogen_reactive_pass"), + tcrAutoGenPass = FindComputeShader("ffx_fsr2_tcr_autogen_pass"), + }; + } + + private static ComputeShader FindComputeShader(string name) + { + string[] assetGuids = UnityEditor.AssetDatabase.FindAssets($"t:ComputeShader {name}"); + if (assetGuids == null || assetGuids.Length == 0) + return null; + + string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assetGuids[0]); + return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); + } +#endif + } + + /// + /// All the compute shaders used by FSR2. + /// + [System.Serializable] + public class Fsr2Shaders + { + /// + /// The compute shader used by the luminance pyramid computation pass. + /// + public ComputeShader computeLuminancePyramidPass; + + /// + /// The compute shader used by the previous depth reconstruction pass. + /// + public ComputeShader reconstructPreviousDepthPass; + + /// + /// The compute shader used by the depth clip pass. + /// + public ComputeShader depthClipPass; + + /// + /// The compute shader used by the lock pass. + /// + public ComputeShader lockPass; + + /// + /// The compute shader used by the accumulation pass. + /// + public ComputeShader accumulatePass; + + /// + /// The compute shader used by the RCAS sharpening pass. + /// + public ComputeShader sharpenPass; + + /// + /// The compute shader used to auto-generate a reactive mask. + /// + public ComputeShader autoGenReactivePass; + + /// + /// The compute shader used to auto-generate a transparency & composition mask. + /// + public ComputeShader tcrAutoGenPass; + + /// + /// Returns a copy of this class and its contents. + /// + public Fsr2Shaders Clone() + { + return (Fsr2Shaders)MemberwiseClone(); + } + + /// + /// Returns a copy of this class with clones of all its shaders. + /// This can be useful if you're running multiple FSR2 instances with different shader configurations. + /// Be sure to clean up these clones through Dispose once you're done with them. + /// + public Fsr2Shaders DeepCopy() + { + return new Fsr2Shaders + { + computeLuminancePyramidPass = Object.Instantiate(computeLuminancePyramidPass), + reconstructPreviousDepthPass = Object.Instantiate(reconstructPreviousDepthPass), + depthClipPass = Object.Instantiate(depthClipPass), + lockPass = Object.Instantiate(lockPass), + accumulatePass = Object.Instantiate(accumulatePass), + sharpenPass = Object.Instantiate(sharpenPass), + autoGenReactivePass = Object.Instantiate(autoGenReactivePass), + tcrAutoGenPass = Object.Instantiate(tcrAutoGenPass), + }; + } + + /// + /// Destroy all the shaders within this instance. + /// Use this only on clones created through DeepCopy. + /// + public void Dispose() + { + Object.Destroy(computeLuminancePyramidPass); + Object.Destroy(reconstructPreviousDepthPass); + Object.Destroy(depthClipPass); + Object.Destroy(lockPass); + Object.Destroy(accumulatePass); + Object.Destroy(sharpenPass); + Object.Destroy(autoGenReactivePass); + Object.Destroy(tcrAutoGenPass); + } + } +} diff --git a/Assets/Scripts/Core/Fsr3UpscalerAssets.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3UpscalerAssets.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Assets.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs new file mode 100644 index 0000000..06151b5 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs @@ -0,0 +1,81 @@ +// 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; + +namespace FidelityFX.FSR2 +{ + /// + /// A collection of callbacks required by the FSR2 process. + /// This allows some customization by the game dev on how to integrate FSR2 upscaling into their own game setup. + /// + public interface IFsr2Callbacks + { + /// + /// Apply a mipmap bias to in-game textures to prevent them from becoming blurry as the internal rendering resolution lowers. + /// This will need to be customized on a per-game basis, as there is no clear universal way to determine what are "in-game" textures. + /// The default implementation will simply apply a mipmap bias to all 2D textures, which will include things like UI textures and which might miss things like terrain texture arrays. + /// + /// Depending on how your game organizes its assets, you will want to create a filter that more specifically selects the textures that need to have this mipmap bias applied. + /// You may also want to store the bias offset value and apply it to any assets that are loaded in on demand. + /// + void ApplyMipmapBias(float biasOffset); + + void UndoMipmapBias(); + } + + /// + /// Default implementation of IFsr2Callbacks. + /// These are fine for testing but a proper game will want to extend and override these methods. + /// + public class Fsr2CallbacksBase: IFsr2Callbacks + { + protected float CurrentBiasOffset = 0; + + public virtual void ApplyMipmapBias(float biasOffset) + { + if (float.IsNaN(biasOffset) || float.IsInfinity(biasOffset)) + return; + + CurrentBiasOffset += biasOffset; + + if (Mathf.Approximately(CurrentBiasOffset, 0f)) + { + CurrentBiasOffset = 0f; + } + + foreach (var texture in Resources.FindObjectsOfTypeAll()) + { + if (texture.mipmapCount <= 1) + continue; + + texture.mipMapBias += biasOffset; + } + } + + public virtual void UndoMipmapBias() + { + if (CurrentBiasOffset == 0f) + return; + + ApplyMipmapBias(-CurrentBiasOffset); + } + } +} diff --git a/Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3UpscalerCallbacks.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Callbacks.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs new file mode 100644 index 0000000..38d380b --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs @@ -0,0 +1,618 @@ +// 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 System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR2 +{ + /// + /// This class loosely matches the FfxFsr2Context struct from the original FSR2 codebase. + /// It manages the various resources and compute passes required by the FSR2 process. + /// Note that this class does not know anything about Unity render pipelines; all it knows is CommandBuffers and RenderTargetIdentifiers. + /// This should make it suitable for integration with any of the available Unity render pipelines. + /// + public class Fsr2Context + { + private const int MaxQueuedFrames = 16; + + private Fsr2.ContextDescription _contextDescription; + private CommandBuffer _commandBuffer; + + private Fsr2Pass _computeLuminancePyramidPass; + private Fsr2Pass _reconstructPreviousDepthPass; + private Fsr2Pass _depthClipPass; + private Fsr2Pass _lockPass; + private Fsr2Pass _accumulatePass; + private Fsr2Pass _sharpenPass; + private Fsr2Pass _generateReactivePass; + private Fsr2Pass _tcrAutogeneratePass; + + private readonly Fsr2Resources _resources = new Fsr2Resources(); + + private ComputeBuffer _upscalerConstantsBuffer; + private readonly Fsr2.UpscalerConstants[] _upscalerConstantsArray = { new Fsr2.UpscalerConstants() }; + private ref Fsr2.UpscalerConstants UpscalerConsts => ref _upscalerConstantsArray[0]; + + private ComputeBuffer _spdConstantsBuffer; + private readonly Fsr2.SpdConstants[] _spdConstantsArray = { new Fsr2.SpdConstants() }; + private ref Fsr2.SpdConstants SpdConsts => ref _spdConstantsArray[0]; + + private ComputeBuffer _rcasConstantsBuffer; + private readonly Fsr2.RcasConstants[] _rcasConstantsArray = new Fsr2.RcasConstants[1]; + private ref Fsr2.RcasConstants RcasConsts => ref _rcasConstantsArray[0]; + + private ComputeBuffer _generateReactiveConstantsBuffer; + private readonly Fsr2.GenerateReactiveConstants[] _generateReactiveConstantsArray = { new Fsr2.GenerateReactiveConstants() }; + private ref Fsr2.GenerateReactiveConstants GenReactiveConsts => ref _generateReactiveConstantsArray[0]; + + private ComputeBuffer _tcrAutogenerateConstantsBuffer; + private readonly Fsr2.GenerateReactiveConstants2[] _tcrAutogenerateConstantsArray = { new Fsr2.GenerateReactiveConstants2() }; + private ref Fsr2.GenerateReactiveConstants2 TcrAutoGenConsts => ref _tcrAutogenerateConstantsArray[0]; + + private bool _firstExecution; + private Vector2 _previousJitterOffset; + private int _resourceFrameIndex; + + public void Create(Fsr2.ContextDescription contextDescription) + { + _contextDescription = contextDescription; + _commandBuffer = new CommandBuffer { name = "FSR2" }; + + _upscalerConstantsBuffer = CreateConstantBuffer(); + _spdConstantsBuffer = CreateConstantBuffer(); + _rcasConstantsBuffer = CreateConstantBuffer(); + _generateReactiveConstantsBuffer = CreateConstantBuffer(); + _tcrAutogenerateConstantsBuffer = CreateConstantBuffer(); + + // Set defaults + _firstExecution = true; + _resourceFrameIndex = 0; + + UpscalerConsts.displaySize = _contextDescription.DisplaySize; + + _resources.Create(_contextDescription); + CreatePasses(); + } + + private void CreatePasses() + { + _computeLuminancePyramidPass = new Fsr2ComputeLuminancePyramidPass(_contextDescription, _resources, _upscalerConstantsBuffer, _spdConstantsBuffer); + _reconstructPreviousDepthPass = new Fsr2ReconstructPreviousDepthPass(_contextDescription, _resources, _upscalerConstantsBuffer); + _depthClipPass = new Fsr2DepthClipPass(_contextDescription, _resources, _upscalerConstantsBuffer); + _lockPass = new Fsr2LockPass(_contextDescription, _resources, _upscalerConstantsBuffer); + _accumulatePass = new Fsr2AccumulatePass(_contextDescription, _resources, _upscalerConstantsBuffer); + _sharpenPass = new Fsr2SharpenPass(_contextDescription, _resources, _upscalerConstantsBuffer, _rcasConstantsBuffer); + _generateReactivePass = new Fsr2GenerateReactivePass(_contextDescription, _resources, _generateReactiveConstantsBuffer); + _tcrAutogeneratePass = new Fsr2TcrAutogeneratePass(_contextDescription, _resources, _upscalerConstantsBuffer, _tcrAutogenerateConstantsBuffer); + } + + public void Destroy() + { + DestroyPass(ref _tcrAutogeneratePass); + DestroyPass(ref _generateReactivePass); + DestroyPass(ref _sharpenPass); + DestroyPass(ref _accumulatePass); + DestroyPass(ref _lockPass); + DestroyPass(ref _depthClipPass); + DestroyPass(ref _reconstructPreviousDepthPass); + DestroyPass(ref _computeLuminancePyramidPass); + + _resources.Destroy(); + + DestroyConstantBuffer(ref _tcrAutogenerateConstantsBuffer); + DestroyConstantBuffer(ref _generateReactiveConstantsBuffer); + DestroyConstantBuffer(ref _rcasConstantsBuffer); + DestroyConstantBuffer(ref _spdConstantsBuffer); + DestroyConstantBuffer(ref _upscalerConstantsBuffer); + + if (_commandBuffer != null) + { + _commandBuffer.Dispose(); + _commandBuffer = null; + } + } + + public void Dispatch(Fsr2.DispatchDescription dispatchParams) + { + _commandBuffer.Clear(); + Dispatch(dispatchParams, _commandBuffer); + Graphics.ExecuteCommandBuffer(_commandBuffer); + } + + public void Dispatch(Fsr2.DispatchDescription dispatchParams, CommandBuffer commandBuffer) + { + if ((_contextDescription.Flags & Fsr2.InitializationFlags.EnableDebugChecking) != 0) + { + DebugCheckDispatch(dispatchParams); + } + + if (dispatchParams.UseTextureArrays) + commandBuffer.EnableShaderKeyword("UNITY_FSR_TEXTURE2D_X_ARRAY"); + + if (_firstExecution) + { + commandBuffer.SetRenderTarget(_resources.LockStatus[0]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.LockStatus[1]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + } + + int frameIndex = _resourceFrameIndex % 2; + bool resetAccumulation = dispatchParams.Reset || _firstExecution; + _firstExecution = false; + + // If auto exposure is enabled use the auto exposure SRV, otherwise what the app sends + if ((_contextDescription.Flags & Fsr2.InitializationFlags.EnableAutoExposure) != 0) + dispatchParams.Exposure = new ResourceView(_resources.AutoExposure); + else if (!dispatchParams.Exposure.IsValid) + dispatchParams.Exposure = new ResourceView(_resources.DefaultExposure); + + if (dispatchParams.EnableAutoReactive) + { + // Create the auto-TCR resources only when we need them + if (_resources.AutoReactive == null) + _resources.CreateTcrAutogenResources(_contextDescription); + + if (resetAccumulation) + { + RenderTargetIdentifier opaqueOnly = dispatchParams.ColorOpaqueOnly.IsValid ? dispatchParams.ColorOpaqueOnly.RenderTarget : Fsr2ShaderIDs.SrvOpaqueOnly; + commandBuffer.Blit(_resources.PrevPreAlpha[frameIndex ^ 1], opaqueOnly); + } + } + else if (_resources.AutoReactive != null) + { + // Destroy the auto-TCR resources if we don't use the feature + _resources.DestroyTcrAutogenResources(); + } + + if (!dispatchParams.Reactive.IsValid) dispatchParams.Reactive = new ResourceView(_resources.DefaultReactive); + if (!dispatchParams.TransparencyAndComposition.IsValid) dispatchParams.TransparencyAndComposition = new ResourceView(_resources.DefaultReactive); + Fsr2Resources.CreateAliasableResources(commandBuffer, _contextDescription, dispatchParams); + + SetupConstants(dispatchParams, resetAccumulation); + + // Reactive mask bias + const int threadGroupWorkRegionDim = 8; + int dispatchSrcX = (UpscalerConsts.renderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchSrcY = (UpscalerConsts.renderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchDstX = (_contextDescription.DisplaySize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchDstY = (_contextDescription.DisplaySize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + // Clear reconstructed depth for max depth store + if (resetAccumulation) + { + commandBuffer.SetRenderTarget(_resources.LockStatus[frameIndex ^ 1]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + + commandBuffer.SetRenderTarget(_resources.InternalUpscaled[frameIndex ^ 1]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + + commandBuffer.SetRenderTarget(_resources.SceneLuminance); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + + // Auto exposure always used to track luma changes in locking logic + commandBuffer.SetRenderTarget(_resources.AutoExposure); + commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1e8f, 0f, 0f)); + + // Reset atomic counter to 0 + commandBuffer.SetRenderTarget(_resources.SpdAtomicCounter); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + } + + // FSR3: need to clear here since we need the content of this surface for frame interpolation, so clearing in the lock pass is not an option + bool depthInverted = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDepthInverted) == Fsr2.InitializationFlags.EnableDepthInverted; + commandBuffer.SetRenderTarget(Fsr2ShaderIDs.UavReconstructedPrevNearestDepth); + commandBuffer.ClearRenderTarget(false, true, depthInverted ? Color.clear : Color.white); + + // Auto exposure + SetupSpdConstants(dispatchParams, out var dispatchThreadGroupCount); + + // Initialize constant buffers data + commandBuffer.SetBufferData(_upscalerConstantsBuffer, _upscalerConstantsArray); + commandBuffer.SetBufferData(_spdConstantsBuffer, _spdConstantsArray); + + // Auto reactive + if (dispatchParams.EnableAutoReactive) + { + GenerateTransparencyCompositionReactive(dispatchParams, commandBuffer, frameIndex); + dispatchParams.Reactive = new ResourceView(_resources.AutoReactive); + dispatchParams.TransparencyAndComposition = new ResourceView(_resources.AutoComposition); + } + + // Compute luminance pyramid + _computeLuminancePyramidPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); + + // Reconstruct previous depth + _reconstructPreviousDepthPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + + // Depth clip + _depthClipPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + + // Create locks + _lockPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + + // Accumulate + _accumulatePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchDstX, dispatchDstY); + + if (dispatchParams.EnableSharpening) + { + // Compute the constants + SetupRcasConstants(dispatchParams); + commandBuffer.SetBufferData(_rcasConstantsBuffer, _rcasConstantsArray); + + // Dispatch RCAS + const int threadGroupWorkRegionDimRcas = 16; + int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; + int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; + _sharpenPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, threadGroupsX, threadGroupsY); + } + + _resourceFrameIndex = (_resourceFrameIndex + 1) % MaxQueuedFrames; + + Fsr2Resources.DestroyAliasableResources(commandBuffer); + + commandBuffer.DisableShaderKeyword("UNITY_FSR_TEXTURE2D_X_ARRAY"); + } + + public void GenerateReactiveMask(Fsr2.GenerateReactiveDescription dispatchParams) + { + _commandBuffer.Clear(); + GenerateReactiveMask(dispatchParams, _commandBuffer); + Graphics.ExecuteCommandBuffer(_commandBuffer); + } + + public void GenerateReactiveMask(Fsr2.GenerateReactiveDescription dispatchParams, CommandBuffer commandBuffer) + { + const int threadGroupWorkRegionDim = 8; + int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + GenReactiveConsts.scale = dispatchParams.Scale; + GenReactiveConsts.threshold = dispatchParams.CutoffThreshold; + GenReactiveConsts.binaryValue = dispatchParams.BinaryValue; + GenReactiveConsts.flags = (uint)dispatchParams.Flags; + commandBuffer.SetBufferData(_generateReactiveConstantsBuffer, _generateReactiveConstantsArray); + + ((Fsr2GenerateReactivePass)_generateReactivePass).ScheduleDispatch(commandBuffer, dispatchParams, dispatchSrcX, dispatchSrcY); + } + + private void GenerateTransparencyCompositionReactive(Fsr2.DispatchDescription dispatchParams, CommandBuffer commandBuffer, int frameIndex) + { + const int threadGroupWorkRegionDim = 8; + int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + TcrAutoGenConsts.autoTcThreshold = dispatchParams.AutoTcThreshold; + TcrAutoGenConsts.autoTcScale = dispatchParams.AutoTcScale; + TcrAutoGenConsts.autoReactiveScale = dispatchParams.AutoReactiveScale; + TcrAutoGenConsts.autoReactiveMax = dispatchParams.AutoReactiveMax; + commandBuffer.SetBufferData(_tcrAutogenerateConstantsBuffer, _tcrAutogenerateConstantsArray); + + _tcrAutogeneratePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + } + + private void SetupConstants(Fsr2.DispatchDescription dispatchParams, bool resetAccumulation) + { + ref Fsr2.UpscalerConstants constants = ref UpscalerConsts; + + constants.jitterOffset = dispatchParams.JitterOffset; + constants.renderSize = dispatchParams.RenderSize; + constants.maxRenderSize = _contextDescription.MaxRenderSize; + constants.inputColorResourceDimensions = dispatchParams.InputResourceSize; + + // Compute the horizontal FOV for the shader from the vertical one + float aspectRatio = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; + float cameraAngleHorizontal = Mathf.Atan(Mathf.Tan(dispatchParams.CameraFovAngleVertical / 2.0f) * aspectRatio) * 2.0f; + constants.tanHalfFOV = Mathf.Tan(cameraAngleHorizontal * 0.5f); + constants.viewSpaceToMetersFactor = (dispatchParams.ViewSpaceToMetersFactor > 0.0f) ? dispatchParams.ViewSpaceToMetersFactor : 1.0f; + + // Compute params to enable device depth to view space depth computation in shader + constants.deviceToViewDepth = SetupDeviceDepthToViewSpaceDepthParams(dispatchParams); + + // To be updated if resource is larger than the actual image size + constants.downscaleFactor = new Vector2((float)constants.renderSize.x / _contextDescription.DisplaySize.x, (float)constants.renderSize.y / _contextDescription.DisplaySize.y); + constants.previousFramePreExposure = constants.preExposure; + constants.preExposure = (dispatchParams.PreExposure != 0) ? dispatchParams.PreExposure : 1.0f; + + // Motion vector data + Vector2Int motionVectorsTargetSize = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) != 0 ? constants.displaySize : constants.renderSize; + constants.motionVectorScale = dispatchParams.MotionVectorScale / motionVectorsTargetSize; + + // Compute jitter cancellation + if ((_contextDescription.Flags & Fsr2.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) + { + constants.motionVectorJitterCancellation = (_previousJitterOffset - constants.jitterOffset) / motionVectorsTargetSize; + _previousJitterOffset = constants.jitterOffset; + } + + int jitterPhaseCount = Fsr2.GetJitterPhaseCount(dispatchParams.RenderSize.x, _contextDescription.DisplaySize.x); + if (resetAccumulation || constants.jitterPhaseCount == 0) + { + constants.jitterPhaseCount = jitterPhaseCount; + } + else + { + int jitterPhaseCountDelta = (int)(jitterPhaseCount - constants.jitterPhaseCount); + if (jitterPhaseCountDelta > 0) + constants.jitterPhaseCount++; + else if (jitterPhaseCountDelta < 0) + constants.jitterPhaseCount--; + } + + // Convert delta time to seconds and clamp to [0, 1] + constants.deltaTime = Mathf.Clamp01(dispatchParams.FrameTimeDelta); + + if (resetAccumulation) + constants.frameIndex = 0; + else + constants.frameIndex++; + + // Shading change usage of the SPD mip levels + constants.lumaMipLevelToUse = Fsr2Pass.ShadingChangeMipLevel; + + float mipDiv = 2 << constants.lumaMipLevelToUse; + constants.lumaMipDimensions.x = (int)(constants.maxRenderSize.x / mipDiv); + constants.lumaMipDimensions.y = (int)(constants.maxRenderSize.y / mipDiv); + } + + private Vector4 SetupDeviceDepthToViewSpaceDepthParams(Fsr2.DispatchDescription dispatchParams) + { + bool inverted = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDepthInverted) != 0; + bool infinite = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDepthInfinite) != 0; + + // make sure it has no impact if near and far plane values are swapped in dispatch params + // the flags "inverted" and "infinite" will decide what transform to use + float min = Mathf.Min(dispatchParams.CameraNear, dispatchParams.CameraFar); + float max = Mathf.Max(dispatchParams.CameraNear, dispatchParams.CameraFar); + + if (inverted) + { + (min, max) = (max, min); + } + + float q = max / (min - max); + float d = -1.0f; + + Vector4 matrixElemC = new Vector4(q, -1.0f - Mathf.Epsilon, q, 0.0f + Mathf.Epsilon); + Vector4 matrixElemE = new Vector4(q * min, -min - Mathf.Epsilon, q * min, max); + + // Revert x and y coords + float aspect = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; + float cotHalfFovY = Mathf.Cos(0.5f * dispatchParams.CameraFovAngleVertical) / Mathf.Sin(0.5f * dispatchParams.CameraFovAngleVertical); + + int matrixIndex = (inverted ? 2 : 0) + (infinite ? 1 : 0); + return new Vector4( + d * matrixElemC[matrixIndex], + matrixElemE[matrixIndex], + aspect / cotHalfFovY, + 1.0f / cotHalfFovY); + } + + private void SetupRcasConstants(Fsr2.DispatchDescription dispatchParams) + { + int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(dispatchParams.Sharpness) * (RcasConfigs.Length - 1)); + RcasConsts = RcasConfigs[sharpnessIndex]; + } + + private void SetupSpdConstants(Fsr2.DispatchDescription dispatchParams, out Vector2Int dispatchThreadGroupCount) + { + RectInt rectInfo = new RectInt(0, 0, dispatchParams.RenderSize.x, dispatchParams.RenderSize.y); + SpdSetup(rectInfo, out dispatchThreadGroupCount, out var workGroupOffset, out var numWorkGroupsAndMips); + + // Downsample + ref Fsr2.SpdConstants spdConstants = ref SpdConsts; + spdConstants.numWorkGroups = (uint)numWorkGroupsAndMips.x; + spdConstants.mips = (uint)numWorkGroupsAndMips.y; + spdConstants.workGroupOffsetX = (uint)workGroupOffset.x; + spdConstants.workGroupOffsetY = (uint)workGroupOffset.y; + spdConstants.renderSizeX = (uint)dispatchParams.RenderSize.x; + spdConstants.renderSizeY = (uint)dispatchParams.RenderSize.y; + } + + private static void SpdSetup(RectInt rectInfo, out Vector2Int dispatchThreadGroupCount, out Vector2Int workGroupOffset, out Vector2Int numWorkGroupsAndMips, int mips = -1) + { + workGroupOffset = new Vector2Int(rectInfo.x / 64, rectInfo.y / 64); + + int endIndexX = (rectInfo.x + rectInfo.width - 1) / 64; + int endIndexY = (rectInfo.y + rectInfo.height - 1) / 64; + + dispatchThreadGroupCount = new Vector2Int(endIndexX + 1 - workGroupOffset.x, endIndexY + 1 - workGroupOffset.y); + + numWorkGroupsAndMips = new Vector2Int(dispatchThreadGroupCount.x * dispatchThreadGroupCount.y, mips); + if (mips < 0) + { + float resolution = Math.Max(rectInfo.width, rectInfo.height); + numWorkGroupsAndMips.y = Math.Min(Mathf.FloorToInt(Mathf.Log(resolution, 2.0f)), 12); + } + } + + private void DebugCheckDispatch(Fsr2.DispatchDescription dispatchParams) + { + if (!dispatchParams.Color.IsValid) + { + Debug.LogError("Color resource is null"); + } + + if (!dispatchParams.Depth.IsValid) + { + Debug.LogError("Depth resource is null"); + } + + if (!dispatchParams.MotionVectors.IsValid) + { + Debug.LogError("MotionVectors resource is null"); + } + + if (!dispatchParams.Output.IsValid) + { + Debug.LogError("Output resource is null"); + } + + if (dispatchParams.Exposure.IsValid && (_contextDescription.Flags & Fsr2.InitializationFlags.EnableAutoExposure) != 0) + { + Debug.LogWarning("Exposure resource provided, however auto exposure flag is present"); + } + + if (Mathf.Abs(dispatchParams.JitterOffset.x) > 1.0f || Mathf.Abs(dispatchParams.JitterOffset.y) > 1.0f) + { + Debug.LogWarning("JitterOffset contains value outside of expected range [-1.0, 1.0]"); + } + + if (dispatchParams.MotionVectorScale.x > _contextDescription.MaxRenderSize.x || dispatchParams.MotionVectorScale.y > _contextDescription.MaxRenderSize.y) + { + Debug.LogWarning("MotionVectorScale contains scale value greater than MaxRenderSize"); + } + + if (dispatchParams.MotionVectorScale.x == 0.0f || dispatchParams.MotionVectorScale.y == 0.0f) + { + Debug.LogWarning("MotionVectorScale contains zero scale value"); + } + + if (dispatchParams.RenderSize.x > _contextDescription.MaxRenderSize.x || dispatchParams.RenderSize.y > _contextDescription.MaxRenderSize.y) + { + Debug.LogWarning("RenderSize is greater than context MaxRenderSize"); + } + + if (dispatchParams.RenderSize.x == 0 || dispatchParams.RenderSize.y == 0) + { + Debug.LogWarning("RenderSize contains zero dimension"); + } + + if (dispatchParams.FrameTimeDelta > 1.0f) + { + Debug.LogWarning("FrameTimeDelta is greater than 1.0f - this value should be seconds (~0.0166 for 60fps)"); + } + + if (dispatchParams.PreExposure == 0.0f) + { + Debug.LogError("PreExposure provided as 0.0f which is invalid"); + } + + bool infiniteDepth = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDepthInfinite) != 0; + bool inverseDepth = (_contextDescription.Flags & Fsr2.InitializationFlags.EnableDepthInverted) != 0; + + if (inverseDepth) + { + if (dispatchParams.CameraNear < dispatchParams.CameraFar) + { + Debug.LogWarning("EnableDepthInverted flag is present yet CameraNear is less than CameraFar"); + } + + if (infiniteDepth) + { + if (dispatchParams.CameraNear < float.MaxValue) + { + Debug.LogWarning("EnableDepthInfinite and EnableDepthInverted present, yet CameraNear != float.MaxValue"); + } + } + + if (dispatchParams.CameraFar < 0.075f) + { + Debug.LogWarning("EnableDepthInverted present, CameraFar value is very low which may result in depth separation artefacting"); + } + } + else + { + if (dispatchParams.CameraNear > dispatchParams.CameraFar) + { + Debug.LogWarning("CameraNear is greater than CameraFar in non-inverted-depth context"); + } + + if (infiniteDepth) + { + if (dispatchParams.CameraFar < float.MaxValue) + { + Debug.LogWarning("EnableDepthInfinite present, yet CameraFar != float.MaxValue"); + } + } + + if (dispatchParams.CameraNear < 0.075f) + { + Debug.LogWarning("CameraNear value is very low which may result in depth separation artefacting"); + } + } + + if (dispatchParams.CameraFovAngleVertical <= 0.0f) + { + Debug.LogError("CameraFovAngleVertical is 0.0f - this value should be > 0.0f"); + } + + if (dispatchParams.CameraFovAngleVertical > Mathf.PI) + { + Debug.LogError("CameraFovAngleVertical is greater than 180 degrees/PI"); + } + } + + /// + /// The FSR2 C++ codebase uses floats bitwise converted to ints to pass sharpness parameters to the RCAS shader. + /// This is not possible in C# without enabling unsafe code compilation, so to avoid that we instead use a table of precomputed values. + /// + private static readonly Fsr2.RcasConstants[] RcasConfigs = new [] + { + new Fsr2.RcasConstants(1048576000u, 872428544u), + new Fsr2.RcasConstants(1049178080u, 877212745u), + new Fsr2.RcasConstants(1049823372u, 882390168u), + new Fsr2.RcasConstants(1050514979u, 887895276u), + new Fsr2.RcasConstants(1051256227u, 893859143u), + new Fsr2.RcasConstants(1052050675u, 900216232u), + new Fsr2.RcasConstants(1052902144u, 907032080u), + new Fsr2.RcasConstants(1053814727u, 914306687u), + new Fsr2.RcasConstants(1054792807u, 922105590u), + new Fsr2.RcasConstants(1055841087u, 930494326u), + new Fsr2.RcasConstants(1056964608u, 939538432u), + new Fsr2.RcasConstants(1057566688u, 944322633u), + new Fsr2.RcasConstants(1058211980u, 949500056u), + new Fsr2.RcasConstants(1058903587u, 955005164u), + new Fsr2.RcasConstants(1059644835u, 960969031u), + new Fsr2.RcasConstants(1060439283u, 967326120u), + new Fsr2.RcasConstants(1061290752u, 974141968u), + new Fsr2.RcasConstants(1062203335u, 981416575u), + new Fsr2.RcasConstants(1063181415u, 989215478u), + new Fsr2.RcasConstants(1064229695u, 997604214u), + new Fsr2.RcasConstants(1065353216u, 1006648320), + }; + + private static ComputeBuffer CreateConstantBuffer() where TConstants: struct + { + return new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); + } + + private static void DestroyConstantBuffer(ref ComputeBuffer bufferRef) + { + if (bufferRef == null) + return; + + bufferRef.Release(); + bufferRef = null; + } + + private static void DestroyPass(ref Fsr2Pass pass) + { + if (pass == null) + return; + + pass.Dispose(); + pass = null; + } + } +} diff --git a/Assets/Scripts/Core/Fsr3UpscalerContext.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3UpscalerContext.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Context.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs new file mode 100644 index 0000000..bda5092 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs @@ -0,0 +1,380 @@ +// 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 System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Profiling; +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: 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; + + protected CustomSampler Sampler; + + protected Fsr2Pass(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) + { + ContextDescription = contextDescription; + Resources = resources; + Constants = constants; + } + + public virtual void Dispose() + { + } + + public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + commandBuffer.BeginSample(Sampler); + DoScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchX, dispatchY); + commandBuffer.EndSample(Sampler); + } + + protected abstract void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); + + protected void InitComputeShader(string passName, ComputeShader shader) + { + InitComputeShader(passName, shader, ContextDescription.Flags); + } + + private void InitComputeShader(string passName, ComputeShader shader, Fsr2.InitializationFlags flags) + { + if (shader == null) + { + throw new MissingReferenceException($"Shader for FSR2 pass '{passName}' could not be loaded! Please ensure it is included in the project correctly."); + } + + ComputeShader = shader; + KernelIndex = ComputeShader.FindKernel("CS"); + Sampler = CustomSampler.Create(passName); + + 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 class Fsr2ComputeLuminancePyramidPass : Fsr2Pass + { + private readonly ComputeBuffer _spdConstants; + + public Fsr2ComputeLuminancePyramidPass(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 DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.Color; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavExposureMipLumaChange, Resources.SceneLuminance, ShadingChangeMipLevel); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavExposureMip5, Resources.SceneLuminance, 5); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoExposure, Resources.AutoExposure); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbSpd, _spdConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr2ReconstructPreviousDepthPass : Fsr2Pass + { + public Fsr2ReconstructPreviousDepthPass(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Reconstruct & Dilate", contextDescription.Shaders.reconstructPreviousDepthPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.Color; + ref var depth = ref dispatchParams.Depth; + ref var motionVectors = ref dispatchParams.MotionVectors; + ref var exposure = ref dispatchParams.Exposure; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr2DepthClipPass : Fsr2Pass + { + public Fsr2DepthClipPass(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Depth Clip", contextDescription.Shaders.depthClipPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.Color; + ref var depth = ref dispatchParams.Depth; + ref var motionVectors = ref dispatchParams.MotionVectors; + ref var exposure = ref dispatchParams.Exposure; + ref var reactive = ref dispatchParams.Reactive; + ref var tac = ref dispatchParams.TransparencyAndComposition; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReconstructedPrevNearestDepth, Fsr2ShaderIDs.UavReconstructedPrevNearestDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedDepth, Fsr2ShaderIDs.UavDilatedDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex ^ 1]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr2LockPass : Fsr2Pass + { + public Fsr2LockPass(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Create Locks", contextDescription.Shaders.lockPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLockInputLuma, Fsr2ShaderIDs.UavLockInputLuma); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal 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(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 DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { +#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 ((ContextDescription.Flags & Fsr2.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) + { + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); + } + else + { + ref var motionVectors = ref dispatchParams.MotionVectors; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + } + + ref var exposure = ref dispatchParams.Exposure; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvDilatedReactiveMasks, Fsr2ShaderIDs.UavDilatedReactiveMasks); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInternalUpscaled, Resources.InternalUpscaled[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvLockStatus, Resources.LockStatus[frameIndex ^ 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[frameIndex ^ 1]); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavInternalUpscaled, Resources.InternalUpscaled[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavLockStatus, Resources.LockStatus[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavLumaHistory, Resources.LumaHistory[frameIndex]); + + ref var output = ref dispatchParams.Output; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr2SharpenPass : Fsr2Pass + { + private readonly ComputeBuffer _rcasConstants; + + public Fsr2SharpenPass(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer rcasConstants) + : base(contextDescription, resources, constants) + { + _rcasConstants = rcasConstants; + + InitComputeShader("RCAS Sharpening", contextDescription.Shaders.sharpenPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var exposure = ref dispatchParams.Exposure; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvRcasInput, Resources.InternalUpscaled[frameIndex]); + + ref var output = ref dispatchParams.Output; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbRcas, _rcasConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr2GenerateReactivePass : Fsr2Pass + { + private readonly ComputeBuffer _generateReactiveConstants; + + public Fsr2GenerateReactivePass(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer generateReactiveConstants) + : base(contextDescription, resources, null) + { + _generateReactiveConstants = generateReactiveConstants; + + InitComputeShader("Auto-Generate Reactive Mask", contextDescription.Shaders.autoGenReactivePass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + } + + public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.GenerateReactiveDescription dispatchParams, int dispatchX, int dispatchY) + { + commandBuffer.BeginSample(Sampler); + + ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; + ref var color = ref dispatchParams.ColorPreUpscale; + ref var reactive = ref dispatchParams.OutReactive; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoReactive, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbGenReactive, _generateReactiveConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + + commandBuffer.EndSample(Sampler); + } + } + + internal class Fsr2TcrAutogeneratePass : Fsr2Pass + { + private readonly ComputeBuffer _tcrAutogenerateConstants; + + public Fsr2TcrAutogeneratePass(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 DoScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.Color; + ref var motionVectors = ref dispatchParams.MotionVectors; + ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; + ref var reactive = ref dispatchParams.Reactive; + ref var tac = ref dispatchParams.TransparencyAndComposition; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoReactive, Resources.AutoReactive); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavAutoComposition, Resources.AutoComposition); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.UavPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbFsr2, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr2ShaderIDs.CbGenReactive, _tcrAutogenerateConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } +} diff --git a/Assets/Scripts/Core/Fsr3UpscalerPass.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3UpscalerPass.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Pass.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs new file mode 100644 index 0000000..4c1c50e --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs @@ -0,0 +1,258 @@ +// 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 System; +using UnityEngine; +using UnityEngine.Experimental.Rendering; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR2 +{ + /// + /// Helper class for bundling and managing persistent resources required by the FSR2 process. + /// This includes lookup tables, default fallback resources and double-buffered resources that get swapped between frames. + /// + internal class Fsr2Resources + { + public Texture2D DefaultExposure; + public Texture2D DefaultReactive; + public Texture2D LanczosLut; + public Texture2D MaximumBiasLut; + public RenderTexture SpdAtomicCounter; + public RenderTexture AutoExposure; + public RenderTexture SceneLuminance; + public RenderTexture AutoReactive; + public RenderTexture AutoComposition; + public readonly RenderTexture[] DilatedMotionVectors = new RenderTexture[2]; + public readonly RenderTexture[] LockStatus = new RenderTexture[2]; + public readonly RenderTexture[] InternalUpscaled = new RenderTexture[2]; + public readonly RenderTexture[] LumaHistory = new RenderTexture[2]; + public readonly RenderTexture[] PrevPreAlpha = new RenderTexture[2]; + public readonly RenderTexture[] PrevPostAlpha = new RenderTexture[2]; + + public void Create(Fsr2.ContextDescription contextDescription) + { + // Generate the data for the LUT + const int lanczos2LutWidth = 128; + float[] lanczos2Weights = new float[lanczos2LutWidth]; + for (int currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex) + { + float x = 2.0f * currentLanczosWidthIndex / (lanczos2LutWidth - 1); + float y = Fsr2.Lanczos2(x); + lanczos2Weights[currentLanczosWidthIndex] = y; + } + + float[] maximumBias = new float[MaximumBiasTextureWidth * MaximumBiasTextureHeight]; + for (int i = 0; i < maximumBias.Length; ++i) + { + maximumBias[i] = MaximumBias[i] / 2.0f; + } + + // Resource FSR2_LanczosLutData: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE + // R16_SNorm textures are not supported by Unity on most platforms, strangely enough. So instead we use R32_SFloat and upload pre-normalized float data. + LanczosLut = new Texture2D(lanczos2LutWidth, 1, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR2_LanczosLutData" }; + LanczosLut.SetPixelData(lanczos2Weights, 0); + LanczosLut.Apply(); + + // Resource FSR2_MaximumUpsampleBias: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE + MaximumBiasLut = new Texture2D(MaximumBiasTextureWidth, MaximumBiasTextureHeight, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR2_MaximumUpsampleBias" }; + MaximumBiasLut.SetPixelData(maximumBias, 0); + MaximumBiasLut.Apply(); + + // Resource FSR2_DefaultExposure: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE + DefaultExposure = new Texture2D(1, 1, GraphicsFormat.R32G32_SFloat, TextureCreationFlags.None) { name = "FSR2_DefaultExposure" }; + DefaultExposure.SetPixel(0, 0, Color.clear); + DefaultExposure.Apply(); + + // Resource FSR2_DefaultReactivityMask: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + DefaultReactive = new Texture2D(1, 1, GraphicsFormat.R8_UNorm, TextureCreationFlags.None) { name = "FSR2_DefaultReactivityMask" }; + DefaultReactive.SetPixel(0, 0, Color.clear); + DefaultReactive.Apply(); + + // Resource FSR2_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE + // Despite what the original FSR2 codebase says, this resource really isn't aliasable. Resetting this counter to 0 every frame breaks auto-exposure on MacOS Metal. + SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR2_SpdAtomicCounter", enableRandomWrite = true }; + SpdAtomicCounter.Create(); + + // Resource FSR2_AutoExposure: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE + AutoExposure = new RenderTexture(1, 1, 0, GraphicsFormat.R32G32_SFloat) { name = "FSR2_AutoExposure", enableRandomWrite = true }; + AutoExposure.Create(); + + // Resource FSR2_ExposureMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE + // This is a rather special case: it's an aliasable resource, but because we require a mipmap chain and bind specific mip levels per shader, we can't easily use temporary RTs for this. + int w = contextDescription.MaxRenderSize.x / 2, h = contextDescription.MaxRenderSize.y / 2; + int mipCount = 1 + Mathf.FloorToInt(Mathf.Log(Math.Max(w, h), 2.0f)); + SceneLuminance = new RenderTexture(w, h, 0, GraphicsFormat.R16_SFloat, mipCount) { name = "FSR2_ExposureMips", enableRandomWrite = true, useMipMap = true, autoGenerateMips = false }; + SceneLuminance.Create(); + + // Resources FSR2_InternalDilatedVelocity1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(DilatedMotionVectors, "FSR2_InternalDilatedVelocity", contextDescription.MaxRenderSize, GraphicsFormat.R16G16_SFloat); + + // Resources FSR2_LockStatus1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(LockStatus, "FSR2_LockStatus", contextDescription.DisplaySize, GraphicsFormat.R16G16_SFloat); + + // Resources FSR2_InternalUpscaled1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(InternalUpscaled, "FSR2_InternalUpscaled", contextDescription.DisplaySize, GraphicsFormat.R16G16B16A16_SFloat); + + // Resources FSR2_LumaHistory1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8B8A8_UNORM, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(LumaHistory, "FSR2_LumaHistory", contextDescription.DisplaySize, GraphicsFormat.R8G8B8A8_UNorm); + } + + public void CreateTcrAutogenResources(Fsr2.ContextDescription contextDescription) + { + // Resource FSR2_AutoReactive: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + AutoReactive = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR2_AutoReactive", enableRandomWrite = true }; + AutoReactive.Create(); + + // Resource FSR2_AutoComposition: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + AutoComposition = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR2_AutoComposition", enableRandomWrite = true }; + AutoComposition.Create(); + + // Resources FSR2_PrevPreAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(PrevPreAlpha, "FSR2_PrevPreAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); + + // Resources FSR2_PrevPostAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(PrevPostAlpha, "FSR2_PrevPostAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); + } + + // 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 + public static void CreateAliasableResources(CommandBuffer commandBuffer, Fsr2.ContextDescription contextDescription, Fsr2.DispatchDescription dispatchParams) + { + Vector2Int displaySize = contextDescription.DisplaySize; + Vector2Int maxRenderSize = contextDescription.MaxRenderSize; + + // FSR2_ReconstructedPrevNearestDepth: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(Fsr2ShaderIDs.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(Fsr2ShaderIDs.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(Fsr2ShaderIDs.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(Fsr2ShaderIDs.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(Fsr2ShaderIDs.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(Fsr2ShaderIDs.UavNewLocks, displaySize.x, displaySize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); + } + + public static void DestroyAliasableResources(CommandBuffer commandBuffer) + { + // Release all of the aliasable resources used this frame + commandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavReconstructedPrevNearestDepth); + commandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavDilatedDepth); + commandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavLockInputLuma); + commandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavDilatedReactiveMasks); + commandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavPreparedInputColor); + commandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavNewLocks); + } + + private static void CreateDoubleBufferedResource(RenderTexture[] resource, string name, Vector2Int size, GraphicsFormat format) + { + for (int i = 0; i < 2; ++i) + { + resource[i] = new RenderTexture(size.x, size.y, 0, format) { name = name + (i + 1), enableRandomWrite = true }; + resource[i].Create(); + } + } + + public void Destroy() + { + DestroyTcrAutogenResources(); + + DestroyResource(LumaHistory); + DestroyResource(InternalUpscaled); + DestroyResource(LockStatus); + DestroyResource(DilatedMotionVectors); + DestroyResource(ref SceneLuminance); + DestroyResource(ref AutoExposure); + DestroyResource(ref DefaultReactive); + DestroyResource(ref DefaultExposure); + DestroyResource(ref MaximumBiasLut); + DestroyResource(ref LanczosLut); + } + + public void DestroyTcrAutogenResources() + { + DestroyResource(PrevPostAlpha); + DestroyResource(PrevPreAlpha); + DestroyResource(ref AutoComposition); + DestroyResource(ref AutoReactive); + } + + private static void DestroyResource(ref Texture2D resource) + { + if (resource == null) + return; + +#if UNITY_EDITOR + if (Application.isPlaying && !UnityEditor.EditorApplication.isPaused) + UnityEngine.Object.Destroy(resource); + else + UnityEngine.Object.DestroyImmediate(resource); +#else + UnityEngine.Object.Destroy(resource); +#endif + resource = null; + } + + private static void DestroyResource(ref RenderTexture resource) + { + if (resource == null) + return; + + resource.Release(); + resource = null; + } + + private static void DestroyResource(RenderTexture[] resource) + { + for (int i = 0; i < resource.Length; ++i) + DestroyResource(ref resource[i]); + } + + private const int MaximumBiasTextureWidth = 16; + private const int MaximumBiasTextureHeight = 16; + private static readonly float[] MaximumBias = + { + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.876f, 1.809f, 1.772f, 1.753f, 1.748f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.869f, 1.801f, 1.764f, 1.745f, 1.739f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.976f, 1.841f, 1.774f, 1.737f, 1.716f, 1.71f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.914f, 1.784f, 1.716f, 1.673f, 1.649f, 1.641f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.793f, 1.676f, 1.604f, 1.562f, 1.54f, 1.533f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.619f, 1.536f, 1.492f, 1.467f, 1.454f, 1.449f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.575f, 1.496f, 1.456f, 1.432f, 1.416f, 1.408f, 1.405f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.555f, 1.479f, 1.438f, 1.413f, 1.398f, 1.387f, 1.381f, 1.379f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.555f, 1.474f, 1.43f, 1.404f, 1.387f, 1.376f, 1.368f, 1.363f, 1.362f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.575f, 1.479f, 1.43f, 1.401f, 1.382f, 1.369f, 1.36f, 1.354f, 1.351f, 1.35f, + 2.0f, 2.0f, 1.976f, 1.914f, 1.793f, 1.619f, 1.496f, 1.438f, 1.404f, 1.382f, 1.367f, 1.357f, 1.349f, 1.344f, 1.341f, 1.34f, + 1.876f, 1.869f, 1.841f, 1.784f, 1.676f, 1.536f, 1.456f, 1.413f, 1.387f, 1.369f, 1.357f, 1.347f, 1.341f, 1.336f, 1.333f, 1.332f, + 1.809f, 1.801f, 1.774f, 1.716f, 1.604f, 1.492f, 1.432f, 1.398f, 1.376f, 1.36f, 1.349f, 1.341f, 1.335f, 1.33f, 1.328f, 1.327f, + 1.772f, 1.764f, 1.737f, 1.673f, 1.562f, 1.467f, 1.416f, 1.387f, 1.368f, 1.354f, 1.344f, 1.336f, 1.33f, 1.326f, 1.323f, 1.323f, + 1.753f, 1.745f, 1.716f, 1.649f, 1.54f, 1.454f, 1.408f, 1.381f, 1.363f, 1.351f, 1.341f, 1.333f, 1.328f, 1.323f, 1.321f, 1.32f, + 1.748f, 1.739f, 1.71f, 1.641f, 1.533f, 1.449f, 1.405f, 1.379f, 1.362f, 1.35f, 1.34f, 1.332f, 1.327f, 1.323f, 1.32f, 1.319f, + }; + } +} diff --git a/Assets/Scripts/Core/Fsr3UpscalerResources.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3UpscalerResources.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2Resources.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs new file mode 100644 index 0000000..0adbf6b --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs @@ -0,0 +1,80 @@ +// 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; + +namespace FidelityFX.FSR2 +{ + public static class Fsr2ShaderIDs + { + // Shader resource views, i.e. read-only bindings + public static readonly int SrvInputColor = Shader.PropertyToID("r_input_color_jittered"); + public static readonly int SrvOpaqueOnly = Shader.PropertyToID("r_input_opaque_only"); + public static readonly int SrvInputMotionVectors = Shader.PropertyToID("r_input_motion_vectors"); + public static readonly int SrvInputDepth = Shader.PropertyToID("r_input_depth"); + public static readonly int SrvInputExposure = Shader.PropertyToID("r_input_exposure"); + public static readonly int SrvAutoExposure = Shader.PropertyToID("r_auto_exposure"); + public static readonly int SrvReactiveMask = Shader.PropertyToID("r_reactive_mask"); + public static readonly int SrvTransparencyAndCompositionMask = Shader.PropertyToID("r_transparency_and_composition_mask"); + public static readonly int SrvReconstructedPrevNearestDepth = Shader.PropertyToID("r_reconstructed_previous_nearest_depth"); + public static readonly int SrvDilatedMotionVectors = Shader.PropertyToID("r_dilated_motion_vectors"); + public static readonly int SrvPrevDilatedMotionVectors = Shader.PropertyToID("r_previous_dilated_motion_vectors"); + public static readonly int SrvDilatedDepth = Shader.PropertyToID("r_dilatedDepth"); + public static readonly int SrvInternalUpscaled = Shader.PropertyToID("r_internal_upscaled_color"); + public static readonly int SrvLockStatus = Shader.PropertyToID("r_lock_status"); + public static readonly int SrvLockInputLuma = Shader.PropertyToID("r_lock_input_luma"); + public static readonly int SrvPreparedInputColor = Shader.PropertyToID("r_prepared_input_color"); + public static readonly int SrvLumaHistory = Shader.PropertyToID("r_luma_history"); + public static readonly int SrvRcasInput = Shader.PropertyToID("r_rcas_input"); + public static readonly int SrvLanczosLut = Shader.PropertyToID("r_lanczos_lut"); + public static readonly int SrvSceneLuminanceMips = Shader.PropertyToID("r_imgMips"); + public static readonly int SrvUpscaleMaximumBiasLut = Shader.PropertyToID("r_upsample_maximum_bias_lut"); + public static readonly int SrvDilatedReactiveMasks = Shader.PropertyToID("r_dilated_reactive_masks"); + public static readonly int SrvPrevColorPreAlpha = Shader.PropertyToID("r_input_prev_color_pre_alpha"); + public static readonly int SrvPrevColorPostAlpha = Shader.PropertyToID("r_input_prev_color_post_alpha"); + + // Unordered access views, i.e. random read/write bindings + public static readonly int UavReconstructedPrevNearestDepth = Shader.PropertyToID("rw_reconstructed_previous_nearest_depth"); + public static readonly int UavDilatedMotionVectors = Shader.PropertyToID("rw_dilated_motion_vectors"); + public static readonly int UavDilatedDepth = Shader.PropertyToID("rw_dilatedDepth"); + public static readonly int UavInternalUpscaled = Shader.PropertyToID("rw_internal_upscaled_color"); + public static readonly int UavLockStatus = Shader.PropertyToID("rw_lock_status"); + public static readonly int UavLockInputLuma = Shader.PropertyToID("rw_lock_input_luma"); + public static readonly int UavNewLocks = Shader.PropertyToID("rw_new_locks"); + public static readonly int UavPreparedInputColor = Shader.PropertyToID("rw_prepared_input_color"); + public static readonly int UavLumaHistory = Shader.PropertyToID("rw_luma_history"); + public static readonly int UavUpscaledOutput = Shader.PropertyToID("rw_upscaled_output"); + public static readonly int UavExposureMipLumaChange = Shader.PropertyToID("rw_img_mip_shading_change"); + public static readonly int UavExposureMip5 = Shader.PropertyToID("rw_img_mip_5"); + public static readonly int UavDilatedReactiveMasks = Shader.PropertyToID("rw_dilated_reactive_masks"); + public static readonly int UavAutoExposure = Shader.PropertyToID("rw_auto_exposure"); + public static readonly int UavSpdAtomicCount = Shader.PropertyToID("rw_spd_global_atomic"); + public static readonly int UavAutoReactive = Shader.PropertyToID("rw_output_autoreactive"); + public static readonly int UavAutoComposition = Shader.PropertyToID("rw_output_autocomposition"); + public static readonly int UavPrevColorPreAlpha = Shader.PropertyToID("rw_output_prev_color_pre_alpha"); + public static readonly int UavPrevColorPostAlpha = Shader.PropertyToID("rw_output_prev_color_post_alpha"); + + // Constant buffer bindings + public static readonly int CbFsr2 = Shader.PropertyToID("cbFSR2"); + public static readonly int CbSpd = Shader.PropertyToID("cbSPD"); + public static readonly int CbRcas = Shader.PropertyToID("cbRCAS"); + public static readonly int CbGenReactive = Shader.PropertyToID("cbGenerateReactive"); + } +} diff --git a/Assets/Scripts/Core/Fsr3ShaderIDs.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs.meta similarity index 100% rename from Assets/Scripts/Core/Fsr3ShaderIDs.cs.meta rename to Packages/fidelityfx.fsr/Runtime/FSR2/Fsr2ShaderIDs.cs.meta diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3.meta b/Packages/fidelityfx.fsr/Runtime/FSR3.meta new file mode 100644 index 0000000..5399ea1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 16d16ad51c3bc2a429d3da6788f65e07 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs new file mode 100644 index 0000000..6e543fe --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs @@ -0,0 +1,91 @@ +// 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; + +namespace FidelityFX.FSR3 +{ + public static class Fsr3ShaderIDs + { + // Shader resource views, i.e. read-only bindings + public static readonly int SrvInputColor = Shader.PropertyToID("r_input_color_jittered"); + public static readonly int SrvOpaqueOnly = Shader.PropertyToID("r_input_opaque_only"); + public static readonly int SrvInputMotionVectors = Shader.PropertyToID("r_input_motion_vectors"); + public static readonly int SrvInputDepth = Shader.PropertyToID("r_input_depth"); + public static readonly int SrvInputExposure = Shader.PropertyToID("r_input_exposure"); + public static readonly int SrvFrameInfo = Shader.PropertyToID("r_frame_info"); + public static readonly int SrvReactiveMask = Shader.PropertyToID("r_reactive_mask"); + public static readonly int SrvTransparencyAndCompositionMask = Shader.PropertyToID("r_transparency_and_composition_mask"); + public static readonly int SrvReconstructedPrevNearestDepth = Shader.PropertyToID("r_reconstructed_previous_nearest_depth"); + public static readonly int SrvDilatedMotionVectors = Shader.PropertyToID("r_dilated_motion_vectors"); + public static readonly int SrvDilatedDepth = Shader.PropertyToID("r_dilated_depth"); + public static readonly int SrvInternalUpscaled = Shader.PropertyToID("r_internal_upscaled_color"); + public static readonly int SrvAccumulation = Shader.PropertyToID("r_accumulation"); + public static readonly int SrvLumaHistory = Shader.PropertyToID("r_luma_history"); + public static readonly int SrvRcasInput = Shader.PropertyToID("r_rcas_input"); + public static readonly int SrvLanczosLut = Shader.PropertyToID("r_lanczos_lut"); + public static readonly int SrvSpdMips = Shader.PropertyToID("r_spd_mips"); + public static readonly int SrvDilatedReactiveMasks = Shader.PropertyToID("r_dilated_reactive_masks"); + public static readonly int SrvNewLocks = Shader.PropertyToID("r_new_locks"); + public static readonly int SrvFarthestDepth = Shader.PropertyToID("r_farthest_depth"); + public static readonly int SrvFarthestDepthMip1 = Shader.PropertyToID("r_farthest_depth_mip1"); + public static readonly int SrvShadingChange = Shader.PropertyToID("r_shading_change"); + public static readonly int SrvCurrentLuma = Shader.PropertyToID("r_current_luma"); + public static readonly int SrvPreviousLuma = Shader.PropertyToID("r_previous_luma"); + public static readonly int SrvLumaInstability = Shader.PropertyToID("r_luma_instability"); + public static readonly int SrvPrevColorPreAlpha = Shader.PropertyToID("r_input_prev_color_pre_alpha"); + public static readonly int SrvPrevColorPostAlpha = Shader.PropertyToID("r_input_prev_color_post_alpha"); + + // Unordered access views, i.e. random read/write bindings + public static readonly int UavReconstructedPrevNearestDepth = Shader.PropertyToID("rw_reconstructed_previous_nearest_depth"); + public static readonly int UavDilatedMotionVectors = Shader.PropertyToID("rw_dilated_motion_vectors"); + public static readonly int UavDilatedDepth = Shader.PropertyToID("rw_dilated_depth"); + public static readonly int UavInternalUpscaled = Shader.PropertyToID("rw_internal_upscaled_color"); + public static readonly int UavAccumulation = Shader.PropertyToID("rw_accumulation"); + public static readonly int UavLumaHistory = Shader.PropertyToID("rw_luma_history"); + public static readonly int UavUpscaledOutput = Shader.PropertyToID("rw_upscaled_output"); + public static readonly int UavDilatedReactiveMasks = Shader.PropertyToID("rw_dilated_reactive_masks"); + public static readonly int UavFrameInfo = Shader.PropertyToID("rw_frame_info"); + public static readonly int UavSpdAtomicCount = Shader.PropertyToID("rw_spd_global_atomic"); + public static readonly int UavNewLocks = Shader.PropertyToID("rw_new_locks"); + public static readonly int UavAutoReactive = Shader.PropertyToID("rw_output_autoreactive"); + public static readonly int UavShadingChange = Shader.PropertyToID("rw_shading_change"); + public static readonly int UavFarthestDepth = Shader.PropertyToID("rw_farthest_depth"); + public static readonly int UavFarthestDepthMip1 = Shader.PropertyToID("rw_farthest_depth_mip1"); + public static readonly int UavCurrentLuma = Shader.PropertyToID("rw_current_luma"); + public static readonly int UavLumaInstability = Shader.PropertyToID("rw_luma_instability"); + public static readonly int UavIntermediate = Shader.PropertyToID("rw_intermediate_fp16x1"); + public static readonly int UavSpdMip0 = Shader.PropertyToID("rw_spd_mip0"); + public static readonly int UavSpdMip1 = Shader.PropertyToID("rw_spd_mip1"); + public static readonly int UavSpdMip2 = Shader.PropertyToID("rw_spd_mip2"); + public static readonly int UavSpdMip3 = Shader.PropertyToID("rw_spd_mip3"); + public static readonly int UavSpdMip4 = Shader.PropertyToID("rw_spd_mip4"); + public static readonly int UavSpdMip5 = Shader.PropertyToID("rw_spd_mip5"); + public static readonly int UavAutoComposition = Shader.PropertyToID("rw_output_autocomposition"); + public static readonly int UavPrevColorPreAlpha = Shader.PropertyToID("rw_output_prev_color_pre_alpha"); + public static readonly int UavPrevColorPostAlpha = Shader.PropertyToID("rw_output_prev_color_post_alpha"); + + // Constant buffer bindings + public static readonly int CbFsr3Upscaler = Shader.PropertyToID("cbFSR3Upscaler"); + public static readonly int CbSpd = Shader.PropertyToID("cbSPD"); + public static readonly int CbRcas = Shader.PropertyToID("cbRCAS"); + public static readonly int CbGenReactive = Shader.PropertyToID("cbGenerateReactive"); + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs.meta new file mode 100644 index 0000000..734aedb --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3ShaderIDs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a1e3e7c189594b94897510b4a20b8a00 +timeCreated: 1679060863 \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs new file mode 100644 index 0000000..d69aa29 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs @@ -0,0 +1,313 @@ +// 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 System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR3 +{ + /// + /// A collection of helper functions and data structures required by the FSR3 Upscaler process. + /// + public static class Fsr3Upscaler + { + /// + /// Creates a new FSR3 Upscaler context with standard parameters that are appropriate for the current platform. + /// + public static Fsr3UpscalerContext CreateContext(Vector2Int displaySize, Vector2Int maxRenderSize, Fsr3UpscalerShaders shaders, InitializationFlags flags = 0) + { + if (SystemInfo.usesReversedZBuffer) + flags |= InitializationFlags.EnableDepthInverted; + else + flags &= ~InitializationFlags.EnableDepthInverted; + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + flags |= InitializationFlags.EnableDebugChecking; +#endif + + Debug.Log($"Setting up FSR3 Upscaler with render size: {maxRenderSize.x}x{maxRenderSize.y}, display size: {displaySize.x}x{displaySize.y}, flags: {flags}"); + + var contextDescription = new ContextDescription + { + Flags = flags, + MaxUpscaleSize = displaySize, + MaxRenderSize = maxRenderSize, + Shaders = shaders, + }; + + var context = new Fsr3UpscalerContext(); + context.Create(contextDescription); + return context; + } + + public static float GetUpscaleRatioFromQualityMode(QualityMode qualityMode) + { + switch (qualityMode) + { + case QualityMode.NativeAA: + return 1.0f; + case QualityMode.UltraQuality: + return 1.2f; + case QualityMode.Quality: + return 1.5f; + case QualityMode.Balanced: + return 1.7f; + case QualityMode.Performance: + return 2.0f; + case QualityMode.UltraPerformance: + return 3.0f; + default: + return 1.0f; + } + } + + public static void GetRenderResolutionFromQualityMode( + out int renderWidth, out int renderHeight, + int displayWidth, int displayHeight, QualityMode qualityMode) + { + float ratio = GetUpscaleRatioFromQualityMode(qualityMode); + renderWidth = Mathf.RoundToInt(displayWidth / ratio); + renderHeight = Mathf.RoundToInt(displayHeight / ratio); + } + + public static float GetMipmapBiasOffset(int renderWidth, int displayWidth) + { + return Mathf.Log((float)renderWidth / displayWidth, 2.0f) - 1.0f; + } + + public static int GetJitterPhaseCount(int renderWidth, int displayWidth) + { + const float basePhaseCount = 8.0f; + int jitterPhaseCount = (int)(basePhaseCount * Mathf.Pow((float)displayWidth / renderWidth, 2.0f)); + return jitterPhaseCount; + } + + public static void GetJitterOffset(out float outX, out float outY, int index, int phaseCount) + { + outX = Halton((index % phaseCount) + 1, 2) - 0.5f; + outY = Halton((index % phaseCount) + 1, 3) - 0.5f; + } + + // Calculate halton number for index and base. + private static float Halton(int index, int @base) + { + float f = 1.0f, result = 0.0f; + + for (int currentIndex = index; currentIndex > 0;) { + + f /= @base; + result += f * (currentIndex % @base); + currentIndex = (int)Mathf.Floor((float)currentIndex / @base); + } + + return result; + } + + public static float Lanczos2(float value) + { + return Mathf.Abs(value) < Mathf.Epsilon ? 1.0f : Mathf.Sin(Mathf.PI * value) / (Mathf.PI * value) * (Mathf.Sin(0.5f * Mathf.PI * value) / (0.5f * Mathf.PI * value)); + } + +#if !UNITY_2021_1_OR_NEWER + internal static void SetBufferData(this CommandBuffer commandBuffer, ComputeBuffer computeBuffer, Array data) + { + commandBuffer.SetComputeBufferData(computeBuffer, data); + } +#endif + + public enum QualityMode + { + NativeAA = 0, + UltraQuality = 1, + Quality = 2, + Balanced = 3, + Performance = 4, + UltraPerformance = 5, + } + + [Flags] + public enum InitializationFlags + { + EnableHighDynamicRange = 1 << 0, + EnableDisplayResolutionMotionVectors = 1 << 1, + EnableMotionVectorsJitterCancellation = 1 << 2, + EnableDepthInverted = 1 << 3, + EnableDepthInfinite = 1 << 4, + EnableAutoExposure = 1 << 5, + EnableDynamicResolution = 1 << 6, + EnableFP16Usage = 1 << 7, + EnableDebugChecking = 1 << 8, + } + + [Flags] + public enum DispatchFlags + { + DrawDebugView = 1 << 0, + } + + /// + /// A structure encapsulating the parameters required to initialize FidelityFX Super Resolution 3 upscaling. + /// + public struct ContextDescription + { + public InitializationFlags Flags; + public Vector2Int MaxRenderSize; + public Vector2Int MaxUpscaleSize; + public Fsr3UpscalerShaders Shaders; + } + + /// + /// A structure encapsulating the parameters for dispatching the various passes of FidelityFX Super Resolution 3. + /// + public class DispatchDescription + { + public ResourceView Color; + public ResourceView Depth; + public ResourceView MotionVectors; + public ResourceView Exposure; // optional + public ResourceView Reactive; // optional + public ResourceView TransparencyAndComposition; // optional + public ResourceView Output; + public Vector2 JitterOffset; + public Vector2 MotionVectorScale; + public Vector2Int RenderSize; + public Vector2Int UpscaleSize; + public bool EnableSharpening; + public float Sharpness; + public float FrameTimeDelta; // in seconds + public float PreExposure; + public bool Reset; + public float CameraNear; + public float CameraFar; + public float CameraFovAngleVertical; + public float ViewSpaceToMetersFactor; + public DispatchFlags Flags; + public bool UseTextureArrays; // Enable texture array bindings, primarily used for HDRP and XR + + // EXPERIMENTAL reactive mask generation parameters + public bool EnableAutoReactive; + public ResourceView ColorOpaqueOnly; + public float AutoTcThreshold = 0.05f; + public float AutoTcScale = 1.0f; + public float AutoReactiveScale = 5.0f; + public float AutoReactiveMax = 0.9f; + } + + /// + /// A structure encapsulating the parameters for automatic generation of a reactive mask. + /// The default values for Scale, CutoffThreshold, BinaryValue and Flags were taken from the FSR3 demo project. + /// + public class GenerateReactiveDescription + { + public ResourceView ColorOpaqueOnly; + public ResourceView ColorPreUpscale; + public ResourceView OutReactive; + public Vector2Int RenderSize; + public float Scale = 0.5f; + public float CutoffThreshold = 0.2f; + public float BinaryValue = 0.9f; + public GenerateReactiveFlags Flags = GenerateReactiveFlags.ApplyTonemap | GenerateReactiveFlags.ApplyThreshold | GenerateReactiveFlags.UseComponentsMax; + } + + [Flags] + public enum GenerateReactiveFlags + { + ApplyTonemap = 1 << 0, + ApplyInverseTonemap = 1 << 1, + ApplyThreshold = 1 << 2, + UseComponentsMax = 1 << 3, + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct UpscalerConstants + { + public Vector2Int renderSize; + public Vector2Int previousFrameRenderSize; + + public Vector2Int upscaleSize; + public Vector2Int previousFrameUpscaleSize; + + public Vector2Int maxRenderSize; + public Vector2Int maxUpscaleSize; + + public Vector4 deviceToViewDepth; + + public Vector2 jitterOffset; + public Vector2 previousFrameJitterOffset; + + public Vector2 motionVectorScale; + public Vector2 downscaleFactor; + + public Vector2 motionVectorJitterCancellation; + public float tanHalfFOV; + public float jitterPhaseCount; + + public float deltaTime; + public float deltaPreExposure; + public float viewSpaceToMetersFactor; + public float frameIndex; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct SpdConstants + { + public uint mips; + public uint numWorkGroups; + public uint workGroupOffsetX, workGroupOffsetY; + public uint renderSizeX, renderSizeY; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct GenerateReactiveConstants + { + public float scale; + public float threshold; + public float binaryValue; + public uint flags; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct GenerateReactiveConstants2 + { + public float autoTcThreshold; + public float autoTcScale; + public float autoReactiveScale; + public float autoReactiveMax; + } + + [Serializable, StructLayout(LayoutKind.Sequential)] + internal struct RcasConstants + { + public RcasConstants(uint sharpness, uint halfSharp) + { + this.sharpness = sharpness; + this.halfSharp = halfSharp; + dummy0 = dummy1 = 0; + } + + public readonly uint sharpness; + public readonly uint halfSharp; + public readonly uint dummy0; + public readonly uint dummy1; + } + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs.meta new file mode 100644 index 0000000..1c62899 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3Upscaler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ee72b891c35d614eac306ca6154b66d +timeCreated: 1673441954 \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs new file mode 100644 index 0000000..84c4474 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs @@ -0,0 +1,177 @@ +// 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.Serialization; + +namespace FidelityFX.FSR3 +{ + /// + /// Scriptable object containing all shader resources required by FidelityFX Super Resolution 3 (FSR3) Upscaler. + /// These can be stored in an asset file and referenced from a scene or prefab, avoiding the need to load the shaders from a Resources folder. + /// + [CreateAssetMenu(fileName = "FSR3 Upscaler Assets", menuName = "FidelityFX/FSR3 Upscaler Assets", order = 1103)] + public class Fsr3UpscalerAssets : ScriptableObject + { + public Fsr3UpscalerShaders shaders; + +#if UNITY_EDITOR + private void Reset() + { + shaders = new Fsr3UpscalerShaders + { + prepareInputsPass = FindComputeShader("ffx_fsr3upscaler_prepare_inputs_pass"), + lumaPyramidPass = FindComputeShader("ffx_fsr3upscaler_luma_pyramid_pass"), + shadingChangePyramidPass = FindComputeShader("ffx_fsr3upscaler_shading_change_pyramid_pass"), + shadingChangePass = FindComputeShader("ffx_fsr3upscaler_shading_change_pass"), + prepareReactivityPass = FindComputeShader("ffx_fsr3upscaler_prepare_reactivity_pass"), + lumaInstabilityPass = FindComputeShader("ffx_fsr3upscaler_luma_instability_pass"), + accumulatePass = FindComputeShader("ffx_fsr3upscaler_accumulate_pass"), + sharpenPass = FindComputeShader("ffx_fsr3upscaler_rcas_pass"), + autoGenReactivePass = FindComputeShader("ffx_fsr3upscaler_autogen_reactive_pass"), + tcrAutoGenPass = FindComputeShader("ffx_fsr3upscaler_tcr_autogen_pass"), + debugViewPass = FindComputeShader("ffx_fsr3upscaler_debug_view_pass"), + }; + } + + private static ComputeShader FindComputeShader(string name) + { + string[] assetGuids = UnityEditor.AssetDatabase.FindAssets($"t:ComputeShader {name}"); + if (assetGuids == null || assetGuids.Length == 0) + return null; + + string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assetGuids[0]); + return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); + } +#endif + } + + /// + /// All the compute shaders used by the FSR3 Upscaler. + /// + [System.Serializable] + public class Fsr3UpscalerShaders + { + /// + /// The compute shader used by the prepare inputs pass. + /// + public ComputeShader prepareInputsPass; + + /// + /// The compute shader used by the luminance pyramid computation pass. + /// + public ComputeShader lumaPyramidPass; + + /// + /// The compute shader used by the shading change pyramid pass. + /// + public ComputeShader shadingChangePyramidPass; + + /// + /// The compute shader used by the shading change pass. + /// + public ComputeShader shadingChangePass; + + /// + /// The compute shader used by the prepare reactivity pass. + /// + public ComputeShader prepareReactivityPass; + + /// + /// The compute shader used by the luma instability pass. + /// + public ComputeShader lumaInstabilityPass; + + /// + /// The compute shader used by the accumulation pass. + /// + public ComputeShader accumulatePass; + + /// + /// The compute shader used by the RCAS sharpening pass. + /// + public ComputeShader sharpenPass; + + /// + /// The compute shader used to auto-generate a reactive mask. + /// + public ComputeShader autoGenReactivePass; + + /// + /// The compute shader used to auto-generate a transparency & composition mask. + /// + public ComputeShader tcrAutoGenPass; + + /// + /// The compute shader used to display a debug view. + /// + public ComputeShader debugViewPass; + + /// + /// Returns a copy of this class and its contents. + /// + public Fsr3UpscalerShaders Clone() + { + return (Fsr3UpscalerShaders)MemberwiseClone(); + } + + /// + /// Returns a copy of this class with clones of all its shaders. + /// This can be useful if you're running multiple FSR3 Upscaler instances with different shader configurations. + /// Be sure to clean up these clones through Dispose once you're done with them. + /// + public Fsr3UpscalerShaders DeepCopy() + { + return new Fsr3UpscalerShaders + { + prepareInputsPass = Object.Instantiate(prepareInputsPass), + lumaPyramidPass = Object.Instantiate(lumaPyramidPass), + shadingChangePyramidPass = Object.Instantiate(shadingChangePyramidPass), + shadingChangePass = Object.Instantiate(shadingChangePass), + prepareReactivityPass = Object.Instantiate(prepareReactivityPass), + lumaInstabilityPass = Object.Instantiate(lumaInstabilityPass), + accumulatePass = Object.Instantiate(accumulatePass), + sharpenPass = Object.Instantiate(sharpenPass), + autoGenReactivePass = Object.Instantiate(autoGenReactivePass), + tcrAutoGenPass = Object.Instantiate(tcrAutoGenPass), + debugViewPass = Object.Instantiate(debugViewPass), + }; + } + + /// + /// Destroy all the shaders within this instance. + /// Use this only on clones created through DeepCopy. + /// + public void Dispose() + { + Object.Destroy(prepareInputsPass); + Object.Destroy(lumaPyramidPass); + Object.Destroy(shadingChangePyramidPass); + Object.Destroy(shadingChangePass); + Object.Destroy(prepareReactivityPass); + Object.Destroy(lumaInstabilityPass); + Object.Destroy(accumulatePass); + Object.Destroy(sharpenPass); + Object.Destroy(autoGenReactivePass); + Object.Destroy(tcrAutoGenPass); + Object.Destroy(debugViewPass); + } + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta new file mode 100644 index 0000000..7c8565b --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2dbcc608a4754d049a14a0bcce2eb40b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs new file mode 100644 index 0000000..2cb48f2 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs @@ -0,0 +1,81 @@ +// 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; + +namespace FidelityFX.FSR3 +{ + /// + /// A collection of callbacks required by the FSR3 Upscaler process. + /// This allows some customization by the game dev on how to integrate FSR3 upscaling into their own game setup. + /// + public interface IFsr3UpscalerCallbacks + { + /// + /// Apply a mipmap bias to in-game textures to prevent them from becoming blurry as the internal rendering resolution lowers. + /// This will need to be customized on a per-game basis, as there is no clear universal way to determine what are "in-game" textures. + /// The default implementation will simply apply a mipmap bias to all 2D textures, which will include things like UI textures and which might miss things like terrain texture arrays. + /// + /// Depending on how your game organizes its assets, you will want to create a filter that more specifically selects the textures that need to have this mipmap bias applied. + /// You may also want to store the bias offset value and apply it to any assets that are loaded in on demand. + /// + void ApplyMipmapBias(float biasOffset); + + void UndoMipmapBias(); + } + + /// + /// Default implementation of IFsr3UpscalerCallbacks. + /// These are fine for testing but a proper game will want to extend and override these methods. + /// + public class Fsr3UpscalerCallbacksBase: IFsr3UpscalerCallbacks + { + protected float CurrentBiasOffset = 0; + + public virtual void ApplyMipmapBias(float biasOffset) + { + if (float.IsNaN(biasOffset) || float.IsInfinity(biasOffset)) + return; + + CurrentBiasOffset += biasOffset; + + if (Mathf.Approximately(CurrentBiasOffset, 0f)) + { + CurrentBiasOffset = 0f; + } + + foreach (var texture in Resources.FindObjectsOfTypeAll()) + { + if (texture.mipmapCount <= 1) + continue; + + texture.mipMapBias += biasOffset; + } + } + + public virtual void UndoMipmapBias() + { + if (CurrentBiasOffset == 0f) + return; + + ApplyMipmapBias(-CurrentBiasOffset); + } + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta new file mode 100644 index 0000000..d68162b --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d52448255f58a6a42bfcf1634f4dd1a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs new file mode 100644 index 0000000..2b7f2ef --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs @@ -0,0 +1,657 @@ +// 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 System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR3 +{ + /// + /// This class loosely matches the FfxFsr3UpscalerContext struct from the original FSR3 codebase. + /// It manages the various resources and compute passes required by the FSR3 Upscaler process. + /// Note that this class does not know anything about Unity render pipelines; all it knows is CommandBuffers and RenderTargetIdentifiers. + /// This should make it suitable for integration with any of the available Unity render pipelines. + /// + public class Fsr3UpscalerContext + { + private const int MaxQueuedFrames = 16; + + private Fsr3Upscaler.ContextDescription _contextDescription; + private CommandBuffer _commandBuffer; + + private Fsr3UpscalerPass _prepareInputsPass; + private Fsr3UpscalerPass _lumaPyramidPass; + private Fsr3UpscalerPass _shadingChangePyramidPass; + private Fsr3UpscalerPass _shadingChangePass; + private Fsr3UpscalerPass _prepareReactivityPass; + private Fsr3UpscalerPass _lumaInstabilityPass; + private Fsr3UpscalerPass _accumulatePass; + private Fsr3UpscalerPass _sharpenPass; + private Fsr3UpscalerPass _generateReactivePass; + private Fsr3UpscalerPass _tcrAutogeneratePass; +#if UNITY_EDITOR || DEVELOPMENT_BUILD + private Fsr3UpscalerPass _debugViewPass; +#endif + + private readonly Fsr3UpscalerResources _resources = new Fsr3UpscalerResources(); + + private ComputeBuffer _upscalerConstantsBuffer; + private readonly Fsr3Upscaler.UpscalerConstants[] _upscalerConstantsArray = { new Fsr3Upscaler.UpscalerConstants() }; + private ref Fsr3Upscaler.UpscalerConstants UpscalerConsts => ref _upscalerConstantsArray[0]; + + private ComputeBuffer _spdConstantsBuffer; + private readonly Fsr3Upscaler.SpdConstants[] _spdConstantsArray = { new Fsr3Upscaler.SpdConstants() }; + private ref Fsr3Upscaler.SpdConstants SpdConsts => ref _spdConstantsArray[0]; + + private ComputeBuffer _rcasConstantsBuffer; + private readonly Fsr3Upscaler.RcasConstants[] _rcasConstantsArray = new Fsr3Upscaler.RcasConstants[1]; + private ref Fsr3Upscaler.RcasConstants RcasConsts => ref _rcasConstantsArray[0]; + + private ComputeBuffer _generateReactiveConstantsBuffer; + private readonly Fsr3Upscaler.GenerateReactiveConstants[] _generateReactiveConstantsArray = { new Fsr3Upscaler.GenerateReactiveConstants() }; + private ref Fsr3Upscaler.GenerateReactiveConstants GenReactiveConsts => ref _generateReactiveConstantsArray[0]; + + private ComputeBuffer _tcrAutogenerateConstantsBuffer; + private readonly Fsr3Upscaler.GenerateReactiveConstants2[] _tcrAutogenerateConstantsArray = { new Fsr3Upscaler.GenerateReactiveConstants2() }; + private ref Fsr3Upscaler.GenerateReactiveConstants2 TcrAutoGenConsts => ref _tcrAutogenerateConstantsArray[0]; + + private bool _firstExecution; + private int _resourceFrameIndex; + private Vector2 _previousJitterOffset; + private float _preExposure; + private float _previousFramePreExposure; + + public void Create(Fsr3Upscaler.ContextDescription contextDescription) + { + _contextDescription = contextDescription; + _commandBuffer = new CommandBuffer { name = "FSR3 Upscaler" }; + + _upscalerConstantsBuffer = CreateConstantBuffer(); + _spdConstantsBuffer = CreateConstantBuffer(); + _rcasConstantsBuffer = CreateConstantBuffer(); + _generateReactiveConstantsBuffer = CreateConstantBuffer(); + _tcrAutogenerateConstantsBuffer = CreateConstantBuffer(); + + // Set defaults + _firstExecution = true; + _resourceFrameIndex = 0; + + UpscalerConsts.maxUpscaleSize = _contextDescription.MaxUpscaleSize; + + _resources.Create(_contextDescription); + CreatePasses(); + } + + private void CreatePasses() + { + _prepareInputsPass = new Fsr3UpscalerPrepareInputsPass(_contextDescription, _resources, _upscalerConstantsBuffer); + _lumaPyramidPass = new Fsr3UpscalerLumaPyramidPass(_contextDescription, _resources, _upscalerConstantsBuffer, _spdConstantsBuffer); + _shadingChangePyramidPass = new Fsr3UpscalerShadingChangePyramidPass(_contextDescription, _resources, _upscalerConstantsBuffer, _spdConstantsBuffer); + _shadingChangePass = new Fsr3UpscalerShadingChangePass(_contextDescription, _resources, _upscalerConstantsBuffer); + _prepareReactivityPass = new Fsr3UpscalerPrepareReactivityPass(_contextDescription, _resources, _upscalerConstantsBuffer); + _lumaInstabilityPass = new Fsr3UpscalerLumaInstabilityPass(_contextDescription, _resources, _upscalerConstantsBuffer); + _accumulatePass = new Fsr3UpscalerAccumulatePass(_contextDescription, _resources, _upscalerConstantsBuffer); + _sharpenPass = new Fsr3UpscalerSharpenPass(_contextDescription, _resources, _upscalerConstantsBuffer, _rcasConstantsBuffer); + _generateReactivePass = new Fsr3UpscalerGenerateReactivePass(_contextDescription, _resources, _generateReactiveConstantsBuffer); + _tcrAutogeneratePass = new Fsr3UpscalerTcrAutogeneratePass(_contextDescription, _resources, _upscalerConstantsBuffer, _tcrAutogenerateConstantsBuffer); +#if UNITY_EDITOR || DEVELOPMENT_BUILD + _debugViewPass = new Fsr3UpscalerDebugViewPass(_contextDescription, _resources, _upscalerConstantsBuffer); +#endif + } + + public void Destroy() + { +#if UNITY_EDITOR || DEVELOPMENT_BUILD + DestroyPass(ref _debugViewPass); +#endif + DestroyPass(ref _tcrAutogeneratePass); + DestroyPass(ref _generateReactivePass); + DestroyPass(ref _lumaPyramidPass); + DestroyPass(ref _sharpenPass); + DestroyPass(ref _accumulatePass); + DestroyPass(ref _prepareReactivityPass); + DestroyPass(ref _shadingChangePass); + DestroyPass(ref _shadingChangePyramidPass); + DestroyPass(ref _lumaInstabilityPass); + DestroyPass(ref _prepareInputsPass); + + _resources.Destroy(); + + DestroyConstantBuffer(ref _tcrAutogenerateConstantsBuffer); + DestroyConstantBuffer(ref _generateReactiveConstantsBuffer); + DestroyConstantBuffer(ref _rcasConstantsBuffer); + DestroyConstantBuffer(ref _spdConstantsBuffer); + DestroyConstantBuffer(ref _upscalerConstantsBuffer); + + if (_commandBuffer != null) + { + _commandBuffer.Dispose(); + _commandBuffer = null; + } + } + + public void Dispatch(Fsr3Upscaler.DispatchDescription dispatchParams) + { + _commandBuffer.Clear(); + Dispatch(dispatchParams, _commandBuffer); + Graphics.ExecuteCommandBuffer(_commandBuffer); + } + + public void Dispatch(Fsr3Upscaler.DispatchDescription dispatchParams, CommandBuffer commandBuffer) + { + if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDebugChecking) != 0) + { + DebugCheckDispatch(dispatchParams); + } + + if (dispatchParams.UseTextureArrays) + commandBuffer.EnableShaderKeyword("UNITY_FSR_TEXTURE2D_X_ARRAY"); + + if (_firstExecution) + { + commandBuffer.SetRenderTarget(_resources.Accumulation[0]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.Accumulation[1]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.Luma[0]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.Luma[1]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + } + + int frameIndex = _resourceFrameIndex % 2; + bool resetAccumulation = dispatchParams.Reset || _firstExecution; + _firstExecution = false; + + // If auto exposure is enabled use the auto exposure SRV, otherwise what the app sends + if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableAutoExposure) != 0) + dispatchParams.Exposure = new ResourceView(_resources.FrameInfo); + else if (!dispatchParams.Exposure.IsValid) + dispatchParams.Exposure = new ResourceView(_resources.DefaultExposure); + + if (dispatchParams.EnableAutoReactive) + { + // Create the auto-TCR resources only when we need them + if (_resources.AutoReactive == null) + _resources.CreateTcrAutogenResources(_contextDescription); + + if (resetAccumulation) + { + RenderTargetIdentifier opaqueOnly = dispatchParams.ColorOpaqueOnly.IsValid ? dispatchParams.ColorOpaqueOnly.RenderTarget : Fsr3ShaderIDs.SrvOpaqueOnly; + commandBuffer.Blit(_resources.PrevPreAlpha[frameIndex ^ 1], opaqueOnly); + } + } + else if (_resources.AutoReactive != null) + { + // Destroy the auto-TCR resources if we don't use the feature + _resources.DestroyTcrAutogenResources(); + } + + if (!dispatchParams.Reactive.IsValid) dispatchParams.Reactive = new ResourceView(_resources.DefaultReactive); + if (!dispatchParams.TransparencyAndComposition.IsValid) dispatchParams.TransparencyAndComposition = new ResourceView(_resources.DefaultReactive); + Fsr3UpscalerResources.CreateAliasableResources(commandBuffer, _contextDescription, dispatchParams); + + SetupConstants(dispatchParams, resetAccumulation); + + // Reactive mask bias + const int threadGroupWorkRegionDim = 8; + int dispatchSrcX = (UpscalerConsts.renderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchSrcY = (UpscalerConsts.renderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchDstX = (UpscalerConsts.upscaleSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchDstY = (UpscalerConsts.upscaleSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchShadingChangePassX = ((UpscalerConsts.renderSize.x / 2) + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchShadingChangePassY = ((UpscalerConsts.renderSize.y / 2) + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + // Clear reconstructed depth for max depth store + if (resetAccumulation) + { + commandBuffer.SetRenderTarget(_resources.Accumulation[frameIndex ^ 1]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + + commandBuffer.SetRenderTarget(_resources.SpdMips); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + + // Auto exposure always used to track luma changes in locking logic + commandBuffer.SetRenderTarget(_resources.FrameInfo); + commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1e8f, 0f, 0f)); + + // Reset atomic counter to 0 + commandBuffer.SetRenderTarget(_resources.SpdAtomicCounter); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + } + + // FSR3: need to clear here since we need the content of this surface for frame interpolation, so clearing in the lock pass is not an option + bool depthInverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) == Fsr3Upscaler.InitializationFlags.EnableDepthInverted; + commandBuffer.SetRenderTarget(_resources.ReconstructedPrevNearestDepth); + commandBuffer.ClearRenderTarget(false, true, depthInverted ? Color.clear : Color.white); + + // Auto exposure + SetupSpdConstants(dispatchParams, out var dispatchThreadGroupCount); + + // Initialize constant buffers data + commandBuffer.SetBufferData(_upscalerConstantsBuffer, _upscalerConstantsArray); + commandBuffer.SetBufferData(_spdConstantsBuffer, _spdConstantsArray); + + // Auto reactive + if (dispatchParams.EnableAutoReactive) + { + GenerateTransparencyCompositionReactive(dispatchParams, commandBuffer, frameIndex); + dispatchParams.Reactive = new ResourceView(_resources.AutoReactive); + dispatchParams.TransparencyAndComposition = new ResourceView(_resources.AutoComposition); + } + + _prepareInputsPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + _lumaPyramidPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); + _shadingChangePyramidPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); + _shadingChangePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchShadingChangePassX, dispatchShadingChangePassY); + _prepareReactivityPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + _lumaInstabilityPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + + _accumulatePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchDstX, dispatchDstY); + + if (dispatchParams.EnableSharpening) + { + // Compute the constants + SetupRcasConstants(dispatchParams); + commandBuffer.SetBufferData(_rcasConstantsBuffer, _rcasConstantsArray); + + // Dispatch RCAS + const int threadGroupWorkRegionDimRcas = 16; + int threadGroupsX = (UpscalerConsts.upscaleSize.x + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; + int threadGroupsY = (UpscalerConsts.upscaleSize.y + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; + _sharpenPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, threadGroupsX, threadGroupsY); + } + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + if ((dispatchParams.Flags & Fsr3Upscaler.DispatchFlags.DrawDebugView) != 0) + { + _debugViewPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchDstX, dispatchDstY); + } +#endif + + _resourceFrameIndex = (_resourceFrameIndex + 1) % MaxQueuedFrames; + + Fsr3UpscalerResources.DestroyAliasableResources(commandBuffer); + + commandBuffer.DisableShaderKeyword("UNITY_FSR_TEXTURE2D_X_ARRAY"); + } + + public void GenerateReactiveMask(Fsr3Upscaler.GenerateReactiveDescription dispatchParams) + { + _commandBuffer.Clear(); + GenerateReactiveMask(dispatchParams, _commandBuffer); + Graphics.ExecuteCommandBuffer(_commandBuffer); + } + + public void GenerateReactiveMask(Fsr3Upscaler.GenerateReactiveDescription dispatchParams, CommandBuffer commandBuffer) + { + const int threadGroupWorkRegionDim = 8; + int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + GenReactiveConsts.scale = dispatchParams.Scale; + GenReactiveConsts.threshold = dispatchParams.CutoffThreshold; + GenReactiveConsts.binaryValue = dispatchParams.BinaryValue; + GenReactiveConsts.flags = (uint)dispatchParams.Flags; + commandBuffer.SetBufferData(_generateReactiveConstantsBuffer, _generateReactiveConstantsArray); + + ((Fsr3UpscalerGenerateReactivePass)_generateReactivePass).ScheduleDispatch(commandBuffer, dispatchParams, dispatchSrcX, dispatchSrcY); + } + + private void GenerateTransparencyCompositionReactive(Fsr3Upscaler.DispatchDescription dispatchParams, CommandBuffer commandBuffer, int frameIndex) + { + const int threadGroupWorkRegionDim = 8; + int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + + TcrAutoGenConsts.autoTcThreshold = dispatchParams.AutoTcThreshold; + TcrAutoGenConsts.autoTcScale = dispatchParams.AutoTcScale; + TcrAutoGenConsts.autoReactiveScale = dispatchParams.AutoReactiveScale; + TcrAutoGenConsts.autoReactiveMax = dispatchParams.AutoReactiveMax; + commandBuffer.SetBufferData(_tcrAutogenerateConstantsBuffer, _tcrAutogenerateConstantsArray); + + _tcrAutogeneratePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + } + + private void SetupConstants(Fsr3Upscaler.DispatchDescription dispatchParams, bool resetAccumulation) + { + ref Fsr3Upscaler.UpscalerConstants constants = ref UpscalerConsts; + + constants.previousFrameJitterOffset = constants.jitterOffset; + constants.jitterOffset = dispatchParams.JitterOffset; + + constants.previousFrameRenderSize = constants.renderSize; + constants.renderSize = dispatchParams.RenderSize; + constants.maxRenderSize = _contextDescription.MaxRenderSize; + + // Compute the horizontal FOV for the shader from the vertical one + float aspectRatio = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; + float cameraAngleHorizontal = Mathf.Atan(Mathf.Tan(dispatchParams.CameraFovAngleVertical / 2.0f) * aspectRatio) * 2.0f; + constants.tanHalfFOV = Mathf.Tan(cameraAngleHorizontal * 0.5f); + constants.viewSpaceToMetersFactor = (dispatchParams.ViewSpaceToMetersFactor > 0.0f) ? dispatchParams.ViewSpaceToMetersFactor : 1.0f; + + // Compute params to enable device depth to view space depth computation in shader + constants.deviceToViewDepth = SetupDeviceDepthToViewSpaceDepthParams(dispatchParams); + + constants.previousFrameUpscaleSize = constants.upscaleSize; + if (dispatchParams.UpscaleSize.x == 0 && dispatchParams.UpscaleSize.y == 0) + { + constants.upscaleSize = _contextDescription.MaxUpscaleSize; + } + else + { + constants.upscaleSize = dispatchParams.UpscaleSize; + } + + // To be updated if resource is larger than the actual image size + constants.downscaleFactor = new Vector2((float)constants.renderSize.x / constants.upscaleSize.x, (float)constants.renderSize.y / constants.upscaleSize.y); + + // Calculate pre-exposure relevant factors + constants.deltaPreExposure = 1.0f; + _previousFramePreExposure = _preExposure; + _preExposure = dispatchParams.PreExposure != 0.0f ? dispatchParams.PreExposure : 1.0f; + + if (_previousFramePreExposure > 0.0f) + { + constants.deltaPreExposure = _preExposure / _previousFramePreExposure; + } + + // Motion vector data + Vector2Int motionVectorsTargetSize = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) != 0 ? constants.upscaleSize : constants.renderSize; + constants.motionVectorScale = dispatchParams.MotionVectorScale / motionVectorsTargetSize; + + // Compute jitter cancellation + if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) + { + constants.motionVectorJitterCancellation = (_previousJitterOffset - constants.jitterOffset) / motionVectorsTargetSize; + _previousJitterOffset = constants.jitterOffset; + } + + int jitterPhaseCount = Fsr3Upscaler.GetJitterPhaseCount(dispatchParams.RenderSize.x, _contextDescription.MaxUpscaleSize.x); + if (resetAccumulation || constants.jitterPhaseCount == 0) + { + constants.jitterPhaseCount = jitterPhaseCount; + } + else + { + int jitterPhaseCountDelta = (int)(jitterPhaseCount - constants.jitterPhaseCount); + if (jitterPhaseCountDelta > 0) + constants.jitterPhaseCount++; + else if (jitterPhaseCountDelta < 0) + constants.jitterPhaseCount--; + } + + // Convert delta time to seconds and clamp to [0, 1] + constants.deltaTime = Mathf.Clamp01(dispatchParams.FrameTimeDelta); + + if (resetAccumulation) + constants.frameIndex = 0; + else + constants.frameIndex += 1.0f; + } + + private Vector4 SetupDeviceDepthToViewSpaceDepthParams(Fsr3Upscaler.DispatchDescription dispatchParams) + { + bool inverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0; + bool infinite = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInfinite) != 0; + + // make sure it has no impact if near and far plane values are swapped in dispatch params + // the flags "inverted" and "infinite" will decide what transform to use + float min = Mathf.Min(dispatchParams.CameraNear, dispatchParams.CameraFar); + float max = Mathf.Max(dispatchParams.CameraNear, dispatchParams.CameraFar); + + if (inverted) + { + (min, max) = (max, min); + } + + float q = max / (min - max); + float d = -1.0f; + + Vector4 matrixElemC = new Vector4(q, -1.0f - Mathf.Epsilon, q, 0.0f + Mathf.Epsilon); + Vector4 matrixElemE = new Vector4(q * min, -min - Mathf.Epsilon, q * min, max); + + // Revert x and y coords + float aspect = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; + float cotHalfFovY = Mathf.Cos(0.5f * dispatchParams.CameraFovAngleVertical) / Mathf.Sin(0.5f * dispatchParams.CameraFovAngleVertical); + + int matrixIndex = (inverted ? 2 : 0) + (infinite ? 1 : 0); + return new Vector4( + d * matrixElemC[matrixIndex], + matrixElemE[matrixIndex], + aspect / cotHalfFovY, + 1.0f / cotHalfFovY); + } + + private void SetupRcasConstants(Fsr3Upscaler.DispatchDescription dispatchParams) + { + int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(dispatchParams.Sharpness) * (RcasConfigs.Length - 1)); + RcasConsts = RcasConfigs[sharpnessIndex]; + } + + private void SetupSpdConstants(Fsr3Upscaler.DispatchDescription dispatchParams, out Vector2Int dispatchThreadGroupCount) + { + RectInt rectInfo = new RectInt(0, 0, dispatchParams.RenderSize.x, dispatchParams.RenderSize.y); + SpdSetup(rectInfo, out dispatchThreadGroupCount, out var workGroupOffset, out var numWorkGroupsAndMips); + + // Downsample + ref Fsr3Upscaler.SpdConstants spdConstants = ref SpdConsts; + spdConstants.numWorkGroups = (uint)numWorkGroupsAndMips.x; + spdConstants.mips = (uint)numWorkGroupsAndMips.y; + spdConstants.workGroupOffsetX = (uint)workGroupOffset.x; + spdConstants.workGroupOffsetY = (uint)workGroupOffset.y; + spdConstants.renderSizeX = (uint)dispatchParams.RenderSize.x; + spdConstants.renderSizeY = (uint)dispatchParams.RenderSize.y; + } + + private static void SpdSetup(RectInt rectInfo, out Vector2Int dispatchThreadGroupCount, out Vector2Int workGroupOffset, out Vector2Int numWorkGroupsAndMips, int mips = -1) + { + workGroupOffset = new Vector2Int(rectInfo.x / 64, rectInfo.y / 64); + + int endIndexX = (rectInfo.x + rectInfo.width - 1) / 64; + int endIndexY = (rectInfo.y + rectInfo.height - 1) / 64; + + dispatchThreadGroupCount = new Vector2Int(endIndexX + 1 - workGroupOffset.x, endIndexY + 1 - workGroupOffset.y); + + numWorkGroupsAndMips = new Vector2Int(dispatchThreadGroupCount.x * dispatchThreadGroupCount.y, mips); + if (mips < 0) + { + float resolution = Math.Max(rectInfo.width, rectInfo.height); + numWorkGroupsAndMips.y = Math.Min(Mathf.FloorToInt(Mathf.Log(resolution, 2.0f)), 12); + } + } + + private void DebugCheckDispatch(Fsr3Upscaler.DispatchDescription dispatchParams) + { + if (!dispatchParams.Color.IsValid) + { + Debug.LogError("Color resource is null"); + } + + if (!dispatchParams.Depth.IsValid) + { + Debug.LogError("Depth resource is null"); + } + + if (!dispatchParams.MotionVectors.IsValid) + { + Debug.LogError("MotionVectors resource is null"); + } + + if (dispatchParams.Exposure.IsValid && (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableAutoExposure) != 0) + { + Debug.LogWarning("Exposure resource provided, however auto exposure flag is present"); + } + + if (!dispatchParams.Output.IsValid) + { + Debug.LogError("Output resource is null"); + } + + if (Mathf.Abs(dispatchParams.JitterOffset.x) > 1.0f || Mathf.Abs(dispatchParams.JitterOffset.y) > 1.0f) + { + Debug.LogWarning("JitterOffset contains value outside of expected range [-1.0, 1.0]"); + } + + if (dispatchParams.MotionVectorScale.x > _contextDescription.MaxRenderSize.x || dispatchParams.MotionVectorScale.y > _contextDescription.MaxRenderSize.y) + { + Debug.LogWarning("MotionVectorScale contains scale value greater than MaxRenderSize"); + } + + if (dispatchParams.MotionVectorScale.x == 0.0f || dispatchParams.MotionVectorScale.y == 0.0f) + { + Debug.LogWarning("MotionVectorScale contains zero scale value"); + } + + if (dispatchParams.RenderSize.x > _contextDescription.MaxRenderSize.x || dispatchParams.RenderSize.y > _contextDescription.MaxRenderSize.y) + { + Debug.LogWarning("RenderSize is greater than context MaxRenderSize"); + } + + if (dispatchParams.RenderSize.x == 0 || dispatchParams.RenderSize.y == 0) + { + Debug.LogWarning("RenderSize contains zero dimension"); + } + + if (dispatchParams.Sharpness < 0.0f || dispatchParams.Sharpness > 1.0f) + { + Debug.LogWarning("Sharpness contains value outside of expected range [0.0, 1.0]"); + } + + if (dispatchParams.FrameTimeDelta > 1.0f) + { + Debug.LogWarning("FrameTimeDelta is greater than 1.0f - this value should be seconds (~0.0166 for 60fps)"); + } + + if (dispatchParams.PreExposure == 0.0f) + { + Debug.LogError("PreExposure provided as 0.0f which is invalid"); + } + + bool infiniteDepth = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInfinite) != 0; + bool inverseDepth = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0; + + if (inverseDepth) + { + if (dispatchParams.CameraNear < dispatchParams.CameraFar) + { + Debug.LogWarning("EnableDepthInverted flag is present yet CameraNear is less than CameraFar"); + } + + if (infiniteDepth) + { + if (dispatchParams.CameraNear < float.MaxValue) + { + Debug.LogWarning("EnableDepthInfinite and EnableDepthInverted present, yet CameraNear != float.MaxValue"); + } + } + + if (dispatchParams.CameraFar < 0.075f) + { + Debug.LogWarning("EnableDepthInverted present, CameraFar value is very low which may result in depth separation artefacting"); + } + } + else + { + if (dispatchParams.CameraNear > dispatchParams.CameraFar) + { + Debug.LogWarning("CameraNear is greater than CameraFar in non-inverted-depth context"); + } + + if (infiniteDepth) + { + if (dispatchParams.CameraFar < float.MaxValue) + { + Debug.LogWarning("EnableDepthInfinite present, yet CameraFar != float.MaxValue"); + } + } + + if (dispatchParams.CameraNear < 0.075f) + { + Debug.LogWarning("CameraNear value is very low which may result in depth separation artefacting"); + } + } + + if (dispatchParams.CameraFovAngleVertical <= 0.0f) + { + Debug.LogError("CameraFovAngleVertical is 0.0f - this value should be > 0.0f"); + } + + if (dispatchParams.CameraFovAngleVertical > Mathf.PI) + { + Debug.LogError("CameraFovAngleVertical is greater than 180 degrees/PI"); + } + } + + /// + /// The FSR3 C++ codebase uses floats bitwise converted to ints to pass sharpness parameters to the RCAS shader. + /// This is not possible in C# without enabling unsafe code compilation, so to avoid that we instead use a table of precomputed values. + /// + private static readonly Fsr3Upscaler.RcasConstants[] RcasConfigs = new [] + { + new Fsr3Upscaler.RcasConstants(1048576000u, 872428544u), + new Fsr3Upscaler.RcasConstants(1049178080u, 877212745u), + new Fsr3Upscaler.RcasConstants(1049823372u, 882390168u), + new Fsr3Upscaler.RcasConstants(1050514979u, 887895276u), + new Fsr3Upscaler.RcasConstants(1051256227u, 893859143u), + new Fsr3Upscaler.RcasConstants(1052050675u, 900216232u), + new Fsr3Upscaler.RcasConstants(1052902144u, 907032080u), + new Fsr3Upscaler.RcasConstants(1053814727u, 914306687u), + new Fsr3Upscaler.RcasConstants(1054792807u, 922105590u), + new Fsr3Upscaler.RcasConstants(1055841087u, 930494326u), + new Fsr3Upscaler.RcasConstants(1056964608u, 939538432u), + new Fsr3Upscaler.RcasConstants(1057566688u, 944322633u), + new Fsr3Upscaler.RcasConstants(1058211980u, 949500056u), + new Fsr3Upscaler.RcasConstants(1058903587u, 955005164u), + new Fsr3Upscaler.RcasConstants(1059644835u, 960969031u), + new Fsr3Upscaler.RcasConstants(1060439283u, 967326120u), + new Fsr3Upscaler.RcasConstants(1061290752u, 974141968u), + new Fsr3Upscaler.RcasConstants(1062203335u, 981416575u), + new Fsr3Upscaler.RcasConstants(1063181415u, 989215478u), + new Fsr3Upscaler.RcasConstants(1064229695u, 997604214u), + new Fsr3Upscaler.RcasConstants(1065353216u, 1006648320), + }; + + private static ComputeBuffer CreateConstantBuffer() where TConstants: struct + { + return new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); + } + + private static void DestroyConstantBuffer(ref ComputeBuffer bufferRef) + { + if (bufferRef == null) + return; + + bufferRef.Release(); + bufferRef = null; + } + + private static void DestroyPass(ref Fsr3UpscalerPass pass) + { + if (pass == null) + return; + + pass.Dispose(); + pass = null; + } + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs.meta new file mode 100644 index 0000000..0b27f6d --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerContext.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 742dda30b4604d5488e7eeda3cf04d56 +timeCreated: 1673442225 \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs new file mode 100644 index 0000000..3b76eb4 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs @@ -0,0 +1,472 @@ +// 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 System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Profiling; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR3 +{ + /// + /// Base class for all of the compute passes that make up the FSR3 Upscaler process. + /// This loosely matches the FfxPipelineState struct from the original FSR3 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 Fsr3UpscalerPass: IDisposable + { + protected readonly Fsr3Upscaler.ContextDescription ContextDescription; + protected readonly Fsr3UpscalerResources Resources; + protected readonly ComputeBuffer Constants; + + protected ComputeShader ComputeShader; + protected int KernelIndex; + + protected CustomSampler Sampler; + + protected Fsr3UpscalerPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + { + ContextDescription = contextDescription; + Resources = resources; + Constants = constants; + } + + public virtual void Dispose() + { + } + + public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + commandBuffer.BeginSample(Sampler); + DoScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchX, dispatchY); + commandBuffer.EndSample(Sampler); + } + + protected abstract void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); + + protected void InitComputeShader(string passName, ComputeShader shader) + { + InitComputeShader(passName, shader, ContextDescription.Flags); + } + + private void InitComputeShader(string passName, ComputeShader shader, Fsr3Upscaler.InitializationFlags flags) + { + if (shader == null) + { + throw new MissingReferenceException($"Shader for FSR3 Upscaler pass '{passName}' could not be loaded! Please ensure it is included in the project correctly."); + } + + ComputeShader = shader; + KernelIndex = ComputeShader.FindKernel("CS"); + Sampler = CustomSampler.Create(passName); + + 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 & Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT"); + if ((flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS"); + if ((flags & Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS"); + if ((flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH"); + if (useLut) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE"); + if ((flags & Fsr3Upscaler.InitializationFlags.EnableFP16Usage) != 0) ComputeShader.EnableKeyword("FFX_HALF"); + } + } + + internal class Fsr3UpscalerPrepareInputsPass : Fsr3UpscalerPass + { + public Fsr3UpscalerPrepareInputsPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Prepare Inputs", contextDescription.Shaders.prepareInputsPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.Color; + ref var depth = ref dispatchParams.Depth; + ref var motionVectors = ref dispatchParams.MotionVectors; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavDilatedMotionVectors, Resources.DilatedVelocity); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavDilatedDepth, Resources.DilatedDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavReconstructedPrevNearestDepth, Resources.ReconstructedPrevNearestDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavFarthestDepth, Fsr3ShaderIDs.UavIntermediate); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavCurrentLuma, Resources.Luma[frameIndex]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerLumaPyramidPass : Fsr3UpscalerPass + { + private readonly ComputeBuffer _spdConstants; + + public Fsr3UpscalerLumaPyramidPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer spdConstants) + : base(contextDescription, resources, constants) + { + _spdConstants = spdConstants; + + InitComputeShader("Compute Luminance Pyramid", contextDescription.Shaders.lumaPyramidPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvCurrentLuma, Resources.Luma[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvFarthestDepth, Fsr3ShaderIDs.UavIntermediate); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavFrameInfo, Resources.FrameInfo); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip0, Resources.SpdMips, 0); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip1, Resources.SpdMips, 1); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip2, Resources.SpdMips, 2); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip3, Resources.SpdMips, 3); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip4, Resources.SpdMips, 4); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip5, Resources.SpdMips, 5); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbSpd, _spdConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerShadingChangePyramidPass : Fsr3UpscalerPass + { + private readonly ComputeBuffer _spdConstants; + + public Fsr3UpscalerShadingChangePyramidPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer spdConstants) + : base(contextDescription, resources, constants) + { + _spdConstants = spdConstants; + + InitComputeShader("Compute Shading Change Pyramid", contextDescription.Shaders.shadingChangePyramidPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var exposure = ref dispatchParams.Exposure; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvCurrentLuma, Resources.Luma[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPreviousLuma, Resources.Luma[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedVelocity); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip0, Resources.SpdMips, 0); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip1, Resources.SpdMips, 1); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip2, Resources.SpdMips, 2); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip3, Resources.SpdMips, 3); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip4, Resources.SpdMips, 4); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdMip5, Resources.SpdMips, 5); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbSpd, _spdConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerShadingChangePass : Fsr3UpscalerPass + { + public Fsr3UpscalerShadingChangePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Compute Shading Change", contextDescription.Shaders.shadingChangePass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvSpdMips, Resources.SpdMips); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerPrepareReactivityPass : Fsr3UpscalerPass + { + public Fsr3UpscalerPrepareReactivityPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Prepare Reactivity", contextDescription.Shaders.prepareReactivityPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var exposure = ref dispatchParams.Exposure; + ref var reactive = ref dispatchParams.Reactive; + ref var tac = ref dispatchParams.TransparencyAndComposition; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReconstructedPrevNearestDepth, Resources.ReconstructedPrevNearestDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedVelocity); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedDepth, Resources.DilatedDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvAccumulation, Resources.Accumulation[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvShadingChange, Fsr3ShaderIDs.UavShadingChange); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvCurrentLuma, Resources.Luma[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAccumulation, Resources.Accumulation[frameIndex]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerLumaInstabilityPass : Fsr3UpscalerPass + { + public Fsr3UpscalerLumaInstabilityPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Compute Luminance Instability", contextDescription.Shaders.lumaInstabilityPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var exposure = ref dispatchParams.Exposure; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedReactiveMasks, Fsr3ShaderIDs.UavDilatedReactiveMasks); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedVelocity); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvFrameInfo, Resources.FrameInfo); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLumaHistory, Resources.LumaHistory[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvFarthestDepthMip1, Fsr3ShaderIDs.UavFarthestDepthMip1); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvCurrentLuma, Resources.Luma[frameIndex]); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavLumaHistory, Resources.LumaHistory[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavLumaInstability, Fsr3ShaderIDs.UavIntermediate); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerAccumulatePass : Fsr3UpscalerPass + { + private const string SharpeningKeyword = "FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING"; + +#if UNITY_2021_2_OR_NEWER + private readonly LocalKeyword _sharpeningKeyword; +#endif + + public Fsr3UpscalerAccumulatePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Accumulate", contextDescription.Shaders.accumulatePass); +#if UNITY_2021_2_OR_NEWER + _sharpeningKeyword = new LocalKeyword(ComputeShader, SharpeningKeyword); +#endif + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { +#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 + + ref var color = ref dispatchParams.Color; + ref var exposure = ref dispatchParams.Exposure; + ref var output = ref dispatchParams.Output; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedReactiveMasks, Fsr3ShaderIDs.UavDilatedReactiveMasks); + + if ((ContextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) + { + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedVelocity); + } + else + { + ref var motionVectors = ref dispatchParams.MotionVectors; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + } + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInternalUpscaled, Resources.InternalUpscaled[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLanczosLut, Resources.LanczosLut); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvFarthestDepthMip1, Fsr3ShaderIDs.UavFarthestDepthMip1); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvCurrentLuma, Resources.Luma[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLumaInstability, Fsr3ShaderIDs.UavIntermediate); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavInternalUpscaled, Resources.InternalUpscaled[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerSharpenPass : Fsr3UpscalerPass + { + private readonly ComputeBuffer _rcasConstants; + + public Fsr3UpscalerSharpenPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer rcasConstants) + : base(contextDescription, resources, constants) + { + _rcasConstants = rcasConstants; + + InitComputeShader("RCAS Sharpening", contextDescription.Shaders.sharpenPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var exposure = ref dispatchParams.Exposure; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvRcasInput, Resources.InternalUpscaled[frameIndex]); + + ref var output = ref dispatchParams.Output; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbRcas, _rcasConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class Fsr3UpscalerGenerateReactivePass : Fsr3UpscalerPass + { + private readonly ComputeBuffer _generateReactiveConstants; + + public Fsr3UpscalerGenerateReactivePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer generateReactiveConstants) + : base(contextDescription, resources, null) + { + _generateReactiveConstants = generateReactiveConstants; + + InitComputeShader("Auto-Generate Reactive Mask", contextDescription.Shaders.autoGenReactivePass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + } + + public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.GenerateReactiveDescription dispatchParams, int dispatchX, int dispatchY) + { + commandBuffer.BeginSample(Sampler); + + ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; + ref var color = ref dispatchParams.ColorPreUpscale; + ref var reactive = ref dispatchParams.OutReactive; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoReactive, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbGenReactive, _generateReactiveConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + + commandBuffer.EndSample(Sampler); + } + } + + internal class Fsr3UpscalerTcrAutogeneratePass : Fsr3UpscalerPass + { + private readonly ComputeBuffer _tcrAutogenerateConstants; + + public Fsr3UpscalerTcrAutogeneratePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer tcrAutogenerateConstants) + : base(contextDescription, resources, constants) + { + _tcrAutogenerateConstants = tcrAutogenerateConstants; + + InitComputeShader("Auto-Generate Transparency & Composition Mask", contextDescription.Shaders.tcrAutoGenPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.Color; + ref var motionVectors = ref dispatchParams.MotionVectors; + ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; + ref var reactive = ref dispatchParams.Reactive; + ref var tac = ref dispatchParams.TransparencyAndComposition; + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoReactive, Resources.AutoReactive); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoComposition, Resources.AutoComposition); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbGenReactive, _tcrAutogenerateConstants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + +#if UNITY_EDITOR || DEVELOPMENT_BUILD + internal class Fsr3UpscalerDebugViewPass : Fsr3UpscalerPass + { + public Fsr3UpscalerDebugViewPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Debug View", contextDescription.Shaders.debugViewPass); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var exposure = ref dispatchParams.Exposure; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedReactiveMasks, Fsr3ShaderIDs.UavDilatedReactiveMasks); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedVelocity); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedDepth, Resources.DilatedDepth); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInternalUpscaled, Resources.InternalUpscaled[frameIndex]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); + + ref var output = ref dispatchParams.Output; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } +#endif +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs.meta new file mode 100644 index 0000000..424e36c --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerPass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9e0cf27d80d1d4d4c81450791a411ead +timeCreated: 1676885169 \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs new file mode 100644 index 0000000..112b98d --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs @@ -0,0 +1,245 @@ +// 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 System; +using UnityEngine; +using UnityEngine.Experimental.Rendering; +using UnityEngine.Rendering; + +namespace FidelityFX.FSR3 +{ + /// + /// Helper class for bundling and managing persistent resources required by the FSR3 Upscaler process. + /// This includes lookup tables, default fallback resources and double-buffered resources that get swapped between frames. + /// + internal class Fsr3UpscalerResources + { + public Texture2D LanczosLut; + public Texture2D DefaultExposure; + public Texture2D DefaultReactive; + + public RenderTexture SpdAtomicCounter; + public RenderTexture SpdMips; + public RenderTexture DilatedVelocity; + public RenderTexture DilatedDepth; + public RenderTexture ReconstructedPrevNearestDepth; + public RenderTexture FrameInfo; + public RenderTexture AutoReactive; + public RenderTexture AutoComposition; + + public readonly RenderTexture[] Accumulation = new RenderTexture[2]; + public readonly RenderTexture[] Luma = new RenderTexture[2]; + public readonly RenderTexture[] InternalUpscaled = new RenderTexture[2]; + public readonly RenderTexture[] LumaHistory = new RenderTexture[2]; + public readonly RenderTexture[] PrevPreAlpha = new RenderTexture[2]; + public readonly RenderTexture[] PrevPostAlpha = new RenderTexture[2]; + + public void Create(Fsr3Upscaler.ContextDescription contextDescription) + { + // Generate the data for the LUT + const int lanczos2LutWidth = 128; + float[] lanczos2Weights = new float[lanczos2LutWidth]; + for (int currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex) + { + float x = 2.0f * currentLanczosWidthIndex / (lanczos2LutWidth - 1); + float y = Fsr3Upscaler.Lanczos2(x); + lanczos2Weights[currentLanczosWidthIndex] = y; + } + + Vector2Int maxRenderSize = contextDescription.MaxRenderSize; + Vector2Int maxRenderSizeDiv2 = maxRenderSize / 2; + + // Resource FSR3UPSCALER_LanczosLutData: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE + // R16_SNorm textures are not supported by Unity on most platforms, strangely enough. So instead we use R32_SFloat and upload pre-normalized float data. + LanczosLut = new Texture2D(lanczos2LutWidth, 1, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_LanczosLutData" }; + LanczosLut.SetPixelData(lanczos2Weights, 0); + LanczosLut.Apply(); + + // Resource FSR3UPSCALER_DefaultReactivityMask: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + DefaultReactive = new Texture2D(1, 1, GraphicsFormat.R8_UNorm, TextureCreationFlags.None) { name = "FSR3UPSCALER_DefaultReactivityMask" }; + DefaultReactive.SetPixel(0, 0, Color.clear); + DefaultReactive.Apply(); + + // Resource FSR3UPSCALER_DefaultExposure: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE + DefaultExposure = new Texture2D(1, 1, GraphicsFormat.R32G32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_DefaultExposure" }; + DefaultExposure.SetPixel(0, 0, Color.clear); + DefaultExposure.Apply(); + + // Resource FSR3UPSCALER_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE + // Despite what the original FSR3 codebase says, this resource really isn't aliasable. Resetting this counter to 0 every frame breaks auto-exposure on MacOS Metal. + SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR3UPSCALER_SpdAtomicCounter", enableRandomWrite = true }; + SpdAtomicCounter.Create(); + + // Resource FSR3UPSCALER_SpdMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE + // This is a rather special case: it's an aliasable resource, but because we require a mipmap chain and bind specific mip levels per shader, we can't easily use temporary RTs for this. + int mipCount = 1 + Mathf.FloorToInt(Mathf.Log(Math.Max(maxRenderSizeDiv2.x, maxRenderSizeDiv2.y), 2.0f)); + SpdMips = new RenderTexture(maxRenderSizeDiv2.x, maxRenderSizeDiv2.y, 0, GraphicsFormat.R16G16_SFloat, mipCount) { name = "FSR3UPSCALER_SpdMips", enableRandomWrite = true, useMipMap = true, autoGenerateMips = false }; + SpdMips.Create(); + + // Resource FSR3UPSCALER_DilatedVelocity: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE + DilatedVelocity = new RenderTexture(maxRenderSize.x, maxRenderSize.y, 0, GraphicsFormat.R16G16_SFloat) { name = "FSR3UPSCALER_DilatedVelocity", enableRandomWrite = true }; + DilatedVelocity.Create(); + + // Resource FSR3UPSCALER_DilatedDepth: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_FLOAT, FFX_RESOURCE_FLAGS_NONE + DilatedDepth = new RenderTexture(maxRenderSize.x, maxRenderSize.y, 0, GraphicsFormat.R32_SFloat) { name = "FSR3UPSCALER_DilatedDepth", enableRandomWrite = true }; + DilatedDepth.Create(); + + // Resource FSR3UPSCALER_ReconstructedPrevNearestDepth: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_NONE + ReconstructedPrevNearestDepth = new RenderTexture(maxRenderSize.x, maxRenderSize.y, 0, GraphicsFormat.R32_UInt) { name = "FSR3UPSCALER_ReconstructedPrevNearestDepth", enableRandomWrite = true }; + ReconstructedPrevNearestDepth.Create(); + + // Resource FSR3UPSCALER_FrameInfo: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT, FFX_RESOURCE_FLAGS_NONE + FrameInfo = new RenderTexture(1, 1, 0, GraphicsFormat.R32G32B32A32_SFloat) { name = "FSR3UPSCALER_FrameInfo", enableRandomWrite = true }; + FrameInfo.Create(); + + // Resources FSR3UPSCALER_Accumulation1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(Accumulation, "FSR3UPSCALER_Accumulation", maxRenderSize, GraphicsFormat.R8_UNorm); + + // Resources FSR3UPSCALER_Luma1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(Luma, "FSR3UPSCALER_Luma", maxRenderSize, GraphicsFormat.R16_SFloat); + + // Resources FSR3UPSCALER_InternalUpscaled1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(InternalUpscaled, "FSR3UPSCALER_InternalUpscaled", contextDescription.MaxUpscaleSize, GraphicsFormat.R16G16B16A16_SFloat); + + // Resources FSR3UPSCALER_LumaHistory1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(LumaHistory, "FSR3UPSCALER_LumaHistory", maxRenderSize, GraphicsFormat.R16G16B16A16_SFloat); + } + + public void CreateTcrAutogenResources(Fsr3Upscaler.ContextDescription contextDescription) + { + // Resource FSR3UPSCALER_AutoReactive: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + AutoReactive = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR3UPSCALER_AutoReactive", enableRandomWrite = true }; + AutoReactive.Create(); + + // Resource FSR3UPSCALER_AutoComposition: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE + AutoComposition = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR3UPSCALER_AutoComposition", enableRandomWrite = true }; + AutoComposition.Create(); + + // Resources FSR3UPSCALER_PrevPreAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(PrevPreAlpha, "FSR3UPSCALER_PrevPreAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); + + // Resources FSR3UPSCALER_PrevPostAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE + CreateDoubleBufferedResource(PrevPostAlpha, "FSR3UPSCALER_PrevPostAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); + } + + // 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 + public static void CreateAliasableResources(CommandBuffer commandBuffer, Fsr3Upscaler.ContextDescription contextDescription, Fsr3Upscaler.DispatchDescription dispatchParams) + { + Vector2Int maxUpscaleSize = contextDescription.MaxUpscaleSize; + Vector2Int maxRenderSize = contextDescription.MaxRenderSize; + Vector2Int maxRenderSizeDiv2 = maxRenderSize / 2; + + // FSR3UPSCALER_IntermediateFp16x1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavIntermediate, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16_SFloat, 1, true); + + // FSR3UPSCALER_ShadingChange: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavShadingChange, maxRenderSizeDiv2.x, maxRenderSizeDiv2.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); + + // FSR3UPSCALER_NewLocks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavNewLocks, maxUpscaleSize.x, maxUpscaleSize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); + + // FSR3UPSCALER_FarthestDepthMip1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavFarthestDepthMip1, maxRenderSizeDiv2.x, maxRenderSizeDiv2.y, 0, default, GraphicsFormat.R16_SFloat, 1, true); + + // FSR3UPSCALER_DilatedReactiveMasks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8B8A8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE + commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavDilatedReactiveMasks, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R8G8B8A8_UNorm, 1, true); + } + + public static void DestroyAliasableResources(CommandBuffer commandBuffer) + { + // Release all of the aliasable resources used this frame + commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavDilatedReactiveMasks); + commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavFarthestDepthMip1); + commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavNewLocks); + commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavShadingChange); + commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavIntermediate); + } + + private static void CreateDoubleBufferedResource(RenderTexture[] resource, string name, Vector2Int size, GraphicsFormat format) + { + for (int i = 0; i < 2; ++i) + { + resource[i] = new RenderTexture(size.x, size.y, 0, format) { name = name + (i + 1), enableRandomWrite = true }; + resource[i].Create(); + } + } + + public void Destroy() + { + DestroyTcrAutogenResources(); + + DestroyResource(LumaHistory); + DestroyResource(InternalUpscaled); + DestroyResource(Luma); + DestroyResource(Accumulation); + + DestroyResource(ref FrameInfo); + DestroyResource(ref ReconstructedPrevNearestDepth); + DestroyResource(ref DilatedDepth); + DestroyResource(ref DilatedVelocity); + DestroyResource(ref SpdMips); + DestroyResource(ref SpdAtomicCounter); + + DestroyResource(ref DefaultReactive); + DestroyResource(ref DefaultExposure); + DestroyResource(ref LanczosLut); + } + + public void DestroyTcrAutogenResources() + { + DestroyResource(PrevPostAlpha); + DestroyResource(PrevPreAlpha); + DestroyResource(ref AutoComposition); + DestroyResource(ref AutoReactive); + } + + private static void DestroyResource(ref Texture2D resource) + { + if (resource == null) + return; + +#if UNITY_EDITOR + if (Application.isPlaying && !UnityEditor.EditorApplication.isPaused) + UnityEngine.Object.Destroy(resource); + else + UnityEngine.Object.DestroyImmediate(resource); +#else + UnityEngine.Object.Destroy(resource); +#endif + resource = null; + } + + private static void DestroyResource(ref RenderTexture resource) + { + if (resource == null) + return; + + resource.Release(); + resource = null; + } + + private static void DestroyResource(RenderTexture[] resource) + { + for (int i = 0; i < resource.Length; ++i) + DestroyResource(ref resource[i]); + } + } +} diff --git a/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs.meta b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs.meta new file mode 100644 index 0000000..62d6dc1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FSR3/Fsr3UpscalerResources.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6749e0e55c9ac5a4f869bc73dfbb7163 +timeCreated: 1677236102 \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef b/Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef new file mode 100644 index 0000000..4d4ce4c --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef @@ -0,0 +1,14 @@ +{ + "name": "FidelityFX.FSR", + "rootNamespace": "FidelityFX", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef.meta b/Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef.meta new file mode 100644 index 0000000..7cae25d --- /dev/null +++ b/Packages/fidelityfx.fsr/Runtime/FidelityFX.FSR.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: eb1f729ceec147d4a813e8e75b65dc12 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders.meta b/Packages/fidelityfx.fsr/Shaders.meta new file mode 100644 index 0000000..cf5a649 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: af3a6660394230a4bb247808bbefe9f4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute new file mode 100644 index 0000000..63532d7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute @@ -0,0 +1,41 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE +#pragma multi_compile_local __ FFX_FSR2_OPTION_HDR_COLOR_INPUT +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH +#pragma multi_compile_local __ FFX_FSR2_OPTION_APPLY_SHARPENING + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +// Ensure the correct value is defined for this keyword, as it is used to select one of multiple sampler functions +#ifdef FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE +#undef FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE +#define FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE 1 +#endif + +#include "shaders/ffx_fsr2_accumulate_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute.meta new file mode 100644 index 0000000..5936e95 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_accumulate_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7e791d69a5be98247a93b63897bc64df +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute new file mode 100644 index 0000000..6bc2301 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute @@ -0,0 +1,32 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr2_autogen_reactive_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute.meta new file mode 100644 index 0000000..69662d6 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_autogen_reactive_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 67ee1b32ca5e4234db9f06984c783dee +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute new file mode 100644 index 0000000..7058cc5 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute @@ -0,0 +1,42 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +// Wave operations require shader model 6.0; this can only be enabled when using DXC on D3D12 +// These pragmas are commented out by default as Unity will sometimes ignore the #if's and try to enable these features anyway. +// Uncomment the below lines if you intend to try wave operations on DX12 with the DXC compiler. +//#if defined(UNITY_COMPILER_DXC) && defined(SHADER_API_D3D12) +//#pragma require WaveBasic // Required for WaveGetLaneIndex +//#pragma require WaveBallot // Required for WaveReadLaneAt +//#else +#define FFX_SPD_NO_WAVE_OPERATIONS +//#endif + +#include "shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute.meta new file mode 100644 index 0000000..91fe4a2 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_compute_luminance_pyramid_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 04c3480675e29a340808141e68d4cc8b +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute new file mode 100644 index 0000000..582ca6b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute @@ -0,0 +1,32 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr2_depth_clip_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute.meta new file mode 100644 index 0000000..72d9877 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_depth_clip_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b207de122e2c4b844b89dcd7c5c77c80 +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute new file mode 100644 index 0000000..fb12d2c --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute @@ -0,0 +1,32 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr2_lock_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute.meta new file mode 100644 index 0000000..3503b36 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_lock_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20b7864a7e7258946aaf0f1996febad3 +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute new file mode 100644 index 0000000..e6ac7df --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute @@ -0,0 +1,31 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr2_rcas_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute.meta new file mode 100644 index 0000000..ecbdb42 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_rcas_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40815651f0f5d994cb73da9816a7ff9b +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute new file mode 100644 index 0000000..12f2ec3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute @@ -0,0 +1,33 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_HDR_COLOR_INPUT +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute.meta new file mode 100644 index 0000000..92da2a6 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_reconstruct_previous_depth_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5060dfafe45aa67459629186ceb7464e +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute new file mode 100644 index 0000000..5d2668a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute @@ -0,0 +1,32 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR2_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr2_tcr_autogen_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute.meta new file mode 100644 index 0000000..fb8a28a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr2_tcr_autogen_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8b1c27fb6a544b43b38903592240500 +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_accumulate_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_accumulate_pass.compute new file mode 100644 index 0000000..a8d6864 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_accumulate_pass.compute @@ -0,0 +1,39 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +// Ensure the correct value is defined for this keyword, as it is used to select one of multiple sampler functions +#ifdef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE +#undef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE +#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 1 +#endif + +#include "shaders/ffx_fsr3upscaler_accumulate_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_accumulate_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_accumulate_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_autogen_reactive_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_autogen_reactive_pass.compute new file mode 100644 index 0000000..500352a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_autogen_reactive_pass.compute @@ -0,0 +1,29 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute new file mode 100644 index 0000000..027e414 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute @@ -0,0 +1,29 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_debug_view_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute.meta new file mode 100644 index 0000000..f90cac3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_debug_view_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cb24a71d54164c54eb5e86839acd48c5 +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute new file mode 100644 index 0000000..5cffc66 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute @@ -0,0 +1,29 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_instability_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute new file mode 100644 index 0000000..347b0d6 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute @@ -0,0 +1,39 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +// Wave operations require shader model 6.0; this can only be enabled when using DXC on D3D12 +// These pragmas are commented out by default as Unity will sometimes ignore the #if's and try to enable these features anyway. +// Uncomment the below lines if you intend to try wave operations on DX12 with the DXC compiler. +//#if defined(UNITY_COMPILER_DXC) && defined(SHADER_API_D3D12) +//#pragma require WaveBasic // Required for WaveGetLaneIndex +//#pragma require WaveBallot // Required for WaveReadLaneAt +//#else +#define FFX_SPD_NO_WAVE_OPERATIONS +//#endif + +#include "shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_luma_pyramid_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute new file mode 100644 index 0000000..b71ea5d --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute @@ -0,0 +1,31 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_inputs_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute new file mode 100644 index 0000000..ebf4e91 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute @@ -0,0 +1,29 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_prepare_reactivity_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_rcas_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_rcas_pass.compute new file mode 100644 index 0000000..282a9ae --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_rcas_pass.compute @@ -0,0 +1,27 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_rcas_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_rcas_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_rcas_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute new file mode 100644 index 0000000..34c3ade --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute @@ -0,0 +1,29 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_shading_change_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute.meta new file mode 100644 index 0000000..c9ae3eb --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a2bff2f97619ed4989d9b0577ba0641 +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute new file mode 100644 index 0000000..ffe5bfe --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute @@ -0,0 +1,32 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +// Wave operations require shader model 6.0; this can only be enabled when using DXC on D3D12 +#define FFX_SPD_NO_WAVE_OPERATIONS + +#include "shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl" diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute.meta new file mode 100644 index 0000000..889be6d --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 251e663738905fa4d8817001682d802f +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_tcr_autogen_pass.compute b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_tcr_autogen_pass.compute new file mode 100644 index 0000000..952c052 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_tcr_autogen_pass.compute @@ -0,0 +1,30 @@ +// 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. + +#pragma kernel CS + +#pragma multi_compile_local __ FFX_HALF +#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS + +#pragma multi_compile __ UNITY_FSR_TEXTURE2D_X_ARRAY + +#include "ffx_fsr_unity_common.cginc" + +#include "shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl" diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta diff --git a/Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc b/Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc new file mode 100644 index 0000000..b1bcb5c --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc @@ -0,0 +1,82 @@ +// 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. + +// Suppress a few warnings produced by FFX's HLSL code +#pragma warning(disable: 3078) // Loop control variable conflicts +#pragma warning(disable: 3203) // Signed/unsigned mismatch +#pragma warning(disable: 3556) // Integer divides might be much slower, try using uints if possible + +#define FFX_GPU // Compiling for GPU +#define FFX_HLSL // Compile for plain HLSL + +// Use the DXC shader compiler on modern graphics APIs to enable a few advanced features +// The DXC-related pragmas are disabled by default, as DXC doesn't support all platforms yet and will break on some platforms when enabled. +// Consider this to be an experimental feature. If you want to benefit from 16-bit floating point and wave operations, and don't care about supporting older graphics APIs, then it's worth a try. +//#if defined(SHADER_API_D3D12) || defined(SHADER_API_VULKAN) || defined(SHADER_API_METAL) +//#pragma use_dxc // Using DXC will currently break DX11 support since DX11 and DX12 share the same shader bytecode in Unity. +//#endif + +// Enable half precision data types on platforms that support it +//#if defined(UNITY_COMPILER_DXC) && defined(FFX_HALF) +//#pragma require Native16Bit +//#endif + +// Hack to work around the lack of texture atomics on Metal +#if defined(SHADER_API_METAL) +#define InterlockedAdd(dest, val, orig) { (orig) = (dest); (dest) += (val); } +#define InterlockedMin(dest, val) { (dest) = min((dest), (val)); } +#define InterlockedMax(dest, val) { (dest) = max((dest), (val)); } +#endif + +// Workaround for HDRP using texture arrays for its camera buffers on some platforms +// The below defines are adapted from: Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureXR.hlsl +#if ((defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12)) && !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_GAMECORE)) || defined(SHADER_API_PSSL) || defined(SHADER_API_VULKAN) + #define UNITY_TEXTURE2D_X_ARRAY_SUPPORTED +#endif + +// Control if TEXTURE2D_X macros will expand to texture arrays +#if defined(UNITY_TEXTURE2D_X_ARRAY_SUPPORTED) && defined(UNITY_FSR_TEXTURE2D_X_ARRAY) + #define USE_TEXTURE2D_X_AS_ARRAY +#endif + +// Early defines for single-pass instancing +#if defined(STEREO_INSTANCING_ON) && defined(UNITY_TEXTURE2D_X_ARRAY_SUPPORTED) + #define UNITY_STEREO_INSTANCING_ENABLED +#endif + +// Helper macros to handle XR single-pass with Texture2DArray +#if defined(USE_TEXTURE2D_X_AS_ARRAY) + + // Only single-pass stereo instancing used array indexing + #if defined(UNITY_STEREO_INSTANCING_ENABLED) + static uint unity_StereoEyeIndex; + #define SLICE_ARRAY_INDEX unity_StereoEyeIndex + #else + #define SLICE_ARRAY_INDEX 0 + #endif + + // Declare and sample camera buffers as texture arrays + #define UNITY_FSR_TEX2D(type) Texture2DArray + #define UNITY_FSR_RWTEX2D(type) RWTexture2DArray + #define UNITY_FSR_POS(pxPos) FfxUInt32x3(pxPos, SLICE_ARRAY_INDEX) + #define UNITY_FSR_UV(uv) FfxFloat32x3(uv, SLICE_ARRAY_INDEX) + #define UNITY_FSR_GETDIMS(tex, w, h) { FfxUInt32 uElements; (tex).GetDimensions((w), (h), uElements); } + +#endif diff --git a/Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta b/Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc.meta similarity index 100% rename from Assets/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta rename to Packages/fidelityfx.fsr/Shaders/ffx_fsr_unity_common.cginc.meta diff --git a/Packages/fidelityfx.fsr/Shaders/shaders.meta b/Packages/fidelityfx.fsr/Shaders/shaders.meta new file mode 100644 index 0000000..8e1a170 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c3f8af1cab72f0e46acba11c5820d923 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h new file mode 100644 index 0000000..09d4502 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h @@ -0,0 +1,654 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_COMMON_TYPES_H +#define FFX_COMMON_TYPES_H + +#if defined(FFX_CPU) +#define FFX_PARAMETER_IN +#define FFX_PARAMETER_OUT +#define FFX_PARAMETER_INOUT +#define FFX_PARAMETER_UNIFORM +#elif defined(FFX_HLSL) +#define FFX_PARAMETER_IN in +#define FFX_PARAMETER_OUT out +#define FFX_PARAMETER_INOUT inout +#define FFX_PARAMETER_UNIFORM uniform +#elif defined(FFX_GLSL) +#define FFX_PARAMETER_IN in +#define FFX_PARAMETER_OUT out +#define FFX_PARAMETER_INOUT inout +#define FFX_PARAMETER_UNIFORM const //[cacao_placeholder] until a better fit is found! +#endif // #if defined(FFX_CPU) + +#if defined(FFX_CPU) +/// A typedef for a boolean value. +/// +/// @ingroup CPUTypes +typedef bool FfxBoolean; + +/// A typedef for a unsigned 8bit integer. +/// +/// @ingroup CPUTypes +typedef uint8_t FfxUInt8; + +/// A typedef for a unsigned 16bit integer. +/// +/// @ingroup CPUTypes +typedef uint16_t FfxUInt16; + +/// A typedef for a unsigned 32bit integer. +/// +/// @ingroup CPUTypes +typedef uint32_t FfxUInt32; + +/// A typedef for a unsigned 64bit integer. +/// +/// @ingroup CPUTypes +typedef uint64_t FfxUInt64; + +/// A typedef for a signed 8bit integer. +/// +/// @ingroup CPUTypes +typedef int8_t FfxInt8; + +/// A typedef for a signed 16bit integer. +/// +/// @ingroup CPUTypes +typedef int16_t FfxInt16; + +/// A typedef for a signed 32bit integer. +/// +/// @ingroup CPUTypes +typedef int32_t FfxInt32; + +/// A typedef for a signed 64bit integer. +/// +/// @ingroup CPUTypes +typedef int64_t FfxInt64; + +/// A typedef for a floating point value. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32; + +/// A typedef for a 2-dimensional floating point value. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x2[2]; + +/// A typedef for a 3-dimensional floating point value. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x3[3]; + +/// A typedef for a 4-dimensional floating point value. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x4[4]; + +/// A typedef for a 2x2 floating point matrix. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x2x2[4]; + +/// A typedef for a 3x3 floating point matrix. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x3x3[9]; + +/// A typedef for a 3x4 floating point matrix. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x3x4[12]; + +/// A typedef for a 4x4 floating point matrix. +/// +/// @ingroup CPUTypes +typedef float FfxFloat32x4x4[16]; + +/// A typedef for a 2-dimensional 32bit signed integer. +/// +/// @ingroup CPUTypes +typedef int32_t FfxInt32x2[2]; + +/// A typedef for a 3-dimensional 32bit signed integer. +/// +/// @ingroup CPUTypes +typedef int32_t FfxInt32x3[3]; + +/// A typedef for a 4-dimensional 32bit signed integer. +/// +/// @ingroup CPUTypes +typedef int32_t FfxInt32x4[4]; + +/// A typedef for a 2-dimensional 32bit usigned integer. +/// +/// @ingroup CPUTypes +typedef uint32_t FfxUInt32x2[2]; + +/// A typedef for a 3-dimensional 32bit unsigned integer. +/// +/// @ingroup CPUTypes +typedef uint32_t FfxUInt32x3[3]; + +/// A typedef for a 4-dimensional 32bit unsigned integer. +/// +/// @ingroup CPUTypes +typedef uint32_t FfxUInt32x4[4]; +#endif // #if defined(FFX_CPU) + +#if defined(FFX_HLSL) + +#define FfxFloat32Mat4 matrix +#define FfxFloat32Mat3 matrix + +/// A typedef for a boolean value. +/// +/// @ingroup HLSLTypes +typedef bool FfxBoolean; + +#if FFX_HLSL_SM>=62 + +/// @defgroup HLSL62Types HLSL 6.2 And Above Types +/// HLSL 6.2 and above type defines for all commonly used variables +/// +/// @ingroup HLSLTypes + +/// A typedef for a floating point value. +/// +/// @ingroup HLSL62Types +typedef float32_t FfxFloat32; + +/// A typedef for a 2-dimensional floating point value. +/// +/// @ingroup HLSL62Types +typedef float32_t2 FfxFloat32x2; + +/// A typedef for a 3-dimensional floating point value. +/// +/// @ingroup HLSL62Types +typedef float32_t3 FfxFloat32x3; + +/// A typedef for a 4-dimensional floating point value. +/// +/// @ingroup HLSL62Types +typedef float32_t4 FfxFloat32x4; + +/// A [cacao_placeholder] typedef for matrix type until confirmed. +typedef float4x4 FfxFloat32x4x4; +typedef float3x4 FfxFloat32x3x4; +typedef float3x3 FfxFloat32x3x3; +typedef float2x2 FfxFloat32x2x2; + +/// A typedef for a unsigned 32bit integer. +/// +/// @ingroup HLSL62Types +typedef uint32_t FfxUInt32; + +/// A typedef for a 2-dimensional 32bit unsigned integer. +/// +/// @ingroup HLSL62Types +typedef uint32_t2 FfxUInt32x2; + +/// A typedef for a 3-dimensional 32bit unsigned integer. +/// +/// @ingroup HLSL62Types +typedef uint32_t3 FfxUInt32x3; + +/// A typedef for a 4-dimensional 32bit unsigned integer. +/// +/// @ingroup HLSL62Types +typedef uint32_t4 FfxUInt32x4; + +/// A typedef for a signed 32bit integer. +/// +/// @ingroup HLSL62Types +typedef int32_t FfxInt32; + +/// A typedef for a 2-dimensional signed 32bit integer. +/// +/// @ingroup HLSL62Types +typedef int32_t2 FfxInt32x2; + +/// A typedef for a 3-dimensional signed 32bit integer. +/// +/// @ingroup HLSL62Types +typedef int32_t3 FfxInt32x3; + +/// A typedef for a 4-dimensional signed 32bit integer. +/// +/// @ingroup HLSL62Types +typedef int32_t4 FfxInt32x4; + +#else // #if FFX_HLSL_SM>=62 + +/// @defgroup HLSLBaseTypes HLSL 6.1 And Below Types +/// HLSL 6.1 and below type defines for all commonly used variables +/// +/// @ingroup HLSLTypes + +#define FfxFloat32 float +#define FfxFloat32x2 float2 +#define FfxFloat32x3 float3 +#define FfxFloat32x4 float4 + +/// A [cacao_placeholder] typedef for matrix type until confirmed. +#define FfxFloat32x4x4 float4x4 +#define FfxFloat32x3x4 float3x4 +#define FfxFloat32x3x3 float3x3 +#define FfxFloat32x2x2 float2x2 + +/// A typedef for a unsigned 32bit integer. +/// +/// @ingroup GPU +typedef uint FfxUInt32; +typedef uint2 FfxUInt32x2; +typedef uint3 FfxUInt32x3; +typedef uint4 FfxUInt32x4; + +typedef int FfxInt32; +typedef int2 FfxInt32x2; +typedef int3 FfxInt32x3; +typedef int4 FfxInt32x4; + +#endif // #if FFX_HLSL_SM>=62 + +#if FFX_HALF + +#if FFX_HLSL_SM >= 62 + +typedef float16_t FfxFloat16; +typedef float16_t2 FfxFloat16x2; +typedef float16_t3 FfxFloat16x3; +typedef float16_t4 FfxFloat16x4; + +/// A typedef for an unsigned 16bit integer. +/// +/// @ingroup HLSLTypes +typedef uint16_t FfxUInt16; +typedef uint16_t2 FfxUInt16x2; +typedef uint16_t3 FfxUInt16x3; +typedef uint16_t4 FfxUInt16x4; + +/// A typedef for a signed 16bit integer. +/// +/// @ingroup HLSLTypes +typedef int16_t FfxInt16; +typedef int16_t2 FfxInt16x2; +typedef int16_t3 FfxInt16x3; +typedef int16_t4 FfxInt16x4; +#elif SHADER_API_PSSL +#pragma argument(realtypes) // Enable true 16-bit types + +typedef half FfxFloat16; +typedef half2 FfxFloat16x2; +typedef half3 FfxFloat16x3; +typedef half4 FfxFloat16x4; + +/// A typedef for an unsigned 16bit integer. +/// +/// @ingroup GPU +typedef ushort FfxUInt16; +typedef ushort2 FfxUInt16x2; +typedef ushort3 FfxUInt16x3; +typedef ushort4 FfxUInt16x4; + +/// A typedef for a signed 16bit integer. +/// +/// @ingroup GPU +typedef short FfxInt16; +typedef short2 FfxInt16x2; +typedef short3 FfxInt16x3; +typedef short4 FfxInt16x4; +#else // #if FFX_HLSL_SM>=62 +typedef min16float FfxFloat16; +typedef min16float2 FfxFloat16x2; +typedef min16float3 FfxFloat16x3; +typedef min16float4 FfxFloat16x4; + +/// A typedef for an unsigned 16bit integer. +/// +/// @ingroup HLSLTypes +typedef min16uint FfxUInt16; +typedef min16uint2 FfxUInt16x2; +typedef min16uint3 FfxUInt16x3; +typedef min16uint4 FfxUInt16x4; + +/// A typedef for a signed 16bit integer. +/// +/// @ingroup HLSLTypes +typedef min16int FfxInt16; +typedef min16int2 FfxInt16x2; +typedef min16int3 FfxInt16x3; +typedef min16int4 FfxInt16x4; +#endif // #if FFX_HLSL_SM>=62 + +#endif // FFX_HALF + +#endif // #if defined(FFX_HLSL) + +#if defined(FFX_GLSL) + +#define FfxFloat32Mat4 mat4 +#define FfxFloat32Mat3 mat3 + +/// A typedef for a boolean value. +/// +/// @ingroup GLSLTypes +#define FfxBoolean bool +#define FfxFloat32 float +#define FfxFloat32x2 vec2 +#define FfxFloat32x3 vec3 +#define FfxFloat32x4 vec4 +#define FfxUInt32 uint +#define FfxUInt32x2 uvec2 +#define FfxUInt32x3 uvec3 +#define FfxUInt32x4 uvec4 +#define FfxInt32 int +#define FfxInt32x2 ivec2 +#define FfxInt32x3 ivec3 +#define FfxInt32x4 ivec4 + +/// A [cacao_placeholder] typedef for matrix type until confirmed. +#define FfxFloat32x4x4 mat4 +#define FfxFloat32x3x4 mat4x3 +#define FfxFloat32x3x3 mat3 +#define FfxFloat32x2x2 mat2 + +#if FFX_HALF +#define FfxFloat16 float16_t +#define FfxFloat16x2 f16vec2 +#define FfxFloat16x3 f16vec3 +#define FfxFloat16x4 f16vec4 +#define FfxUInt16 uint16_t +#define FfxUInt16x2 u16vec2 +#define FfxUInt16x3 u16vec3 +#define FfxUInt16x4 u16vec4 +#define FfxInt16 int16_t +#define FfxInt16x2 i16vec2 +#define FfxInt16x3 i16vec3 +#define FfxInt16x4 i16vec4 +#endif // FFX_HALF +#endif // #if defined(FFX_GLSL) + +// Global toggles: +// #define FFX_HALF (1) +// #define FFX_HLSL_SM (62) + +#if FFX_HALF && !defined(SHADER_API_PSSL) + +#if FFX_HLSL_SM >= 62 + +#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType##16_t TypeName; +#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; +#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; + +#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType##16_t TypeName; +#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; +#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; + +#else //FFX_HLSL_SM>=62 + +#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef min16##BaseComponentType TypeName; +#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; +#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; + +#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) FFX_MIN16_SCALAR( TypeName, BaseComponentType ); +#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ); +#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ); + +#endif //FFX_HLSL_SM>=62 + +#else //FFX_HALF + +#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType TypeName; +#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; +#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; + +#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType TypeName; +#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; +#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; + +#endif //FFX_HALF + +#if defined(FFX_GPU) +// Common typedefs: +#if defined(FFX_HLSL) && !defined(SHADER_API_PSSL) +FFX_MIN16_SCALAR( FFX_MIN16_F , float ); +FFX_MIN16_VECTOR( FFX_MIN16_F2, float, 2 ); +FFX_MIN16_VECTOR( FFX_MIN16_F3, float, 3 ); +FFX_MIN16_VECTOR( FFX_MIN16_F4, float, 4 ); + +FFX_MIN16_SCALAR( FFX_MIN16_I, int ); +FFX_MIN16_VECTOR( FFX_MIN16_I2, int, 2 ); +FFX_MIN16_VECTOR( FFX_MIN16_I3, int, 3 ); +FFX_MIN16_VECTOR( FFX_MIN16_I4, int, 4 ); + +FFX_MIN16_SCALAR( FFX_MIN16_U, uint ); +FFX_MIN16_VECTOR( FFX_MIN16_U2, uint, 2 ); +FFX_MIN16_VECTOR( FFX_MIN16_U3, uint, 3 ); +FFX_MIN16_VECTOR( FFX_MIN16_U4, uint, 4 ); + +FFX_16BIT_SCALAR( FFX_F16_t , float ); +FFX_16BIT_VECTOR( FFX_F16_t2, float, 2 ); +FFX_16BIT_VECTOR( FFX_F16_t3, float, 3 ); +FFX_16BIT_VECTOR( FFX_F16_t4, float, 4 ); + +FFX_16BIT_SCALAR( FFX_I16_t, int ); +FFX_16BIT_VECTOR( FFX_I16_t2, int, 2 ); +FFX_16BIT_VECTOR( FFX_I16_t3, int, 3 ); +FFX_16BIT_VECTOR( FFX_I16_t4, int, 4 ); + +FFX_16BIT_SCALAR( FFX_U16_t, uint ); +FFX_16BIT_VECTOR( FFX_U16_t2, uint, 2 ); +FFX_16BIT_VECTOR( FFX_U16_t3, uint, 3 ); +FFX_16BIT_VECTOR( FFX_U16_t4, uint, 4 ); + +#define TYPEDEF_MIN16_TYPES(Prefix) \ +typedef FFX_MIN16_F Prefix##_F; \ +typedef FFX_MIN16_F2 Prefix##_F2; \ +typedef FFX_MIN16_F3 Prefix##_F3; \ +typedef FFX_MIN16_F4 Prefix##_F4; \ +typedef FFX_MIN16_I Prefix##_I; \ +typedef FFX_MIN16_I2 Prefix##_I2; \ +typedef FFX_MIN16_I3 Prefix##_I3; \ +typedef FFX_MIN16_I4 Prefix##_I4; \ +typedef FFX_MIN16_U Prefix##_U; \ +typedef FFX_MIN16_U2 Prefix##_U2; \ +typedef FFX_MIN16_U3 Prefix##_U3; \ +typedef FFX_MIN16_U4 Prefix##_U4; + +#define TYPEDEF_16BIT_TYPES(Prefix) \ +typedef FFX_16BIT_F Prefix##_F; \ +typedef FFX_16BIT_F2 Prefix##_F2; \ +typedef FFX_16BIT_F3 Prefix##_F3; \ +typedef FFX_16BIT_F4 Prefix##_F4; \ +typedef FFX_16BIT_I Prefix##_I; \ +typedef FFX_16BIT_I2 Prefix##_I2; \ +typedef FFX_16BIT_I3 Prefix##_I3; \ +typedef FFX_16BIT_I4 Prefix##_I4; \ +typedef FFX_16BIT_U Prefix##_U; \ +typedef FFX_16BIT_U2 Prefix##_U2; \ +typedef FFX_16BIT_U3 Prefix##_U3; \ +typedef FFX_16BIT_U4 Prefix##_U4; + +#define TYPEDEF_FULL_PRECISION_TYPES(Prefix) \ +typedef FfxFloat32 Prefix##_F; \ +typedef FfxFloat32x2 Prefix##_F2; \ +typedef FfxFloat32x3 Prefix##_F3; \ +typedef FfxFloat32x4 Prefix##_F4; \ +typedef FfxInt32 Prefix##_I; \ +typedef FfxInt32x2 Prefix##_I2; \ +typedef FfxInt32x3 Prefix##_I3; \ +typedef FfxInt32x4 Prefix##_I4; \ +typedef FfxUInt32 Prefix##_U; \ +typedef FfxUInt32x2 Prefix##_U2; \ +typedef FfxUInt32x3 Prefix##_U3; \ +typedef FfxUInt32x4 Prefix##_U4; +#endif // #if defined(FFX_HLSL) + +#if defined(SHADER_API_PSSL) + +#define unorm +#define globallycoherent + +#if FFX_HALF + +#define FFX_MIN16_F half +#define FFX_MIN16_F2 half2 +#define FFX_MIN16_F3 half3 +#define FFX_MIN16_F4 half4 + +#define FFX_MIN16_I short +#define FFX_MIN16_I2 short2 +#define FFX_MIN16_I3 short3 +#define FFX_MIN16_I4 short4 + +#define FFX_MIN16_U ushort +#define FFX_MIN16_U2 ushort2 +#define FFX_MIN16_U3 ushort3 +#define FFX_MIN16_U4 ushort4 + +#define FFX_16BIT_F half +#define FFX_16BIT_F2 half2 +#define FFX_16BIT_F3 half3 +#define FFX_16BIT_F4 half4 + +#define FFX_16BIT_I short +#define FFX_16BIT_I2 short2 +#define FFX_16BIT_I3 short3 +#define FFX_16BIT_I4 short4 + +#define FFX_16BIT_U ushort +#define FFX_16BIT_U2 ushort2 +#define FFX_16BIT_U3 ushort3 +#define FFX_16BIT_U4 ushort4 + +#else // FFX_HALF + +#define FFX_MIN16_F float +#define FFX_MIN16_F2 float2 +#define FFX_MIN16_F3 float3 +#define FFX_MIN16_F4 float4 + +#define FFX_MIN16_I int +#define FFX_MIN16_I2 int2 +#define FFX_MIN16_I3 int3 +#define FFX_MIN16_I4 int4 + +#define FFX_MIN16_U uint +#define FFX_MIN16_U2 uint2 +#define FFX_MIN16_U3 uint3 +#define FFX_MIN16_U4 uint4 + +#define FFX_16BIT_F float +#define FFX_16BIT_F2 float2 +#define FFX_16BIT_F3 float3 +#define FFX_16BIT_F4 float4 + +#define FFX_16BIT_I int +#define FFX_16BIT_I2 int2 +#define FFX_16BIT_I3 int3 +#define FFX_16BIT_I4 int4 + +#define FFX_16BIT_U uint +#define FFX_16BIT_U2 uint2 +#define FFX_16BIT_U3 uint3 +#define FFX_16BIT_U4 uint4 + +#endif // FFX_HALF + +#endif // #if defined(SHADER_API_PSSL) + +#if defined(FFX_GLSL) + +#if FFX_HALF + +#define FFX_MIN16_F float16_t +#define FFX_MIN16_F2 f16vec2 +#define FFX_MIN16_F3 f16vec3 +#define FFX_MIN16_F4 f16vec4 + +#define FFX_MIN16_I int16_t +#define FFX_MIN16_I2 i16vec2 +#define FFX_MIN16_I3 i16vec3 +#define FFX_MIN16_I4 i16vec4 + +#define FFX_MIN16_U uint16_t +#define FFX_MIN16_U2 u16vec2 +#define FFX_MIN16_U3 u16vec3 +#define FFX_MIN16_U4 u16vec4 + +#define FFX_16BIT_F float16_t +#define FFX_16BIT_F2 f16vec2 +#define FFX_16BIT_F3 f16vec3 +#define FFX_16BIT_F4 f16vec4 + +#define FFX_16BIT_I int16_t +#define FFX_16BIT_I2 i16vec2 +#define FFX_16BIT_I3 i16vec3 +#define FFX_16BIT_I4 i16vec4 + +#define FFX_16BIT_U uint16_t +#define FFX_16BIT_U2 u16vec2 +#define FFX_16BIT_U3 u16vec3 +#define FFX_16BIT_U4 u16vec4 + +#else // FFX_HALF + +#define FFX_MIN16_F float +#define FFX_MIN16_F2 vec2 +#define FFX_MIN16_F3 vec3 +#define FFX_MIN16_F4 vec4 + +#define FFX_MIN16_I int +#define FFX_MIN16_I2 ivec2 +#define FFX_MIN16_I3 ivec3 +#define FFX_MIN16_I4 ivec4 + +#define FFX_MIN16_U uint +#define FFX_MIN16_U2 uvec2 +#define FFX_MIN16_U3 uvec3 +#define FFX_MIN16_U4 uvec4 + +#define FFX_16BIT_F float +#define FFX_16BIT_F2 vec2 +#define FFX_16BIT_F3 vec3 +#define FFX_16BIT_F4 vec4 + +#define FFX_16BIT_I int +#define FFX_16BIT_I2 ivec2 +#define FFX_16BIT_I3 ivec3 +#define FFX_16BIT_I4 ivec4 + +#define FFX_16BIT_U uint +#define FFX_16BIT_U2 uvec2 +#define FFX_16BIT_U3 uvec3 +#define FFX_16BIT_U4 uvec4 + +#endif // FFX_HALF + +#endif // #if defined(FFX_GLSL) + +#endif // #if defined(FFX_GPU) +#endif // #ifndef FFX_COMMON_TYPES_H diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h.meta new file mode 100644 index 0000000..d0f05eb --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_common_types.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: ec0f8c94ee9930b438b99b82735d181b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h new file mode 100644 index 0000000..d1ed144 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h @@ -0,0 +1,80 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +/// @defgroup FfxGPU GPU +/// The FidelityFX SDK GPU References +/// +/// @ingroup ffxSDK + +/// @defgroup FfxHLSL HLSL References +/// FidelityFX SDK HLSL GPU References +/// +/// @ingroup FfxGPU + +/// @defgroup FfxGLSL GLSL References +/// FidelityFX SDK GLSL GPU References +/// +/// @ingroup FfxGPU + +/// @defgroup FfxGPUEffects FidelityFX GPU References +/// FidelityFX Effect GPU Reference Documentation +/// +/// @ingroup FfxGPU + +/// @defgroup GPUCore GPU Core +/// GPU defines and functions +/// +/// @ingroup FfxGPU + +#if !defined(FFX_CORE_H) +#define FFX_CORE_H + +#ifdef __hlsl_dx_compiler +#pragma dxc diagnostic push +#pragma dxc diagnostic ignored "-Wambig-lit-shift" +#endif //__hlsl_dx_compiler + +#include "ffx_common_types.h" + +#if defined(FFX_CPU) + #include "ffx_core_cpu.h" +#endif // #if defined(FFX_CPU) + +#if defined(FFX_GLSL) && defined(FFX_GPU) + #include "ffx_core_glsl.h" +#endif // #if defined(FFX_GLSL) && defined(FFX_GPU) + +#if defined(FFX_HLSL) && defined(FFX_GPU) + #include "ffx_core_hlsl.h" +#endif // #if defined(FFX_HLSL) && defined(FFX_GPU) + +#if defined(FFX_GPU) + #include "ffx_core_gpu_common.h" + #include "ffx_core_gpu_common_half.h" + #include "ffx_core_portability.h" +#endif // #if defined(FFX_GPU) + +#ifdef __hlsl_dx_compiler +#pragma dxc diagnostic pop +#endif //__hlsl_dx_compiler + +#endif // #if !defined(FFX_CORE_H) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h.meta new file mode 100644 index 0000000..18282d1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 223ad96bb47790e4d8658dd82ba950f3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h new file mode 100644 index 0000000..9f88c94 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h @@ -0,0 +1,2736 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +/// A define for a true value in a boolean expression. +/// +/// @ingroup GPUCore +#define FFX_TRUE (true) + +/// A define for a false value in a boolean expression. +/// +/// @ingroup GPUCore +#define FFX_FALSE (false) + +/// A define value for positive infinity. +/// +/// @ingroup GPUCore +#define FFX_POSITIVE_INFINITY_FLOAT ffxAsFloat(0x7f800000u) + +/// A define value for negative infinity. +/// +/// @ingroup GPUCore +#define FFX_NEGATIVE_INFINITY_FLOAT ffxAsFloat(0xff800000u) + +/// A define value for PI. +/// +/// @ingroup GPUCore +#define FFX_PI (3.14159) + +FFX_STATIC const FfxFloat32 FFX_FP16_MIN = 6.10e-05f; +FFX_STATIC const FfxFloat32 FFX_FP16_MAX = 65504.0f; +FFX_STATIC const FfxFloat32 FFX_TONEMAP_EPSILON = 1.0f / FFX_FP16_MAX; + +#define FFX_HAS_FLAG(v, f) ((v & f) == f) + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32 ffxMin(FfxFloat32 x, FfxFloat32 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxMin(FfxFloat32x2 x, FfxFloat32x2 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxMin(FfxFloat32x3 x, FfxFloat32x3 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxMin(FfxFloat32x4 x, FfxFloat32x4 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32 ffxMin(FfxInt32 x, FfxInt32 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32x2 ffxMin(FfxInt32x2 x, FfxInt32x2 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32x3 ffxMin(FfxInt32x3 x, FfxInt32x3 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32x4 ffxMin(FfxInt32x4 x, FfxInt32x4 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32 ffxMin(FfxUInt32 x, FfxUInt32 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxMin(FfxUInt32x2 x, FfxUInt32x2 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32x3 ffxMin(FfxUInt32x3 x, FfxUInt32x3 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32x4 ffxMin(FfxUInt32x4 x, FfxUInt32x4 y) +{ + return min(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32 ffxMax(FfxFloat32 x, FfxFloat32 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxMax(FfxFloat32x2 x, FfxFloat32x2 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxMax(FfxFloat32x3 x, FfxFloat32x3 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxMax(FfxFloat32x4 x, FfxFloat32x4 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32 ffxMax(FfxInt32 x, FfxInt32 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32x2 ffxMax(FfxInt32x2 x, FfxInt32x2 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32x3 ffxMax(FfxInt32x3 x, FfxInt32x3 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt32x4 ffxMax(FfxInt32x4 x, FfxInt32x4 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32 ffxMax(FfxUInt32 x, FfxUInt32 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxMax(FfxUInt32x2 x, FfxUInt32x2 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32x3 ffxMax(FfxUInt32x3 x, FfxUInt32x3 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt32x4 ffxMax(FfxUInt32x4 x, FfxUInt32x4 y) +{ + return max(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat32 ffxPow(FfxFloat32 x, FfxFloat32 y) +{ + return pow(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxPow(FfxFloat32x2 x, FfxFloat32x2 y) +{ + return pow(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxPow(FfxFloat32x3 x, FfxFloat32x3 y) +{ + return pow(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxPow(FfxFloat32x4 x, FfxFloat32x4 y) +{ + return pow(x, y); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat32 ffxSqrt(FfxFloat32 x) +{ + return sqrt(x); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxSqrt(FfxFloat32x2 x) +{ + return sqrt(x); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxSqrt(FfxFloat32x3 x) +{ + return sqrt(x); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxSqrt(FfxFloat32x4 x) +{ + return sqrt(x); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat32 ffxCopySignBit(FfxFloat32 d, FfxFloat32 s) +{ + return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & FfxUInt32(0x80000000u))); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxCopySignBit(FfxFloat32x2 d, FfxFloat32x2 s) +{ + return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast2(0x80000000u))); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxCopySignBit(FfxFloat32x3 d, FfxFloat32x3 s) +{ + return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast3(0x80000000u))); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxCopySignBit(FfxFloat32x4 d, FfxFloat32x4 s) +{ + return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast4(0x80000000u))); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat32 ffxIsSigned(FfxFloat32 m) +{ + return ffxSaturate(m * FfxFloat32(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxIsSigned(FfxFloat32x2 m) +{ + return ffxSaturate(m * ffxBroadcast2(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxIsSigned(FfxFloat32x3 m) +{ + return ffxSaturate(m * ffxBroadcast3(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against for have the sign set. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or positive. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxIsSigned(FfxFloat32x4 m) +{ + return ffxSaturate(m * ffxBroadcast4(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat32 ffxIsGreaterThanZero(FfxFloat32 m) +{ + return ffxSaturate(m * FfxFloat32(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxIsGreaterThanZero(FfxFloat32x2 m) +{ + return ffxSaturate(m * ffxBroadcast2(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxIsGreaterThanZero(FfxFloat32x3 m) +{ + return ffxSaturate(m * ffxBroadcast3(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxIsGreaterThanZero(FfxFloat32x4 m) +{ + return ffxSaturate(m * ffxBroadcast4(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// Convert a 32bit floating point value to sortable integer. +/// +/// - If sign bit=0, flip the sign bit (positives). +/// - If sign bit=1, flip all bits (negatives). +/// +/// The function has the side effects that: +/// - Larger integers are more positive values. +/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +/// +/// @param [in] value The floating point value to make sortable. +/// +/// @returns +/// The sortable integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxFloatToSortableInteger(FfxUInt32 value) +{ + return value ^ ((ffxAShrSU1(value, FfxUInt32(31))) | FfxUInt32(0x80000000)); +} + +/// Convert a sortable integer to a 32bit floating point value. +/// +/// The function has the side effects that: +/// - If sign bit=1, flip the sign bit (positives). +/// - If sign bit=0, flip all bits (negatives). +/// +/// @param [in] value The floating point value to make sortable. +/// +/// @returns +/// The sortable integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxSortableIntegerToFloat(FfxUInt32 value) +{ + return value ^ ((~ffxAShrSU1(value, FfxUInt32(31))) | FfxUInt32(0x80000000)); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateSqrt(FfxFloat32 value) +{ + return ffxAsFloat((ffxAsUInt32(value) >> FfxUInt32(1)) + FfxUInt32(0x1fbc4639)); +} + +/// Calculate a low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateReciprocal(FfxFloat32 value) +{ + return ffxAsFloat(FfxUInt32(0x7ef07ebb) - ffxAsUInt32(value)); +} + +/// Calculate a medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateReciprocalMedium(FfxFloat32 value) +{ + FfxFloat32 b = ffxAsFloat(FfxUInt32(0x7ef19fff) - ffxAsUInt32(value)); + return b * (-b * value + FfxFloat32(2.0)); +} + +/// Calculate a low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal square root for. +/// +/// @returns +/// An approximation of the reciprocal square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateReciprocalSquareRoot(FfxFloat32 value) +{ + return ffxAsFloat(FfxUInt32(0x5f347d74) - (ffxAsUInt32(value) >> FfxUInt32(1))); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateSqrt(FfxFloat32x2 value) +{ + return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast2(1u)) + ffxBroadcast2(0x1fbc4639u)); +} + +/// Calculate a low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateReciprocal(FfxFloat32x2 value) +{ + return ffxAsFloat(ffxBroadcast2(0x7ef07ebbu) - ffxAsUInt32(value)); +} + +/// Calculate a medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateReciprocalMedium(FfxFloat32x2 value) +{ + FfxFloat32x2 b = ffxAsFloat(ffxBroadcast2(0x7ef19fffu) - ffxAsUInt32(value)); + return b * (-b * value + ffxBroadcast2(2.0f)); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateReciprocalSquareRoot(FfxFloat32x2 value) +{ + return ffxAsFloat(ffxBroadcast2(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast2(1u))); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateSqrt(FfxFloat32x3 value) +{ + return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast3(1u)) + ffxBroadcast3(0x1fbc4639u)); +} + +/// Calculate a low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateReciprocal(FfxFloat32x3 value) +{ + return ffxAsFloat(ffxBroadcast3(0x7ef07ebbu) - ffxAsUInt32(value)); +} + +/// Calculate a medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateReciprocalMedium(FfxFloat32x3 value) +{ + FfxFloat32x3 b = ffxAsFloat(ffxBroadcast3(0x7ef19fffu) - ffxAsUInt32(value)); + return b * (-b * value + ffxBroadcast3(2.0f)); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateReciprocalSquareRoot(FfxFloat32x3 value) +{ + return ffxAsFloat(ffxBroadcast3(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast3(1u))); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateSqrt(FfxFloat32x4 value) +{ + return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast4(1u)) + ffxBroadcast4(0x1fbc4639u)); +} + +/// Calculate a low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateReciprocal(FfxFloat32x4 value) +{ + return ffxAsFloat(ffxBroadcast4(0x7ef07ebbu) - ffxAsUInt32(value)); +} + +/// Calculate a medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateReciprocalMedium(FfxFloat32x4 value) +{ + FfxFloat32x4 b = ffxAsFloat(ffxBroadcast4(0x7ef19fffu) - ffxAsUInt32(value)); + return b * (-b * value + ffxBroadcast4(2.0f)); +} + +/// Calculate a low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] value The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateReciprocalSquareRoot(FfxFloat32x4 value) +{ + return ffxAsFloat(ffxBroadcast4(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast4(1u))); +} + +/// Calculate dot product of 'a' and 'b'. +/// +/// @param [in] a First vector input. +/// @param [in] b Second vector input. +/// +/// @returns +/// The value of a dot b. +/// +/// @ingroup GPUCore +FfxFloat32 ffxDot2(FfxFloat32x2 a, FfxFloat32x2 b) +{ + return dot(a, b); +} + +/// Calculate dot product of 'a' and 'b'. +/// +/// @param [in] a First vector input. +/// @param [in] b Second vector input. +/// +/// @returns +/// The value of a dot b. +/// +/// @ingroup GPUCore +FfxFloat32 ffxDot3(FfxFloat32x3 a, FfxFloat32x3 b) +{ + return dot(a, b); +} + +/// Calculate dot product of 'a' and 'b'. +/// +/// @param [in] a First vector input. +/// @param [in] b Second vector input. +/// +/// @returns +/// The value of a dot b. +/// +/// @ingroup GPUCore +FfxFloat32 ffxDot4(FfxFloat32x4 a, FfxFloat32x4 b) +{ + return dot(a, b); +} + + +/// Compute an approximate conversion from PQ to Gamma2 space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and Gamma2. +/// +/// @returns +/// The value a converted into Gamma2. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximatePQToGamma2Medium(FfxFloat32 a) +{ + return a * a * a * a; +} + +/// Compute an approximate conversion from PQ to linear space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and linear. +/// +/// @returns +/// The value a converted into linear. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximatePQToLinear(FfxFloat32 a) +{ + return a * a * a * a * a * a * a * a; +} + +/// Compute an approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateGamma2ToPQ(FfxFloat32 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(2)) + FfxUInt32(0x2F9A4E46)); +} + +/// Compute a more accurate approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateGamma2ToPQMedium(FfxFloat32 a) +{ + FfxFloat32 b = ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(2)) + FfxUInt32(0x2F9A4E46)); + FfxFloat32 b4 = b * b * b * b; + return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); +} + +/// Compute a high accuracy approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateGamma2ToPQHigh(FfxFloat32 a) +{ + return ffxSqrt(ffxSqrt(a)); +} + +/// Compute an approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateLinearToPQ(FfxFloat32 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(3)) + FfxUInt32(0x378D8723)); +} + +/// Compute a more accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateLinearToPQMedium(FfxFloat32 a) +{ + FfxFloat32 b = ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(3)) + FfxUInt32(0x378D8723)); + FfxFloat32 b8 = b * b * b * b * b * b * b * b; + return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); +} + +/// Compute a very accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32 ffxApproximateLinearToPQHigh(FfxFloat32 a) +{ + return ffxSqrt(ffxSqrt(ffxSqrt(a))); +} + +/// Compute an approximate conversion from PQ to Gamma2 space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and Gamma2. +/// +/// @returns +/// The value a converted into Gamma2. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximatePQToGamma2Medium(FfxFloat32x2 a) +{ + return a * a * a * a; +} + +/// Compute an approximate conversion from PQ to linear space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and linear. +/// +/// @returns +/// The value a converted into linear. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximatePQToLinear(FfxFloat32x2 a) +{ + return a * a * a * a * a * a * a * a; +} + +/// Compute an approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateGamma2ToPQ(FfxFloat32x2 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(2u)) + ffxBroadcast2(0x2F9A4E46u)); +} + +/// Compute a more accurate approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateGamma2ToPQMedium(FfxFloat32x2 a) +{ + FfxFloat32x2 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(2u)) + ffxBroadcast2(0x2F9A4E46u)); + FfxFloat32x2 b4 = b * b * b * b; + return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); +} + +/// Compute a high accuracy approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateGamma2ToPQHigh(FfxFloat32x2 a) +{ + return ffxSqrt(ffxSqrt(a)); +} + +/// Compute an approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateLinearToPQ(FfxFloat32x2 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(3u)) + ffxBroadcast2(0x378D8723u)); +} + +/// Compute a more accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateLinearToPQMedium(FfxFloat32x2 a) +{ + FfxFloat32x2 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(3u)) + ffxBroadcast2(0x378D8723u)); + FfxFloat32x2 b8 = b * b * b * b * b * b * b * b; + return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); +} + +/// Compute a very accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxApproximateLinearToPQHigh(FfxFloat32x2 a) +{ + return ffxSqrt(ffxSqrt(ffxSqrt(a))); +} + +/// Compute an approximate conversion from PQ to Gamma2 space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and Gamma2. +/// +/// @returns +/// The value a converted into Gamma2. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximatePQToGamma2Medium(FfxFloat32x3 a) +{ + return a * a * a * a; +} + +/// Compute an approximate conversion from PQ to linear space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and linear. +/// +/// @returns +/// The value a converted into linear. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximatePQToLinear(FfxFloat32x3 a) +{ + return a * a * a * a * a * a * a * a; +} + +/// Compute an approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateGamma2ToPQ(FfxFloat32x3 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(2u)) + ffxBroadcast3(0x2F9A4E46u)); +} + +/// Compute a more accurate approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateGamma2ToPQMedium(FfxFloat32x3 a) +{ + FfxFloat32x3 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(2u)) + ffxBroadcast3(0x2F9A4E46u)); + FfxFloat32x3 b4 = b * b * b * b; + return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); +} + +/// Compute a high accuracy approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateGamma2ToPQHigh(FfxFloat32x3 a) +{ + return ffxSqrt(ffxSqrt(a)); +} + +/// Compute an approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateLinearToPQ(FfxFloat32x3 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(3u)) + ffxBroadcast3(0x378D8723u)); +} + +/// Compute a more accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateLinearToPQMedium(FfxFloat32x3 a) +{ + FfxFloat32x3 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(3u)) + ffxBroadcast3(0x378D8723u)); + FfxFloat32x3 b8 = b * b * b * b * b * b * b * b; + return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); +} + +/// Compute a very accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxApproximateLinearToPQHigh(FfxFloat32x3 a) +{ + return ffxSqrt(ffxSqrt(ffxSqrt(a))); +} + +/// Compute an approximate conversion from PQ to Gamma2 space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and Gamma2. +/// +/// @returns +/// The value a converted into Gamma2. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximatePQToGamma2Medium(FfxFloat32x4 a) +{ + return a * a * a * a; +} + +/// Compute an approximate conversion from PQ to linear space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between PQ and linear. +/// +/// @returns +/// The value a converted into linear. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximatePQToLinear(FfxFloat32x4 a) +{ + return a * a * a * a * a * a * a * a; +} + +/// Compute an approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateGamma2ToPQ(FfxFloat32x4 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(2u)) + ffxBroadcast4(0x2F9A4E46u)); +} + +/// Compute a more accurate approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateGamma2ToPQMedium(FfxFloat32x4 a) +{ + FfxFloat32x4 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(2u)) + ffxBroadcast4(0x2F9A4E46u)); + FfxFloat32x4 b4 = b * b * b * b * b * b * b * b; + return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); +} + +/// Compute a high accuracy approximate conversion from gamma2 to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between gamma2 and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateGamma2ToPQHigh(FfxFloat32x4 a) +{ + return ffxSqrt(ffxSqrt(a)); +} + +/// Compute an approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateLinearToPQ(FfxFloat32x4 a) +{ + return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(3u)) + ffxBroadcast4(0x378D8723u)); +} + +/// Compute a more accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateLinearToPQMedium(FfxFloat32x4 a) +{ + FfxFloat32x4 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(3u)) + ffxBroadcast4(0x378D8723u)); + FfxFloat32x4 b8 = b * b * b * b * b * b * b * b; + return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); +} + +/// Compute a very accurate approximate conversion from linear to PQ space. +/// +/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do +/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear +/// (8th power and fast 8th root). The maximum error is approximately 0.2%. +/// +/// @param a The value to convert between linear and PQ. +/// +/// @returns +/// The value a converted into PQ. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxApproximateLinearToPQHigh(FfxFloat32x4 a) +{ + return ffxSqrt(ffxSqrt(ffxSqrt(a))); +} + +// An approximation of sine. +// +// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +// is {-1/4 to 1/4} representing {-1 to 1}. +// +// @param [in] value The value to calculate approximate sine for. +// +// @returns +// The approximate sine of value. +FfxFloat32 ffxParabolicSin(FfxFloat32 value) +{ + return value * abs(value) - value; +} + +// An approximation of sine. +// +// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +// is {-1/4 to 1/4} representing {-1 to 1}. +// +// @param [in] value The value to calculate approximate sine for. +// +// @returns +// The approximate sine of value. +FfxFloat32x2 ffxParabolicSin(FfxFloat32x2 x) +{ + return x * abs(x) - x; +} + +// An approximation of cosine. +// +// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +// is {-1/4 to 1/4} representing {-1 to 1}. +// +// @param [in] value The value to calculate approximate cosine for. +// +// @returns +// The approximate cosine of value. +FfxFloat32 ffxParabolicCos(FfxFloat32 x) +{ + x = ffxFract(x * FfxFloat32(0.5) + FfxFloat32(0.75)); + x = x * FfxFloat32(2.0) - FfxFloat32(1.0); + return ffxParabolicSin(x); +} + +// An approximation of cosine. +// +// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +// is {-1/4 to 1/4} representing {-1 to 1}. +// +// @param [in] value The value to calculate approximate cosine for. +// +// @returns +// The approximate cosine of value. +FfxFloat32x2 ffxParabolicCos(FfxFloat32x2 x) +{ + x = ffxFract(x * ffxBroadcast2(0.5f) + ffxBroadcast2(0.75f)); + x = x * ffxBroadcast2(2.0f) - ffxBroadcast2(1.0f); + return ffxParabolicSin(x); +} + +// An approximation of both sine and cosine. +// +// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +// is {-1/4 to 1/4} representing {-1 to 1}. +// +// @param [in] value The value to calculate approximate cosine for. +// +// @returns +// A FfxFloat32x2 containing approximations of both sine and cosine of value. +FfxFloat32x2 ffxParabolicSinCos(FfxFloat32 x) +{ + FfxFloat32 y = ffxFract(x * FfxFloat32(0.5) + FfxFloat32(0.75)); + y = y * FfxFloat32(2.0) - FfxFloat32(1.0); + return ffxParabolicSin(FfxFloat32x2(x, y)); +} + +/// Conditional free logic AND operation using values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt32 ffxZeroOneAnd(FfxUInt32 x, FfxUInt32 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxZeroOneAnd(FfxUInt32x2 x, FfxUInt32x2 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt32x3 ffxZeroOneAnd(FfxUInt32x3 x, FfxUInt32x3 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt32x4 ffxZeroOneAnd(FfxUInt32x4 x, FfxUInt32x4 y) +{ + return min(x, y); +} + +/// Conditional free logic NOT operation using two values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt32 ffxZeroOneAnd(FfxUInt32 x) +{ + return x ^ FfxUInt32(1); +} + +/// Conditional free logic NOT operation using two values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxZeroOneAnd(FfxUInt32x2 x) +{ + return x ^ ffxBroadcast2(1u); +} + +/// Conditional free logic NOT operation using two values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt32x3 ffxZeroOneAnd(FfxUInt32x3 x) +{ + return x ^ ffxBroadcast3(1u); +} + +/// Conditional free logic NOT operation using two values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt32x4 ffxZeroOneAnd(FfxUInt32x4 x) +{ + return x ^ ffxBroadcast4(1u); +} + +/// Conditional free logic OR operation using two values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt32 ffxZeroOneOr(FfxUInt32 x, FfxUInt32 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxZeroOneOr(FfxUInt32x2 x, FfxUInt32x2 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt32x3 ffxZeroOneOr(FfxUInt32x3 x, FfxUInt32x3 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt32x4 ffxZeroOneOr(FfxUInt32x4 x, FfxUInt32x4 y) +{ + return max(x, y); +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxUInt32 ffxZeroOneAndToU1(FfxFloat32 x) +{ + return FfxUInt32(FfxFloat32(1.0) - x); +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxZeroOneAndToU2(FfxFloat32x2 x) +{ + return FfxUInt32x2(ffxBroadcast2(1.0) - x); +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxUInt32x3 ffxZeroOneAndToU3(FfxFloat32x3 x) +{ + return FfxUInt32x3(ffxBroadcast3(1.0) - x); +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxUInt32x4 ffxZeroOneAndToU4(FfxFloat32x4 x) +{ + return FfxUInt32x4(ffxBroadcast4(1.0) - x); +} + +/// Conditional free logic AND operation using two values followed by a NOT operation +/// using the resulting value and a third value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32 ffxZeroOneAndOr(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) +{ + return ffxSaturate(x * y + z); +} + +/// Conditional free logic AND operation using two values followed by a NOT operation +/// using the resulting value and a third value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxZeroOneAndOr(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) +{ + return ffxSaturate(x * y + z); +} + +/// Conditional free logic AND operation using two values followed by a NOT operation +/// using the resulting value and a third value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxZeroOneAndOr(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) +{ + return ffxSaturate(x * y + z); +} + +/// Conditional free logic AND operation using two values followed by a NOT operation +/// using the resulting value and a third value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxZeroOneAndOr(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) +{ + return ffxSaturate(x * y + z); +} + +/// Given a value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat32 ffxZeroOneIsGreaterThanZero(FfxFloat32 x) +{ + return ffxSaturate(x * FfxFloat32(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// Given a value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxZeroOneIsGreaterThanZero(FfxFloat32x2 x) +{ + return ffxSaturate(x * ffxBroadcast2(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// Given a value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxZeroOneIsGreaterThanZero(FfxFloat32x3 x) +{ + return ffxSaturate(x * ffxBroadcast3(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// Given a value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxZeroOneIsGreaterThanZero(FfxFloat32x4 x) +{ + return ffxSaturate(x * ffxBroadcast4(FFX_POSITIVE_INFINITY_FLOAT)); +} + +/// Conditional free logic signed NOT operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32 ffxZeroOneAnd(FfxFloat32 x) +{ + return FfxFloat32(1.0) - x; +} + +/// Conditional free logic signed NOT operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxZeroOneAnd(FfxFloat32x2 x) +{ + return ffxBroadcast2(1.0) - x; +} + +/// Conditional free logic signed NOT operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxZeroOneAnd(FfxFloat32x3 x) +{ + return ffxBroadcast3(1.0) - x; +} + +/// Conditional free logic signed NOT operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxZeroOneAnd(FfxFloat32x4 x) +{ + return ffxBroadcast4(1.0) - x; +} + +/// Conditional free logic OR operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat32 ffxZeroOneOr(FfxFloat32 x, FfxFloat32 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxZeroOneOr(FfxFloat32x2 x, FfxFloat32x2 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxZeroOneOr(FfxFloat32x3 x, FfxFloat32x3 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxZeroOneOr(FfxFloat32x4 x, FfxFloat32x4 y) +{ + return max(x, y); +} + +/// Choose between two FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat32 ffxZeroOneSelect(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) +{ + FfxFloat32 r = (-x) * z + z; + return x * y + r; +} + +/// Choose between two FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxZeroOneSelect(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) +{ + FfxFloat32x2 r = (-x) * z + z; + return x * y + r; +} + +/// Choose between two FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxZeroOneSelect(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) +{ + FfxFloat32x3 r = (-x) * z + z; + return x * y + r; +} + +/// Choose between two FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxZeroOneSelect(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) +{ + FfxFloat32x4 r = (-x) * z + z; + return x * y + r; +} + +/// Given a value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat32 ffxZeroOneIsSigned(FfxFloat32 x) +{ + return ffxSaturate(x * FfxFloat32(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// Given a value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxZeroOneIsSigned(FfxFloat32x2 x) +{ + return ffxSaturate(x * ffxBroadcast2(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// Given a value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxZeroOneIsSigned(FfxFloat32x3 x) +{ + return ffxSaturate(x * ffxBroadcast3(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// Given a value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat32x4 ffxZeroOneIsSigned(FfxFloat32x4 x) +{ + return ffxSaturate(x * ffxBroadcast4(FFX_NEGATIVE_INFINITY_FLOAT)); +} + +/// Compute a Rec.709 color space. +/// +/// Rec.709 is used for some HDTVs. +/// +/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +/// +/// @param [in] color The color to convert to Rec. 709. +/// +/// @returns +/// The color in linear space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxRec709FromLinear(FfxFloat32 color) +{ + FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); + FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); + return clamp(j.x, color * j.y, pow(color, j.z) * k.x + k.y); +} + +/// Compute a Rec.709 color space. +/// +/// Rec.709 is used for some HDTVs. +/// +/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +/// +/// @param [in] color The color to convert to Rec. 709. +/// +/// @returns +/// The color in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxRec709FromLinear(FfxFloat32x2 color) +{ + FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); + FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); + return clamp(j.xx, color * j.yy, pow(color, j.zz) * k.xx + k.yy); +} + +/// Compute a Rec.709 color space. +/// +/// Rec.709 is used for some HDTVs. +/// +/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +/// +/// @param [in] color The color to convert to Rec. 709. +/// +/// @returns +/// The color in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxRec709FromLinear(FfxFloat32x3 color) +{ + FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); + FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); + return clamp(j.xxx, color * j.yyy, pow(color, j.zzz) * k.xxx + k.yyy); +} + +/// Compute a linear value from a REC.709 value. +/// +/// @param [in] color The value to convert to linear from REC.709. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxLinearFromRec709(FfxFloat32 color) +{ + FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); + FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); + return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.x), color * j.y, pow(color * k.x + k.y, j.z)); +} + +/// Compute a linear value from a REC.709 value. +/// +/// @param [in] color The value to convert to linear from REC.709. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxLinearFromRec709(FfxFloat32x2 color) +{ + FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); + FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); + return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.xx), color * j.yy, pow(color * k.xx + k.yy, j.zz)); +} + +/// Compute a linear value from a REC.709 value. +/// +/// @param [in] color The value to convert to linear from REC.709. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxLinearFromRec709(FfxFloat32x3 color) +{ + FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); + FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); + return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.xxx), color * j.yyy, pow(color * k.xxx + k.yyy, j.zzz)); +} + +/// Compute a gamma value from a linear value. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. +/// +/// @param [in] value The value to convert to gamma space from linear. +/// @param [in] power The reciprocal of power value used for the gamma curve. +/// +/// @returns +/// A value in gamma space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxGammaFromLinear(FfxFloat32 value, FfxFloat32 power) +{ + return pow(value, FfxFloat32(power)); +} + +/// Compute a gamma value from a linear value. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. +/// +/// @param [in] value The value to convert to gamma space from linear. +/// @param [in] power The reciprocal of power value used for the gamma curve. +/// +/// @returns +/// A value in gamma space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxGammaFromLinear(FfxFloat32x2 value, FfxFloat32 power) +{ + return pow(value, ffxBroadcast2(power)); +} + +/// Compute a gamma value from a linear value. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. +/// +/// @param [in] value The value to convert to gamma space from linear. +/// @param [in] power The reciprocal of power value used for the gamma curve. +/// +/// @returns +/// A value in gamma space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxGammaFromLinear(FfxFloat32x3 value, FfxFloat32 power) +{ + return pow(value, ffxBroadcast3(power)); +} + +/// Compute a linear value from a value in a gamma space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] color The value to convert to linear in gamma space. +/// @param [in] power The power value used for the gamma curve. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxLinearFromGamma(FfxFloat32 color, FfxFloat32 power) +{ + return pow(color, FfxFloat32(power)); +} + +/// Compute a linear value from a value in a gamma space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] color The value to convert to linear in gamma space. +/// @param [in] power The power value used for the gamma curve. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxLinearFromGamma(FfxFloat32x2 color, FfxFloat32 power) +{ + return pow(color, ffxBroadcast2(power)); +} + +/// Compute a linear value from a value in a gamma space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] color The value to convert to linear in gamma space. +/// @param [in] power The power value used for the gamma curve. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxLinearFromGamma(FfxFloat32x3 color, FfxFloat32 power) +{ + return pow(color, ffxBroadcast3(power)); +} + +/// Compute a PQ value from a linear value. +/// +/// @param [in] value The value to convert to PQ from linear. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxPQFromLinear(FfxFloat32 value) +{ + FfxFloat32 p = pow(value, FfxFloat32(0.159302)); + return pow((FfxFloat32(0.835938) + FfxFloat32(18.8516) * p) / (FfxFloat32(1.0) + FfxFloat32(18.6875) * p), FfxFloat32(78.8438)); +} + +/// Compute a PQ value from a linear value. +/// +/// @param [in] value The value to convert to PQ from linear. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxPQFromLinear(FfxFloat32x2 value) +{ + FfxFloat32x2 p = pow(value, ffxBroadcast2(0.159302)); + return pow((ffxBroadcast2(0.835938) + ffxBroadcast2(18.8516) * p) / (ffxBroadcast2(1.0) + ffxBroadcast2(18.6875) * p), ffxBroadcast2(78.8438)); +} + +/// Compute a PQ value from a linear value. +/// +/// @param [in] value The value to convert to PQ from linear. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxPQFromLinear(FfxFloat32x3 value) +{ + FfxFloat32x3 p = pow(value, ffxBroadcast3(0.159302)); + return pow((ffxBroadcast3(0.835938) + ffxBroadcast3(18.8516) * p) / (ffxBroadcast3(1.0) + ffxBroadcast3(18.6875) * p), ffxBroadcast3(78.8438)); +} + +/// Compute a linear value from a value in a PQ space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] value The value to convert to linear in PQ space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxLinearFromPQ(FfxFloat32 value) +{ + FfxFloat32 p = pow(value, FfxFloat32(0.0126833)); + return pow(ffxSaturate(p - FfxFloat32(0.835938)) / (FfxFloat32(18.8516) - FfxFloat32(18.6875) * p), FfxFloat32(6.27739)); +} + +/// Compute a linear value from a value in a PQ space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] value The value to convert to linear in PQ space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxLinearFromPQ(FfxFloat32x2 value) +{ + FfxFloat32x2 p = pow(value, ffxBroadcast2(0.0126833)); + return pow(ffxSaturate(p - ffxBroadcast2(0.835938)) / (ffxBroadcast2(18.8516) - ffxBroadcast2(18.6875) * p), ffxBroadcast2(6.27739)); +} + +/// Compute a linear value from a value in a PQ space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] value The value to convert to linear in PQ space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxLinearFromPQ(FfxFloat32x3 value) +{ + FfxFloat32x3 p = pow(value, ffxBroadcast3(0.0126833)); + return pow(ffxSaturate(p - ffxBroadcast3(0.835938)) / (ffxBroadcast3(18.8516) - ffxBroadcast3(18.6875) * p), ffxBroadcast3(6.27739)); +} + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] value The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxSrgbFromLinear(FfxFloat32 value) +{ + FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); + return clamp(j.x, value * j.y, pow(value, j.z) * k.x + k.y); +} + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] value The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxSrgbFromLinear(FfxFloat32x2 value) +{ + FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); + return clamp(j.xx, value * j.yy, pow(value, j.zz) * k.xx + k.yy); +} + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] value The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxSrgbFromLinear(FfxFloat32x3 value) +{ + FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); + return clamp(j.xxx, value * j.yyy, pow(value, j.zzz) * k.xxx + k.yyy); +} + +/// Compute a linear value from a value in a SRGB space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] value The value to convert to linear in SRGB space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32 ffxLinearFromSrgb(FfxFloat32 value) +{ + FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); + FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); + return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.x), value * j.y, pow(value * k.x + k.y, j.z)); +} + +/// Compute a linear value from a value in a SRGB space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] value The value to convert to linear in SRGB space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x2 ffxLinearFromSrgb(FfxFloat32x2 value) +{ + FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); + FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); + return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.xx), value * j.yy, pow(value * k.xx + k.yy, j.zz)); +} + +/// Compute a linear value from a value in a SRGB space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] value The value to convert to linear in SRGB space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat32x3 ffxLinearFromSrgb(FfxFloat32x3 value) +{ + FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); + FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); + return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.xxx), value * j.yyy, pow(value * k.xxx + k.yyy, j.zzz)); +} + +/// A remapping of 64x1 to 8x8 imposing rotated 2x2 pixel quads in quad linear. +/// +/// Remap illustration: +/// +/// 543210 +/// ~~~~~~ +/// ..xxx. +/// yy...y +/// +/// @param [in] a The input 1D coordinates to remap. +/// +/// @returns +/// The remapped 2D coordinates. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxRemapForQuad(FfxUInt32 a) +{ + return FfxUInt32x2(ffxBitfieldExtract(a, 1u, 3u), ffxBitfieldInsertMask(ffxBitfieldExtract(a, 3u, 3u), a, 1u)); +} + +/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. +/// +/// The 64-wide lane indices to 8x8 remapping is performed as follows: +/// +/// 00 01 08 09 10 11 18 19 +/// 02 03 0a 0b 12 13 1a 1b +/// 04 05 0c 0d 14 15 1c 1d +/// 06 07 0e 0f 16 17 1e 1f +/// 20 21 28 29 30 31 38 39 +/// 22 23 2a 2b 32 33 3a 3b +/// 24 25 2c 2d 34 35 3c 3d +/// 26 27 2e 2f 36 37 3e 3f +/// +/// @param [in] a The input 1D coordinate to remap. +/// +/// @returns +/// The remapped 2D coordinates. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxRemapForWaveReduction(FfxUInt32 a) +{ + return FfxUInt32x2(ffxBitfieldInsertMask(ffxBitfieldExtract(a, 2u, 3u), a, 1u), ffxBitfieldInsertMask(ffxBitfieldExtract(a, 3u, 3u), ffxBitfieldExtract(a, 1u, 2u), 2u)); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h.meta new file mode 100644 index 0000000..070d7a5 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 4950d6c78609df549a0ed96137bf3bf1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h new file mode 100644 index 0000000..1cb780b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h @@ -0,0 +1,2981 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#if FFX_HALF +#if FFX_HLSL_SM >= 62 +/// A define value for 16bit positive infinity. +/// +/// @ingroup GPUCore +#define FFX_POSITIVE_INFINITY_HALF FFX_TO_FLOAT16((uint16_t)0x7c00u) + +/// A define value for 16bit negative infinity. +/// +/// @ingroup GPUCore +#define FFX_NEGATIVE_INFINITY_HALF FFX_TO_FLOAT16((uint16_t)0xfc00u) +#else +/// A define value for 16bit positive infinity. +/// +/// @ingroup GPUCore +#define FFX_POSITIVE_INFINITY_HALF FFX_TO_FLOAT16(0x7c00u) + +/// A define value for 16bit negative infinity. +/// +/// @ingroup GPUCore +#define FFX_NEGATIVE_INFINITY_HALF FFX_TO_FLOAT16(0xfc00u) +#endif // #if FFX_HLSL_SM>=62 + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16 ffxMin(FfxFloat16 x, FfxFloat16 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxMin(FfxFloat16x2 x, FfxFloat16x2 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxMin(FfxFloat16x3 x, FfxFloat16x3 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxMin(FfxFloat16x4 x, FfxFloat16x4 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16 ffxMin(FfxInt16 x, FfxInt16 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16x2 ffxMin(FfxInt16x2 x, FfxInt16x2 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16x3 ffxMin(FfxInt16x3 x, FfxInt16x3 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16x4 ffxMin(FfxInt16x4 x, FfxInt16x4 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16 ffxMin(FfxUInt16 x, FfxUInt16 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxMin(FfxUInt16x2 x, FfxUInt16x2 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16x3 ffxMin(FfxUInt16x3 x, FfxUInt16x3 y) +{ + return min(x, y); +} + +/// Compute the min of two values. +/// +/// @param [in] x The first value to compute the min of. +/// @param [in] y The second value to compute the min of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16x4 ffxMin(FfxUInt16x4 x, FfxUInt16x4 y) +{ + return min(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16 ffxMax(FfxFloat16 x, FfxFloat16 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxMax(FfxFloat16x2 x, FfxFloat16x2 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxMax(FfxFloat16x3 x, FfxFloat16x3 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxMax(FfxFloat16x4 x, FfxFloat16x4 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16 ffxMax(FfxInt16 x, FfxInt16 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16x2 ffxMax(FfxInt16x2 x, FfxInt16x2 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16x3 ffxMax(FfxInt16x3 x, FfxInt16x3 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxInt16x4 ffxMax(FfxInt16x4 x, FfxInt16x4 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16 ffxMax(FfxUInt16 x, FfxUInt16 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxMax(FfxUInt16x2 x, FfxUInt16x2 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16x3 ffxMax(FfxUInt16x3 x, FfxUInt16x3 y) +{ + return max(x, y); +} + +/// Compute the max of two values. +/// +/// @param [in] x The first value to compute the max of. +/// @param [in] y The second value to compute the max of. +/// +/// @returns +/// The the lowest of two values. +/// +/// @ingroup GPUCore +FfxUInt16x4 ffxMax(FfxUInt16x4 x, FfxUInt16x4 y) +{ + return max(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat16 ffxPow(FfxFloat16 x, FfxFloat16 y) +{ + return pow(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPow(FfxFloat16x2 x, FfxFloat16x2 y) +{ + return pow(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxPow(FfxFloat16x3 x, FfxFloat16x3 y) +{ + return pow(x, y); +} + +/// Compute the value of the first parameter raised to the power of the second. +/// +/// @param [in] x The value to raise to the power y. +/// @param [in] y The power to which to raise x. +/// +/// @returns +/// The value of the first parameter raised to the power of the second. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxPow(FfxFloat16x4 x, FfxFloat16x4 y) +{ + return pow(x, y); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat16 ffxSqrt(FfxFloat16 x) +{ + return sqrt(x); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxSqrt(FfxFloat16x2 x) +{ + return sqrt(x); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxSqrt(FfxFloat16x3 x) +{ + return sqrt(x); +} + +/// Compute the square root of a value. +/// +/// @param [in] x The first value to compute the min of. +/// +/// @returns +/// The the square root of x. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxSqrt(FfxFloat16x4 x) +{ + return sqrt(x); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat16 ffxCopySignBitHalf(FfxFloat16 d, FfxFloat16 s) +{ + return FFX_TO_FLOAT16(FFX_TO_UINT16(d) | (FFX_TO_UINT16(s) & FFX_BROADCAST_UINT16(0x8000u))); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxCopySignBitHalf(FfxFloat16x2 d, FfxFloat16x2 s) +{ + return FFX_TO_FLOAT16X2(FFX_TO_UINT16X2(d) | (FFX_TO_UINT16X2(s) & FFX_BROADCAST_UINT16X2(0x8000u))); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxCopySignBitHalf(FfxFloat16x3 d, FfxFloat16x3 s) +{ + return FFX_TO_FLOAT16X3(FFX_TO_UINT16X3(d) | (FFX_TO_UINT16X3(s) & FFX_BROADCAST_UINT16X3(0x8000u))); +} + +/// Copy the sign bit from 's' to positive 'd'. +/// +/// @param [in] d The value to copy the sign bit into. +/// @param [in] s The value to copy the sign bit from. +/// +/// @returns +/// The value of d with the sign bit from s. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxCopySignBitHalf(FfxFloat16x4 d, FfxFloat16x4 s) +{ + return FFX_TO_FLOAT16X4(FFX_TO_UINT16X4(d) | (FFX_TO_UINT16X4(s) & FFX_BROADCAST_UINT16X4(0x8000u))); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat16 ffxIsSignedHalf(FfxFloat16 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxIsSignedHalf(FfxFloat16x2 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16X2(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxIsSignedHalf(FfxFloat16x3 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16X3(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 0 +/// m >= 0 := 0 +/// m < 0 := 1 +/// +/// Uses the following useful floating point logic, +/// saturate(+a*(-INF)==-INF) := 0 +/// saturate( 0*(-INF)== NaN) := 0 +/// saturate(-a*(-INF)==+INF) := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against 0. +/// +/// @returns +/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxIsSignedHalf(FfxFloat16x4 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16X4(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat16 ffxIsGreaterThanZeroHalf(FfxFloat16 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16(FFX_POSITIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxIsGreaterThanZeroHalf(FfxFloat16x2 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16X2(FFX_POSITIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxIsGreaterThanZeroHalf(FfxFloat16x3 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16X3(FFX_POSITIVE_INFINITY_HALF)); +} + +/// A single operation to return the following: +/// m = NaN := 1 +/// m > 0 := 0 +/// m <= 0 := 1 +/// +/// This function is useful when creating masks for branch-free logic. +/// +/// @param [in] m The value to test against zero. +/// +/// @returns +/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxIsGreaterThanZeroHalf(FfxFloat16x4 m) +{ + return ffxSaturate(m * FFX_BROADCAST_FLOAT16X4(FFX_POSITIVE_INFINITY_HALF)); +} + +/// Convert a 16bit floating point value to sortable integer. +/// +/// - If sign bit=0, flip the sign bit (positives). +/// - If sign bit=1, flip all bits (negatives). +/// +/// The function has the side effects that: +/// - Larger integers are more positive values. +/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +/// +/// @param [in] x The floating point value to make sortable. +/// +/// @returns +/// The sortable integer value. +/// +/// @ingroup GPUCore +FfxUInt16 ffxFloatToSortableIntegerHalf(FfxUInt16 x) +{ + return x ^ ((ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16(15))) | FFX_BROADCAST_UINT16(0x8000)); +} + +/// Convert a sortable integer to a 16bit floating point value. +/// +/// The function has the side effects that: +/// - If sign bit=1, flip the sign bit (positives). +/// - If sign bit=0, flip all bits (negatives). +/// +/// @param [in] x The sortable integer value to make floating point. +/// +/// @returns +/// The floating point value. +/// +/// @ingroup GPUCore +FfxUInt16 ffxSortableIntegerToFloatHalf(FfxUInt16 x) +{ + return x ^ ((~ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16(15))) | FFX_BROADCAST_UINT16(0x8000)); +} + +/// Convert a pair of 16bit floating point values to a pair of sortable integers. +/// +/// - If sign bit=0, flip the sign bit (positives). +/// - If sign bit=1, flip all bits (negatives). +/// +/// The function has the side effects that: +/// - Larger integers are more positive values. +/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +/// +/// @param [in] x The floating point values to make sortable. +/// +/// @returns +/// The sortable integer values. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxFloatToSortableIntegerHalf(FfxUInt16x2 x) +{ + return x ^ ((ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16X2(15))) | FFX_BROADCAST_UINT16X2(0x8000)); +} + +/// Convert a pair of sortable integers to a pair of 16bit floating point values. +/// +/// The function has the side effects that: +/// - If sign bit=1, flip the sign bit (positives). +/// - If sign bit=0, flip all bits (negatives). +/// +/// @param [in] x The sortable integer values to make floating point. +/// +/// @returns +/// The floating point values. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxSortableIntegerToFloatHalf(FfxUInt16x2 x) +{ + return x ^ ((~ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16X2(15))) | FFX_BROADCAST_UINT16X2(0x8000)); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// [Zero] Y0 [Zero] X0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesZeroY0ZeroX0(FfxUInt32x2 i) +{ + return ((i.x) & 0xffu) | ((i.y << 16) & 0xff0000u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// [Zero] Y1 [Zero] X1 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesZeroY1ZeroX1(FfxUInt32x2 i) +{ + return ((i.x >> 8) & 0xffu) | ((i.y << 8) & 0xff0000u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// [Zero] Y2 [Zero] X2 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesZeroY2ZeroX2(FfxUInt32x2 i) +{ + return ((i.x >> 16) & 0xffu) | ((i.y) & 0xff0000u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// [Zero] Y3 [Zero] X3 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesZeroY3ZeroX3(FfxUInt32x2 i) +{ + return ((i.x >> 24) & 0xffu) | ((i.y >> 8) & 0xff0000u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y3 Y2 Y1 X0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY3Y2Y1X0(FfxUInt32x2 i) +{ + return ((i.x) & 0x000000ffu) | (i.y & 0xffffff00u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y3 Y2 Y1 X2 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY3Y2Y1X2(FfxUInt32x2 i) +{ + return ((i.x >> 16) & 0x000000ffu) | (i.y & 0xffffff00u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y3 Y2 X0 Y0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY3Y2X0Y0(FfxUInt32x2 i) +{ + return ((i.x << 8) & 0x0000ff00u) | (i.y & 0xffff00ffu); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y3 Y2 X2 Y0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY3Y2X2Y0(FfxUInt32x2 i) +{ + return ((i.x >> 8) & 0x0000ff00u) | (i.y & 0xffff00ffu); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y3 X0 Y1 Y0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY3X0Y1Y0(FfxUInt32x2 i) +{ + return ((i.x << 16) & 0x00ff0000u) | (i.y & 0xff00ffffu); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y3 X2 Y1 Y0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY3X2Y1Y0(FfxUInt32x2 i) +{ + return ((i.x) & 0x00ff0000u) | (i.y & 0xff00ffffu); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// X0 Y2 Y1 Y0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesX0Y2Y1Y0(FfxUInt32x2 i) +{ + return ((i.x << 24) & 0xff000000u) | (i.y & 0x00ffffffu); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// X2 Y2 Y1 Y0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesX2Y2Y1Y0(FfxUInt32x2 i) +{ + return ((i.x << 8) & 0xff000000u) | (i.y & 0x00ffffffu); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y2 X2 Y0 X0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY2X2Y0X0(FfxUInt32x2 i) +{ + return ((i.x) & 0x00ff00ffu) | ((i.y << 8) & 0xff00ff00u); +} + +/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. +/// +/// The resulting integer will contain bytes in the following order, from most to least significant: +/// Y2 Y0 X2 X0 +/// +/// @param [in] i The integer pair to pack. +/// +/// @returns +/// The packed integer value. +/// +/// @ingroup GPUCore +FfxUInt32 ffxPackBytesY2Y0X2X0(FfxUInt32x2 i) +{ + return (((i.x) & 0xffu) | ((i.x >> 8) & 0xff00u) | ((i.y << 16) & 0xff0000u) | ((i.y << 8) & 0xff000000u)); +} + +/// Takes two Float16x2 values x and y, normalizes them and builds a single Uint16x2 value in the format {{x0,y0},{x1,y1}}. +/// +/// @param [in] x The first float16x2 value to pack. +/// @param [in] y The second float16x2 value to pack. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxPackX0Y0X1Y1UnsignedToUint16x2(FfxFloat16x2 x, FfxFloat16x2 y) +{ + x *= FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0); + y *= FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0); + return FFX_UINT32_TO_UINT16X2(ffxPackBytesY2X2Y0X0(FfxUInt32x2(FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(x)), FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(y))))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], +/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. +/// +/// r=ffxPermuteUByte0Float16x2ToUint2(d,i) +/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +/// Where 'k1' is an SGPR with 0x???? +/// Where 'k2' is an SGPR with 0x???? +/// V_PK_FMA_F16 i,i,k0.x,0 +/// V_PERM_B32 r.x,i,i,k1 +/// V_PERM_B32 r.y,i,i,k2 +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteUByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); + return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], +/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. +/// +/// r=ffxPermuteUByte1Float16x2ToUint2(d,i) +/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +/// Where 'k1' is an SGPR with 0x???? +/// Where 'k2' is an SGPR with 0x???? +/// V_PK_FMA_F16 i,i,k0.x,0 +/// V_PERM_B32 r.x,i,i,k1 +/// V_PERM_B32 r.y,i,i,k2 +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteUByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); + return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], +/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. +/// +/// r=ffxPermuteUByte2Float16x2ToUint2(d,i) +/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +/// Where 'k1' is an SGPR with 0x???? +/// Where 'k2' is an SGPR with 0x???? +/// V_PK_FMA_F16 i,i,k0.x,0 +/// V_PERM_B32 r.x,i,i,k1 +/// V_PERM_B32 r.y,i,i,k2 +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteUByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); + return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], +/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. +/// +/// r=ffxPermuteUByte3Float16x2ToUint2(d,i) +/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +/// Where 'k1' is an SGPR with 0x???? +/// Where 'k2' is an SGPR with 0x???? +/// V_PK_FMA_F16 i,i,k0.x,0 +/// V_PERM_B32 r.x,i,i,k1 +/// V_PERM_B32 r.y,i,i,k2 +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteUByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); + return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteUByte0Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteUByte1Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteUByte2Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteUByte3Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); +} + +/// Takes two Float16x2 values x and y, normalizes them and builds a single Uint16x2 value in the format {{x0,y0},{x1,y1}}. +/// +/// @param [in] x The first float16x2 value to pack. +/// @param [in] y The second float16x2 value to pack. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxPackX0Y0X1Y1SignedToUint16x2(FfxFloat16x2 x, FfxFloat16x2 y) +{ + x = x * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0); + y = y * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0); + return FFX_UINT32_TO_UINT16X2(ffxPackBytesY2X2Y0X0(FfxUInt32x2(FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(x)), FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(y))))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], +/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteSByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); + return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], +/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteSByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); + return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], +/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteSByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); + return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], +/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteSByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); + return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], +/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. +/// +/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). +/// This is useful if there is a desire for cleared values to decode as zero. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteZeroBasedSByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; + return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], +/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. +/// +/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). +/// This is useful if there is a desire for cleared values to decode as zero. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteZeroBasedSByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; + return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], +/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. +/// +/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). +/// This is useful if there is a desire for cleared values to decode as zero. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteZeroBasedSByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; + return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], +/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. +/// +/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). +/// This is useful if there is a desire for cleared values to decode as zero. +/// +/// Handles signed byte values. +/// +/// @param [in] d The FfxUInt32x2 value to be packed. +/// @param [in] i The FfxFloat16x2 value to be packed. +/// +/// @returns +/// The packed FfxUInt32x2 value. +/// +/// @ingroup GPUCore +FfxUInt32x2 ffxPermuteZeroBasedSByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) +{ + FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; + return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteSByte0Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteSByte1Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteSByte2Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteSByte3Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteZeroBasedSByte0Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteZeroBasedSByte1Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteZeroBasedSByte2Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. +/// +/// Handles signed byte values. +/// +/// @param [in] i The FfxUInt32x2 value to be unpacked. +/// +/// @returns +/// The unpacked FfxFloat16x2. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxPermuteZeroBasedSByte3Uint2ToFloat16x2(FfxUInt32x2 i) +{ + return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); +} + +/// Calculate a half-precision low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16 ffxApproximateSqrtHalf(FfxFloat16 a) +{ + return FFX_TO_FLOAT16((FFX_TO_UINT16(a) >> FFX_BROADCAST_UINT16(1)) + FFX_BROADCAST_UINT16(0x1de2)); +} + +/// Calculate a half-precision low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxApproximateSqrtHalf(FfxFloat16x2 a) +{ + return FFX_TO_FLOAT16X2((FFX_TO_UINT16X2(a) >> FFX_BROADCAST_UINT16X2(1)) + FFX_BROADCAST_UINT16X2(0x1de2)); +} + +/// Calculate a half-precision low-quality approximation for the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the square root for. +/// +/// @returns +/// An approximation of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxApproximateSqrtHalf(FfxFloat16x3 a) +{ + return FFX_TO_FLOAT16X3((FFX_TO_UINT16X3(a) >> FFX_BROADCAST_UINT16X3(1)) + FFX_BROADCAST_UINT16X3(0x1de2)); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16 ffxApproximateReciprocalHalf(FfxFloat16 a) +{ + return FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x7784) - FFX_TO_UINT16(a)); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxApproximateReciprocalHalf(FfxFloat16x2 a) +{ + return FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x7784) - FFX_TO_UINT16X2(a)); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxApproximateReciprocalHalf(FfxFloat16x3 a) +{ + return FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x7784) - FFX_TO_UINT16X3(a)); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxApproximateReciprocalHalf(FfxFloat16x4 a) +{ + return FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x7784) - FFX_TO_UINT16X4(a)); +} + +/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat16 ffxApproximateReciprocalMediumHalf(FfxFloat16 a) +{ + FfxFloat16 b = FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x778d) - FFX_TO_UINT16(a)); + return b * (-b * a + FFX_BROADCAST_FLOAT16(2.0)); +} + +/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxApproximateReciprocalMediumHalf(FfxFloat16x2 a) +{ + FfxFloat16x2 b = FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x778d) - FFX_TO_UINT16X2(a)); + return b * (-b * a + FFX_BROADCAST_FLOAT16X2(2.0)); +} + +/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxApproximateReciprocalMediumHalf(FfxFloat16x3 a) +{ + FfxFloat16x3 b = FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x778d) - FFX_TO_UINT16X3(a)); + return b * (-b * a + FFX_BROADCAST_FLOAT16X3(2.0)); +} + +/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal for. +/// +/// @returns +/// An approximation of the reciprocal, estimated to medium quality. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxApproximateReciprocalMediumHalf(FfxFloat16x4 a) +{ + FfxFloat16x4 b = FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x778d) - FFX_TO_UINT16X4(a)); + return b * (-b * a + FFX_BROADCAST_FLOAT16X4(2.0)); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. +/// +/// @returns +/// An approximation of the reciprocal of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16 ffxApproximateReciprocalSquareRootHalf(FfxFloat16 a) +{ + return FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x59a3) - (FFX_TO_UINT16(a) >> FFX_BROADCAST_UINT16(1))); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. +/// +/// @returns +/// An approximation of the reciprocal of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x2 a) +{ + return FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x59a3) - (FFX_TO_UINT16X2(a) >> FFX_BROADCAST_UINT16X2(1))); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. +/// +/// @returns +/// An approximation of the reciprocal of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x3 a) +{ + return FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x59a3) - (FFX_TO_UINT16X3(a) >> FFX_BROADCAST_UINT16X3(1))); +} + +/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. +/// +/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent +/// presentation materials: +/// +/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +/// +/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. +/// +/// @returns +/// An approximation of the reciprocal of the square root, estimated to low quality. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x4 a) +{ + return FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x59a3) - (FFX_TO_UINT16X4(a) >> FFX_BROADCAST_UINT16X4(1))); +} + +/// An approximation of sine. +/// +/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +/// is {-1/4 to 1/4} representing {-1 to 1}. +/// +/// @param [in] x The value to calculate approximate sine for. +/// +/// @returns +/// The approximate sine of value. +FfxFloat16 ffxParabolicSinHalf(FfxFloat16 x) +{ + return x * abs(x) - x; +} + +/// An approximation of sine. +/// +/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +/// is {-1/4 to 1/4} representing {-1 to 1}. +/// +/// @param [in] x The value to calculate approximate sine for. +/// +/// @returns +/// The approximate sine of value. +FfxFloat16x2 ffxParabolicSinHalf(FfxFloat16x2 x) +{ + return x * abs(x) - x; +} + +/// An approximation of cosine. +/// +/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +/// is {-1/4 to 1/4} representing {-1 to 1}. +/// +/// @param [in] x The value to calculate approximate cosine for. +/// +/// @returns +/// The approximate cosine of value. +FfxFloat16 ffxParabolicCosHalf(FfxFloat16 x) +{ + x = ffxFract(x * FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16(0.75)); + x = x * FFX_BROADCAST_FLOAT16(2.0) - FFX_BROADCAST_FLOAT16(1.0); + return ffxParabolicSinHalf(x); +} + +/// An approximation of cosine. +/// +/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +/// is {-1/4 to 1/4} representing {-1 to 1}. +/// +/// @param [in] x The value to calculate approximate cosine for. +/// +/// @returns +/// The approximate cosine of value. +FfxFloat16x2 ffxParabolicCosHalf(FfxFloat16x2 x) +{ + x = ffxFract(x * FFX_BROADCAST_FLOAT16X2(0.5) + FFX_BROADCAST_FLOAT16X2(0.75)); + x = x * FFX_BROADCAST_FLOAT16X2(2.0) - FFX_BROADCAST_FLOAT16X2(1.0); + return ffxParabolicSinHalf(x); +} + +/// An approximation of both sine and cosine. +/// +/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range +/// is {-1/4 to 1/4} representing {-1 to 1}. +/// +/// @param [in] x The value to calculate approximate cosine for. +/// +/// @returns +/// A FfxFloat32x2 containing approximations of both sine and cosine of value. +FfxFloat16x2 ffxParabolicSinCosHalf(FfxFloat16 x) +{ + FfxFloat16 y = ffxFract(x * FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16(0.75)); + y = y * FFX_BROADCAST_FLOAT16(2.0) - FFX_BROADCAST_FLOAT16(1.0); + return ffxParabolicSinHalf(FfxFloat16x2(x, y)); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt16 ffxZeroOneAndHalf(FfxUInt16 x, FfxUInt16 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxZeroOneAndHalf(FfxUInt16x2 x, FfxUInt16x2 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt16x3 ffxZeroOneAndHalf(FfxUInt16x3 x, FfxUInt16x3 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxUInt16x4 ffxZeroOneAndHalf(FfxUInt16x4 x, FfxUInt16x4 y) +{ + return min(x, y); +} + +/// Conditional free logic NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// @param [in] y The second value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt16 ffxZeroOneNotHalf(FfxUInt16 x) +{ + return x ^ FFX_BROADCAST_UINT16(1); +} + +/// Conditional free logic NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// @param [in] y The second value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxZeroOneNotHalf(FfxUInt16x2 x) +{ + return x ^ FFX_BROADCAST_UINT16X2(1); +} + +/// Conditional free logic NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// @param [in] y The second value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt16x3 ffxZeroOneNotHalf(FfxUInt16x3 x) +{ + return x ^ FFX_BROADCAST_UINT16X3(1); +} + +/// Conditional free logic NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the NOT operator. +/// @param [in] y The second value to be fed into the NOT operator. +/// +/// @returns +/// Result of the NOT operation. +/// +/// @ingroup GPUCore +FfxUInt16x4 ffxZeroOneNotHalf(FfxUInt16x4 x) +{ + return x ^ FFX_BROADCAST_UINT16X4(1); +} + +/// Conditional free logic OR operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt16 ffxZeroOneOrHalf(FfxUInt16 x, FfxUInt16 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxZeroOneOrHalf(FfxUInt16x2 x, FfxUInt16x2 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt16x3 ffxZeroOneOrHalf(FfxUInt16x3 x, FfxUInt16x3 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxUInt16x4 ffxZeroOneOrHalf(FfxUInt16x4 x, FfxUInt16x4 y) +{ + return max(x, y); +} + +/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. +/// +/// @param [in] x The value to converted to a Uint. +/// +/// @returns +/// The converted Uint value. +/// +/// @ingroup GPUCore +FfxUInt16 ffxZeroOneFloat16ToUint16(FfxFloat16 x) +{ + return FFX_TO_UINT16(x * FFX_TO_FLOAT16(FFX_TO_UINT16(1))); +} + +/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. +/// +/// @param [in] x The value to converted to a Uint. +/// +/// @returns +/// The converted Uint value. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxZeroOneFloat16x2ToUint16x2(FfxFloat16x2 x) +{ + return FFX_TO_UINT16X2(x * FFX_TO_FLOAT16X2(FfxUInt16x2(1, 1))); +} + +/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. +/// +/// @param [in] x The value to converted to a Uint. +/// +/// @returns +/// The converted Uint value. +/// +/// @ingroup GPUCore +FfxUInt16x3 ffxZeroOneFloat16x3ToUint16x3(FfxFloat16x3 x) +{ + return FFX_TO_UINT16X3(x * FFX_TO_FLOAT16X3(FfxUInt16x3(1, 1, 1))); +} + +/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. +/// +/// @param [in] x The value to converted to a Uint. +/// +/// @returns +/// The converted Uint value. +/// +/// @ingroup GPUCore +FfxUInt16x4 ffxZeroOneFloat16x4ToUint16x4(FfxFloat16x4 x) +{ + return FFX_TO_UINT16X4(x * FFX_TO_FLOAT16X4(FfxUInt16x4(1, 1, 1, 1))); +} + +/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. +/// +/// @param [in] x The value to converted to a half-precision FfxFloat32. +/// +/// @returns +/// The converted half-precision FfxFloat32 value. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneUint16ToFloat16(FfxUInt16 x) +{ + return FFX_TO_FLOAT16(x * FFX_TO_UINT16(FFX_TO_FLOAT16(1.0))); +} + +/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. +/// +/// @param [in] x The value to converted to a half-precision FfxFloat32. +/// +/// @returns +/// The converted half-precision FfxFloat32 value. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneUint16x2ToFloat16x2(FfxUInt16x2 x) +{ + return FFX_TO_FLOAT16X2(x * FFX_TO_UINT16X2(FfxUInt16x2(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); +} + +/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. +/// +/// @param [in] x The value to converted to a half-precision FfxFloat32. +/// +/// @returns +/// The converted half-precision FfxFloat32 value. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneUint16x3ToFloat16x3(FfxUInt16x3 x) +{ + return FFX_TO_FLOAT16X3(x * FFX_TO_UINT16X3(FfxUInt16x3(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); +} + +/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. +/// +/// @param [in] x The value to converted to a half-precision FfxFloat32. +/// +/// @returns +/// The converted half-precision FfxFloat32 value. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneUint16x4ToFloat16x4(FfxUInt16x4 x) +{ + return FFX_TO_FLOAT16X4(x * FFX_TO_UINT16X4(FfxUInt16x4(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneAndHalf(FfxFloat16 x, FfxFloat16 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneAndHalf(FfxFloat16x2 x, FfxFloat16x2 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneAndHalf(FfxFloat16x3 x, FfxFloat16x3 y) +{ + return min(x, y); +} + +/// Conditional free logic AND operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// +/// @returns +/// Result of the AND operation. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneAndHalf(FfxFloat16x4 x, FfxFloat16x4 y) +{ + return min(x, y); +} + +/// Conditional free logic AND NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND NOT operator. +/// @param [in] y The second value to be fed into the AND NOT operator. +/// +/// @returns +/// Result of the AND NOT operation. +/// +/// @ingroup GPUCore +FfxFloat16 ffxSignedZeroOneAndOrHalf(FfxFloat16 x, FfxFloat16 y) +{ + return (-x) * y + FFX_BROADCAST_FLOAT16(1.0); +} + +/// Conditional free logic AND NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND NOT operator. +/// @param [in] y The second value to be fed into the AND NOT operator. +/// +/// @returns +/// Result of the AND NOT operation. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxSignedZeroOneAndOrHalf(FfxFloat16x2 x, FfxFloat16x2 y) +{ + return (-x) * y + FFX_BROADCAST_FLOAT16X2(1.0); +} + +/// Conditional free logic AND NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND NOT operator. +/// @param [in] y The second value to be fed into the AND NOT operator. +/// +/// @returns +/// Result of the AND NOT operation. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxSignedZeroOneAndOrHalf(FfxFloat16x3 x, FfxFloat16x3 y) +{ + return (-x) * y + FFX_BROADCAST_FLOAT16X3(1.0); +} + +/// Conditional free logic AND NOT operation using two half-precision values. +/// +/// @param [in] x The first value to be fed into the AND NOT operator. +/// @param [in] y The second value to be fed into the AND NOT operator. +/// +/// @returns +/// Result of the AND NOT operation. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxSignedZeroOneAndOrHalf(FfxFloat16x4 x, FfxFloat16x4 y) +{ + return (-x) * y + FFX_BROADCAST_FLOAT16X4(1.0); +} + +/// Conditional free logic AND operation using two half-precision values followed by +/// a NOT operation using the resulting value and a third half-precision value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneAndOrHalf(FfxFloat16 x, FfxFloat16 y, FfxFloat16 z) +{ + return ffxSaturate(x * y + z); +} + +/// Conditional free logic AND operation using two half-precision values followed by +/// a NOT operation using the resulting value and a third half-precision value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneAndOrHalf(FfxFloat16x2 x, FfxFloat16x2 y, FfxFloat16x2 z) +{ + return ffxSaturate(x * y + z); +} + +/// Conditional free logic AND operation using two half-precision values followed by +/// a NOT operation using the resulting value and a third half-precision value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneAndOrHalf(FfxFloat16x3 x, FfxFloat16x3 y, FfxFloat16x3 z) +{ + return ffxSaturate(x * y + z); +} + +/// Conditional free logic AND operation using two half-precision values followed by +/// a NOT operation using the resulting value and a third half-precision value. +/// +/// @param [in] x The first value to be fed into the AND operator. +/// @param [in] y The second value to be fed into the AND operator. +/// @param [in] z The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneAndOrHalf(FfxFloat16x4 x, FfxFloat16x4 y, FfxFloat16x4 z) +{ + return ffxSaturate(x * y + z); +} + +/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16(FFX_POSITIVE_INFINITY_HALF)); +} + +/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x2 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16X2(FFX_POSITIVE_INFINITY_HALF)); +} + +/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x3 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16X3(FFX_POSITIVE_INFINITY_HALF)); +} + +/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the greater than zero comparison. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x4 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16X4(FFX_POSITIVE_INFINITY_HALF)); +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneNotHalf(FfxFloat16 x) +{ + return FFX_BROADCAST_FLOAT16(1.0) - x; +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneNotHalf(FfxFloat16x2 x) +{ + return FFX_BROADCAST_FLOAT16X2(1.0) - x; +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneNotHalf(FfxFloat16x3 x) +{ + return FFX_BROADCAST_FLOAT16X3(1.0) - x; +} + +/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the AND OR operator. +/// +/// @returns +/// Result of the AND OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneNotHalf(FfxFloat16x4 x) +{ + return FFX_BROADCAST_FLOAT16X4(1.0) - x; +} + +/// Conditional free logic OR operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneOrHalf(FfxFloat16 x, FfxFloat16 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneOrHalf(FfxFloat16x2 x, FfxFloat16x2 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneOrHalf(FfxFloat16x3 x, FfxFloat16x3 y) +{ + return max(x, y); +} + +/// Conditional free logic OR operation using two half-precision FfxFloat32 values. +/// +/// @param [in] x The first value to be fed into the OR operator. +/// @param [in] y The second value to be fed into the OR operator. +/// +/// @returns +/// Result of the OR operation. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneOrHalf(FfxFloat16x4 x, FfxFloat16x4 y) +{ + return max(x, y); +} + +/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneSelectHalf(FfxFloat16 x, FfxFloat16 y, FfxFloat16 z) +{ + FfxFloat16 r = (-x) * z + z; + return x * y + r; +} + +/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneSelectHalf(FfxFloat16x2 x, FfxFloat16x2 y, FfxFloat16x2 z) +{ + FfxFloat16x2 r = (-x) * z + z; + return x * y + r; +} + +/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneSelectHalf(FfxFloat16x3 x, FfxFloat16x3 y, FfxFloat16x3 z) +{ + FfxFloat16x3 r = (-x) * z + z; + return x * y + r; +} + +/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. +/// +/// @param [in] x The value to compare against zero. +/// @param [in] y The value to return if the comparision is greater than zero. +/// @param [in] z The value to return if the comparision is less than or equal to zero. +/// +/// @returns +/// The selected value. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneSelectHalf(FfxFloat16x4 x, FfxFloat16x4 y, FfxFloat16x4 z) +{ + FfxFloat16x4 r = (-x) * z + z; + return x * y + r; +} + +/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat16 ffxZeroOneIsSignedHalf(FfxFloat16 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxZeroOneIsSignedHalf(FfxFloat16x2 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16X2(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxZeroOneIsSignedHalf(FfxFloat16x3 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16X3(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. +/// +/// @param [in] x The value to be compared. +/// +/// @returns +/// Result of the sign value. +/// +/// @ingroup GPUCore +FfxFloat16x4 ffxZeroOneIsSignedHalf(FfxFloat16x4 x) +{ + return ffxSaturate(x * FFX_BROADCAST_FLOAT16X4(FFX_NEGATIVE_INFINITY_HALF)); +} + +/// Compute a Rec.709 color space. +/// +/// Rec.709 is used for some HDTVs. +/// +/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +/// +/// @param [in] c The color to convert to Rec. 709. +/// +/// @returns +/// The color in Rec.709 space. +/// +/// @ingroup GPUCore +FfxFloat16 ffxRec709FromLinearHalf(FfxFloat16 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); + FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); + return clamp(j.x, c * j.y, pow(c, j.z) * k.x + k.y); +} + +/// Compute a Rec.709 color space. +/// +/// Rec.709 is used for some HDTVs. +/// +/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +/// +/// @param [in] c The color to convert to Rec. 709. +/// +/// @returns +/// The color in Rec.709 space. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxRec709FromLinearHalf(FfxFloat16x2 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); + FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); + return clamp(j.xx, c * j.yy, pow(c, j.zz) * k.xx + k.yy); +} + +/// Compute a Rec.709 color space. +/// +/// Rec.709 is used for some HDTVs. +/// +/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +/// +/// @param [in] c The color to convert to Rec. 709. +/// +/// @returns +/// The color in Rec.709 space. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxRec709FromLinearHalf(FfxFloat16x3 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); + FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); + return clamp(j.xxx, c * j.yyy, pow(c, j.zzz) * k.xxx + k.yyy); +} + +/// Compute a gamma value from a linear value. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. +/// +/// @param [in] c The value to convert to gamma space from linear. +/// @param [in] rcpX The reciprocal of power value used for the gamma curve. +/// +/// @returns +/// A value in gamma space. +/// +/// @ingroup GPUCore +FfxFloat16 ffxGammaFromLinearHalf(FfxFloat16 c, FfxFloat16 rcpX) +{ + return pow(c, FFX_BROADCAST_FLOAT16(rcpX)); +} + +/// Compute a gamma value from a linear value. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. +/// +/// @param [in] c The value to convert to gamma space from linear. +/// @param [in] rcpX The reciprocal of power value used for the gamma curve. +/// +/// @returns +/// A value in gamma space. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxGammaFromLinearHalf(FfxFloat16x2 c, FfxFloat16 rcpX) +{ + return pow(c, FFX_BROADCAST_FLOAT16X2(rcpX)); +} + +/// Compute a gamma value from a linear value. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. +/// +/// @param [in] c The value to convert to gamma space from linear. +/// @param [in] rcpX The reciprocal of power value used for the gamma curve. +/// +/// @returns +/// A value in gamma space. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxGammaFromLinearHalf(FfxFloat16x3 c, FfxFloat16 rcpX) +{ + return pow(c, FFX_BROADCAST_FLOAT16X3(rcpX)); +} + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] c The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +FfxFloat16 ffxSrgbFromLinearHalf(FfxFloat16 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); + return clamp(j.x, c * j.y, pow(c, j.z) * k.x + k.y); +} + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] c The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxSrgbFromLinearHalf(FfxFloat16x2 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); + return clamp(j.xx, c * j.yy, pow(c, j.zz) * k.xx + k.yy); +} + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] c The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxSrgbFromLinearHalf(FfxFloat16x3 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); + return clamp(j.xxx, c * j.yyy, pow(c, j.zzz) * k.xxx + k.yyy); +} + +/// Compute the square root of a value. +/// +/// @param [in] c The value to compute the square root for. +/// +/// @returns +/// A square root of the input value. +/// +/// @ingroup GPUCore +FfxFloat16 ffxSquareRootHalf(FfxFloat16 c) +{ + return sqrt(c); +} + +/// Compute the square root of a value. +/// +/// @param [in] c The value to compute the square root for. +/// +/// @returns +/// A square root of the input value. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxSquareRootHalf(FfxFloat16x2 c) +{ + return sqrt(c); +} + +/// Compute the square root of a value. +/// +/// @param [in] c The value to compute the square root for. +/// +/// @returns +/// A square root of the input value. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxSquareRootHalf(FfxFloat16x3 c) +{ + return sqrt(c); +} + +/// Compute the cube root of a value. +/// +/// @param [in] c The value to compute the cube root for. +/// +/// @returns +/// A cube root of the input value. +/// +/// @ingroup GPUCore +FfxFloat16 ffxCubeRootHalf(FfxFloat16 c) +{ + return pow(c, FFX_BROADCAST_FLOAT16(1.0 / 3.0)); +} + +/// Compute the cube root of a value. +/// +/// @param [in] c The value to compute the cube root for. +/// +/// @returns +/// A cube root of the input value. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxCubeRootHalf(FfxFloat16x2 c) +{ + return pow(c, FFX_BROADCAST_FLOAT16X2(1.0 / 3.0)); +} + +/// Compute the cube root of a value. +/// +/// @param [in] c The value to compute the cube root for. +/// +/// @returns +/// A cube root of the input value. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxCubeRootHalf(FfxFloat16x3 c) +{ + return pow(c, FFX_BROADCAST_FLOAT16X3(1.0 / 3.0)); +} + +/// Compute a linear value from a REC.709 value. +/// +/// @param [in] c The value to convert to linear from REC.709. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16 ffxLinearFromRec709Half(FfxFloat16 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); + FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); + return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.x), c * j.y, pow(c * k.x + k.y, j.z)); +} + +/// Compute a linear value from a REC.709 value. +/// +/// @param [in] c The value to convert to linear from REC.709. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxLinearFromRec709Half(FfxFloat16x2 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); + FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); + return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xx), c * j.yy, pow(c * k.xx + k.yy, j.zz)); +} + +/// Compute a linear value from a REC.709 value. +/// +/// @param [in] c The value to convert to linear from REC.709. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxLinearFromRec709Half(FfxFloat16x3 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); + FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); + return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xxx), c * j.yyy, pow(c * k.xxx + k.yyy, j.zzz)); +} + +/// Compute a linear value from a value in a gamma space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] c The value to convert to linear in gamma space. +/// @param [in] x The power value used for the gamma curve. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16 ffxLinearFromGammaHalf(FfxFloat16 c, FfxFloat16 x) +{ + return pow(c, FFX_BROADCAST_FLOAT16(x)); +} + +/// Compute a linear value from a value in a gamma space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] c The value to convert to linear in gamma space. +/// @param [in] x The power value used for the gamma curve. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxLinearFromGammaHalf(FfxFloat16x2 c, FfxFloat16 x) +{ + return pow(c, FFX_BROADCAST_FLOAT16X2(x)); +} + +/// Compute a linear value from a value in a gamma space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] c The value to convert to linear in gamma space. +/// @param [in] x The power value used for the gamma curve. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxLinearFromGammaHalf(FfxFloat16x3 c, FfxFloat16 x) +{ + return pow(c, FFX_BROADCAST_FLOAT16X3(x)); +} + +/// Compute a linear value from a value in a SRGB space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] c The value to convert to linear in SRGB space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16 ffxLinearFromSrgbHalf(FfxFloat16 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); + FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); + return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.x), c * j.y, pow(c * k.x + k.y, j.z)); +} + +/// Compute a linear value from a value in a SRGB space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] c The value to convert to linear in SRGB space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16x2 ffxLinearFromSrgbHalf(FfxFloat16x2 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); + FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); + return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xx), c * j.yy, pow(c * k.xx + k.yy, j.zz)); +} + +/// Compute a linear value from a value in a SRGB space. +/// +/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. +/// +/// @param [in] c The value to convert to linear in SRGB space. +/// +/// @returns +/// A value in linear space. +/// +/// @ingroup GPUCore +FfxFloat16x3 ffxLinearFromSrgbHalf(FfxFloat16x3 c) +{ + FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); + FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); + return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xxx), c * j.yyy, pow(c * k.xxx + k.yyy, j.zzz)); +} + +/// A remapping of 64x1 to 8x8 imposing rotated 2x2 pixel quads in quad linear. +/// +/// Remap illustration: +/// +/// 543210 +/// ~~~~~~ +/// ..xxx. +/// yy...y +/// +/// @param [in] a The input 1D coordinates to remap. +/// +/// @returns +/// The remapped 2D coordinates. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxRemapForQuadHalf(FfxUInt32 a) +{ + return FfxUInt16x2(ffxBitfieldExtract(a, 1u, 3u), ffxBitfieldInsertMask(ffxBitfieldExtract(a, 3u, 3u), a, 1u)); +} + +/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. +/// +/// The 64-wide lane indices to 8x8 remapping is performed as follows: +/// +/// 00 01 08 09 10 11 18 19 +/// 02 03 0a 0b 12 13 1a 1b +/// 04 05 0c 0d 14 15 1c 1d +/// 06 07 0e 0f 16 17 1e 1f +/// 20 21 28 29 30 31 38 39 +/// 22 23 2a 2b 32 33 3a 3b +/// 24 25 2c 2d 34 35 3c 3d +/// 26 27 2e 2f 36 37 3e 3f +/// +/// @param [in] a The input 1D coordinate to remap. +/// +/// @returns +/// The remapped 2D coordinates. +/// +/// @ingroup GPUCore +FfxUInt16x2 ffxRemapForWaveReductionHalf(FfxUInt32 a) +{ + return FfxUInt16x2(ffxBitfieldInsertMask(ffxBitfieldExtract(a, 2u, 3u), a, 1u), ffxBitfieldInsertMask(ffxBitfieldExtract(a, 3u, 3u), ffxBitfieldExtract(a, 1u, 2u), 2u)); +} + +#endif // FFX_HALF diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h.meta new file mode 100644 index 0000000..cda0dd4 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_gpu_common_half.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 93c5f50cadb9ff14cbf03fa7cb1de897 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h new file mode 100644 index 0000000..28827d9 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h @@ -0,0 +1,1898 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +/// @defgroup HLSLCore HLSL Core +/// HLSL core defines and functions +/// +/// @ingroup FfxHLSL + +#define DECLARE_SRV_REGISTER(regIndex) t##regIndex +#define DECLARE_UAV_REGISTER(regIndex) u##regIndex +#define DECLARE_CB_REGISTER(regIndex) b##regIndex +#define FFX_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) +#define FFX_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) +#define FFX_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) + +/// A define for abstracting select functionality for pre/post HLSL 21 +/// +/// @ingroup HLSLCore +#if __HLSL_VERSION >= 2021 + +#define FFX_SELECT(cond, arg1, arg2) select(cond, arg1, arg2) + +#else // #if __HLSL_VERSION >= 2021 + +#define FFX_SELECT(cond, arg1, arg2) cond ? arg1 : arg2 + +#endif // #if __HLSL_VERSION >= 2021 + +/// A define for abstracting shared memory between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_GROUPSHARED groupshared + +/// A define for abstracting compute memory barriers between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_GROUP_MEMORY_BARRIER GroupMemoryBarrierWithGroupSync() + +/// A define for abstracting compute atomic additions between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_ATOMIC_ADD(x, y) InterlockedAdd(x, y) + +/// A define for abstracting compute atomic additions between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_ATOMIC_ADD_RETURN(x, y, r) InterlockedAdd(x, y, r) + +/// A define for abstracting compute atomic OR between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_ATOMIC_OR(x, y) InterlockedOr(x, y) + +/// A define for abstracting compute atomic min between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_ATOMIC_MIN(x, y) InterlockedMin(x, y) + +/// A define for abstracting compute atomic max between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_ATOMIC_MAX(x, y) InterlockedMax(x, y) + +/// A define added to accept static markup on functions to aid CPU/GPU portability of code. +/// +/// @ingroup HLSLCore +#define FFX_STATIC static + +/// A define for abstracting loop unrolling between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_UNROLL [unroll] + +/// A define for abstracting a 'greater than' comparison operator between two types. +/// +/// @ingroup HLSLCore +#define FFX_GREATER_THAN(x, y) x > y + +/// A define for abstracting a 'greater than or equal' comparison operator between two types. +/// +/// @ingroup HLSLCore +#define FFX_GREATER_THAN_EQUAL(x, y) x >= y + +/// A define for abstracting a 'less than' comparison operator between two types. +/// +/// @ingroup HLSLCore +#define FFX_LESS_THAN(x, y) x < y + +/// A define for abstracting a 'less than or equal' comparison operator between two types. +/// +/// @ingroup HLSLCore +#define FFX_LESS_THAN_EQUAL(x, y) x <= y + +/// A define for abstracting an 'equal' comparison operator between two types. +/// +/// @ingroup HLSLCore +#define FFX_EQUAL(x, y) x == y + +/// A define for abstracting a 'not equal' comparison operator between two types. +/// +/// @ingroup HLSLCore +#define FFX_NOT_EQUAL(x, y) x != y + +/// A define for abstracting matrix multiply operations between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_MATRIX_MULTIPLY(a, b) mul(a, b) + +/// A define for abstracting vector transformations between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_TRANSFORM_VECTOR(a, b) mul(a, b) + +/// A define for abstracting modulo operations between shading languages. +/// +/// @ingroup HLSLCore +#define FFX_MODULO(a, b) (fmod(a, b)) + +/// Broadcast a scalar value to a 1-dimensional floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_FLOAT32(x) FfxFloat32(x) + +/// Broadcast a scalar value to a 2-dimensional floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_FLOAT32X2(x) FfxFloat32(x) + +/// Broadcast a scalar value to a 3-dimensional floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_FLOAT32X3(x) FfxFloat32(x) + +/// Broadcast a scalar value to a 4-dimensional floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_FLOAT32X4(x) FfxFloat32(x) + +/// Broadcast a scalar value to a 1-dimensional unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_UINT32(x) FfxUInt32(x) + +/// Broadcast a scalar value to a 2-dimensional unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_UINT32X2(x) FfxUInt32(x) + +/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_UINT32X3(x) FfxUInt32(x) + +/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_UINT32X4(x) FfxUInt32(x) + +/// Broadcast a scalar value to a 1-dimensional signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_INT32(x) FfxInt32(x) + +/// Broadcast a scalar value to a 2-dimensional signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_INT32X2(x) FfxInt32(x) + +/// Broadcast a scalar value to a 3-dimensional signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_INT32X3(x) FfxInt32(x) + +/// Broadcast a scalar value to a 4-dimensional signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_INT32X4(x) FfxInt32(x) + +/// Broadcast a scalar value to a 1-dimensional half-precision floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_FLOAT16(a) FFX_MIN16_F(a) + +/// Broadcast a scalar value to a 2-dimensional half-precision floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_FLOAT16X2(a) FFX_MIN16_F(a) + +/// Broadcast a scalar value to a 3-dimensional half-precision floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_FLOAT16X3(a) FFX_MIN16_F(a) + +/// Broadcast a scalar value to a 4-dimensional half-precision floating point vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_FLOAT16X4(a) FFX_MIN16_F(a) + +/// Broadcast a scalar value to a 1-dimensional half-precision unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_UINT16(a) FFX_MIN16_U(a) + +/// Broadcast a scalar value to a 2-dimensional half-precision unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_UINT16X2(a) FFX_MIN16_U(a) + +/// Broadcast a scalar value to a 3-dimensional half-precision unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_UINT16X3(a) FFX_MIN16_U(a) + +/// Broadcast a scalar value to a 4-dimensional half-precision unsigned integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_UINT16X4(a) FFX_MIN16_U(a) + +/// Broadcast a scalar value to a 1-dimensional half-precision signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_INT16(a) FFX_MIN16_I(a) + +/// Broadcast a scalar value to a 2-dimensional half-precision signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_INT16X2(a) FFX_MIN16_I(a) + +/// Broadcast a scalar value to a 3-dimensional half-precision signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_INT16X3(a) FFX_MIN16_I(a) + +/// Broadcast a scalar value to a 4-dimensional half-precision signed integer vector. +/// +/// @ingroup HLSLCore +#define FFX_BROADCAST_MIN_INT16X4(a) FFX_MIN16_I(a) + +/// Convert FfxFloat32 to half (in lower 16-bits of output). +/// +/// This function implements the same fast technique that is documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf +/// +/// The function supports denormals. +/// +/// Some conversion rules are to make computations possibly "safer" on the GPU, +/// -INF & -NaN -> -65504 +/// +INF & +NaN -> +65504 +/// +/// @param [in] f The 32bit floating point value to convert. +/// +/// @returns +/// The closest 16bit floating point value to f. +/// +/// @ingroup HLSLCore +#define ffxF32ToF16 f32tof16 + +/// Pack 2x32-bit floating point values in a single 32bit value. +/// +/// This function first converts each component of value into their nearest 16-bit floating +/// point representation, and then stores the X and Y components in the lower and upper 16 bits of the +/// 32bit unsigned integer respectively. +/// +/// @param [in] value A 2-dimensional floating point value to convert and pack. +/// +/// @returns +/// A packed 32bit value containing 2 16bit floating point values. +/// +/// @ingroup HLSLCore +FfxUInt32 ffxPackHalf2x16(FfxFloat32x2 value) +{ + return ffxF32ToF16(value.x) | (ffxF32ToF16(value.y) << 16); +} + +/// Broadcast a scalar value to a 2-dimensional floating point vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 2-dimensional floating point vector with value in each component. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxBroadcast2(FfxFloat32 value) +{ + return FfxFloat32x2(value, value); +} + +/// Broadcast a scalar value to a 3-dimensional floating point vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 3-dimensional floating point vector with value in each component. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxBroadcast3(FfxFloat32 value) +{ + return FfxFloat32x3(value, value, value); +} + +/// Broadcast a scalar value to a 4-dimensional floating point vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 4-dimensional floating point vector with value in each component. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxBroadcast4(FfxFloat32 value) +{ + return FfxFloat32x4(value, value, value, value); +} + +/// Broadcast a scalar value to a 2-dimensional signed integer vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 2-dimensional signed integer vector with value in each component. +/// +/// @ingroup HLSLCore +FfxInt32x2 ffxBroadcast2(FfxInt32 value) +{ + return FfxInt32x2(value, value); +} + +/// Broadcast a scalar value to a 3-dimensional signed integer vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 3-dimensional signed integer vector with value in each component. +/// +/// @ingroup HLSLCore +FfxInt32x3 ffxBroadcast3(FfxInt32 value) +{ + return FfxInt32x3(value, value, value); +} + +/// Broadcast a scalar value to a 4-dimensional signed integer vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 4-dimensional signed integer vector with value in each component. +/// +/// @ingroup HLSLCore +FfxInt32x4 ffxBroadcast4(FfxInt32 value) +{ + return FfxInt32x4(value, value, value, value); +} + +/// Broadcast a scalar value to a 2-dimensional unsigned integer vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 2-dimensional unsigned integer vector with value in each component. +/// +/// @ingroup HLSLCore +FfxUInt32x2 ffxBroadcast2(FfxUInt32 value) +{ + return FfxUInt32x2(value, value); +} + +/// Broadcast a scalar value to a 3-dimensional unsigned integer vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 3-dimensional unsigned integer vector with value in each component. +/// +/// @ingroup HLSLCore +FfxUInt32x3 ffxBroadcast3(FfxUInt32 value) +{ + return FfxUInt32x3(value, value, value); +} + +/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. +/// +/// @param [in] value The value to to broadcast. +/// +/// @returns +/// A 4-dimensional unsigned integer vector with value in each component. +/// +/// @ingroup HLSLCore +FfxUInt32x4 ffxBroadcast4(FfxUInt32 value) +{ + return FfxUInt32x4(value, value, value, value); +} + +FfxUInt32 ffxBitfieldExtract(FfxUInt32 src, FfxUInt32 off, FfxUInt32 bits) +{ + FfxUInt32 mask = (1u << bits) - 1; + return (src >> off) & mask; +} + +FfxUInt32 ffxBitfieldInsert(FfxUInt32 src, FfxUInt32 ins, FfxUInt32 mask) +{ + return (ins & mask) | (src & (~mask)); +} + +FfxUInt32 ffxBitfieldInsertMask(FfxUInt32 src, FfxUInt32 ins, FfxUInt32 bits) +{ + FfxUInt32 mask = (1u << bits) - 1; + return (ins & mask) | (src & (~mask)); +} + +/// Interprets the bit pattern of x as an unsigned integer. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as an unsigned integer. +/// +/// @ingroup HLSLCore +FfxUInt32 ffxAsUInt32(FfxFloat32 x) +{ + return asuint(x); +} + +/// Interprets the bit pattern of x as an unsigned integer. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as an unsigned integer. +/// +/// @ingroup HLSLCore +FfxUInt32x2 ffxAsUInt32(FfxFloat32x2 x) +{ + return asuint(x); +} + +/// Interprets the bit pattern of x as an unsigned integer. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as an unsigned integer. +/// +/// @ingroup HLSLCore +FfxUInt32x3 ffxAsUInt32(FfxFloat32x3 x) +{ + return asuint(x); +} + +/// Interprets the bit pattern of x as an unsigned integer. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as an unsigned integer. +/// +/// @ingroup HLSLCore +FfxUInt32x4 ffxAsUInt32(FfxFloat32x4 x) +{ + return asuint(x); +} + +/// Interprets the bit pattern of x as a floating-point number. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as a floating-point number. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxAsFloat(FfxUInt32 x) +{ + return asfloat(x); +} + +/// Interprets the bit pattern of x as a floating-point number. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as a floating-point number. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxAsFloat(FfxUInt32x2 x) +{ + return asfloat(x); +} + +/// Interprets the bit pattern of x as a floating-point number. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as a floating-point number. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxAsFloat(FfxUInt32x3 x) +{ + return asfloat(x); +} + +/// Interprets the bit pattern of x as a floating-point number. +/// +/// @param [in] x The input value. +/// +/// @returns +/// The input interpreted as a floating-point number. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxAsFloat(FfxUInt32x4 x) +{ + return asfloat(x); +} + +/// Compute the inverse of a value. +/// +/// @param [in] x The value to calulate the inverse of. +/// +/// @returns +/// The inverse of x. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxReciprocal(FfxFloat32 x) +{ + return rcp(x); +} + +/// Compute the inverse of a value. +/// +/// @param [in] x The value to calulate the inverse of. +/// +/// @returns +/// The inverse of x. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxReciprocal(FfxFloat32x2 x) +{ + return rcp(x); +} + +/// Compute the inverse of a value. +/// +/// @param [in] x The value to calulate the inverse of. +/// +/// @returns +/// The inverse of x. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxReciprocal(FfxFloat32x3 x) +{ + return rcp(x); +} + +/// Compute the inverse of a value. +/// +/// @param [in] x The value to calulate the inverse of. +/// +/// @returns +/// The inverse of x. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxReciprocal(FfxFloat32x4 x) +{ + return rcp(x); +} + +/// Compute the inverse square root of a value. +/// +/// @param [in] x The value to calulate the inverse square root of. +/// +/// @returns +/// The inverse square root of x. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxRsqrt(FfxFloat32 x) +{ + return rsqrt(x); +} + +/// Compute the inverse square root of a value. +/// +/// @param [in] x The value to calulate the inverse square root of. +/// +/// @returns +/// The inverse square root of x. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxRsqrt(FfxFloat32x2 x) +{ + return rsqrt(x); +} + +/// Compute the inverse square root of a value. +/// +/// @param [in] x The value to calulate the inverse square root of. +/// +/// @returns +/// The inverse square root of x. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxRsqrt(FfxFloat32x3 x) +{ + return rsqrt(x); +} + +/// Compute the inverse square root of a value. +/// +/// @param [in] x The value to calulate the inverse square root of. +/// +/// @returns +/// The inverse square root of x. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxRsqrt(FfxFloat32x4 x) +{ + return rsqrt(x); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxLerp(FfxFloat32 x, FfxFloat32 y, FfxFloat32 t) +{ + return lerp(x, y, t); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxLerp(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32 t) +{ + return lerp(x, y, t); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxLerp(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 t) +{ + return lerp(x, y, t); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxLerp(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32 t) +{ + return lerp(x, y, t); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxLerp(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 t) +{ + return lerp(x, y, t); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxLerp(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32 t) +{ + return lerp(x, y, t); +} + +/// Compute the linear interopation between two values. +/// +/// Implemented by calling the HLSL mix instrinsic function. Implements the +/// following math: +/// +/// (1 - t) * x + t * y +/// +/// @param [in] x The first value to lerp between. +/// @param [in] y The second value to lerp between. +/// @param [in] t The value to determine how much of x and how much of y. +/// +/// @returns +/// A linearly interpolated value between x and y according to t. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxLerp(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 t) +{ + return lerp(x, y, t); +} + +/// Clamp a value to a [0..1] range. +/// +/// @param [in] x The value to clamp to [0..1] range. +/// +/// @returns +/// The clamped version of x. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxSaturate(FfxFloat32 x) +{ + return saturate(x); +} + +/// Clamp a value to a [0..1] range. +/// +/// @param [in] x The value to clamp to [0..1] range. +/// +/// @returns +/// The clamped version of x. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxSaturate(FfxFloat32x2 x) +{ + return saturate(x); +} + +/// Clamp a value to a [0..1] range. +/// +/// @param [in] x The value to clamp to [0..1] range. +/// +/// @returns +/// The clamped version of x. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxSaturate(FfxFloat32x3 x) +{ + return saturate(x); +} + +/// Clamp a value to a [0..1] range. +/// +/// @param [in] x The value to clamp to [0..1] range. +/// +/// @returns +/// The clamped version of x. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxSaturate(FfxFloat32x4 x) +{ + return saturate(x); +} + +/// Compute the factional part of a decimal value. +/// +/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is +/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic +/// function. +/// +/// @param [in] x The value to compute the fractional part from. +/// +/// @returns +/// The fractional part of x. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxFract(FfxFloat32 x) +{ + return x - floor(x); +} + +/// Compute the factional part of a decimal value. +/// +/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is +/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic +/// function. +/// +/// @param [in] x The value to compute the fractional part from. +/// +/// @returns +/// The fractional part of x. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxFract(FfxFloat32x2 x) +{ + return x - floor(x); +} + +/// Compute the factional part of a decimal value. +/// +/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is +/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic +/// function. +/// +/// @param [in] x The value to compute the fractional part from. +/// +/// @returns +/// The fractional part of x. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxFract(FfxFloat32x3 x) +{ + return x - floor(x); +} + +/// Compute the factional part of a decimal value. +/// +/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is +/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic +/// function. +/// +/// @param [in] x The value to compute the fractional part from. +/// +/// @returns +/// The fractional part of x. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxFract(FfxFloat32x4 x) +{ + return x - floor(x); +} + +/// Rounds to the nearest integer. In case the fractional part is 0.5, it will round to the nearest even integer. +/// +/// @param [in] x The value to be rounded. +/// +/// @returns +/// The nearest integer from x. The nearest even integer from x if equidistant from 2 integer. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxRound(FfxFloat32 x) +{ + return round(x); +} + +/// Rounds to the nearest integer. In case the fractional part is 0.5, it will round to the nearest even integer. +/// +/// @param [in] x The value to be rounded. +/// +/// @returns +/// The nearest integer from x. The nearest even integer from x if equidistant from 2 integer. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxRound(FfxFloat32x2 x) +{ + return round(x); +} + +/// Rounds to the nearest integer. In case the fractional part is 0.5, it will round to the nearest even integer. +/// +/// @param [in] x The value to be rounded. +/// +/// @returns +/// The nearest integer from x. The nearest even integer from x if equidistant from 2 integer. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxRound(FfxFloat32x3 x) +{ + return round(x); +} + +/// Rounds to the nearest integer. In case the fractional part is 0.5, it will round to the nearest even integer. +/// +/// @param [in] x The value to be rounded. +/// +/// @returns +/// The nearest integer from x. The nearest even integer from x if equidistant from 2 integer. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxRound(FfxFloat32x4 x) +{ + return round(x); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxMax3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxMax3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxMax3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxMax3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32 ffxMax3(FfxUInt32 x, FfxUInt32 y, FfxUInt32 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32x2 ffxMax3(FfxUInt32x2 x, FfxUInt32x2 y, FfxUInt32x2 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32x3 ffxMax3(FfxUInt32x3 x, FfxUInt32x3 y, FfxUInt32x3 z) +{ + return max(x, max(y, z)); +} + +/// Compute the maximum of three values. +/// +/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the max calculation. +/// @param [in] y The second value to include in the max calcuation. +/// @param [in] z The third value to include in the max calcuation. +/// +/// @returns +/// The maximum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32x4 ffxMax3(FfxUInt32x4 x, FfxUInt32x4 y, FfxUInt32x4 z) +{ + return max(x, max(y, z)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxMed3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) +{ + return max(min(x, y), min(max(x, y), z)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxMed3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) +{ + return max(min(x, y), min(max(x, y), z)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxMed3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) +{ + return max(min(x, y), min(max(x, y), z)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxMed3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) +{ + return max(min(x, y), min(max(x, y), z)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSL +FfxInt32 ffxMed3(FfxInt32 x, FfxInt32 y, FfxInt32 z) +{ + return max(min(x, y), min(max(x, y), z)); + // return min(max(min(y, z), x), max(y, z)); + // return max(max(x, y), z) == x ? max(y, z) : (max(max(x, y), z) == y ? max(x, z) : max(x, y)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSL +FfxInt32x2 ffxMed3(FfxInt32x2 x, FfxInt32x2 y, FfxInt32x2 z) +{ + return max(min(x, y), min(max(x, y), z)); + // return min(max(min(y, z), x), max(y, z)); + // return max(max(x, y), z) == x ? max(y, z) : (max(max(x, y), z) == y ? max(x, z) : max(x, y)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSL +FfxInt32x3 ffxMed3(FfxInt32x3 x, FfxInt32x3 y, FfxInt32x3 z) +{ + return max(min(x, y), min(max(x, y), z)); +} + +/// Compute the median of three values. +/// +/// NOTE: This function should compile down to a single V_MED3_I32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the median calculation. +/// @param [in] y The second value to include in the median calcuation. +/// @param [in] z The third value to include in the median calcuation. +/// +/// @returns +/// The median value of x, y, and z. +/// +/// @ingroup HLSL +FfxInt32x4 ffxMed3(FfxInt32x4 x, FfxInt32x4 y, FfxInt32x4 z) +{ + return max(min(x, y), min(max(x, y), z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32 ffxMin3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x2 ffxMin3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x3 ffxMin3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxFloat32x4 ffxMin3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32 ffxMin3(FfxUInt32 x, FfxUInt32 y, FfxUInt32 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32x2 ffxMin3(FfxUInt32x2 x, FfxUInt32x2 y, FfxUInt32x2 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calculation. +/// @param [in] z The third value to include in the min calculation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32x3 ffxMin3(FfxUInt32x3 x, FfxUInt32x3 y, FfxUInt32x3 z) +{ + return min(x, min(y, z)); +} + +/// Compute the minimum of three values. +/// +/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. +/// +/// @param [in] x The first value to include in the min calculation. +/// @param [in] y The second value to include in the min calcuation. +/// @param [in] z The third value to include in the min calcuation. +/// +/// @returns +/// The minimum value of x, y, and z. +/// +/// @ingroup HLSLCore +FfxUInt32x4 ffxMin3(FfxUInt32x4 x, FfxUInt32x4 y, FfxUInt32x4 z) +{ + return min(x, min(y, z)); +} + + +FfxUInt32 ffxAShrSU1(FfxUInt32 a, FfxUInt32 b) +{ + return FfxUInt32(FfxInt32(a) >> FfxInt32(b)); +} + +FfxUInt32 ffxPackF32(FfxFloat32x2 v){ + FfxUInt32x2 p = FfxUInt32x2(ffxF32ToF16(FfxFloat32x2(v).x), ffxF32ToF16(FfxFloat32x2(v).y)); + return p.x | (p.y << 16); +} + +FfxFloat32x2 ffxUnpackF32(FfxUInt32 a){ + return f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16)); +} + +FfxUInt32x2 ffxPackF32x2(FfxFloat32x4 v){ + return FfxUInt32x2(ffxPackF32(v.xy), ffxPackF32(v.zw)); +} + +FfxFloat32x4 ffxUnpackF32x2(FfxUInt32x2 a){ + return FfxFloat32x4(ffxUnpackF32(a.x), ffxUnpackF32(a.y)); +} + +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== +//============================================================================================================================== +// Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). +// Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ +FFX_MIN16_F2 ffxUint32ToFloat16x2(FfxUInt32 x) +{ + FfxFloat32x2 t = f16tof32(FfxUInt32x2(x & 0xFFFF, x >> 16)); + return FFX_MIN16_F2(t); +} +FFX_MIN16_F4 ffxUint32x2ToFloat16x4(FfxUInt32x2 x) +{ + return FFX_MIN16_F4(ffxUint32ToFloat16x2(x.x), ffxUint32ToFloat16x2(x.y)); +} +FFX_MIN16_U2 ffxUint32ToUint16x2(FfxUInt32 x) +{ + FfxUInt32x2 t = FfxUInt32x2(x & 0xFFFF, x >> 16); + return FFX_MIN16_U2(t); +} +FFX_MIN16_U4 ffxUint32x2ToUint16x4(FfxUInt32x2 x) +{ + return FFX_MIN16_U4(ffxUint32ToUint16x2(x.x), ffxUint32ToUint16x2(x.y)); +} + +FfxUInt32x2 ffxFloat16x4ToUint32x2(FFX_MIN16_F4 v) +{ + FfxUInt32x2 result; + result.x = ffxF32ToF16(v.x) | (ffxF32ToF16(v.y) << 16); + result.y = ffxF32ToF16(v.z) | (ffxF32ToF16(v.w) << 16); + return result; +} + +/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. +/// @param v Value to invert. +/// @return If v = 0 returns 0. If v != 0 returns 1/v. +FfxFloat32 ffxInvertSafe(FfxFloat32 v){ + FfxFloat32 s = FfxFloat32(sign(v)); + FfxFloat32 s2 = s*s; + return s2/(v + s2 - 1.0); +} + +/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. +/// @param v Value to invert. +/// @return If v = 0 returns 0. If v != 0 returns 1/v. +FfxFloat32x2 ffxInvertSafe(FfxFloat32x2 v){ + FfxFloat32x2 s = FfxFloat32x2(sign(v)); + FfxFloat32x2 s2 = s*s; + return s2/(v + s2 - FfxFloat32x2(1.0, 1.0)); +} + +/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. +/// @param v Value to invert. +/// @return If v = 0 returns 0. If v != 0 returns 1/v. +FfxFloat32x3 ffxInvertSafe(FfxFloat32x3 v){ + FfxFloat32x3 s = FfxFloat32x3(sign(v)); + FfxFloat32x3 s2 = s*s; + return s2/(v + s2 - FfxFloat32x3(1.0, 1.0, 1.0)); +} + +/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. +/// @param v Value to invert. +/// @return If v = 0 returns 0. If v != 0 returns 1/v. +FfxFloat32x4 ffxInvertSafe(FfxFloat32x4 v){ + FfxFloat32x4 s = FfxFloat32x4(sign(v)); + FfxFloat32x4 s2 = s*s; + return s2/(v + s2 - FfxFloat32x4(1.0, 1.0, 1.0, 1.0)); +} + +#define FFX_UINT32_TO_FLOAT16X2(x) ffxUint32ToFloat16x2(FfxUInt32(x)) +#if FFX_HALF + +#define FFX_UINT32X2_TO_FLOAT16X4(x) ffxUint32x2ToFloat16x4(FfxUInt32x2(x)) +#define FFX_UINT32_TO_UINT16X2(x) ffxUint32ToUint16x2(FfxUInt32(x)) +#define FFX_UINT32X2_TO_UINT16X4(x) ffxUint32x2ToUint16x4(FfxUInt32x2(x)) + +FfxUInt32 ffxPackF16(FfxFloat16x2 v){ + FfxUInt32x2 p = FfxUInt32x2(ffxF32ToF16(FfxFloat32x2(v).x), ffxF32ToF16(FfxFloat32x2(v).y)); + return p.x | (p.y << 16); +} + +FfxFloat16x2 ffxUnpackF16(FfxUInt32 a){ + return FfxFloat16x2(f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16))); +} + +//------------------------------------------------------------------------------------------------------------------------------ +FfxUInt32 FFX_MIN16_F2ToUint32(FFX_MIN16_F2 x) +{ + return ffxF32ToF16(x.x) + (ffxF32ToF16(x.y) << 16); +} +FfxUInt32x2 FFX_MIN16_F4ToUint32x2(FFX_MIN16_F4 x) +{ + return FfxUInt32x2(FFX_MIN16_F2ToUint32(x.xy), FFX_MIN16_F2ToUint32(x.zw)); +} +FfxUInt32 FFX_MIN16_U2ToUint32(FFX_MIN16_U2 x) +{ + return FfxUInt32(x.x) + (FfxUInt32(x.y) << 16); +} +FfxUInt32x2 FFX_MIN16_U4ToUint32x2(FFX_MIN16_U4 x) +{ + return FfxUInt32x2(FFX_MIN16_U2ToUint32(x.xy), FFX_MIN16_U2ToUint32(x.zw)); +} +#define FFX_FLOAT16X2_TO_UINT32(x) FFX_MIN16_F2ToUint32(FFX_MIN16_F2(x)) +#define FFX_FLOAT16X4_TO_UINT32X2(x) FFX_MIN16_F4ToUint32x2(FFX_MIN16_F4(x)) +#define FFX_UINT16X2_TO_UINT32(x) FFX_MIN16_U2ToUint32(FFX_MIN16_U2(x)) +#define FFX_UINT16X4_TO_UINT32X2(x) FFX_MIN16_U4ToUint32x2(FFX_MIN16_U4(x)) + +#if (FFX_HLSL_SM >= 62) && !defined(FFX_NO_16_BIT_CAST) +#define FFX_TO_UINT16(x) asuint16(x) +#define FFX_TO_UINT16X2(x) asuint16(x) +#define FFX_TO_UINT16X3(x) asuint16(x) +#define FFX_TO_UINT16X4(x) asuint16(x) +#else +#define FFX_TO_UINT16(a) FFX_MIN16_U(ffxF32ToF16(FfxFloat32(a))) +#define FFX_TO_UINT16X2(a) FFX_MIN16_U2(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y)) +#define FFX_TO_UINT16X3(a) FFX_MIN16_U3(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y), FFX_TO_UINT16((a).z)) +#define FFX_TO_UINT16X4(a) FFX_MIN16_U4(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y), FFX_TO_UINT16((a).z), FFX_TO_UINT16((a).w)) +#endif // #if (FFX_HLSL_SM>=62) && !defined(FFX_NO_16_BIT_CAST) + +#if (FFX_HLSL_SM >= 62) && !defined(FFX_NO_16_BIT_CAST) +#define FFX_TO_FLOAT16(x) asfloat16(x) +#define FFX_TO_FLOAT16X2(x) asfloat16(x) +#define FFX_TO_FLOAT16X3(x) asfloat16(x) +#define FFX_TO_FLOAT16X4(x) asfloat16(x) +#else +#define FFX_TO_FLOAT16(a) FFX_MIN16_F(f16tof32(FfxUInt32(a))) +#define FFX_TO_FLOAT16X2(a) FFX_MIN16_F2(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y)) +#define FFX_TO_FLOAT16X3(a) FFX_MIN16_F3(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y), FFX_TO_FLOAT16((a).z)) +#define FFX_TO_FLOAT16X4(a) FFX_MIN16_F4(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y), FFX_TO_FLOAT16((a).z), FFX_TO_FLOAT16((a).w)) +#endif // #if (FFX_HLSL_SM>=62) && !defined(FFX_NO_16_BIT_CAST) + +//============================================================================================================================== +#define FFX_BROADCAST_FLOAT16(a) FFX_MIN16_F(a) +#define FFX_BROADCAST_FLOAT16X2(a) FFX_MIN16_F(a) +#define FFX_BROADCAST_FLOAT16X3(a) FFX_MIN16_F(a) +#define FFX_BROADCAST_FLOAT16X4(a) FFX_MIN16_F(a) + +//------------------------------------------------------------------------------------------------------------------------------ +#define FFX_BROADCAST_INT16(a) FFX_MIN16_I(a) +#define FFX_BROADCAST_INT16X2(a) FFX_MIN16_I(a) +#define FFX_BROADCAST_INT16X3(a) FFX_MIN16_I(a) +#define FFX_BROADCAST_INT16X4(a) FFX_MIN16_I(a) + +//------------------------------------------------------------------------------------------------------------------------------ +#define FFX_BROADCAST_UINT16(a) FFX_MIN16_U(a) +#define FFX_BROADCAST_UINT16X2(a) FFX_MIN16_U(a) +#define FFX_BROADCAST_UINT16X3(a) FFX_MIN16_U(a) +#define FFX_BROADCAST_UINT16X4(a) FFX_MIN16_U(a) + +//============================================================================================================================== +FFX_MIN16_U ffxAbsHalf(FFX_MIN16_U a) +{ + return FFX_MIN16_U(abs(FFX_MIN16_I(a))); +} +FFX_MIN16_U2 ffxAbsHalf(FFX_MIN16_U2 a) +{ + return FFX_MIN16_U2(abs(FFX_MIN16_I2(a))); +} +FFX_MIN16_U3 ffxAbsHalf(FFX_MIN16_U3 a) +{ + return FFX_MIN16_U3(abs(FFX_MIN16_I3(a))); +} +FFX_MIN16_U4 ffxAbsHalf(FFX_MIN16_U4 a) +{ + return FFX_MIN16_U4(abs(FFX_MIN16_I4(a))); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxClampHalf(FFX_MIN16_F x, FFX_MIN16_F n, FFX_MIN16_F m) +{ + return max(n, min(x, m)); +} +FFX_MIN16_F2 ffxClampHalf(FFX_MIN16_F2 x, FFX_MIN16_F2 n, FFX_MIN16_F2 m) +{ + return max(n, min(x, m)); +} +FFX_MIN16_F3 ffxClampHalf(FFX_MIN16_F3 x, FFX_MIN16_F3 n, FFX_MIN16_F3 m) +{ + return max(n, min(x, m)); +} +FFX_MIN16_F4 ffxClampHalf(FFX_MIN16_F4 x, FFX_MIN16_F4 n, FFX_MIN16_F4 m) +{ + return max(n, min(x, m)); +} +//------------------------------------------------------------------------------------------------------------------------------ +// V_FRACT_F16 (note DX frac() is different). +FFX_MIN16_F ffxFract(FFX_MIN16_F x) +{ + return x - floor(x); +} +FFX_MIN16_F2 ffxFract(FFX_MIN16_F2 x) +{ + return x - floor(x); +} +FFX_MIN16_F3 ffxFract(FFX_MIN16_F3 x) +{ + return x - floor(x); +} +FFX_MIN16_F4 ffxFract(FFX_MIN16_F4 x) +{ + return x - floor(x); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxLerp(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F a) +{ + return lerp(x, y, a); +} +FFX_MIN16_F2 ffxLerp(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F a) +{ + return lerp(x, y, a); +} +FFX_MIN16_F2 ffxLerp(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 a) +{ + return lerp(x, y, a); +} +FFX_MIN16_F3 ffxLerp(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F a) +{ + return lerp(x, y, a); +} +FFX_MIN16_F3 ffxLerp(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 a) +{ + return lerp(x, y, a); +} +FFX_MIN16_F4 ffxLerp(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F a) +{ + return lerp(x, y, a); +} +FFX_MIN16_F4 ffxLerp(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 a) +{ + return lerp(x, y, a); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxMax3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) +{ + return max(x, max(y, z)); +} +FFX_MIN16_F2 ffxMax3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) +{ + return max(x, max(y, z)); +} +FFX_MIN16_F3 ffxMax3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) +{ + return max(x, max(y, z)); +} +FFX_MIN16_F4 ffxMax3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) +{ + return max(x, max(y, z)); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxMin3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) +{ + return min(x, min(y, z)); +} +FFX_MIN16_F2 ffxMin3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) +{ + return min(x, min(y, z)); +} +FFX_MIN16_F3 ffxMin3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) +{ + return min(x, min(y, z)); +} +FFX_MIN16_F4 ffxMin3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) +{ + return min(x, min(y, z)); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxMed3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) +{ + return max(min(x, y), min(max(x, y), z)); +} +FFX_MIN16_F2 ffxMed3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) +{ + return max(min(x, y), min(max(x, y), z)); +} +FFX_MIN16_F3 ffxMed3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) +{ + return max(min(x, y), min(max(x, y), z)); +} +FFX_MIN16_F4 ffxMed3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) +{ + return max(min(x, y), min(max(x, y), z)); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_I ffxMed3Half(FFX_MIN16_I x, FFX_MIN16_I y, FFX_MIN16_I z) +{ + return max(min(x, y), min(max(x, y), z)); +} +FFX_MIN16_I2 ffxMed3Half(FFX_MIN16_I2 x, FFX_MIN16_I2 y, FFX_MIN16_I2 z) +{ + return max(min(x, y), min(max(x, y), z)); +} +FFX_MIN16_I3 ffxMed3Half(FFX_MIN16_I3 x, FFX_MIN16_I3 y, FFX_MIN16_I3 z) +{ + return max(min(x, y), min(max(x, y), z)); +} +FFX_MIN16_I4 ffxMed3Half(FFX_MIN16_I4 x, FFX_MIN16_I4 y, FFX_MIN16_I4 z) +{ + return max(min(x, y), min(max(x, y), z)); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxReciprocalHalf(FFX_MIN16_F x) +{ + return rcp(x); +} +FFX_MIN16_F2 ffxReciprocalHalf(FFX_MIN16_F2 x) +{ + return rcp(x); +} +FFX_MIN16_F3 ffxReciprocalHalf(FFX_MIN16_F3 x) +{ + return rcp(x); +} +FFX_MIN16_F4 ffxReciprocalHalf(FFX_MIN16_F4 x) +{ + return rcp(x); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxReciprocalSquareRootHalf(FFX_MIN16_F x) +{ + return rsqrt(x); +} +FFX_MIN16_F2 ffxReciprocalSquareRootHalf(FFX_MIN16_F2 x) +{ + return rsqrt(x); +} +FFX_MIN16_F3 ffxReciprocalSquareRootHalf(FFX_MIN16_F3 x) +{ + return rsqrt(x); +} +FFX_MIN16_F4 ffxReciprocalSquareRootHalf(FFX_MIN16_F4 x) +{ + return rsqrt(x); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_F ffxSaturate(FFX_MIN16_F x) +{ + return saturate(x); +} +FFX_MIN16_F2 ffxSaturate(FFX_MIN16_F2 x) +{ + return saturate(x); +} +FFX_MIN16_F3 ffxSaturate(FFX_MIN16_F3 x) +{ + return saturate(x); +} +FFX_MIN16_F4 ffxSaturate(FFX_MIN16_F4 x) +{ + return saturate(x); +} +//------------------------------------------------------------------------------------------------------------------------------ +FFX_MIN16_U ffxBitShiftRightHalf(FFX_MIN16_U a, FFX_MIN16_U b) +{ + return FFX_MIN16_U(FFX_MIN16_I(a) >> FFX_MIN16_I(b)); +} +FFX_MIN16_U2 ffxBitShiftRightHalf(FFX_MIN16_U2 a, FFX_MIN16_U2 b) +{ + return FFX_MIN16_U2(FFX_MIN16_I2(a) >> FFX_MIN16_I2(b)); +} +FFX_MIN16_U3 ffxBitShiftRightHalf(FFX_MIN16_U3 a, FFX_MIN16_U3 b) +{ + return FFX_MIN16_U3(FFX_MIN16_I3(a) >> FFX_MIN16_I3(b)); +} +FFX_MIN16_U4 ffxBitShiftRightHalf(FFX_MIN16_U4 a, FFX_MIN16_U4 b) +{ + return FFX_MIN16_U4(FFX_MIN16_I4(a) >> FFX_MIN16_I4(b)); +} +#endif // FFX_HALF + +//============================================================================================================================== +// HLSL WAVE +//============================================================================================================================== +#if defined(FFX_WAVE) +// Where 'x' must be a compile time literal. +FfxFloat32 ffxWaveXorF1(FfxFloat32 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxFloat32x2 ffxWaveXorF2(FfxFloat32x2 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxFloat32x3 ffxWaveXorF3(FfxFloat32x3 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxFloat32x4 ffxWaveXorF4(FfxFloat32x4 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxUInt32 ffxWaveXorU1(FfxUInt32 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxUInt32x2 ffxWaveXorU1(FfxUInt32x2 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxUInt32x3 ffxWaveXorU1(FfxUInt32x3 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxUInt32x4 ffxWaveXorU1(FfxUInt32x4 v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); +} +FfxBoolean ffxWaveIsFirstLane() +{ + return WaveIsFirstLane(); +} +FfxUInt32 ffxWaveLaneIndex() +{ + return WaveGetLaneIndex(); +} +FfxBoolean ffxWaveReadAtLaneIndexB1(FfxBoolean v, FfxUInt32 x) +{ + return WaveReadLaneAt(v, x); +} +FfxUInt32 ffxWavePrefixCountBits(FfxBoolean v) +{ + return WavePrefixCountBits(v); +} +FfxUInt32 ffxWaveActiveCountBits(FfxBoolean v) +{ + return WaveActiveCountBits(v); +} +FfxUInt32 ffxWaveReadLaneFirstU1(FfxUInt32 v) +{ + return WaveReadLaneFirst(v); +} +FfxUInt32x2 ffxWaveReadLaneFirstU2(FfxUInt32x2 v) +{ + return WaveReadLaneFirst(v); +} +FfxBoolean ffxWaveReadLaneFirstB1(FfxBoolean v) +{ + return WaveReadLaneFirst(v); +} +FfxUInt32 ffxWaveOr(FfxUInt32 a) +{ + return WaveActiveBitOr(a); +} +FfxUInt32 ffxWaveMin(FfxUInt32 a) +{ + return WaveActiveMin(a); +} +FfxFloat32 ffxWaveMin(FfxFloat32 a) +{ + return WaveActiveMin(a); +} +FfxUInt32 ffxWaveMax(FfxUInt32 a) +{ + return WaveActiveMax(a); +} +FfxFloat32 ffxWaveMax(FfxFloat32 a) +{ + return WaveActiveMax(a); +} +FfxUInt32 ffxWaveSum(FfxUInt32 a) +{ + return WaveActiveSum(a); +} +FfxFloat32 ffxWaveSum(FfxFloat32 a) +{ + return WaveActiveSum(a); +} +FfxUInt32 ffxWaveLaneCount() +{ + return WaveGetLaneCount(); +} +FfxBoolean ffxWaveAllTrue(FfxBoolean v) +{ + return WaveActiveAllTrue(v); +} +FfxFloat32 ffxQuadReadX(FfxFloat32 v) +{ + return QuadReadAcrossX(v); +} +FfxFloat32x2 ffxQuadReadX(FfxFloat32x2 v) +{ + return QuadReadAcrossX(v); +} +FfxFloat32 ffxQuadReadY(FfxFloat32 v) +{ + return QuadReadAcrossY(v); +} +FfxFloat32x2 ffxQuadReadY(FfxFloat32x2 v) +{ + return QuadReadAcrossY(v); +} + +#if FFX_HALF +FfxFloat16x2 ffxWaveXorFloat16x2(FfxFloat16x2 v, FfxUInt32 x) +{ + return FFX_UINT32_TO_FLOAT16X2(WaveReadLaneAt(FFX_FLOAT16X2_TO_UINT32(v), WaveGetLaneIndex() ^ x)); +} +FfxFloat16x4 ffxWaveXorFloat16x4(FfxFloat16x4 v, FfxUInt32 x) +{ + return FFX_UINT32X2_TO_FLOAT16X4(WaveReadLaneAt(FFX_FLOAT16X4_TO_UINT32X2(v), WaveGetLaneIndex() ^ x)); +} +FfxUInt16x2 ffxWaveXorUint16x2(FfxUInt16x2 v, FfxUInt32 x) +{ + return FFX_UINT32_TO_UINT16X2(WaveReadLaneAt(FFX_UINT16X2_TO_UINT32(v), WaveGetLaneIndex() ^ x)); +} +FfxUInt16x4 ffxWaveXorUint16x4(FfxUInt16x4 v, FfxUInt32 x) +{ + return FFX_UINT32X2_TO_UINT16X4(WaveReadLaneAt(FFX_UINT16X4_TO_UINT32X2(v), WaveGetLaneIndex() ^ x)); +} +#endif // FFX_HALF +#endif // #if defined(FFX_WAVE) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h.meta new file mode 100644 index 0000000..7c889bd --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_hlsl.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 47a04cb434355164b8da169fbd474688 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h new file mode 100644 index 0000000..12147b9 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h @@ -0,0 +1,46 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +void ffxOpAAddOneF3(FFX_PARAMETER_OUT FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) +{ + d = a + ffxBroadcast3(b); +} + +void ffxOpACpyF3(FFX_PARAMETER_OUT FfxFloat32x3 d, FfxFloat32x3 a) +{ + d = a; +} + +void ffxOpAMulF3(FFX_PARAMETER_OUT FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32x3 b) +{ + d = a * b; +} + +void ffxOpAMulOneF3(FFX_PARAMETER_OUT FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) +{ + d = a * b; +} + +void ffxOpARcpF3(FFX_PARAMETER_OUT FfxFloat32x3 d, FfxFloat32x3 a) +{ + d = ffxReciprocal(a); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h.meta new file mode 100644 index 0000000..2f87125 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_core_portability.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 9dc8a5ba90431934588adf0dabbf0cdd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl new file mode 100644 index 0000000..59cfd80 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl @@ -0,0 +1,79 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_INPUT_EXPOSURE 0 +#define FSR2_BIND_SRV_DILATED_REACTIVE_MASKS 1 +#if FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#define FSR2_BIND_SRV_DILATED_MOTION_VECTORS 2 +#else +#define FSR2_BIND_SRV_INPUT_MOTION_VECTORS 2 +#endif +#define FSR2_BIND_SRV_INTERNAL_UPSCALED 3 +#define FSR2_BIND_SRV_LOCK_STATUS 4 +#define FSR2_BIND_SRV_PREPARED_INPUT_COLOR 5 +#define FSR2_BIND_SRV_LANCZOS_LUT 6 +#define FSR2_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT 7 +#define FSR2_BIND_SRV_SCENE_LUMINANCE_MIPS 8 +#define FSR2_BIND_SRV_AUTO_EXPOSURE 9 +#define FSR2_BIND_SRV_LUMA_HISTORY 10 + +#define FSR2_BIND_UAV_INTERNAL_UPSCALED 0 +#define FSR2_BIND_UAV_LOCK_STATUS 1 +#define FSR2_BIND_UAV_UPSCALED_OUTPUT 2 +#define FSR2_BIND_UAV_NEW_LOCKS 3 +#define FSR2_BIND_UAV_LUMA_HISTORY 4 + +#define FSR2_BIND_CB_FSR2 0 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_sample.h" +#include "fsr2/ffx_fsr2_upsample.h" +#include "fsr2/ffx_fsr2_postprocess_lock_status.h" +#include "fsr2/ffx_fsr2_reproject.h" +#include "fsr2/ffx_fsr2_accumulate.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_CONTENT +void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) +{ + const uint GroupRows = (uint(DisplaySize().y) + FFX_FSR2_THREAD_GROUP_HEIGHT - 1) / FFX_FSR2_THREAD_GROUP_HEIGHT; + uGroupId.y = GroupRows - uGroupId.y - 1; + + uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT) + uGroupThreadId; + + Accumulate(uDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl.meta new file mode 100644 index 0000000..a5a43a9 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_accumulate_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2587b11e2d882c649ac7fd1a51f6dc8c +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl new file mode 100644 index 0000000..1a5995b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl @@ -0,0 +1,78 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_INPUT_OPAQUE_ONLY 0 +#define FSR2_BIND_SRV_INPUT_COLOR 1 + +#define FSR2_BIND_UAV_AUTOREACTIVE 0 + +#define FSR2_BIND_CB_FSR2 0 +#define FSR2_BIND_CB_REACTIVE 1 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_REACTIVE_CONTENT +void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) +{ + uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT) + uGroupThreadId; + + float3 ColorPreAlpha = LoadOpaqueOnly( FFX_MIN16_I2(uDispatchThreadId) ).rgb; + float3 ColorPostAlpha = LoadInputColor(uDispatchThreadId).rgb; + + if (GenReactiveFlags() & FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_TONEMAP) + { + ColorPreAlpha = Tonemap(ColorPreAlpha); + ColorPostAlpha = Tonemap(ColorPostAlpha); + } + + if (GenReactiveFlags() & FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP) + { + ColorPreAlpha = InverseTonemap(ColorPreAlpha); + ColorPostAlpha = InverseTonemap(ColorPostAlpha); + } + + float out_reactive_value = 0.f; + float3 delta = abs(ColorPostAlpha - ColorPreAlpha); + + out_reactive_value = (GenReactiveFlags() & FFX_FSR2_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX) ? max(delta.x, max(delta.y, delta.z)) : length(delta); + out_reactive_value *= GenReactiveScale(); + + out_reactive_value = (GenReactiveFlags() & FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_THRESHOLD) ? (out_reactive_value < GenReactiveThreshold() ? 0 : GenReactiveBinaryValue()) : out_reactive_value; + + rw_output_autoreactive[uDispatchThreadId] = out_reactive_value; +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl.meta new file mode 100644 index 0000000..776a7af --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_autogen_reactive_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 15c74efa821aaf3429f92b8d5d9901d5 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl new file mode 100644 index 0000000..31de05f --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl @@ -0,0 +1,56 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_INPUT_COLOR 0 + +#define FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC 0 +#define FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE 1 +#define FSR2_BIND_UAV_EXPOSURE_MIP_5 2 +#define FSR2_BIND_UAV_AUTO_EXPOSURE 3 + +#define FSR2_BIND_CB_FSR2 0 +#define FSR2_BIND_CB_SPD 1 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_compute_luminance_pyramid.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 256 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_CB2_ROOTSIG_CONTENT +void CS(uint3 WorkGroupId : SV_GroupID, uint LocalThreadIndex : SV_GroupIndex) +{ + ComputeAutoExposure(WorkGroupId, LocalThreadIndex); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl.meta new file mode 100644 index 0000000..8ed0c35 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 630d959ddf3e02e4087e181b7d7cd7ed +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl new file mode 100644 index 0000000..30bbcc0 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl @@ -0,0 +1,67 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 +#define FSR2_BIND_SRV_DILATED_MOTION_VECTORS 1 +#define FSR2_BIND_SRV_DILATED_DEPTH 2 +#define FSR2_BIND_SRV_REACTIVE_MASK 3 +#define FSR2_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 4 +#define FSR2_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS 5 +#define FSR2_BIND_SRV_INPUT_MOTION_VECTORS 6 +#define FSR2_BIND_SRV_INPUT_COLOR 7 +#define FSR2_BIND_SRV_INPUT_DEPTH 8 +#define FSR2_BIND_SRV_INPUT_EXPOSURE 9 + +#define FSR2_BIND_UAV_DILATED_REACTIVE_MASKS 0 +#define FSR2_BIND_UAV_PREPARED_INPUT_COLOR 1 + +#define FSR2_BIND_CB_FSR2 0 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_sample.h" +#include "fsr2/ffx_fsr2_depth_clip.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_CONTENT +void CS( + int2 iGroupId : SV_GroupID, + int2 iDispatchThreadId : SV_DispatchThreadID, + int2 iGroupThreadId : SV_GroupThreadID, + int iGroupIndex : SV_GroupIndex) +{ + DepthClip(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl.meta new file mode 100644 index 0000000..794fe20 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_depth_clip_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 38a518d5929d9134096f87f66f59a285 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl new file mode 100644 index 0000000..dec17fe --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl @@ -0,0 +1,56 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_LOCK_INPUT_LUMA 0 + +#define FSR2_BIND_UAV_NEW_LOCKS 0 +#define FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 1 + +#define FSR2_BIND_CB_FSR2 0 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_sample.h" +#include "fsr2/ffx_fsr2_lock.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_CONTENT +void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) +{ + uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT) + uGroupThreadId; + + ComputeLock(uDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl.meta new file mode 100644 index 0000000..7dcfaed --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_lock_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c3bf93953aee91541a1be089c2ed268f +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl new file mode 100644 index 0000000..6fd6d29 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl @@ -0,0 +1,54 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_INPUT_EXPOSURE 0 +#define FSR2_BIND_SRV_RCAS_INPUT 1 + +#define FSR2_BIND_UAV_UPSCALED_OUTPUT 0 + +#define FSR2_BIND_CB_FSR2 0 +#define FSR2_BIND_CB_RCAS 1 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_rcas.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 64 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_CB2_ROOTSIG_CONTENT +void CS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID) +{ + RCAS(LocalThreadId, WorkGroupId, Dtid); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl.meta new file mode 100644 index 0000000..b1198a1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_rcas_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 43f9d03e57211f8478e3388eecb8d5f8 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl new file mode 100644 index 0000000..7e17318 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl @@ -0,0 +1,64 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_INPUT_MOTION_VECTORS 0 +#define FSR2_BIND_SRV_INPUT_DEPTH 1 +#define FSR2_BIND_SRV_INPUT_COLOR 2 +#define FSR2_BIND_SRV_INPUT_EXPOSURE 3 + +#define FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 +#define FSR2_BIND_UAV_DILATED_MOTION_VECTORS 1 +#define FSR2_BIND_UAV_DILATED_DEPTH 2 +#define FSR2_BIND_UAV_LOCK_INPUT_LUMA 3 + +#define FSR2_BIND_CB_FSR2 0 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_sample.h" +#include "fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_CONTENT +void CS( + int2 iGroupId : SV_GroupID, + int2 iDispatchThreadId : SV_DispatchThreadID, + int2 iGroupThreadId : SV_GroupThreadID, + int iGroupIndex : SV_GroupIndex +) +{ + ReconstructAndDilate(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl.meta new file mode 100644 index 0000000..622615b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c7220f38e87579a4788ddaabba0a6d97 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl new file mode 100644 index 0000000..765cc8b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl @@ -0,0 +1,92 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR2_BIND_SRV_INPUT_OPAQUE_ONLY 0 +#define FSR2_BIND_SRV_INPUT_COLOR 1 +#define FSR2_BIND_SRV_INPUT_MOTION_VECTORS 2 +#define FSR2_BIND_SRV_PREV_PRE_ALPHA_COLOR 3 +#define FSR2_BIND_SRV_PREV_POST_ALPHA_COLOR 4 +#define FSR2_BIND_SRV_REACTIVE_MASK 4 +#define FSR2_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 5 + +#define FSR2_BIND_UAV_AUTOREACTIVE 0 +#define FSR2_BIND_UAV_AUTOCOMPOSITION 1 +#define FSR2_BIND_UAV_PREV_PRE_ALPHA_COLOR 2 +#define FSR2_BIND_UAV_PREV_POST_ALPHA_COLOR 3 + +#define FSR2_BIND_CB_FSR2 0 +#define FSR2_BIND_CB_AUTOREACTIVE 1 + +#include "fsr2/ffx_fsr2_callbacks_hlsl.h" +#include "fsr2/ffx_fsr2_common.h" +#include "fsr2/ffx_fsr2_tcr_autogen.h" + +#ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#define FFX_FSR2_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_WIDTH +#ifndef FFX_FSR2_THREAD_GROUP_HEIGHT +#define FFX_FSR2_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR2_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#define FFX_FSR2_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR2_THREAD_GROUP_DEPTH +#ifndef FFX_FSR2_NUM_THREADS +#define FFX_FSR2_NUM_THREADS [numthreads(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT, FFX_FSR2_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR2_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_REACTIVE_CONTENT +void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) +{ + FFX_MIN16_I2 uDispatchThreadId = FFX_MIN16_I2(uGroupId * uint2(FFX_FSR2_THREAD_GROUP_WIDTH, FFX_FSR2_THREAD_GROUP_HEIGHT) + uGroupThreadId); + + // ToDo: take into account jitter (i.e. add delta of previous jitter and current jitter to previous UV + // fetch pre- and post-alpha color values + FFX_MIN16_F2 fUv = ( FFX_MIN16_F2(uDispatchThreadId) + FFX_MIN16_F2(0.5f, 0.5f) ) / FFX_MIN16_F2( RenderSize() ); + FFX_MIN16_F2 fPrevUV = fUv + FFX_MIN16_F2( LoadInputMotionVector(uDispatchThreadId) ); + FFX_MIN16_I2 iPrevIdx = FFX_MIN16_I2(fPrevUV * FFX_MIN16_F2(RenderSize()) - 0.5f); + + FFX_MIN16_F3 colorPreAlpha = FFX_MIN16_F3( LoadOpaqueOnly( uDispatchThreadId ) ); + FFX_MIN16_F3 colorPostAlpha = FFX_MIN16_F3( LoadInputColor( uDispatchThreadId ) ); + + FFX_MIN16_F2 outReactiveMask = 0; + + outReactiveMask.y = ComputeTransparencyAndComposition(uDispatchThreadId, iPrevIdx); + + if (outReactiveMask.y > 0.5f) + { + outReactiveMask.x = ComputeReactive(uDispatchThreadId, iPrevIdx); + outReactiveMask.x *= FFX_MIN16_F(ReactiveScale()); + outReactiveMask.x = outReactiveMask.x < ReactiveMax() ? outReactiveMask.x : FFX_MIN16_F(ReactiveMax()); + } + + outReactiveMask.y *= FFX_MIN16_F( TcScale() ); + + outReactiveMask.x = max( outReactiveMask.x, FFX_MIN16_F( LoadReactiveMask(uDispatchThreadId) ) ); + outReactiveMask.y = max( outReactiveMask.y, FFX_MIN16_F( LoadTransparencyAndCompositionMask(uDispatchThreadId) ) ); + + StoreAutoReactive(uDispatchThreadId, outReactiveMask); + + StorePrevPreAlpha(uDispatchThreadId, colorPreAlpha); + StorePrevPostAlpha(uDispatchThreadId, colorPostAlpha); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta new file mode 100644 index 0000000..b6fd348 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d96e2a0e75dd01d47a231e3c4d18be0c +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl new file mode 100644 index 0000000..c000624 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl @@ -0,0 +1,70 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 +#define FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS 1 +#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS +#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 2 +#else +#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 2 +#endif +#define FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED 3 +#define FSR3UPSCALER_BIND_SRV_LANCZOS_LUT 4 +#define FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH_MIP1 5 + +#define FSR3UPSCALER_BIND_SRV_CURRENT_LUMA 6 +#define FSR3UPSCALER_BIND_SRV_LUMA_INSTABILITY 7 +#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 8 + +#define FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED 0 +#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 1 +#define FSR3UPSCALER_BIND_UAV_NEW_LOCKS 2 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" +#include "fsr3upscaler/ffx_fsr3upscaler_upsample.h" +#include "fsr3upscaler/ffx_fsr3upscaler_reproject.h" +#include "fsr3upscaler/ffx_fsr3upscaler_accumulate.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(int2 iDispatchThreadId : SV_DispatchThreadID) +{ + Accumulate(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta new file mode 100644 index 0000000..5cdfe03 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 28932314459baa6449098e1b5862470c +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl new file mode 100644 index 0000000..c052a1d --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl @@ -0,0 +1,78 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY 0 +#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 1 + +#define FSR3UPSCALER_BIND_UAV_AUTOREACTIVE 0 +#define FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION 1 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 +#define FSR3UPSCALER_BIND_CB_REACTIVE 1 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) +{ + uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; + + float3 ColorPreAlpha = LoadOpaqueOnly( FFX_MIN16_I2(uDispatchThreadId) ).rgb; + float3 ColorPostAlpha = LoadInputColor(uDispatchThreadId).rgb; + + if (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP) + { + ColorPreAlpha = Tonemap(ColorPreAlpha); + ColorPostAlpha = Tonemap(ColorPostAlpha); + } + + if (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP) + { + ColorPreAlpha = InverseTonemap(ColorPreAlpha); + ColorPostAlpha = InverseTonemap(ColorPostAlpha); + } + + float out_reactive_value = 0.f; + float3 delta = abs(ColorPostAlpha - ColorPreAlpha); + + out_reactive_value = (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX) ? max(delta.x, max(delta.y, delta.z)) : length(delta); + out_reactive_value *= GenReactiveScale(); + + out_reactive_value = (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD) ? (out_reactive_value < GenReactiveThreshold() ? 0 : GenReactiveBinaryValue()) : out_reactive_value; + + rw_output_autoreactive[uDispatchThreadId] = out_reactive_value; +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta new file mode 100644 index 0000000..0eb056a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0731dfbd04968084582397e48795d9d8 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl new file mode 100644 index 0000000..6ac5067 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl @@ -0,0 +1,56 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS 0 +#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 1 +#define FSR3UPSCALER_BIND_SRV_DILATED_DEPTH 2 +#define FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED 3 +#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 4 + +#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 0 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_debug_view.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(FfxInt32x2 iPxPos : SV_DispatchThreadID) +{ + DebugView(iPxPos); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl.meta new file mode 100644 index 0000000..2a75154 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_debug_view_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 00c8328353e3dba449b5e3855add5a35 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl new file mode 100644 index 0000000..44d97b3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl @@ -0,0 +1,59 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 +#define FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS 1 +#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 2 +#define FSR3UPSCALER_BIND_SRV_FRAME_INFO 3 +#define FSR3UPSCALER_BIND_SRV_LUMA_HISTORY 4 +#define FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH_MIP1 5 +#define FSR3UPSCALER_BIND_SRV_CURRENT_LUMA 6 + +#define FSR3UPSCALER_BIND_UAV_LUMA_HISTORY 0 +#define FSR3UPSCALER_BIND_UAV_LUMA_INSTABILITY 1 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_luma_instability.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(int2 iDispatchThreadId : SV_DispatchThreadID) +{ + LumaInstability(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl.meta new file mode 100644 index 0000000..7e261f7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_instability_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 68eed10cb1a8742438be0ba0c08d5b05 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl new file mode 100644 index 0000000..37fa1b7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl @@ -0,0 +1,62 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_CURRENT_LUMA 0 +#define FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH 1 + +#define FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC 0 +#define FSR3UPSCALER_BIND_UAV_FRAME_INFO 1 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_0 2 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_1 3 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_2 4 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_3 5 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_4 6 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_5 7 +#define FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH_MIP1 8 + + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 +#define FSR3UPSCALER_BIND_CB_SPD 1 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 256 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT +void CS(uint3 WorkGroupId : SV_GroupID, uint LocalThreadIndex : SV_GroupIndex) +{ + ComputeAutoExposure(WorkGroupId, LocalThreadIndex); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl.meta new file mode 100644 index 0000000..71ca10c --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_luma_pyramid_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 10b66892c68bbe1408580a330cccb381 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl new file mode 100644 index 0000000..d40b7d2 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl @@ -0,0 +1,58 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 0 +#define FSR3UPSCALER_BIND_SRV_INPUT_DEPTH 1 +#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 2 + +#define FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS 0 +#define FSR3UPSCALER_BIND_UAV_DILATED_DEPTH 1 +#define FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 2 +#define FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH 3 +#define FSR3UPSCALER_BIND_UAV_CURRENT_LUMA 4 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(int2 iDispatchThreadId : SV_DispatchThreadID) +{ + PrepareInputs(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl.meta new file mode 100644 index 0000000..a56ca2a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_inputs_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f6bb10da6010f8a479c9560de12f92f2 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl new file mode 100644 index 0000000..b09e9b8 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl @@ -0,0 +1,62 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 +#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 1 +#define FSR3UPSCALER_BIND_SRV_DILATED_DEPTH 2 +#define FSR3UPSCALER_BIND_SRV_REACTIVE_MASK 3 +#define FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 4 +#define FSR3UPSCALER_BIND_SRV_ACCUMULATION 5 +#define FSR3UPSCALER_BIND_SRV_SHADING_CHANGE 6 +#define FSR3UPSCALER_BIND_SRV_CURRENT_LUMA 7 +#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 8 + +#define FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS 0 +#define FSR3UPSCALER_BIND_UAV_NEW_LOCKS 1 +#define FSR3UPSCALER_BIND_UAV_ACCUMULATION 2 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(int2 iDispatchThreadId : SV_DispatchThreadID) +{ + PrepareReactivity(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl.meta new file mode 100644 index 0000000..6eb9fe3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_prepare_reactivity_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0ac3112d79ef65e4eb6f4787ffe37fdd +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl new file mode 100644 index 0000000..2963562 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl @@ -0,0 +1,53 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 +#define FSR3UPSCALER_BIND_SRV_RCAS_INPUT 1 + +#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 0 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 +#define FSR3UPSCALER_BIND_CB_RCAS 1 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_rcas.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 64 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT +void CS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID) +{ + RCAS(LocalThreadId, WorkGroupId, Dtid); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta new file mode 100644 index 0000000..10533cf --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3e953864b9b462f468b64a78463b84aa +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl new file mode 100644 index 0000000..ab2b545 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl @@ -0,0 +1,52 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_SPD_MIPS 0 + +#define FSR3UPSCALER_BIND_UAV_SHADING_CHANGE 0 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_shading_change.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_PREFER_WAVE64 +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(int2 iDispatchThreadId : SV_DispatchThreadID) +{ + ShadingChange(iDispatchThreadId); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl.meta new file mode 100644 index 0000000..e8c5fe8 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 503052090415bdf4c9307ea6c5aa94b6 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl new file mode 100644 index 0000000..5403792 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl @@ -0,0 +1,63 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_CURRENT_LUMA 0 +#define FSR3UPSCALER_BIND_SRV_PREVIOUS_LUMA 1 +#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 2 +#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 3 + + +#define FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC 0 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_0 1 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_1 2 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_2 3 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_3 4 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_4 5 +#define FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_5 6 + + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 +#define FSR3UPSCALER_BIND_CB_SPD 1 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 256 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT +void CS(uint3 WorkGroupId : SV_GroupID, uint LocalThreadIndex : SV_GroupIndex) +{ + ComputeShadingChangePyramid(WorkGroupId, LocalThreadIndex); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl.meta new file mode 100644 index 0000000..1694ed4 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_shading_change_pyramid_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8a6fef31a72aae649803d98a0ced428d +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl new file mode 100644 index 0000000..8a3c882 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl @@ -0,0 +1,90 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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. + +#define FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY 0 +#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 1 +#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 2 +#define FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR 3 +#define FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR 4 +#define FSR3UPSCALER_BIND_SRV_REACTIVE_MASK 5 +#define FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 6 + +#define FSR3UPSCALER_BIND_UAV_AUTOREACTIVE 0 +#define FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION 1 +#define FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR 2 +#define FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR 3 + +#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 +#define FSR3UPSCALER_BIND_CB_AUTOREACTIVE 1 + +#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" +#include "fsr3upscaler/ffx_fsr3upscaler_common.h" +#include "fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h" + +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 +#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT +#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 +#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH +#ifndef FFX_FSR3UPSCALER_NUM_THREADS +#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] +#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS + +FFX_FSR3UPSCALER_NUM_THREADS +FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) +{ + FFX_MIN16_I2 uDispatchThreadId = FFX_MIN16_I2(uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId); + + // ToDo: take into account jitter (i.e. add delta of previous jitter and current jitter to previous UV + // fetch pre- and post-alpha color values + FFX_MIN16_F2 fUv = ( FFX_MIN16_F2(uDispatchThreadId) + FFX_MIN16_F2(0.5f, 0.5f) ) / FFX_MIN16_F2( RenderSize() ); + FFX_MIN16_F2 fPrevUV = fUv + FFX_MIN16_F2( LoadInputMotionVector(uDispatchThreadId) ); + FFX_MIN16_I2 iPrevIdx = FFX_MIN16_I2(fPrevUV * FFX_MIN16_F2(RenderSize()) - 0.5f); + + FFX_MIN16_F3 colorPreAlpha = FFX_MIN16_F3( LoadOpaqueOnly( uDispatchThreadId ) ); + FFX_MIN16_F3 colorPostAlpha = FFX_MIN16_F3( LoadInputColor( uDispatchThreadId ) ); + + FFX_MIN16_F2 outReactiveMask = 0; + + outReactiveMask.y = ComputeTransparencyAndComposition(uDispatchThreadId, iPrevIdx); + + if (outReactiveMask.y > 0.5f) + { + outReactiveMask.x = ComputeReactive(uDispatchThreadId, iPrevIdx); + outReactiveMask.x *= FFX_MIN16_F(fReactiveScale); + outReactiveMask.x = outReactiveMask.x < fReactiveMax ? outReactiveMask.x : FFX_MIN16_F( fReactiveMax ); + } + + outReactiveMask.y *= FFX_MIN16_F(fTcScale ); + + outReactiveMask.x = max( outReactiveMask.x, FFX_MIN16_F( LoadReactiveMask(uDispatchThreadId) ) ); + outReactiveMask.y = max( outReactiveMask.y, FFX_MIN16_F( LoadTransparencyAndCompositionMask(uDispatchThreadId) ) ); + + StoreAutoReactive(uDispatchThreadId, outReactiveMask); + + StorePrevPreAlpha(uDispatchThreadId, colorPreAlpha); + StorePrevPostAlpha(uDispatchThreadId, colorPostAlpha); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta new file mode 100644 index 0000000..c6923a5 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 877b3428f6073db41b9fc80bb6c6d4d7 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr1.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr1.meta new file mode 100644 index 0000000..e52d0ac --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dafd228fb8a2f4e478fe1df6d38589f9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h new file mode 100644 index 0000000..82ebf21 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h @@ -0,0 +1,1252 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +/// @defgroup FfxGPUFsr1 FidelityFX FSR1 +/// FidelityFX Super Resolution 1 GPU documentation +/// +/// @ingroup FfxGPUEffects + +/// Setup required constant values for EASU (works on CPU or GPU). +/// +/// @param [out] con0 +/// @param [out] con1 +/// @param [out] con2 +/// @param [out] con3 +/// @param [in] inputViewportInPixelsX The rendered image resolution being upscaled in X dimension. +/// @param [in] inputViewportInPixelsY The rendered image resolution being upscaled in Y dimension. +/// @param [in] inputSizeInPixelsX The resolution of the resource containing the input image (useful for dynamic resolution) in X dimension. +/// @param [in] inputSizeInPixelsY The resolution of the resource containing the input image (useful for dynamic resolution) in Y dimension. +/// @param [in] outputSizeInPixelsX The display resolution which the input image gets upscaled to in X dimension. +/// @param [in] outputSizeInPixelsY The display resolution which the input image gets upscaled to in Y dimension. +/// +/// @ingroup FfxGPUFsr1 +FFX_STATIC void ffxFsrPopulateEasuConstants( + FFX_PARAMETER_INOUT FfxUInt32x4 con0, + FFX_PARAMETER_INOUT FfxUInt32x4 con1, + FFX_PARAMETER_INOUT FfxUInt32x4 con2, + FFX_PARAMETER_INOUT FfxUInt32x4 con3, + FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsX, + FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsY, + FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsX, + FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsY, + FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsX, + FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsY) +{ + // Output integer position to a pixel position in viewport. + con0[0] = ffxAsUInt32(inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX)); + con0[1] = ffxAsUInt32(inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY)); + con0[2] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX) - FfxFloat32(0.5)); + con0[3] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY) - FfxFloat32(0.5)); + + // Viewport pixel position to normalized image space. + // This is used to get upper-left of 'F' tap. + con1[0] = ffxAsUInt32(ffxReciprocal(inputSizeInPixelsX)); + con1[1] = ffxAsUInt32(ffxReciprocal(inputSizeInPixelsY)); + + // Centers of gather4, first offset from upper-left of 'F'. + // +---+---+ + // | | | + // +--(0)--+ + // | b | c | + // +---F---+---+---+ + // | e | f | g | h | + // +--(1)--+--(2)--+ + // | i | j | k | l | + // +---+---+---+---+ + // | n | o | + // +--(3)--+ + // | | | + // +---+---+ + con1[2] = ffxAsUInt32(FfxFloat32(1.0) * ffxReciprocal(inputSizeInPixelsX)); + con1[3] = ffxAsUInt32(FfxFloat32(-1.0) * ffxReciprocal(inputSizeInPixelsY)); + + // These are from (0) instead of 'F'. + con2[0] = ffxAsUInt32(FfxFloat32(-1.0) * ffxReciprocal(inputSizeInPixelsX)); + con2[1] = ffxAsUInt32(FfxFloat32(2.0) * ffxReciprocal(inputSizeInPixelsY)); + con2[2] = ffxAsUInt32(FfxFloat32(1.0) * ffxReciprocal(inputSizeInPixelsX)); + con2[3] = ffxAsUInt32(FfxFloat32(2.0) * ffxReciprocal(inputSizeInPixelsY)); + con3[0] = ffxAsUInt32(FfxFloat32(0.0) * ffxReciprocal(inputSizeInPixelsX)); + con3[1] = ffxAsUInt32(FfxFloat32(4.0) * ffxReciprocal(inputSizeInPixelsY)); + con3[2] = con3[3] = 0; +} + +/// Setup required constant values for EASU (works on CPU or GPU). +/// +/// @param [out] con0 +/// @param [out] con1 +/// @param [out] con2 +/// @param [out] con3 +/// @param [in] inputViewportInPixelsX The resolution of the input in the X dimension. +/// @param [in] inputViewportInPixelsY The resolution of the input in the Y dimension. +/// @param [in] inputSizeInPixelsX The input size in pixels in the X dimension. +/// @param [in] inputSizeInPixelsY The input size in pixels in the Y dimension. +/// @param [in] outputSizeInPixelsX The output size in pixels in the X dimension. +/// @param [in] outputSizeInPixelsY The output size in pixels in the Y dimension. +/// @param [in] inputOffsetInPixelsX The input image offset in the X dimension into the resource containing it (useful for dynamic resolution). +/// @param [in] inputOffsetInPixelsY The input image offset in the Y dimension into the resource containing it (useful for dynamic resolution). +/// +/// @ingroup FfxGPUFsr1 +FFX_STATIC void ffxFsrPopulateEasuConstantsOffset( + FFX_PARAMETER_INOUT FfxUInt32x4 con0, + FFX_PARAMETER_INOUT FfxUInt32x4 con1, + FFX_PARAMETER_INOUT FfxUInt32x4 con2, + FFX_PARAMETER_INOUT FfxUInt32x4 con3, + FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsX, + FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsY, + FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsX, + FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsY, + FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsX, + FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsY, + FFX_PARAMETER_IN FfxFloat32 inputOffsetInPixelsX, + FFX_PARAMETER_IN FfxFloat32 inputOffsetInPixelsY) +{ + ffxFsrPopulateEasuConstants( + con0, + con1, + con2, + con3, + inputViewportInPixelsX, + inputViewportInPixelsY, + inputSizeInPixelsX, + inputSizeInPixelsY, + outputSizeInPixelsX, + outputSizeInPixelsY); + + // override + con0[2] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX) - FfxFloat32(0.5) + inputOffsetInPixelsX); + con0[3] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY) - FfxFloat32(0.5) + inputOffsetInPixelsY); +} + +#if defined(FFX_GPU) && defined(FFX_FSR_EASU_FLOAT) +// Input callback prototypes, need to be implemented by calling shader +FfxFloat32x4 FsrEasuRF(FfxFloat32x2 p); +FfxFloat32x4 FsrEasuGF(FfxFloat32x2 p); +FfxFloat32x4 FsrEasuBF(FfxFloat32x2 p); + +// Filtering for a given tap for the scalar. +void fsrEasuTapFloat( + FFX_PARAMETER_INOUT FfxFloat32x3 accumulatedColor, // Accumulated color, with negative lobe. + FFX_PARAMETER_INOUT FfxFloat32 accumulatedWeight, // Accumulated weight. + FFX_PARAMETER_IN FfxFloat32x2 pixelOffset, // Pixel offset from resolve position to tap. + FFX_PARAMETER_IN FfxFloat32x2 gradientDirection, // Gradient direction. + FFX_PARAMETER_IN FfxFloat32x2 length, // Length. + FFX_PARAMETER_IN FfxFloat32 negativeLobeStrength, // Negative lobe strength. + FFX_PARAMETER_IN FfxFloat32 clippingPoint, // Clipping point. + FFX_PARAMETER_IN FfxFloat32x3 color) // Tap color. +{ + // Rotate offset by direction. + FfxFloat32x2 rotatedOffset; + rotatedOffset.x = (pixelOffset.x * (gradientDirection.x)) + (pixelOffset.y * gradientDirection.y); + rotatedOffset.y = (pixelOffset.x * (-gradientDirection.y)) + (pixelOffset.y * gradientDirection.x); + + // Anisotropy. + rotatedOffset *= length; + + // Compute distance^2. + FfxFloat32 distanceSquared = rotatedOffset.x * rotatedOffset.x + rotatedOffset.y * rotatedOffset.y; + + // Limit to the window as at corner, 2 taps can easily be outside. + distanceSquared = ffxMin(distanceSquared, clippingPoint); + + // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. + // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 + // |_______________________________________| |_______________| + // base window + // The general form of the 'base' is, + // (a*(b*x^2-1)^2-(a-1)) + // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. + FfxFloat32 weightB = FfxFloat32(2.0 / 5.0) * distanceSquared + FfxFloat32(-1.0); + FfxFloat32 weightA = negativeLobeStrength * distanceSquared + FfxFloat32(-1.0); + weightB *= weightB; + weightA *= weightA; + weightB = FfxFloat32(25.0 / 16.0) * weightB + FfxFloat32(-(25.0 / 16.0 - 1.0)); + FfxFloat32 weight = weightB * weightA; + + // Do weighted average. + accumulatedColor += color * weight; + accumulatedWeight += weight; +} + +// Accumulate direction and length. +void fsrEasuSetFloat( + FFX_PARAMETER_INOUT FfxFloat32x2 direction, + FFX_PARAMETER_INOUT FfxFloat32 length, + FFX_PARAMETER_IN FfxFloat32x2 pp, + FFX_PARAMETER_IN FfxBoolean biS, + FFX_PARAMETER_IN FfxBoolean biT, + FFX_PARAMETER_IN FfxBoolean biU, + FFX_PARAMETER_IN FfxBoolean biV, + FFX_PARAMETER_IN FfxFloat32 lA, + FFX_PARAMETER_IN FfxFloat32 lB, + FFX_PARAMETER_IN FfxFloat32 lC, + FFX_PARAMETER_IN FfxFloat32 lD, + FFX_PARAMETER_IN FfxFloat32 lE) +{ + // Compute bilinear weight, branches factor out as predicates are compiler time immediates. + // s t + // u v + FfxFloat32 weight = FfxFloat32(0.0); + if (biS) + weight = (FfxFloat32(1.0) - pp.x) * (FfxFloat32(1.0) - pp.y); + if (biT) + weight = pp.x * (FfxFloat32(1.0) - pp.y); + if (biU) + weight = (FfxFloat32(1.0) - pp.x) * pp.y; + if (biV) + weight = pp.x * pp.y; + + // Direction is the '+' diff. + // a + // b c d + // e + // Then takes magnitude from abs average of both sides of 'c'. + // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. + FfxFloat32 dc = lD - lC; + FfxFloat32 cb = lC - lB; + FfxFloat32 lengthX = max(abs(dc), abs(cb)); + lengthX = ffxApproximateReciprocal(lengthX); + FfxFloat32 directionX = lD - lB; + direction.x += directionX * weight; + lengthX = ffxSaturate(abs(directionX) * lengthX); + lengthX *= lengthX; + length += lengthX * weight; + + // Repeat for the y axis. + FfxFloat32 ec = lE - lC; + FfxFloat32 ca = lC - lA; + FfxFloat32 lengthY = max(abs(ec), abs(ca)); + lengthY = ffxApproximateReciprocal(lengthY); + FfxFloat32 directionY = lE - lA; + direction.y += directionY * weight; + lengthY = ffxSaturate(abs(directionY) * lengthY); + lengthY *= lengthY; + length += lengthY * weight; +} + +/// Apply edge-aware spatial upsampling using 32bit floating point precision calculations. +/// +/// @param [out] outPixel The computed color of a pixel. +/// @param [in] integerPosition Integer pixel position within the output. +/// @param [in] con0 The first constant value generated by ffxFsrPopulateEasuConstants. +/// @param [in] con1 The second constant value generated by ffxFsrPopulateEasuConstants. +/// @param [in] con2 The third constant value generated by ffxFsrPopulateEasuConstants. +/// @param [in] con3 The fourth constant value generated by ffxFsrPopulateEasuConstants. +/// +/// @ingroup FSR +void ffxFsrEasuFloat( + FFX_PARAMETER_OUT FfxFloat32x3 pix, + FFX_PARAMETER_IN FfxUInt32x2 ip, + FFX_PARAMETER_IN FfxUInt32x4 con0, + FFX_PARAMETER_IN FfxUInt32x4 con1, + FFX_PARAMETER_IN FfxUInt32x4 con2, + FFX_PARAMETER_IN FfxUInt32x4 con3) +{ + // Get position of 'f'. + FfxFloat32x2 pp = FfxFloat32x2(ip) * ffxAsFloat(con0.xy) + ffxAsFloat(con0.zw); + FfxFloat32x2 fp = floor(pp); + pp -= fp; + + // 12-tap kernel. + // b c + // e f g h + // i j k l + // n o + // Gather 4 ordering. + // a b + // r g + // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, + // a b <- unused (z) + // r g + // a b a b + // r g r g + // a b + // r g <- unused (z) + // Allowing dead-code removal to remove the 'z's. + FfxFloat32x2 p0 = fp * ffxAsFloat(con1.xy) + ffxAsFloat(con1.zw); + + // These are from p0 to avoid pulling two constants on pre-Navi hardware. + FfxFloat32x2 p1 = p0 + ffxAsFloat(con2.xy); + FfxFloat32x2 p2 = p0 + ffxAsFloat(con2.zw); + FfxFloat32x2 p3 = p0 + ffxAsFloat(con3.xy); + FfxFloat32x4 bczzR = FsrEasuRF(p0); + FfxFloat32x4 bczzG = FsrEasuGF(p0); + FfxFloat32x4 bczzB = FsrEasuBF(p0); + FfxFloat32x4 ijfeR = FsrEasuRF(p1); + FfxFloat32x4 ijfeG = FsrEasuGF(p1); + FfxFloat32x4 ijfeB = FsrEasuBF(p1); + FfxFloat32x4 klhgR = FsrEasuRF(p2); + FfxFloat32x4 klhgG = FsrEasuGF(p2); + FfxFloat32x4 klhgB = FsrEasuBF(p2); + FfxFloat32x4 zzonR = FsrEasuRF(p3); + FfxFloat32x4 zzonG = FsrEasuGF(p3); + FfxFloat32x4 zzonB = FsrEasuBF(p3); + + // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). + FfxFloat32x4 bczzL = bczzB * ffxBroadcast4(0.5) + (bczzR * ffxBroadcast4(0.5) + bczzG); + FfxFloat32x4 ijfeL = ijfeB * ffxBroadcast4(0.5) + (ijfeR * ffxBroadcast4(0.5) + ijfeG); + FfxFloat32x4 klhgL = klhgB * ffxBroadcast4(0.5) + (klhgR * ffxBroadcast4(0.5) + klhgG); + FfxFloat32x4 zzonL = zzonB * ffxBroadcast4(0.5) + (zzonR * ffxBroadcast4(0.5) + zzonG); + + // Rename. + FfxFloat32 bL = bczzL.x; + FfxFloat32 cL = bczzL.y; + FfxFloat32 iL = ijfeL.x; + FfxFloat32 jL = ijfeL.y; + FfxFloat32 fL = ijfeL.z; + FfxFloat32 eL = ijfeL.w; + FfxFloat32 kL = klhgL.x; + FfxFloat32 lL = klhgL.y; + FfxFloat32 hL = klhgL.z; + FfxFloat32 gL = klhgL.w; + FfxFloat32 oL = zzonL.z; + FfxFloat32 nL = zzonL.w; + + // Accumulate for bilinear interpolation. + FfxFloat32x2 dir = ffxBroadcast2(0.0); + FfxFloat32 len = FfxFloat32(0.0); + fsrEasuSetFloat(dir, len, pp, FFX_TRUE, FFX_FALSE, FFX_FALSE, FFX_FALSE, bL, eL, fL, gL, jL); + fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_TRUE, FFX_FALSE, FFX_FALSE, cL, fL, gL, hL, kL); + fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_FALSE, FFX_TRUE, FFX_FALSE, fL, iL, jL, kL, nL); + fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_FALSE, FFX_FALSE, FFX_TRUE, gL, jL, kL, lL, oL); + + // Normalize with approximation, and cleanup close to zero. + FfxFloat32x2 dir2 = dir * dir; + FfxFloat32 dirR = dir2.x + dir2.y; + FfxBoolean zro = dirR < FfxFloat32(1.0 / 32768.0); + dirR = ffxApproximateReciprocalSquareRoot(dirR); + dirR = zro ? FfxFloat32(1.0) : dirR; + dir.x = zro ? FfxFloat32(1.0) : dir.x; + dir *= ffxBroadcast2(dirR); + + // Transform from {0 to 2} to {0 to 1} range, and shape with square. + len = len * FfxFloat32(0.5); + len *= len; + + // Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}. + FfxFloat32 stretch = (dir.x * dir.x + dir.y * dir.y) * ffxApproximateReciprocal(max(abs(dir.x), abs(dir.y))); + + // Anisotropic length after rotation, + // x := 1.0 lerp to 'stretch' on edges + // y := 1.0 lerp to 2x on edges + FfxFloat32x2 len2 = FfxFloat32x2(FfxFloat32(1.0) + (stretch - FfxFloat32(1.0)) * len, FfxFloat32(1.0) + FfxFloat32(-0.5) * len); + + // Based on the amount of 'edge', + // the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}. + FfxFloat32 lob = FfxFloat32(0.5) + FfxFloat32((1.0 / 4.0 - 0.04) - 0.5) * len; + + // Set distance^2 clipping point to the end of the adjustable window. + FfxFloat32 clp = ffxApproximateReciprocal(lob); + + // Accumulation mixed with min/max of 4 nearest. + // b c + // e f g h + // i j k l + // n o + FfxFloat32x3 min4 = + ffxMin(ffxMin3(FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z), FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w), FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)), + FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); + FfxFloat32x3 max4 = + max(ffxMax3(FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z), FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w), FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)), FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); + + // Accumulation. + FfxFloat32x3 aC = ffxBroadcast3(0.0); + FfxFloat32 aW = FfxFloat32(0.0); + fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, -1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(bczzR.x, bczzG.x, bczzB.x)); // b + fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, -1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(bczzR.y, bczzG.y, bczzB.y)); // c + fsrEasuTapFloat(aC, aW, FfxFloat32x2(-1.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.x, ijfeG.x, ijfeB.x)); // i + fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)); // j + fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z)); // f + fsrEasuTapFloat(aC, aW, FfxFloat32x2(-1.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.w, ijfeG.w, ijfeB.w)); // e + fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); // k + fsrEasuTapFloat(aC, aW, FfxFloat32x2(2.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.y, klhgG.y, klhgB.y)); // l + fsrEasuTapFloat(aC, aW, FfxFloat32x2(2.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.z, klhgG.z, klhgB.z)); // h + fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w)); // g + fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 2.0) - pp, dir, len2, lob, clp, FfxFloat32x3(zzonR.z, zzonG.z, zzonB.z)); // o + fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 2.0) - pp, dir, len2, lob, clp, FfxFloat32x3(zzonR.w, zzonG.w, zzonB.w)); // n + + // Normalize and dering. + pix = ffxMin(max4, max(min4, aC * ffxBroadcast3(ffxReciprocal(aW)))); +} +#endif // #if defined(FFX_GPU) && defined(FFX_FSR_EASU_FLOAT) + +#if defined(FFX_GPU) && FFX_HALF == 1 && defined(FFX_FSR_EASU_HALF) +// Input callback prototypes, need to be implemented by calling shader +FfxFloat16x4 FsrEasuRH(FfxFloat32x2 p); +FfxFloat16x4 FsrEasuGH(FfxFloat32x2 p); +FfxFloat16x4 FsrEasuBH(FfxFloat32x2 p); + +// This runs 2 taps in parallel. +void FsrEasuTapH( + FFX_PARAMETER_INOUT FfxFloat16x2 aCR, + FFX_PARAMETER_INOUT FfxFloat16x2 aCG, + FFX_PARAMETER_INOUT FfxFloat16x2 aCB, + FFX_PARAMETER_INOUT FfxFloat16x2 aW, + FFX_PARAMETER_IN FfxFloat16x2 offX, + FFX_PARAMETER_IN FfxFloat16x2 offY, + FFX_PARAMETER_IN FfxFloat16x2 dir, + FFX_PARAMETER_IN FfxFloat16x2 len, + FFX_PARAMETER_IN FfxFloat16 lob, + FFX_PARAMETER_IN FfxFloat16 clp, + FFX_PARAMETER_IN FfxFloat16x2 cR, + FFX_PARAMETER_IN FfxFloat16x2 cG, + FFX_PARAMETER_IN FfxFloat16x2 cB) +{ + FfxFloat16x2 vX, vY; + vX = offX * dir.xx + offY * dir.yy; + vY = offX * (-dir.yy) + offY * dir.xx; + vX *= len.x; + vY *= len.y; + FfxFloat16x2 d2 = vX * vX + vY * vY; + d2 = min(d2, FFX_BROADCAST_FLOAT16X2(clp)); + FfxFloat16x2 wB = FFX_BROADCAST_FLOAT16X2(2.0 / 5.0) * d2 + FFX_BROADCAST_FLOAT16X2(-1.0); + FfxFloat16x2 wA = FFX_BROADCAST_FLOAT16X2(lob) * d2 + FFX_BROADCAST_FLOAT16X2(-1.0); + wB *= wB; + wA *= wA; + wB = FFX_BROADCAST_FLOAT16X2(25.0 / 16.0) * wB + FFX_BROADCAST_FLOAT16X2(-(25.0 / 16.0 - 1.0)); + FfxFloat16x2 w = wB * wA; + aCR += cR * w; + aCG += cG * w; + aCB += cB * w; + aW += w; +} + +// This runs 2 taps in parallel. +void FsrEasuSetH( + FFX_PARAMETER_INOUT FfxFloat16x2 dirPX, + FFX_PARAMETER_INOUT FfxFloat16x2 dirPY, + FFX_PARAMETER_INOUT FfxFloat16x2 lenP, + FFX_PARAMETER_IN FfxFloat16x2 pp, + FFX_PARAMETER_IN FfxBoolean biST, + FFX_PARAMETER_IN FfxBoolean biUV, + FFX_PARAMETER_IN FfxFloat16x2 lA, + FFX_PARAMETER_IN FfxFloat16x2 lB, + FFX_PARAMETER_IN FfxFloat16x2 lC, + FFX_PARAMETER_IN FfxFloat16x2 lD, + FFX_PARAMETER_IN FfxFloat16x2 lE) +{ + FfxFloat16x2 w = FFX_BROADCAST_FLOAT16X2(0.0); + + if (biST) + w = (FfxFloat16x2(1.0, 0.0) + FfxFloat16x2(-pp.x, pp.x)) * FFX_BROADCAST_FLOAT16X2(FFX_BROADCAST_FLOAT16(1.0) - pp.y); + + if (biUV) + w = (FfxFloat16x2(1.0, 0.0) + FfxFloat16x2(-pp.x, pp.x)) * FFX_BROADCAST_FLOAT16X2(pp.y); + + // ABS is not free in the packed FP16 path. + FfxFloat16x2 dc = lD - lC; + FfxFloat16x2 cb = lC - lB; + FfxFloat16x2 lenX = max(abs(dc), abs(cb)); + lenX = ffxReciprocalHalf(lenX); + + FfxFloat16x2 dirX = lD - lB; + dirPX += dirX * w; + lenX = ffxSaturate(abs(dirX) * lenX); + lenX *= lenX; + lenP += lenX * w; + FfxFloat16x2 ec = lE - lC; + FfxFloat16x2 ca = lC - lA; + FfxFloat16x2 lenY = max(abs(ec), abs(ca)); + lenY = ffxReciprocalHalf(lenY); + FfxFloat16x2 dirY = lE - lA; + dirPY += dirY * w; + lenY = ffxSaturate(abs(dirY) * lenY); + lenY *= lenY; + lenP += lenY * w; +} + +void FsrEasuH( + FFX_PARAMETER_OUT FfxFloat16x3 pix, + FFX_PARAMETER_IN FfxUInt32x2 ip, + FFX_PARAMETER_IN FfxUInt32x4 con0, + FFX_PARAMETER_IN FfxUInt32x4 con1, + FFX_PARAMETER_IN FfxUInt32x4 con2, + FFX_PARAMETER_IN FfxUInt32x4 con3) +{ + FfxFloat32x2 pp = FfxFloat32x2(ip) * ffxAsFloat(con0.xy) + ffxAsFloat(con0.zw); + FfxFloat32x2 fp = floor(pp); + pp -= fp; + FfxFloat16x2 ppp = FfxFloat16x2(pp); + + FfxFloat32x2 p0 = fp * ffxAsFloat(con1.xy) + ffxAsFloat(con1.zw); + FfxFloat32x2 p1 = p0 + ffxAsFloat(con2.xy); + FfxFloat32x2 p2 = p0 + ffxAsFloat(con2.zw); + FfxFloat32x2 p3 = p0 + ffxAsFloat(con3.xy); + FfxFloat16x4 bczzR = FsrEasuRH(p0); + FfxFloat16x4 bczzG = FsrEasuGH(p0); + FfxFloat16x4 bczzB = FsrEasuBH(p0); + FfxFloat16x4 ijfeR = FsrEasuRH(p1); + FfxFloat16x4 ijfeG = FsrEasuGH(p1); + FfxFloat16x4 ijfeB = FsrEasuBH(p1); + FfxFloat16x4 klhgR = FsrEasuRH(p2); + FfxFloat16x4 klhgG = FsrEasuGH(p2); + FfxFloat16x4 klhgB = FsrEasuBH(p2); + FfxFloat16x4 zzonR = FsrEasuRH(p3); + FfxFloat16x4 zzonG = FsrEasuGH(p3); + FfxFloat16x4 zzonB = FsrEasuBH(p3); + + FfxFloat16x4 bczzL = bczzB * FFX_BROADCAST_FLOAT16X4(0.5) + (bczzR * FFX_BROADCAST_FLOAT16X4(0.5) + bczzG); + FfxFloat16x4 ijfeL = ijfeB * FFX_BROADCAST_FLOAT16X4(0.5) + (ijfeR * FFX_BROADCAST_FLOAT16X4(0.5) + ijfeG); + FfxFloat16x4 klhgL = klhgB * FFX_BROADCAST_FLOAT16X4(0.5) + (klhgR * FFX_BROADCAST_FLOAT16X4(0.5) + klhgG); + FfxFloat16x4 zzonL = zzonB * FFX_BROADCAST_FLOAT16X4(0.5) + (zzonR * FFX_BROADCAST_FLOAT16X4(0.5) + zzonG); + FfxFloat16 bL = bczzL.x; + FfxFloat16 cL = bczzL.y; + FfxFloat16 iL = ijfeL.x; + FfxFloat16 jL = ijfeL.y; + FfxFloat16 fL = ijfeL.z; + FfxFloat16 eL = ijfeL.w; + FfxFloat16 kL = klhgL.x; + FfxFloat16 lL = klhgL.y; + FfxFloat16 hL = klhgL.z; + FfxFloat16 gL = klhgL.w; + FfxFloat16 oL = zzonL.z; + FfxFloat16 nL = zzonL.w; + + // This part is different, accumulating 2 taps in parallel. + FfxFloat16x2 dirPX = FFX_BROADCAST_FLOAT16X2(0.0); + FfxFloat16x2 dirPY = FFX_BROADCAST_FLOAT16X2(0.0); + FfxFloat16x2 lenP = FFX_BROADCAST_FLOAT16X2(0.0); + FsrEasuSetH(dirPX, + dirPY, + lenP, + ppp, + FfxBoolean(true), + FfxBoolean(false), + FfxFloat16x2(bL, cL), + FfxFloat16x2(eL, fL), + FfxFloat16x2(fL, gL), + FfxFloat16x2(gL, hL), + FfxFloat16x2(jL, kL)); + FsrEasuSetH(dirPX, + dirPY, + lenP, + ppp, + FfxBoolean(false), + FfxBoolean(true), + FfxFloat16x2(fL, gL), + FfxFloat16x2(iL, jL), + FfxFloat16x2(jL, kL), + FfxFloat16x2(kL, lL), + FfxFloat16x2(nL, oL)); + FfxFloat16x2 dir = FfxFloat16x2(dirPX.r + dirPX.g, dirPY.r + dirPY.g); + FfxFloat16 len = lenP.r + lenP.g; + + FfxFloat16x2 dir2 = dir * dir; + FfxFloat16 dirR = dir2.x + dir2.y; + FfxUInt32 zro = FfxUInt32(dirR < FFX_BROADCAST_FLOAT16(1.0 / 32768.0)); + dirR = ffxApproximateReciprocalSquareRootHalf(dirR); + dirR = (zro > 0) ? FFX_BROADCAST_FLOAT16(1.0) : dirR; + dir.x = (zro > 0) ? FFX_BROADCAST_FLOAT16(1.0) : dir.x; + dir *= FFX_BROADCAST_FLOAT16X2(dirR); + len = len * FFX_BROADCAST_FLOAT16(0.5); + len *= len; + FfxFloat16 stretch = (dir.x * dir.x + dir.y * dir.y) * ffxApproximateReciprocalHalf(max(abs(dir.x), abs(dir.y))); + FfxFloat16x2 len2 = + FfxFloat16x2(FFX_BROADCAST_FLOAT16(1.0) + (stretch - FFX_BROADCAST_FLOAT16(1.0)) * len, FFX_BROADCAST_FLOAT16(1.0) + FFX_BROADCAST_FLOAT16(-0.5) * len); + FfxFloat16 lob = FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16((1.0 / 4.0 - 0.04) - 0.5) * len; + FfxFloat16 clp = ffxApproximateReciprocalHalf(lob); + + // FP16 is different, using packed trick to do min and max in same operation. + FfxFloat16x2 bothR = + max(max(FfxFloat16x2(-ijfeR.z, ijfeR.z), FfxFloat16x2(-klhgR.w, klhgR.w)), max(FfxFloat16x2(-ijfeR.y, ijfeR.y), FfxFloat16x2(-klhgR.x, klhgR.x))); + FfxFloat16x2 bothG = + max(max(FfxFloat16x2(-ijfeG.z, ijfeG.z), FfxFloat16x2(-klhgG.w, klhgG.w)), max(FfxFloat16x2(-ijfeG.y, ijfeG.y), FfxFloat16x2(-klhgG.x, klhgG.x))); + FfxFloat16x2 bothB = + max(max(FfxFloat16x2(-ijfeB.z, ijfeB.z), FfxFloat16x2(-klhgB.w, klhgB.w)), max(FfxFloat16x2(-ijfeB.y, ijfeB.y), FfxFloat16x2(-klhgB.x, klhgB.x))); + + // This part is different for FP16, working pairs of taps at a time. + FfxFloat16x2 pR = FFX_BROADCAST_FLOAT16X2(0.0); + FfxFloat16x2 pG = FFX_BROADCAST_FLOAT16X2(0.0); + FfxFloat16x2 pB = FFX_BROADCAST_FLOAT16X2(0.0); + FfxFloat16x2 pW = FFX_BROADCAST_FLOAT16X2(0.0); + FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(0.0, 1.0) - ppp.xx, FfxFloat16x2(-1.0, -1.0) - ppp.yy, dir, len2, lob, clp, bczzR.xy, bczzG.xy, bczzB.xy); + FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(-1.0, 0.0) - ppp.xx, FfxFloat16x2(1.0, 1.0) - ppp.yy, dir, len2, lob, clp, ijfeR.xy, ijfeG.xy, ijfeB.xy); + FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(0.0, -1.0) - ppp.xx, FfxFloat16x2(0.0, 0.0) - ppp.yy, dir, len2, lob, clp, ijfeR.zw, ijfeG.zw, ijfeB.zw); + FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(1.0, 2.0) - ppp.xx, FfxFloat16x2(1.0, 1.0) - ppp.yy, dir, len2, lob, clp, klhgR.xy, klhgG.xy, klhgB.xy); + FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(2.0, 1.0) - ppp.xx, FfxFloat16x2(0.0, 0.0) - ppp.yy, dir, len2, lob, clp, klhgR.zw, klhgG.zw, klhgB.zw); + FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(1.0, 0.0) - ppp.xx, FfxFloat16x2(2.0, 2.0) - ppp.yy, dir, len2, lob, clp, zzonR.zw, zzonG.zw, zzonB.zw); + FfxFloat16x3 aC = FfxFloat16x3(pR.x + pR.y, pG.x + pG.y, pB.x + pB.y); + FfxFloat16 aW = pW.x + pW.y; + + // Slightly different for FP16 version due to combined min and max. + pix = min(FfxFloat16x3(bothR.y, bothG.y, bothB.y), max(-FfxFloat16x3(bothR.x, bothG.x, bothB.x), aC * FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(aW)))); +} +#endif // #if defined(FFX_GPU) && defined(FFX_HALF) && defined(FFX_FSR_EASU_HALF) + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING +// +//------------------------------------------------------------------------------------------------------------------------------ +// CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness. +// RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. +// RCAS also has a built in process to limit sharpening of what it detects as possible noise. +// RCAS sharper does not support scaling, as it should be applied after EASU scaling. +// Pass EASU output straight into RCAS, no color conversions necessary. +//------------------------------------------------------------------------------------------------------------------------------ +// RCAS is based on the following logic. +// RCAS uses a 5 tap filter in a cross pattern (same as CAS), +// w n +// w 1 w for taps w m e +// w s +// Where 'w' is the negative lobe weight. +// output = (w*(n+e+w+s)+m)/(4*w+1) +// RCAS solves for 'w' by seeing where the signal might clip out of the {0 to 1} input range, +// 0 == (w*(n+e+w+s)+m)/(4*w+1) -> w = -m/(n+e+w+s) +// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) +// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. +// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. +// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. +// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. +// This stabilizes RCAS. +// RCAS does a simple highpass which is normalized against the local contrast then shaped, +// 0.25 +// 0.25 -1 0.25 +// 0.25 +// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. +// +// GLSL example for the required callbacks : +// +// FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p){return FfxFloat16x4(imageLoad(imgSrc,FfxInt32x2(p)));} +// void FsrRcasInputH(inout FfxFloat16 r,inout FfxFloat16 g,inout FfxFloat16 b) +// { +// //do any simple input color conversions here or leave empty if none needed +// } +// +// FsrRcasCon need to be called from the CPU or GPU to set up constants. +// Including a GPU example here, the 'con' value would be stored out to a constant buffer. +// +// FfxUInt32x4 con; +// FsrRcasCon(con, +// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +// --------------- +// RCAS sharpening supports a CAS-like pass-through alpha via, +// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 +// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. +// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, +// #define FSR_RCAS_DENOISE 1 +//============================================================================================================================== +// This is set at the limit of providing unnatural results for sharpening. +#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). + FFX_STATIC void FsrRcasCon(FfxUInt32x4 con, + // The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. + FfxFloat32 sharpness) + { + // Transform from stops to linear value. + sharpness = exp2(-sharpness); + FfxFloat32x2 hSharp = {sharpness, sharpness}; + con[0] = ffxAsUInt32(sharpness); + con[1] = ffxPackHalf2x16(hSharp); + con[2] = 0; + con[3] = 0; + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(FFX_GPU)&&defined(FSR_RCAS_F) + // Input callback prototypes that need to be implemented by calling shader + FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p); + void FsrRcasInputF(inout FfxFloat32 r,inout FfxFloat32 g,inout FfxFloat32 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasF(out FfxFloat32 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out FfxFloat32 pixG, + out FfxFloat32 pixB, +#ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out FfxFloat32 pixA, +#endif + FfxUInt32x2 ip, // Integer pixel position in output. + FfxUInt32x4 con) + { // Constant generated by RcasSetup(). + // Algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + FfxInt32x2 sp = FfxInt32x2(ip); + FfxFloat32x3 b = FsrRcasLoadF(sp + FfxInt32x2(0, -1)).rgb; + FfxFloat32x3 d = FsrRcasLoadF(sp + FfxInt32x2(-1, 0)).rgb; +#ifdef FSR_RCAS_PASSTHROUGH_ALPHA + FfxFloat32x4 ee = FsrRcasLoadF(sp); + FfxFloat32x3 e = ee.rgb; + pixA = ee.a; +#else + FfxFloat32x3 e = FsrRcasLoadF(sp).rgb; +#endif + FfxFloat32x3 f = FsrRcasLoadF(sp + FfxInt32x2(1, 0)).rgb; + FfxFloat32x3 h = FsrRcasLoadF(sp + FfxInt32x2(0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + FfxFloat32 bR = b.r; + FfxFloat32 bG = b.g; + FfxFloat32 bB = b.b; + FfxFloat32 dR = d.r; + FfxFloat32 dG = d.g; + FfxFloat32 dB = d.b; + FfxFloat32 eR = e.r; + FfxFloat32 eG = e.g; + FfxFloat32 eB = e.b; + FfxFloat32 fR = f.r; + FfxFloat32 fG = f.g; + FfxFloat32 fB = f.b; + FfxFloat32 hR = h.r; + FfxFloat32 hG = h.g; + FfxFloat32 hB = h.b; + // Run optional input transform. + FsrRcasInputF(bR, bG, bB); + FsrRcasInputF(dR, dG, dB); + FsrRcasInputF(eR, eG, eB); + FsrRcasInputF(fR, fG, fB); + FsrRcasInputF(hR, hG, hB); + // Luma times 2. + FfxFloat32 bL = bB * FfxFloat32(0.5) + (bR * FfxFloat32(0.5) + bG); + FfxFloat32 dL = dB * FfxFloat32(0.5) + (dR * FfxFloat32(0.5) + dG); + FfxFloat32 eL = eB * FfxFloat32(0.5) + (eR * FfxFloat32(0.5) + eG); + FfxFloat32 fL = fB * FfxFloat32(0.5) + (fR * FfxFloat32(0.5) + fG); + FfxFloat32 hL = hB * FfxFloat32(0.5) + (hR * FfxFloat32(0.5) + hG); + // Noise detection. + FfxFloat32 nz = FfxFloat32(0.25) * bL + FfxFloat32(0.25) * dL + FfxFloat32(0.25) * fL + FfxFloat32(0.25) * hL - eL; + nz = ffxSaturate(abs(nz) * ffxApproximateReciprocalMedium(ffxMax3(ffxMax3(bL, dL, eL), fL, hL) - ffxMin3(ffxMin3(bL, dL, eL), fL, hL))); + nz = FfxFloat32(-0.5) * nz + FfxFloat32(1.0); + // Min and max of ring. + FfxFloat32 mn4R = ffxMin(ffxMin3(bR, dR, fR), hR); + FfxFloat32 mn4G = ffxMin(ffxMin3(bG, dG, fG), hG); + FfxFloat32 mn4B = ffxMin(ffxMin3(bB, dB, fB), hB); + FfxFloat32 mx4R = max(ffxMax3(bR, dR, fR), hR); + FfxFloat32 mx4G = max(ffxMax3(bG, dG, fG), hG); + FfxFloat32 mx4B = max(ffxMax3(bB, dB, fB), hB); + // Immediate constants for peak range. + FfxFloat32x2 peakC = FfxFloat32x2(1.0, -1.0 * 4.0); + // Limiters, these need to be high precision RCPs. + FfxFloat32 hitMinR = mn4R * ffxReciprocal(FfxFloat32(4.0) * mx4R); + FfxFloat32 hitMinG = mn4G * ffxReciprocal(FfxFloat32(4.0) * mx4G); + FfxFloat32 hitMinB = mn4B * ffxReciprocal(FfxFloat32(4.0) * mx4B); + FfxFloat32 hitMaxR = (peakC.x - mx4R) * ffxReciprocal(FfxFloat32(4.0) * mn4R + peakC.y); + FfxFloat32 hitMaxG = (peakC.x - mx4G) * ffxReciprocal(FfxFloat32(4.0) * mn4G + peakC.y); + FfxFloat32 hitMaxB = (peakC.x - mx4B) * ffxReciprocal(FfxFloat32(4.0) * mn4B + peakC.y); + FfxFloat32 lobeR = max(-hitMinR, hitMaxR); + FfxFloat32 lobeG = max(-hitMinG, hitMaxG); + FfxFloat32 lobeB = max(-hitMinB, hitMaxB); + FfxFloat32 lobe = max(FfxFloat32(-FSR_RCAS_LIMIT), ffxMin(ffxMax3(lobeR, lobeG, lobeB), FfxFloat32(0.0))) * ffxAsFloat + (con.x); + // Apply noise removal. +#ifdef FSR_RCAS_DENOISE + lobe *= nz; +#endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + FfxFloat32 rcpL = ffxApproximateReciprocalMedium(FfxFloat32(4.0) * lobe + FfxFloat32(1.0)); + pixR = (lobe * bR + lobe * dR + lobe * hR + lobe * fR + eR) * rcpL; + pixG = (lobe * bG + lobe * dG + lobe * hG + lobe * fG + eG) * rcpL; + pixB = (lobe * bB + lobe * dB + lobe * hB + lobe * fB + eB) * rcpL; + return; + } +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(FFX_GPU) && FFX_HALF == 1 && defined(FSR_RCAS_H) + // Input callback prototypes that need to be implemented by calling shader + FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p); + void FsrRcasInputH(inout FfxFloat16 r,inout FfxFloat16 g,inout FfxFloat16 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasH( + out FfxFloat16 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out FfxFloat16 pixG, + out FfxFloat16 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out FfxFloat16 pixA, + #endif + FfxUInt32x2 ip, // Integer pixel position in output. + FfxUInt32x4 con){ // Constant generated by RcasSetup(). + // Sharpening algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + FfxInt16x2 sp=FfxInt16x2(ip); + FfxFloat16x3 b=FsrRcasLoadH(sp+FfxInt16x2( 0,-1)).rgb; + FfxFloat16x3 d=FsrRcasLoadH(sp+FfxInt16x2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + FfxFloat16x4 ee=FsrRcasLoadH(sp); + FfxFloat16x3 e=ee.rgb;pixA=ee.a; + #else + FfxFloat16x3 e=FsrRcasLoadH(sp).rgb; + #endif + FfxFloat16x3 f=FsrRcasLoadH(sp+FfxInt16x2( 1, 0)).rgb; + FfxFloat16x3 h=FsrRcasLoadH(sp+FfxInt16x2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + FfxFloat16 bR=b.r; + FfxFloat16 bG=b.g; + FfxFloat16 bB=b.b; + FfxFloat16 dR=d.r; + FfxFloat16 dG=d.g; + FfxFloat16 dB=d.b; + FfxFloat16 eR=e.r; + FfxFloat16 eG=e.g; + FfxFloat16 eB=e.b; + FfxFloat16 fR=f.r; + FfxFloat16 fG=f.g; + FfxFloat16 fB=f.b; + FfxFloat16 hR=h.r; + FfxFloat16 hG=h.g; + FfxFloat16 hB=h.b; + // Run optional input transform. + FsrRcasInputH(bR,bG,bB); + FsrRcasInputH(dR,dG,dB); + FsrRcasInputH(eR,eG,eB); + FsrRcasInputH(fR,fG,fB); + FsrRcasInputH(hR,hG,hB); + // Luma times 2. + FfxFloat16 bL=bB*FFX_BROADCAST_FLOAT16(0.5)+(bR*FFX_BROADCAST_FLOAT16(0.5)+bG); + FfxFloat16 dL=dB*FFX_BROADCAST_FLOAT16(0.5)+(dR*FFX_BROADCAST_FLOAT16(0.5)+dG); + FfxFloat16 eL=eB*FFX_BROADCAST_FLOAT16(0.5)+(eR*FFX_BROADCAST_FLOAT16(0.5)+eG); + FfxFloat16 fL=fB*FFX_BROADCAST_FLOAT16(0.5)+(fR*FFX_BROADCAST_FLOAT16(0.5)+fG); + FfxFloat16 hL=hB*FFX_BROADCAST_FLOAT16(0.5)+(hR*FFX_BROADCAST_FLOAT16(0.5)+hG); + // Noise detection. + FfxFloat16 nz=FFX_BROADCAST_FLOAT16(0.25)*bL+FFX_BROADCAST_FLOAT16(0.25)*dL+FFX_BROADCAST_FLOAT16(0.25)*fL+FFX_BROADCAST_FLOAT16(0.25)*hL-eL; + nz=ffxSaturate(abs(nz)*ffxApproximateReciprocalMediumHalf(ffxMax3Half(ffxMax3Half(bL,dL,eL),fL,hL)-ffxMin3Half(ffxMin3Half(bL,dL,eL),fL,hL))); + nz=FFX_BROADCAST_FLOAT16(-0.5)*nz+FFX_BROADCAST_FLOAT16(1.0); + // Min and max of ring. + FfxFloat16 mn4R=min(ffxMin3Half(bR,dR,fR),hR); + FfxFloat16 mn4G=min(ffxMin3Half(bG,dG,fG),hG); + FfxFloat16 mn4B=min(ffxMin3Half(bB,dB,fB),hB); + FfxFloat16 mx4R=max(ffxMax3Half(bR,dR,fR),hR); + FfxFloat16 mx4G=max(ffxMax3Half(bG,dG,fG),hG); + FfxFloat16 mx4B=max(ffxMax3Half(bB,dB,fB),hB); + // Immediate constants for peak range. + FfxFloat16x2 peakC=FfxFloat16x2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + FfxFloat16 hitMinR=mn4R*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4R); + FfxFloat16 hitMinG=mn4G*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4G); + FfxFloat16 hitMinB=mn4B*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4B); + FfxFloat16 hitMaxR=(peakC.x-mx4R)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4R+peakC.y); + FfxFloat16 hitMaxG=(peakC.x-mx4G)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4G+peakC.y); + FfxFloat16 hitMaxB=(peakC.x-mx4B)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4B+peakC.y); + FfxFloat16 lobeR=max(-hitMinR,hitMaxR); + FfxFloat16 lobeG=max(-hitMinG,hitMaxG); + FfxFloat16 lobeB=max(-hitMinB,hitMaxB); + FfxFloat16 lobe=max(FFX_BROADCAST_FLOAT16(-FSR_RCAS_LIMIT),min(ffxMax3Half(lobeR,lobeG,lobeB),FFX_BROADCAST_FLOAT16(0.0)))*FFX_UINT32_TO_FLOAT16X2(con.y).x; + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + FfxFloat16 rcpL=ffxApproximateReciprocalMediumHalf(FFX_BROADCAST_FLOAT16(4.0)*lobe+FFX_BROADCAST_FLOAT16(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; +} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(FFX_GPU)&& FFX_HALF == 1 && defined(FSR_RCAS_HX2) + // Input callback prototypes that need to be implemented by the calling shader + FfxFloat16x4 FsrRcasLoadHx2(FfxInt16x2 p); + void FsrRcasInputHx2(inout FfxFloat16x2 r,inout FfxFloat16x2 g,inout FfxFloat16x2 b); +//------------------------------------------------------------------------------------------------------------------------------ + // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. + void FsrRcasDepackHx2(out FfxFloat16x4 pix0,out FfxFloat16x4 pix1,FfxFloat16x2 pixR,FfxFloat16x2 pixG,FfxFloat16x2 pixB){ + #ifdef FFX_HLSL + // Invoke a slower path for DX only, since it won't allow uninitialized values. + pix0.a=pix1.a=0.0; + #endif + pix0.rgb=FfxFloat16x3(pixR.x,pixG.x,pixB.x); + pix1.rgb=FfxFloat16x3(pixR.y,pixG.y,pixB.y);} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasHx2( + // Output values are for 2 8x8 tiles in a 16x8 region. + // pix.x = left 8x8 tile + // pix.y = right 8x8 tile + // This enables later processing to easily be packed as well. + out FfxFloat16x2 pixR, + out FfxFloat16x2 pixG, + out FfxFloat16x2 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out FfxFloat16x2 pixA, + #endif + FfxUInt32x2 ip, // Integer pixel position in output. + FfxUInt32x4 con){ // Constant generated by RcasSetup(). + // No scaling algorithm uses minimal 3x3 pixel neighborhood. + FfxInt16x2 sp0=FfxInt16x2(ip); + FfxFloat16x3 b0=FsrRcasLoadHx2(sp0+FfxInt16x2( 0,-1)).rgb; + FfxFloat16x3 d0=FsrRcasLoadHx2(sp0+FfxInt16x2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + FfxFloat16x4 ee0=FsrRcasLoadHx2(sp0); + FfxFloat16x3 e0=ee0.rgb;pixA.r=ee0.a; + #else + FfxFloat16x3 e0=FsrRcasLoadHx2(sp0).rgb; + #endif + FfxFloat16x3 f0=FsrRcasLoadHx2(sp0+FfxInt16x2( 1, 0)).rgb; + FfxFloat16x3 h0=FsrRcasLoadHx2(sp0+FfxInt16x2( 0, 1)).rgb; + FfxInt16x2 sp1=sp0+FfxInt16x2(8,0); + FfxFloat16x3 b1=FsrRcasLoadHx2(sp1+FfxInt16x2( 0,-1)).rgb; + FfxFloat16x3 d1=FsrRcasLoadHx2(sp1+FfxInt16x2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + FfxFloat16x4 ee1=FsrRcasLoadHx2(sp1); + FfxFloat16x3 e1=ee1.rgb;pixA.g=ee1.a; + #else + FfxFloat16x3 e1=FsrRcasLoadHx2(sp1).rgb; + #endif + FfxFloat16x3 f1=FsrRcasLoadHx2(sp1+FfxInt16x2( 1, 0)).rgb; + FfxFloat16x3 h1=FsrRcasLoadHx2(sp1+FfxInt16x2( 0, 1)).rgb; + // Arrays of Structures to Structures of Arrays conversion. + FfxFloat16x2 bR=FfxFloat16x2(b0.r,b1.r); + FfxFloat16x2 bG=FfxFloat16x2(b0.g,b1.g); + FfxFloat16x2 bB=FfxFloat16x2(b0.b,b1.b); + FfxFloat16x2 dR=FfxFloat16x2(d0.r,d1.r); + FfxFloat16x2 dG=FfxFloat16x2(d0.g,d1.g); + FfxFloat16x2 dB=FfxFloat16x2(d0.b,d1.b); + FfxFloat16x2 eR=FfxFloat16x2(e0.r,e1.r); + FfxFloat16x2 eG=FfxFloat16x2(e0.g,e1.g); + FfxFloat16x2 eB=FfxFloat16x2(e0.b,e1.b); + FfxFloat16x2 fR=FfxFloat16x2(f0.r,f1.r); + FfxFloat16x2 fG=FfxFloat16x2(f0.g,f1.g); + FfxFloat16x2 fB=FfxFloat16x2(f0.b,f1.b); + FfxFloat16x2 hR=FfxFloat16x2(h0.r,h1.r); + FfxFloat16x2 hG=FfxFloat16x2(h0.g,h1.g); + FfxFloat16x2 hB=FfxFloat16x2(h0.b,h1.b); + // Run optional input transform. + FsrRcasInputHx2(bR,bG,bB); + FsrRcasInputHx2(dR,dG,dB); + FsrRcasInputHx2(eR,eG,eB); + FsrRcasInputHx2(fR,fG,fB); + FsrRcasInputHx2(hR,hG,hB); + // Luma times 2. + FfxFloat16x2 bL=bB*FFX_BROADCAST_FLOAT16X2(0.5)+(bR*FFX_BROADCAST_FLOAT16X2(0.5)+bG); + FfxFloat16x2 dL=dB*FFX_BROADCAST_FLOAT16X2(0.5)+(dR*FFX_BROADCAST_FLOAT16X2(0.5)+dG); + FfxFloat16x2 eL=eB*FFX_BROADCAST_FLOAT16X2(0.5)+(eR*FFX_BROADCAST_FLOAT16X2(0.5)+eG); + FfxFloat16x2 fL=fB*FFX_BROADCAST_FLOAT16X2(0.5)+(fR*FFX_BROADCAST_FLOAT16X2(0.5)+fG); + FfxFloat16x2 hL=hB*FFX_BROADCAST_FLOAT16X2(0.5)+(hR*FFX_BROADCAST_FLOAT16X2(0.5)+hG); + // Noise detection. + FfxFloat16x2 nz=FFX_BROADCAST_FLOAT16X2(0.25)*bL+FFX_BROADCAST_FLOAT16X2(0.25)*dL+FFX_BROADCAST_FLOAT16X2(0.25)*fL+FFX_BROADCAST_FLOAT16X2(0.25)*hL-eL; + nz=ffxSaturate(abs(nz)*ffxApproximateReciprocalMediumHalf(ffxMax3Half(ffxMax3Half(bL,dL,eL),fL,hL)-ffxMin3Half(ffxMin3Half(bL,dL,eL),fL,hL))); + nz=FFX_BROADCAST_FLOAT16X2(-0.5)*nz+FFX_BROADCAST_FLOAT16X2(1.0); + // Min and max of ring. + FfxFloat16x2 mn4R=min(ffxMin3Half(bR,dR,fR),hR); + FfxFloat16x2 mn4G=min(ffxMin3Half(bG,dG,fG),hG); + FfxFloat16x2 mn4B=min(ffxMin3Half(bB,dB,fB),hB); + FfxFloat16x2 mx4R=max(ffxMax3Half(bR,dR,fR),hR); + FfxFloat16x2 mx4G=max(ffxMax3Half(bG,dG,fG),hG); + FfxFloat16x2 mx4B=max(ffxMax3Half(bB,dB,fB),hB); + // Immediate constants for peak range. + FfxFloat16x2 peakC=FfxFloat16x2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + FfxFloat16x2 hitMinR=mn4R*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4R); + FfxFloat16x2 hitMinG=mn4G*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4G); + FfxFloat16x2 hitMinB=mn4B*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4B); + FfxFloat16x2 hitMaxR=(peakC.x-mx4R)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4R+peakC.y); + FfxFloat16x2 hitMaxG=(peakC.x-mx4G)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4G+peakC.y); + FfxFloat16x2 hitMaxB=(peakC.x-mx4B)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4B+peakC.y); + FfxFloat16x2 lobeR=max(-hitMinR,hitMaxR); + FfxFloat16x2 lobeG=max(-hitMinG,hitMaxG); + FfxFloat16x2 lobeB=max(-hitMinB,hitMaxB); + FfxFloat16x2 lobe=max(FFX_BROADCAST_FLOAT16X2(-FSR_RCAS_LIMIT),min(ffxMax3Half(lobeR,lobeG,lobeB),FFX_BROADCAST_FLOAT16X2(0.0)))*FFX_BROADCAST_FLOAT16X2(FFX_UINT32_TO_FLOAT16X2(con.y).x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + FfxFloat16x2 rcpL=ffxApproximateReciprocalMediumHalf(FFX_BROADCAST_FLOAT16X2(4.0)*lobe+FFX_BROADCAST_FLOAT16X2(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR +// +//------------------------------------------------------------------------------------------------------------------------------ +// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. +// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. +// The 'Lfga*()' functions provide a convenient way to introduce grain. +// These functions limit grain based on distance to signal limits. +// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. +// Grain application should be done in a linear colorspace. +// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). +//------------------------------------------------------------------------------------------------------------------------------ +// Usage, +// FsrLfga*( +// color, // In/out linear colorspace color {0 to 1} ranged. +// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. +// amount); // Amount of grain (0 to 1} ranged. +//------------------------------------------------------------------------------------------------------------------------------ +// Example if grain texture is monochrome: 'FsrLfgaF(color,ffxBroadcast3(grain),amount)' +//============================================================================================================================== +#if defined(FFX_GPU) + // Maximum grain is the minimum distance to the signal limit. + void FsrLfgaF(inout FfxFloat32x3 c, FfxFloat32x3 t, FfxFloat32 a) + { + c += (t * ffxBroadcast3(a)) * ffxMin(ffxBroadcast3(1.0) - c, c); + } +#endif +//============================================================================================================================== +#if defined(FFX_GPU)&& FFX_HALF == 1 + // Half precision version (slower). + void FsrLfgaH(inout FfxFloat16x3 c, FfxFloat16x3 t, FfxFloat16 a) + { + c += (t * FFX_BROADCAST_FLOAT16X3(a)) * min(FFX_BROADCAST_FLOAT16X3(1.0) - c, c); + } + //------------------------------------------------------------------------------------------------------------------------------ + // Packed half precision version (faster). + void FsrLfgaHx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB,FfxFloat16x2 tR,FfxFloat16x2 tG,FfxFloat16x2 tB,FfxFloat16 a){ + cR+=(tR*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cR,cR);cG+=(tG*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cG,cG);cB+=(tB*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cB,cB);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER +// +//------------------------------------------------------------------------------------------------------------------------------ +// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. +// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. +//------------------------------------------------------------------------------------------------------------------------------ +// Reversible tonemapper usage, +// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. +// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. +//============================================================================================================================== +#if defined(FFX_GPU) + void FsrSrtmF(inout FfxFloat32x3 c) + { + c *= ffxBroadcast3(ffxReciprocal(ffxMax3(c.r, c.g, c.b) + FfxFloat32(1.0))); + } + // The extra max solves the c=1.0 case (which is a /0). + void FsrSrtmInvF(inout FfxFloat32x3 c){c*=ffxBroadcast3(ffxReciprocal(max(FfxFloat32(1.0/32768.0),FfxFloat32(1.0)-ffxMax3(c.r,c.g,c.b))));} +#endif +//============================================================================================================================== +#if defined(FFX_GPU )&& FFX_HALF == 1 + void FsrSrtmH(inout FfxFloat16x3 c) + { + c *= FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(ffxMax3Half(c.r, c.g, c.b) + FFX_BROADCAST_FLOAT16(1.0))); + } + void FsrSrtmInvH(inout FfxFloat16x3 c) + { + c *= FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(max(FFX_BROADCAST_FLOAT16(1.0 / 32768.0), FFX_BROADCAST_FLOAT16(1.0) - ffxMax3Half(c.r, c.g, c.b)))); + } + //------------------------------------------------------------------------------------------------------------------------------ + void FsrSrtmHx2(inout FfxFloat16x2 cR, inout FfxFloat16x2 cG, inout FfxFloat16x2 cB) + { + FfxFloat16x2 rcp = ffxReciprocalHalf(ffxMax3Half(cR, cG, cB) + FFX_BROADCAST_FLOAT16X2(1.0)); + cR *= rcp; + cG *= rcp; + cB *= rcp; + } + void FsrSrtmInvHx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB) + { + FfxFloat16x2 rcp=ffxReciprocalHalf(max(FFX_BROADCAST_FLOAT16X2(1.0/32768.0),FFX_BROADCAST_FLOAT16X2(1.0)-ffxMax3Half(cR,cG,cB))); + cR*=rcp; + cG*=rcp; + cB*=rcp; + } +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER +// +//------------------------------------------------------------------------------------------------------------------------------ +// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// Gamma 2.0 is used so that the conversion back to linear is just to square the color. +// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. +// Given good non-biased temporal blue noise as dither input, +// the output dither will temporally conserve energy. +// This is done by choosing the linear nearest step point instead of perceptual nearest. +// See code below for details. +//------------------------------------------------------------------------------------------------------------------------------ +// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION +// =============================================== +// - Output is 'FfxUInt32(floor(saturate(n)*255.0+0.5))'. +// - Thus rounding is to nearest. +// - NaN gets converted to zero. +// - INF is clamped to {0.0 to 1.0}. +//============================================================================================================================== +#if defined(FFX_GPU) + // Hand tuned integer position to dither value, with more values than simple checkerboard. + // Only 32-bit has enough precision for this compddation. + // Output is {0 to <1}. + FfxFloat32 FsrTepdDitF(FfxUInt32x2 p, FfxUInt32 f) + { + FfxFloat32 x = FfxFloat32(p.x + f); + FfxFloat32 y = FfxFloat32(p.y); + // The 1.61803 golden ratio. + FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); + // Number designed to provide a good visual pattern. + FfxFloat32 b = FfxFloat32(1.0 / 3.69); + x = x * a + (y * b); + return ffxFract(x); + } + //------------------------------------------------------------------------------------------------------------------------------ + // This version is 8-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC8F(inout FfxFloat32x3 c, FfxFloat32 dit) + { + FfxFloat32x3 n = ffxSqrt(c); + n = floor(n * ffxBroadcast3(255.0)) * ffxBroadcast3(1.0 / 255.0); + FfxFloat32x3 a = n * n; + FfxFloat32x3 b = n + ffxBroadcast3(1.0 / 255.0); + b = b * b; + // Ratio of 'a' to 'b' required to produce 'c'. + // ffxApproximateReciprocal() won't work here (at least for very high dynamic ranges). + // ffxApproximateReciprocalMedium() is an IADD,FMA,MUL. + FfxFloat32x3 r = (c - b) * ffxApproximateReciprocalMedium(a - b); + // Use the ratio as a cutoff to choose 'a' or 'b'. + // ffxIsGreaterThanZero() is a MUL. + c = ffxSaturate(n + ffxIsGreaterThanZero(ffxBroadcast3(dit) - r) * ffxBroadcast3(1.0 / 255.0)); + } + //------------------------------------------------------------------------------------------------------------------------------ + // This version is 10-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC10F(inout FfxFloat32x3 c, FfxFloat32 dit) + { + FfxFloat32x3 n = ffxSqrt(c); + n = floor(n * ffxBroadcast3(1023.0)) * ffxBroadcast3(1.0 / 1023.0); + FfxFloat32x3 a = n * n; + FfxFloat32x3 b = n + ffxBroadcast3(1.0 / 1023.0); + b = b * b; + FfxFloat32x3 r = (c - b) * ffxApproximateReciprocalMedium(a - b); + c = ffxSaturate(n + ffxIsGreaterThanZero(ffxBroadcast3(dit) - r) * ffxBroadcast3(1.0 / 1023.0)); + } +#endif +//============================================================================================================================== +#if defined(FFX_GPU)&& FFX_HALF == 1 + FfxFloat16 FsrTepdDitH(FfxUInt32x2 p, FfxUInt32 f) + { + FfxFloat32 x = FfxFloat32(p.x + f); + FfxFloat32 y = FfxFloat32(p.y); + FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); + FfxFloat32 b = FfxFloat32(1.0 / 3.69); + x = x * a + (y * b); + return FfxFloat16(ffxFract(x)); + } + //------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8H(inout FfxFloat16x3 c, FfxFloat16 dit) + { + FfxFloat16x3 n = sqrt(c); + n = floor(n * FFX_BROADCAST_FLOAT16X3(255.0)) * FFX_BROADCAST_FLOAT16X3(1.0 / 255.0); + FfxFloat16x3 a = n * n; + FfxFloat16x3 b = n + FFX_BROADCAST_FLOAT16X3(1.0 / 255.0); + b = b * b; + FfxFloat16x3 r = (c - b) * ffxApproximateReciprocalMediumHalf(a - b); + c = ffxSaturate(n + ffxIsGreaterThanZeroHalf(FFX_BROADCAST_FLOAT16X3(dit) - r) * FFX_BROADCAST_FLOAT16X3(1.0 / 255.0)); + } + //------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10H(inout FfxFloat16x3 c, FfxFloat16 dit) + { + FfxFloat16x3 n = sqrt(c); + n = floor(n * FFX_BROADCAST_FLOAT16X3(1023.0)) * FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0); + FfxFloat16x3 a = n * n; + FfxFloat16x3 b = n + FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0); + b = b * b; + FfxFloat16x3 r = (c - b) * ffxApproximateReciprocalMediumHalf(a - b); + c = ffxSaturate(n + ffxIsGreaterThanZeroHalf(FFX_BROADCAST_FLOAT16X3(dit) - r) * FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0)); + } + //============================================================================================================================== + // This computes dither for positions 'p' and 'p+{8,0}'. + FfxFloat16x2 FsrTepdDitHx2(FfxUInt32x2 p, FfxUInt32 f) + { + FfxFloat32x2 x; + x.x = FfxFloat32(p.x + f); + x.y = x.x + FfxFloat32(8.0); + FfxFloat32 y = FfxFloat32(p.y); + FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); + FfxFloat32 b = FfxFloat32(1.0 / 3.69); + x = x * ffxBroadcast2(a) + ffxBroadcast2(y * b); + return FfxFloat16x2(ffxFract(x)); + } + //------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8Hx2(inout FfxFloat16x2 cR, inout FfxFloat16x2 cG, inout FfxFloat16x2 cB, FfxFloat16x2 dit) + { + FfxFloat16x2 nR = sqrt(cR); + FfxFloat16x2 nG = sqrt(cG); + FfxFloat16x2 nB = sqrt(cB); + nR = floor(nR * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); + nG = floor(nG * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); + nB = floor(nB * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); + FfxFloat16x2 aR = nR * nR; + FfxFloat16x2 aG = nG * nG; + FfxFloat16x2 aB = nB * nB; + FfxFloat16x2 bR = nR + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); + bR = bR * bR; + FfxFloat16x2 bG = nG + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); + bG = bG * bG; + FfxFloat16x2 bB = nB + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); + bB = bB * bB; + FfxFloat16x2 rR = (cR - bR) * ffxApproximateReciprocalMediumHalf(aR - bR); + FfxFloat16x2 rG = (cG - bG) * ffxApproximateReciprocalMediumHalf(aG - bG); + FfxFloat16x2 rB = (cB - bB) * ffxApproximateReciprocalMediumHalf(aB - bB); + cR = ffxSaturate(nR + ffxIsGreaterThanZeroHalf(dit - rR) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0)); + cG = ffxSaturate(nG + ffxIsGreaterThanZeroHalf(dit - rG) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0)); + cB = ffxSaturate(nB + ffxIsGreaterThanZeroHalf(dit - rB) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0)); + } + //------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10Hx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB,FfxFloat16x2 dit){ + FfxFloat16x2 nR=sqrt(cR); + FfxFloat16x2 nG=sqrt(cG); + FfxFloat16x2 nB=sqrt(cB); + nR=floor(nR*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); + nG=floor(nG*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); + nB=floor(nB*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); + FfxFloat16x2 aR=nR*nR; + FfxFloat16x2 aG=nG*nG; + FfxFloat16x2 aB=nB*nB; + FfxFloat16x2 bR=nR+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bR=bR*bR; + FfxFloat16x2 bG=nG+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bG=bG*bG; + FfxFloat16x2 bB=nB+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bB=bB*bB; + FfxFloat16x2 rR=(cR-bR)*ffxApproximateReciprocalMediumHalf(aR-bR); + FfxFloat16x2 rG=(cG-bG)*ffxApproximateReciprocalMediumHalf(aG-bG); + FfxFloat16x2 rB=(cB-bB)*ffxApproximateReciprocalMediumHalf(aB-bB); + cR=ffxSaturate(nR+ffxIsGreaterThanZeroHalf(dit-rR)*FFX_BROADCAST_FLOAT16X2(1.0/1023.0)); + cG=ffxSaturate(nG+ffxIsGreaterThanZeroHalf(dit-rG)*FFX_BROADCAST_FLOAT16X2(1.0/1023.0)); + cB = ffxSaturate(nB + ffxIsGreaterThanZeroHalf(dit - rB) * FFX_BROADCAST_FLOAT16X2(1.0 / 1023.0)); +} +#endif diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h.meta new file mode 100644 index 0000000..4a8bdf5 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr1/ffx_fsr1.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: e73be06d181f19f42a1e8f1cf6ebc92b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2.meta new file mode 100644 index 0000000..dcb75fc --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3aeed5c87f2e3e345adb5ef1906cd480 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h new file mode 100644 index 0000000..d87c748 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h @@ -0,0 +1,295 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_ACCUMULATE_H +#define FFX_FSR2_ACCUMULATE_H + +FfxFloat32 GetPxHrVelocity(FfxFloat32x2 fMotionVector) +{ + return length(fMotionVector * DisplaySize()); +} +#if FFX_HALF +FFX_MIN16_F GetPxHrVelocity(FFX_MIN16_F2 fMotionVector) +{ + return length(fMotionVector * FFX_MIN16_F2(DisplaySize())); +} +#endif + +void Accumulate(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT FfxFloat32x3 fHistoryColor, FfxFloat32x3 fAccumulation, FFX_PARAMETER_IN FfxFloat32x4 fUpsampledColorAndWeight) +{ + // Avoid invalid values when accumulation and upsampled weight is 0 + fAccumulation = ffxMax(FSR2_EPSILON.xxx, fAccumulation + fUpsampledColorAndWeight.www); + +#if FFX_FSR2_OPTION_HDR_COLOR_INPUT + //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation) + fUpsampledColorAndWeight.xyz = RGBToYCoCg(Tonemap(YCoCgToRGB(fUpsampledColorAndWeight.xyz))); + fHistoryColor = RGBToYCoCg(Tonemap(YCoCgToRGB(fHistoryColor))); +#endif + + const FfxFloat32x3 fAlpha = fUpsampledColorAndWeight.www / fAccumulation; + fHistoryColor = ffxLerp(fHistoryColor, fUpsampledColorAndWeight.xyz, fAlpha); + + fHistoryColor = YCoCgToRGB(fHistoryColor); + +#if FFX_FSR2_OPTION_HDR_COLOR_INPUT + fHistoryColor = InverseTonemap(fHistoryColor); +#endif +} + +void RectifyHistory( + const AccumulationPassCommonParams params, + RectificationBox clippingBox, + FFX_PARAMETER_INOUT FfxFloat32x3 fHistoryColor, + FFX_PARAMETER_INOUT FfxFloat32x3 fAccumulation, + FfxFloat32 fLockContributionThisFrame, + FfxFloat32 fTemporalReactiveFactor, + FfxFloat32 fLumaInstabilityFactor) +{ + FfxFloat32 fScaleFactorInfluence = ffxMin(20.0f, ffxPow(FfxFloat32(1.0f / length(DownscaleFactor().x * DownscaleFactor().y)), 3.0f)); + + const FfxFloat32 fVecolityFactor = ffxSaturate(params.fHrVelocity / 20.0f); + const FfxFloat32 fBoxScaleT = ffxMax(params.fDepthClipFactor, ffxMax(params.fAccumulationMask, fVecolityFactor)); + FfxFloat32 fBoxScale = ffxLerp(fScaleFactorInfluence, 1.0f, fBoxScaleT); + + FfxFloat32x3 fScaledBoxVec = clippingBox.boxVec * fBoxScale; + FfxFloat32x3 boxMin = clippingBox.boxCenter - fScaledBoxVec; + FfxFloat32x3 boxMax = clippingBox.boxCenter + fScaledBoxVec; + FfxFloat32x3 boxCenter = clippingBox.boxCenter; + FfxFloat32 boxVecSize = length(clippingBox.boxVec); + + boxMin = ffxMax(clippingBox.aabbMin, boxMin); + boxMax = ffxMin(clippingBox.aabbMax, boxMax); + + if (any(FFX_GREATER_THAN(boxMin, fHistoryColor)) || any(FFX_GREATER_THAN(fHistoryColor, boxMax))) { + + const FfxFloat32x3 fClampedHistoryColor = clamp(fHistoryColor, boxMin, boxMax); + + FfxFloat32x3 fHistoryContribution = ffxMax(fLumaInstabilityFactor, fLockContributionThisFrame).xxx; + + const FfxFloat32 fReactiveFactor = params.fDilatedReactiveFactor; + const FfxFloat32 fReactiveContribution = 1.0f - ffxPow(fReactiveFactor, 1.0f / 2.0f); + fHistoryContribution *= fReactiveContribution; + + // Scale history color using rectification info, also using accumulation mask to avoid potential invalid color protection + fHistoryColor = ffxLerp(fClampedHistoryColor, fHistoryColor, ffxSaturate(fHistoryContribution)); + + // Scale accumulation using rectification info + const FfxFloat32x3 fAccumulationMin = ffxMin(fAccumulation, FFX_BROADCAST_FLOAT32X3(0.1f)); + fAccumulation = ffxLerp(fAccumulationMin, fAccumulation, ffxSaturate(fHistoryContribution)); + } +} + +void WriteUpscaledOutput(FfxInt32x2 iPxHrPos, FfxFloat32x3 fUpscaledColor) +{ + StoreUpscaledOutput(iPxHrPos, fUpscaledColor); +} + +void FinalizeLockStatus(const AccumulationPassCommonParams params, FfxFloat32x2 fLockStatus, FfxFloat32 fUpsampledWeight) +{ + // we expect similar motion for next frame + // kill lock if that location is outside screen, avoid locks to be clamped to screen borders + FfxFloat32x2 fEstimatedUvNextFrame = params.fHrUv - params.fMotionVector; + if (IsUvInside(fEstimatedUvNextFrame) == false) { + KillLock(fLockStatus); + } + else { + // Decrease lock lifetime + const FfxFloat32 fLifetimeDecreaseLanczosMax = FfxFloat32(JitterSequenceLength()) * FfxFloat32(fAverageLanczosWeightPerFrame); + const FfxFloat32 fLifetimeDecrease = FfxFloat32(fUpsampledWeight / fLifetimeDecreaseLanczosMax); + fLockStatus[LOCK_LIFETIME_REMAINING] = ffxMax(FfxFloat32(0), fLockStatus[LOCK_LIFETIME_REMAINING] - fLifetimeDecrease); + } + + StoreLockStatus(params.iPxHrPos, fLockStatus); +} + + +FfxFloat32x3 ComputeBaseAccumulationWeight(const AccumulationPassCommonParams params, FfxFloat32 fThisFrameReactiveFactor, FfxBoolean bInMotionLastFrame, FfxFloat32 fUpsampledWeight, LockState lockState) +{ + // Always assume max accumulation was reached + FfxFloat32 fBaseAccumulation = fMaxAccumulationLanczosWeight * FfxFloat32(params.bIsExistingSample) * (1.0f - fThisFrameReactiveFactor) * (1.0f - params.fDepthClipFactor); + + fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, fUpsampledWeight * 10.0f, ffxMax(FfxFloat32(bInMotionLastFrame), ffxSaturate(params.fHrVelocity * FfxFloat32(10))))); + + fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, fUpsampledWeight, ffxSaturate(params.fHrVelocity / FfxFloat32(20)))); + + return fBaseAccumulation.xxx; +} + +FfxFloat32 ComputeLumaInstabilityFactor(const AccumulationPassCommonParams params, RectificationBox clippingBox, FfxFloat32 fThisFrameReactiveFactor, FfxFloat32 fLuminanceDiff) +{ + const FfxFloat32 fUnormThreshold = 1.0f / 255.0f; + const FfxInt32 N_MINUS_1 = 0; + const FfxInt32 N_MINUS_2 = 1; + const FfxInt32 N_MINUS_3 = 2; + const FfxInt32 N_MINUS_4 = 3; + + FfxFloat32 fCurrentFrameLuma = clippingBox.boxCenter.x; + +#if FFX_FSR2_OPTION_HDR_COLOR_INPUT + fCurrentFrameLuma = fCurrentFrameLuma / (1.0f + ffxMax(0.0f, fCurrentFrameLuma)); +#endif + + fCurrentFrameLuma = round(fCurrentFrameLuma * 255.0f) / 255.0f; + + const FfxBoolean bSampleLumaHistory = (ffxMax(ffxMax(params.fDepthClipFactor, params.fAccumulationMask), fLuminanceDiff) < 0.1f) && (params.bIsNewSample == false); + FfxFloat32x4 fCurrentFrameLumaHistory = bSampleLumaHistory ? SampleLumaHistory(params.fReprojectedHrUv) : FFX_BROADCAST_FLOAT32X4(0.0f); + + FfxFloat32 fLumaInstability = 0.0f; + FfxFloat32 fDiffs0 = (fCurrentFrameLuma - fCurrentFrameLumaHistory[N_MINUS_1]); + + FfxFloat32 fMin = abs(fDiffs0); + + if (fMin >= fUnormThreshold) { + for (int i = N_MINUS_2; i <= N_MINUS_4; i++) { + FfxFloat32 fDiffs1 = (fCurrentFrameLuma - fCurrentFrameLumaHistory[i]); + + if (sign(fDiffs0) == sign(fDiffs1)) { + + // Scale difference to protect historically similar values + const FfxFloat32 fMinBias = 1.0f; + fMin = ffxMin(fMin, abs(fDiffs1) * fMinBias); + } + } + + const FfxFloat32 fBoxSize = clippingBox.boxVec.x; + const FfxFloat32 fBoxSizeFactor = ffxPow(ffxSaturate(fBoxSize / 0.1f), 6.0f); + + fLumaInstability = FfxFloat32(fMin != abs(fDiffs0)) * fBoxSizeFactor; + fLumaInstability = FfxFloat32(fLumaInstability > fUnormThreshold); + + fLumaInstability *= 1.0f - ffxMax(params.fAccumulationMask, ffxPow(fThisFrameReactiveFactor, 1.0f / 6.0f)); + } + + //shift history + fCurrentFrameLumaHistory[N_MINUS_4] = fCurrentFrameLumaHistory[N_MINUS_3]; + fCurrentFrameLumaHistory[N_MINUS_3] = fCurrentFrameLumaHistory[N_MINUS_2]; + fCurrentFrameLumaHistory[N_MINUS_2] = fCurrentFrameLumaHistory[N_MINUS_1]; + fCurrentFrameLumaHistory[N_MINUS_1] = fCurrentFrameLuma; + + StoreLumaHistory(params.iPxHrPos, fCurrentFrameLumaHistory); + + return fLumaInstability * FfxFloat32(fCurrentFrameLumaHistory[N_MINUS_4] != 0); +} + +FfxFloat32 ComputeTemporalReactiveFactor(const AccumulationPassCommonParams params, FfxFloat32 fTemporalReactiveFactor) +{ + FfxFloat32 fNewFactor = ffxMin(0.99f, fTemporalReactiveFactor); + + fNewFactor = ffxMax(fNewFactor, ffxLerp(fNewFactor, 0.4f, ffxSaturate(params.fHrVelocity))); + + fNewFactor = ffxMax(fNewFactor * fNewFactor, ffxMax(params.fDepthClipFactor * 0.1f, params.fDilatedReactiveFactor)); + + // Force reactive factor for new samples + fNewFactor = params.bIsNewSample ? 1.0f : fNewFactor; + + if (ffxSaturate(params.fHrVelocity * 10.0f) >= 1.0f) { + fNewFactor = ffxMax(FSR2_EPSILON, fNewFactor) * -1.0f; + } + + return fNewFactor; +} + +AccumulationPassCommonParams InitParams(FfxInt32x2 iPxHrPos) +{ + AccumulationPassCommonParams params; + + params.iPxHrPos = iPxHrPos; + const FfxFloat32x2 fHrUv = (iPxHrPos + 0.5f) / DisplaySize(); + params.fHrUv = fHrUv; + + const FfxFloat32x2 fLrUvJittered = fHrUv + Jitter() / RenderSize(); + params.fLrUv_HwSampler = ClampUv(fLrUvJittered, RenderSize(), MaxRenderSize()); + + params.fMotionVector = GetMotionVector(iPxHrPos, fHrUv); + params.fHrVelocity = GetPxHrVelocity(params.fMotionVector); + + ComputeReprojectedUVs(params, params.fReprojectedHrUv, params.bIsExistingSample); + + params.fDepthClipFactor = ffxSaturate(SampleDepthClip(params.fLrUv_HwSampler)); + + const FfxFloat32x2 fDilatedReactiveMasks = SampleDilatedReactiveMasks(params.fLrUv_HwSampler); + params.fDilatedReactiveFactor = fDilatedReactiveMasks.x; + params.fAccumulationMask = fDilatedReactiveMasks.y; + params.bIsResetFrame = (0 == FrameIndex()); + + params.bIsNewSample = (params.bIsExistingSample == false || params.bIsResetFrame); + + return params; +} + +void Accumulate(FfxInt32x2 iPxHrPos) +{ + const AccumulationPassCommonParams params = InitParams(iPxHrPos); + + FfxFloat32x3 fHistoryColor = FfxFloat32x3(0, 0, 0); + FfxFloat32x2 fLockStatus; + InitializeNewLockSample(fLockStatus); + + FfxFloat32 fTemporalReactiveFactor = 0.0f; + FfxBoolean bInMotionLastFrame = FFX_FALSE; + LockState lockState = { FFX_FALSE , FFX_FALSE }; + if (params.bIsExistingSample && !params.bIsResetFrame) { + ReprojectHistoryColor(params, fHistoryColor, fTemporalReactiveFactor, bInMotionLastFrame); + lockState = ReprojectHistoryLockStatus(params, fLockStatus); + } + + FfxFloat32 fThisFrameReactiveFactor = ffxMax(params.fDilatedReactiveFactor, fTemporalReactiveFactor); + + FfxFloat32 fLuminanceDiff = 0.0f; + FfxFloat32 fLockContributionThisFrame = 0.0f; + UpdateLockStatus(params, fThisFrameReactiveFactor, lockState, fLockStatus, fLockContributionThisFrame, fLuminanceDiff); + + // Load upsampled input color + RectificationBox clippingBox; + FfxFloat32x4 fUpsampledColorAndWeight = ComputeUpsampledColorAndWeight(params, clippingBox, fThisFrameReactiveFactor); + + const FfxFloat32 fLumaInstabilityFactor = ComputeLumaInstabilityFactor(params, clippingBox, fThisFrameReactiveFactor, fLuminanceDiff); + + + FfxFloat32x3 fAccumulation = ComputeBaseAccumulationWeight(params, fThisFrameReactiveFactor, bInMotionLastFrame, fUpsampledColorAndWeight.w, lockState); + + if (params.bIsNewSample) { + fHistoryColor = YCoCgToRGB(fUpsampledColorAndWeight.xyz); + } + else { + RectifyHistory(params, clippingBox, fHistoryColor, fAccumulation, fLockContributionThisFrame, fThisFrameReactiveFactor, fLumaInstabilityFactor); + + Accumulate(params, fHistoryColor, fAccumulation, fUpsampledColorAndWeight); + } + + fHistoryColor = UnprepareRgb(fHistoryColor, Exposure()); + + FinalizeLockStatus(params, fLockStatus, fUpsampledColorAndWeight.w); + + // Get new temporal reactive factor + fTemporalReactiveFactor = ComputeTemporalReactiveFactor(params, fThisFrameReactiveFactor); + + StoreInternalColorAndWeight(iPxHrPos, FfxFloat32x4(fHistoryColor, fTemporalReactiveFactor)); + + // Output final color when RCAS is disabled +#if FFX_FSR2_OPTION_APPLY_SHARPENING == 0 + WriteUpscaledOutput(iPxHrPos, fHistoryColor); +#endif + StoreNewLocks(iPxHrPos, 0); +} + +#endif // FFX_FSR2_ACCUMULATE_H diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h.meta new file mode 100644 index 0000000..7b61108 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_accumulate.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: ad56ff15a05a8c1479276e80fc3ea721 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h new file mode 100644 index 0000000..c52cc1a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h @@ -0,0 +1,945 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#include "ffx_fsr2_resources.h" + +#if defined(FFX_GPU) +#ifdef __hlsl_dx_compiler +#pragma dxc diagnostic push +#pragma dxc diagnostic ignored "-Wambig-lit-shift" +#endif //__hlsl_dx_compiler +#include "../ffx_core.h" +#ifdef __hlsl_dx_compiler +#pragma dxc diagnostic pop +#endif //__hlsl_dx_compiler + +#ifndef FFX_PREFER_WAVE64 +#define FFX_PREFER_WAVE64 +#endif // #ifndef FFX_PREFER_WAVE64 + +#pragma warning(disable: 3205) // conversion from larger type to smaller + +#define DECLARE_SRV_REGISTER(regIndex) t##regIndex +#define DECLARE_UAV_REGISTER(regIndex) u##regIndex +#define DECLARE_CB_REGISTER(regIndex) b##regIndex +#define FFX_FSR2_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) +#define FFX_FSR2_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) +#define FFX_FSR2_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) + +#if defined(FSR2_BIND_CB_FSR2) + cbuffer cbFSR2 : FFX_FSR2_DECLARE_CB(FSR2_BIND_CB_FSR2) + { + FfxInt32x2 iRenderSize; + FfxInt32x2 iMaxRenderSize; + FfxInt32x2 iDisplaySize; + FfxInt32x2 iInputColorResourceDimensions; + FfxInt32x2 iLumaMipDimensions; + FfxInt32 iLumaMipLevelToUse; + FfxInt32 iFrameIndex; + + FfxFloat32x4 fDeviceToViewDepth; + FfxFloat32x2 fJitter; + FfxFloat32x2 fMotionVectorScale; + FfxFloat32x2 fDownscaleFactor; + FfxFloat32x2 fMotionVectorJitterCancellation; + FfxFloat32 fPreExposure; + FfxFloat32 fPreviousFramePreExposure; + FfxFloat32 fTanHalfFOV; + FfxFloat32 fJitterSequenceLength; + FfxFloat32 fDeltaTime; + FfxFloat32 fDynamicResChangeFactor; + FfxFloat32 fViewSpaceToMetersFactor; + FfxFloat32 fPadding; + }; + +#define FFX_FSR2_CONSTANT_BUFFER_1_SIZE 32 + +/* Define getter functions in the order they are defined in the CB! */ +FfxInt32x2 RenderSize() +{ + return iRenderSize; +} + +FfxInt32x2 MaxRenderSize() +{ + return iMaxRenderSize; +} + +FfxInt32x2 DisplaySize() +{ + return iDisplaySize; +} + +FfxInt32x2 InputColorResourceDimensions() +{ + return iInputColorResourceDimensions; +} + +FfxInt32x2 LumaMipDimensions() +{ + return iLumaMipDimensions; +} + +FfxInt32 LumaMipLevelToUse() +{ + return iLumaMipLevelToUse; +} + +FfxInt32 FrameIndex() +{ + return iFrameIndex; +} + +FfxFloat32x2 Jitter() +{ + return fJitter; +} + +FfxFloat32x4 DeviceToViewSpaceTransformFactors() +{ + return fDeviceToViewDepth; +} + +FfxFloat32x2 MotionVectorScale() +{ + return fMotionVectorScale; +} + +FfxFloat32x2 DownscaleFactor() +{ + return fDownscaleFactor; +} + +FfxFloat32x2 MotionVectorJitterCancellation() +{ + return fMotionVectorJitterCancellation; +} + +FfxFloat32 PreExposure() +{ + return fPreExposure; +} + +FfxFloat32 PreviousFramePreExposure() +{ + return fPreviousFramePreExposure; +} + +FfxFloat32 TanHalfFoV() +{ + return fTanHalfFOV; +} + +FfxFloat32 JitterSequenceLength() +{ + return fJitterSequenceLength; +} + +FfxFloat32 DeltaTime() +{ + return fDeltaTime; +} + +FfxFloat32 DynamicResChangeFactor() +{ + return fDynamicResChangeFactor; +} + +FfxFloat32 ViewSpaceToMetersFactor() +{ + return fViewSpaceToMetersFactor; +} +#endif // #if defined(FSR2_BIND_CB_FSR2) + +#define FFX_FSR2_ROOTSIG_STRINGIFY(p) FFX_FSR2_ROOTSIG_STR(p) +#define FFX_FSR2_ROOTSIG_STR(p) #p +#define FFX_FSR2_ROOTSIG [RootSignature("DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR2_ROOTSIG_STRINGIFY(FFX_FSR2_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR2_ROOTSIG_STRINGIFY(FFX_FSR2_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "CBV(b0), " \ + "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ + "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] + +#define FFX_FSR2_CONSTANT_BUFFER_2_SIZE 6 // Number of 32-bit values. This must be kept in sync with max( cbRCAS , cbSPD) size. + +#define FFX_FSR2_CB2_ROOTSIG [RootSignature("DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR2_ROOTSIG_STRINGIFY(FFX_FSR2_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR2_ROOTSIG_STRINGIFY(FFX_FSR2_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "CBV(b0), " \ + "CBV(b1), " \ + "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ + "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] + +#define FFX_FSR2_CONSTANT_BUFFER_3_SIZE 4 // Number of 32-bit values. This must be kept in sync with cbGenerateReactive size. + +#define FFX_FSR2_REACTIVE_ROOTSIG [RootSignature("DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR2_ROOTSIG_STRINGIFY(FFX_FSR2_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR2_ROOTSIG_STRINGIFY(FFX_FSR2_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "CBV(b0), " \ + "CBV(b1), " \ + "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ + "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] + +#if defined(FFX_FSR2_EMBED_ROOTSIG) +#define FFX_FSR2_EMBED_ROOTSIG_CONTENT FFX_FSR2_ROOTSIG +#define FFX_FSR2_EMBED_CB2_ROOTSIG_CONTENT FFX_FSR2_CB2_ROOTSIG +#define FFX_FSR2_EMBED_ROOTSIG_REACTIVE_CONTENT FFX_FSR2_REACTIVE_ROOTSIG +#else +#define FFX_FSR2_EMBED_ROOTSIG_CONTENT +#define FFX_FSR2_EMBED_CB2_ROOTSIG_CONTENT +#define FFX_FSR2_EMBED_ROOTSIG_REACTIVE_CONTENT +#endif // #if FFX_FSR2_EMBED_ROOTSIG + +#if defined(FSR2_BIND_CB_AUTOREACTIVE) +cbuffer cbGenerateReactive : FFX_FSR2_DECLARE_CB(FSR2_BIND_CB_AUTOREACTIVE) +{ + FfxFloat32 fTcThreshold; // 0.1 is a good starting value, lower will result in more TC pixels + FfxFloat32 fTcScale; + FfxFloat32 fReactiveScale; + FfxFloat32 fReactiveMax; +}; + +FfxFloat32 TcThreshold() +{ + return fTcThreshold; +} + +FfxFloat32 TcScale() +{ + return fTcScale; +} + +FfxFloat32 ReactiveScale() +{ + return fReactiveScale; +} + +FfxFloat32 ReactiveMax() +{ + return fReactiveMax; +} +#endif // #if defined(FSR2_BIND_CB_AUTOREACTIVE) + +#if defined(FSR2_BIND_CB_RCAS) +cbuffer cbRCAS : FFX_FSR2_DECLARE_CB(FSR2_BIND_CB_RCAS) +{ + FfxUInt32x4 rcasConfig; +}; + +FfxUInt32x4 RCASConfig() +{ + return rcasConfig; +} +#endif // #if defined(FSR2_BIND_CB_RCAS) + + +#if defined(FSR2_BIND_CB_REACTIVE) +cbuffer cbGenerateReactive : FFX_FSR2_DECLARE_CB(FSR2_BIND_CB_REACTIVE) +{ + FfxFloat32 gen_reactive_scale; + FfxFloat32 gen_reactive_threshold; + FfxFloat32 gen_reactive_binaryValue; + FfxUInt32 gen_reactive_flags; +}; + +FfxFloat32 GenReactiveScale() +{ + return gen_reactive_scale; +} + +FfxFloat32 GenReactiveThreshold() +{ + return gen_reactive_threshold; +} + +FfxFloat32 GenReactiveBinaryValue() +{ + return gen_reactive_binaryValue; +} + +FfxUInt32 GenReactiveFlags() +{ + return gen_reactive_flags; +} +#endif // #if defined(FSR2_BIND_CB_REACTIVE) + +#if defined(FSR2_BIND_CB_SPD) +cbuffer cbSPD : FFX_FSR2_DECLARE_CB(FSR2_BIND_CB_SPD) { + + FfxUInt32 mips; + FfxUInt32 numWorkGroups; + FfxUInt32x2 workGroupOffset; + FfxUInt32x2 renderSize; +}; + +FfxUInt32 MipCount() +{ + return mips; +} + +FfxUInt32 NumWorkGroups() +{ + return numWorkGroups; +} + +FfxUInt32x2 WorkGroupOffset() +{ + return workGroupOffset; +} + +FfxUInt32x2 SPD_RenderSize() +{ + return renderSize; +} +#endif // #if defined(FSR2_BIND_CB_SPD) + +// Declare and sample camera buffers as regular textures, unless overridden +#if !defined(UNITY_FSR_TEX2D) +#define UNITY_FSR_TEX2D(type) Texture2D +#endif +#if !defined(UNITY_FSR_RWTEX2D) +#define UNITY_FSR_RWTEX2D(type) RWTexture2D +#endif +#if !defined(UNITY_FSR_POS) +#define UNITY_FSR_POS(pxPos) (pxPos) +#endif +#if !defined(UNITY_FSR_UV) +#define UNITY_FSR_UV(uv) (uv) +#endif + +SamplerState s_PointClamp : register(s0); +SamplerState s_LinearClamp : register(s1); + + // SRVs + #if defined FSR2_BIND_SRV_INPUT_COLOR + UNITY_FSR_TEX2D(FfxFloat32x4) r_input_color_jittered : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_INPUT_COLOR); + #endif + #if defined FSR2_BIND_SRV_INPUT_OPAQUE_ONLY + UNITY_FSR_TEX2D(FfxFloat32x4) r_input_opaque_only : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_INPUT_OPAQUE_ONLY); + #endif + #if defined FSR2_BIND_SRV_INPUT_MOTION_VECTORS + UNITY_FSR_TEX2D(FfxFloat32x4) r_input_motion_vectors : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_INPUT_MOTION_VECTORS); + #endif + #if defined FSR2_BIND_SRV_INPUT_DEPTH + UNITY_FSR_TEX2D(FfxFloat32) r_input_depth : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_INPUT_DEPTH); + #endif + #if defined FSR2_BIND_SRV_INPUT_EXPOSURE + Texture2D r_input_exposure : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_INPUT_EXPOSURE); + #endif + #if defined FSR2_BIND_SRV_AUTO_EXPOSURE + Texture2D r_auto_exposure : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_AUTO_EXPOSURE); + #endif + #if defined FSR2_BIND_SRV_REACTIVE_MASK + UNITY_FSR_TEX2D(FfxFloat32) r_reactive_mask : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_REACTIVE_MASK); + #endif + #if defined FSR2_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK + UNITY_FSR_TEX2D(FfxFloat32) r_transparency_and_composition_mask : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK); + #endif + #if defined FSR2_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH + Texture2D r_reconstructed_previous_nearest_depth : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH); + #endif + #if defined FSR2_BIND_SRV_DILATED_MOTION_VECTORS + Texture2D r_dilated_motion_vectors : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_DILATED_MOTION_VECTORS); + #endif + #if defined FSR2_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS + Texture2D r_previous_dilated_motion_vectors : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS); + #endif + #if defined FSR2_BIND_SRV_DILATED_DEPTH + Texture2D r_dilatedDepth : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_DILATED_DEPTH); + #endif + #if defined FSR2_BIND_SRV_INTERNAL_UPSCALED + Texture2D r_internal_upscaled_color : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_INTERNAL_UPSCALED); + #endif + #if defined FSR2_BIND_SRV_LOCK_STATUS + Texture2D r_lock_status : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_LOCK_STATUS); + #endif + #if defined FSR2_BIND_SRV_LOCK_INPUT_LUMA + Texture2D r_lock_input_luma : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_LOCK_INPUT_LUMA); + #endif + #if defined FSR2_BIND_SRV_NEW_LOCKS + Texture2D r_new_locks : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_NEW_LOCKS); + #endif + #if defined FSR2_BIND_SRV_PREPARED_INPUT_COLOR + Texture2D r_prepared_input_color : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_PREPARED_INPUT_COLOR); + #endif + #if defined FSR2_BIND_SRV_LUMA_HISTORY + Texture2D r_luma_history : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_LUMA_HISTORY); + #endif + #if defined FSR2_BIND_SRV_RCAS_INPUT + Texture2D r_rcas_input : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_RCAS_INPUT); + #endif + #if defined FSR2_BIND_SRV_LANCZOS_LUT + Texture2D r_lanczos_lut : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_LANCZOS_LUT); + #endif + #if defined FSR2_BIND_SRV_SCENE_LUMINANCE_MIPS + Texture2D r_imgMips : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_SCENE_LUMINANCE_MIPS); + #endif + #if defined FSR2_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT + Texture2D r_upsample_maximum_bias_lut : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT); + #endif + #if defined FSR2_BIND_SRV_DILATED_REACTIVE_MASKS + Texture2D r_dilated_reactive_masks : FFX_FSR2_DECLARE_SRV(FSR2_BIND_SRV_DILATED_REACTIVE_MASKS); + #endif + + #if defined FSR2_BIND_SRV_PREV_PRE_ALPHA_COLOR + Texture2D r_input_prev_color_pre_alpha : FFX_FSR2_DECLARE_SRV(FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR); + #endif + #if defined FSR2_BIND_SRV_PREV_POST_ALPHA_COLOR + Texture2D r_input_prev_color_post_alpha : FFX_FSR2_DECLARE_SRV(FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR); + #endif + + // UAV declarations + #if defined FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH + RWTexture2D rw_reconstructed_previous_nearest_depth : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH); + #endif + #if defined FSR2_BIND_UAV_DILATED_MOTION_VECTORS + RWTexture2D rw_dilated_motion_vectors : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_DILATED_MOTION_VECTORS); + #endif + #if defined FSR2_BIND_UAV_DILATED_DEPTH + RWTexture2D rw_dilatedDepth : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_DILATED_DEPTH); + #endif + #if defined FSR2_BIND_UAV_INTERNAL_UPSCALED + RWTexture2D rw_internal_upscaled_color : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_INTERNAL_UPSCALED); + #endif + #if defined FSR2_BIND_UAV_LOCK_STATUS + RWTexture2D rw_lock_status : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_LOCK_STATUS); + #endif + #if defined FSR2_BIND_UAV_LOCK_INPUT_LUMA + RWTexture2D rw_lock_input_luma : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_LOCK_INPUT_LUMA); + #endif + #if defined FSR2_BIND_UAV_NEW_LOCKS + RWTexture2D rw_new_locks : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_NEW_LOCKS); + #endif + #if defined FSR2_BIND_UAV_PREPARED_INPUT_COLOR + RWTexture2D rw_prepared_input_color : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_PREPARED_INPUT_COLOR); + #endif + #if defined FSR2_BIND_UAV_LUMA_HISTORY + RWTexture2D rw_luma_history : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_LUMA_HISTORY); + #endif + #if defined FSR2_BIND_UAV_UPSCALED_OUTPUT + UNITY_FSR_RWTEX2D(FfxFloat32x4) rw_upscaled_output : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_UPSCALED_OUTPUT); + #endif + #if defined FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE + globallycoherent RWTexture2D rw_img_mip_shading_change : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE); + #endif + #if defined FSR2_BIND_UAV_EXPOSURE_MIP_5 + globallycoherent RWTexture2D rw_img_mip_5 : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_EXPOSURE_MIP_5); + #endif + #if defined FSR2_BIND_UAV_DILATED_REACTIVE_MASKS + RWTexture2D rw_dilated_reactive_masks : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_DILATED_REACTIVE_MASKS); + #endif + #if defined FSR2_BIND_UAV_EXPOSURE + RWTexture2D rw_exposure : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_EXPOSURE); + #endif + #if defined FSR2_BIND_UAV_AUTO_EXPOSURE + RWTexture2D rw_auto_exposure : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_AUTO_EXPOSURE); + #endif + #if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC + globallycoherent RWTexture2D rw_spd_global_atomic : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC); + #endif + + #if defined FSR2_BIND_UAV_AUTOREACTIVE + RWTexture2D rw_output_autoreactive : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_AUTOREACTIVE); + #endif + #if defined FSR2_BIND_UAV_AUTOCOMPOSITION + RWTexture2D rw_output_autocomposition : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_AUTOCOMPOSITION); + #endif + #if defined FSR2_BIND_UAV_PREV_PRE_ALPHA_COLOR + RWTexture2D rw_output_prev_color_pre_alpha : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_PREV_PRE_ALPHA_COLOR); + #endif + #if defined FSR2_BIND_UAV_PREV_POST_ALPHA_COLOR + RWTexture2D rw_output_prev_color_post_alpha : FFX_FSR2_DECLARE_UAV(FSR2_BIND_UAV_PREV_POST_ALPHA_COLOR); + #endif + +#if defined(FSR2_BIND_SRV_SCENE_LUMINANCE_MIPS) +FfxFloat32 LoadMipLuma(FfxUInt32x2 iPxPos, FfxUInt32 mipLevel) +{ + return r_imgMips.mips[mipLevel][iPxPos]; +} +#endif + +#if defined(FSR2_BIND_SRV_SCENE_LUMINANCE_MIPS) +FfxFloat32 SampleMipLuma(FfxFloat32x2 fUV, FfxUInt32 mipLevel) +{ + return r_imgMips.SampleLevel(s_LinearClamp, fUV, mipLevel); +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_DEPTH) +FfxFloat32 LoadInputDepth(FfxUInt32x2 iPxPos) +{ + return r_input_depth[UNITY_FSR_POS(iPxPos)]; +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_DEPTH) +FfxFloat32 SampleInputDepth(FfxFloat32x2 fUV) +{ + return r_input_depth.SampleLevel(s_LinearClamp, UNITY_FSR_UV(fUV), 0).x; +} +#endif + +#if defined(FSR2_BIND_SRV_REACTIVE_MASK) +FfxFloat32 LoadReactiveMask(FfxUInt32x2 iPxPos) +{ + return r_reactive_mask[UNITY_FSR_POS(iPxPos)]; +} +#endif + +#if defined(FSR2_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK) +FfxFloat32 LoadTransparencyAndCompositionMask(FfxUInt32x2 iPxPos) +{ + return r_transparency_and_composition_mask[UNITY_FSR_POS(iPxPos)]; +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_COLOR) +FfxFloat32x3 LoadInputColor(FfxUInt32x2 iPxPos) +{ + return r_input_color_jittered[UNITY_FSR_POS(iPxPos)].rgb; +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_COLOR) +FfxFloat32x3 SampleInputColor(FfxFloat32x2 fUV) +{ + return r_input_color_jittered.SampleLevel(s_LinearClamp, UNITY_FSR_UV(fUV), 0).rgb; +} +#endif + +#if defined(FSR2_BIND_SRV_PREPARED_INPUT_COLOR) +FfxFloat32x3 LoadPreparedInputColor(FfxUInt32x2 iPxPos) +{ + return r_prepared_input_color[iPxPos].xyz; +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_MOTION_VECTORS) +FfxFloat32x2 LoadInputMotionVector(FfxUInt32x2 iPxDilatedMotionVectorPos) +{ + FfxFloat32x2 fSrcMotionVector = r_input_motion_vectors[UNITY_FSR_POS(iPxDilatedMotionVectorPos)].xy; + + FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale(); + +#if FFX_FSR2_OPTION_JITTERED_MOTION_VECTORS + fUvMotionVector -= MotionVectorJitterCancellation(); +#endif + + return fUvMotionVector; +} +#endif + +#if defined(FSR2_BIND_SRV_INTERNAL_UPSCALED) +FfxFloat32x4 LoadHistory(FfxUInt32x2 iPxHistory) +{ + return r_internal_upscaled_color[iPxHistory]; +} +#endif + +#if defined(FSR2_BIND_UAV_LUMA_HISTORY) +void StoreLumaHistory(FfxUInt32x2 iPxPos, FfxFloat32x4 fLumaHistory) +{ + rw_luma_history[iPxPos] = fLumaHistory; +} +#endif + +#if defined(FSR2_BIND_SRV_LUMA_HISTORY) +FfxFloat32x4 SampleLumaHistory(FfxFloat32x2 fUV) +{ + return r_luma_history.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +FfxFloat32x4 LoadRCAS_Input(FfxInt32x2 iPxPos) +{ +#if defined(FSR2_BIND_SRV_RCAS_INPUT) + return r_rcas_input[iPxPos]; +#else + return 0.0; +#endif +} + +#if defined(FSR2_BIND_UAV_INTERNAL_UPSCALED) +void StoreReprojectedHistory(FfxUInt32x2 iPxHistory, FfxFloat32x4 fHistory) +{ + rw_internal_upscaled_color[iPxHistory] = fHistory; +} +#endif + +#if defined(FSR2_BIND_UAV_INTERNAL_UPSCALED) +void StoreInternalColorAndWeight(FfxUInt32x2 iPxPos, FfxFloat32x4 fColorAndWeight) +{ + rw_internal_upscaled_color[iPxPos] = fColorAndWeight; +} +#endif + +#if defined(FSR2_BIND_UAV_UPSCALED_OUTPUT) +void StoreUpscaledOutput(FfxUInt32x2 iPxPos, FfxFloat32x3 fColor) +{ + rw_upscaled_output[UNITY_FSR_POS(iPxPos)] = FfxFloat32x4(fColor, 1.f); +} +#endif + +//LOCK_LIFETIME_REMAINING == 0 +//Should make LockInitialLifetime() return a const 1.0f later +#if defined(FSR2_BIND_SRV_LOCK_STATUS) +FfxFloat32x2 LoadLockStatus(FfxUInt32x2 iPxPos) +{ + return r_lock_status[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_UAV_LOCK_STATUS) +void StoreLockStatus(FfxUInt32x2 iPxPos, FfxFloat32x2 fLockStatus) +{ + rw_lock_status[iPxPos] = fLockStatus; +} +#endif + +#if defined(FSR2_BIND_SRV_LOCK_INPUT_LUMA) +FfxFloat32 LoadLockInputLuma(FfxUInt32x2 iPxPos) +{ + return r_lock_input_luma[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_UAV_LOCK_INPUT_LUMA) +void StoreLockInputLuma(FfxUInt32x2 iPxPos, FfxFloat32 fLuma) +{ + rw_lock_input_luma[iPxPos] = fLuma; +} +#endif + +#if defined(FSR2_BIND_SRV_NEW_LOCKS) +FfxFloat32 LoadNewLocks(FfxUInt32x2 iPxPos) +{ + return r_new_locks[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_UAV_NEW_LOCKS) +FfxFloat32 LoadRwNewLocks(FfxUInt32x2 iPxPos) +{ + return rw_new_locks[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_UAV_NEW_LOCKS) +void StoreNewLocks(FfxUInt32x2 iPxPos, FfxFloat32 newLock) +{ + rw_new_locks[iPxPos] = newLock; +} +#endif + +#if defined(FSR2_BIND_UAV_PREPARED_INPUT_COLOR) +void StorePreparedInputColor(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x4 fTonemapped) +{ + rw_prepared_input_color[iPxPos] = fTonemapped; +} +#endif + +#if defined(FSR2_BIND_SRV_PREPARED_INPUT_COLOR) +FfxFloat32 SampleDepthClip(FfxFloat32x2 fUV) +{ + return r_prepared_input_color.SampleLevel(s_LinearClamp, fUV, 0).w; +} +#endif + +#if defined(FSR2_BIND_SRV_LOCK_STATUS) +FfxFloat32x2 SampleLockStatus(FfxFloat32x2 fUV) +{ + FfxFloat32x2 fLockStatus = r_lock_status.SampleLevel(s_LinearClamp, fUV, 0); + return fLockStatus; +} +#endif + +#if defined(FSR2_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH) +FfxFloat32 LoadReconstructedPrevDepth(FfxUInt32x2 iPxPos) +{ + return asfloat(r_reconstructed_previous_nearest_depth[iPxPos]); +} +#endif + +#if defined(FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) +void StoreReconstructedDepth(FfxUInt32x2 iPxSample, FfxFloat32 fDepth) +{ + FfxUInt32 uDepth = asuint(fDepth); + + #if FFX_FSR2_OPTION_INVERTED_DEPTH + InterlockedMax(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); + #else + InterlockedMin(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); // min for standard, max for inverted depth + #endif +} +#endif + +#if defined(FSR2_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) +void SetReconstructedDepth(FfxUInt32x2 iPxSample, const FfxUInt32 uValue) +{ + rw_reconstructed_previous_nearest_depth[iPxSample] = uValue; +} +#endif + +#if defined(FSR2_BIND_UAV_DILATED_DEPTH) +void StoreDilatedDepth(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32 fDepth) +{ + rw_dilatedDepth[iPxPos] = fDepth; +} +#endif + +#if defined(FSR2_BIND_UAV_DILATED_MOTION_VECTORS) +void StoreDilatedMotionVector(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fMotionVector) +{ + rw_dilated_motion_vectors[iPxPos] = fMotionVector; +} +#endif + +#if defined(FSR2_BIND_SRV_DILATED_MOTION_VECTORS) +FfxFloat32x2 LoadDilatedMotionVector(FfxUInt32x2 iPxInput) +{ + return r_dilated_motion_vectors[iPxInput].xy; +} +#endif + +#if defined(FSR2_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS) +FfxFloat32x2 LoadPreviousDilatedMotionVector(FfxUInt32x2 iPxInput) +{ + return r_previous_dilated_motion_vectors[iPxInput].xy; +} + +FfxFloat32x2 SamplePreviousDilatedMotionVector(FfxFloat32x2 uv) +{ + return r_previous_dilated_motion_vectors.SampleLevel(s_LinearClamp, uv, 0).xy; +} +#endif + +#if defined(FSR2_BIND_SRV_DILATED_DEPTH) +FfxFloat32 LoadDilatedDepth(FfxUInt32x2 iPxInput) +{ + return r_dilatedDepth[iPxInput]; +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_EXPOSURE) +FfxFloat32 Exposure() +{ + FfxFloat32 exposure = r_input_exposure[FfxUInt32x2(0, 0)].x; + + if (exposure == 0.0f) { + exposure = 1.0f; + } + + return exposure; +} +#endif + +#if defined(FSR2_BIND_SRV_AUTO_EXPOSURE) +FfxFloat32 AutoExposure() +{ + FfxFloat32 exposure = r_auto_exposure[FfxUInt32x2(0, 0)].x; + + if (exposure == 0.0f) { + exposure = 1.0f; + } + + return exposure; +} +#endif + +FfxFloat32 SampleLanczos2Weight(FfxFloat32 x) +{ +#if defined(FSR2_BIND_SRV_LANCZOS_LUT) + return r_lanczos_lut.SampleLevel(s_LinearClamp, FfxFloat32x2(x / 2, 0.5f), 0); +#else + return 0.f; +#endif +} + +#if defined(FSR2_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT) +FfxFloat32 SampleUpsampleMaximumBias(FfxFloat32x2 uv) +{ + // Stored as a SNORM, so make sure to multiply by 2 to retrieve the actual expected range. + return FfxFloat32(2.0) * r_upsample_maximum_bias_lut.SampleLevel(s_LinearClamp, abs(uv) * 2.0, 0); +} +#endif + +#if defined(FSR2_BIND_SRV_DILATED_REACTIVE_MASKS) +FfxFloat32x2 SampleDilatedReactiveMasks(FfxFloat32x2 fUV) +{ + return r_dilated_reactive_masks.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR2_BIND_SRV_DILATED_REACTIVE_MASKS) +FfxFloat32x2 LoadDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos) +{ + return r_dilated_reactive_masks[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_UAV_DILATED_REACTIVE_MASKS) +void StoreDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fDilatedReactiveMasks) +{ + rw_dilated_reactive_masks[iPxPos] = fDilatedReactiveMasks; +} +#endif + +#if defined(FSR2_BIND_SRV_INPUT_OPAQUE_ONLY) +FfxFloat32x3 LoadOpaqueOnly(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) +{ + return r_input_opaque_only[UNITY_FSR_POS(iPxPos)].xyz; +} +#endif + +#if defined(FSR2_BIND_SRV_PREV_PRE_ALPHA_COLOR) +FfxFloat32x3 LoadPrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) +{ + return r_input_prev_color_pre_alpha[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_SRV_PREV_POST_ALPHA_COLOR) +FfxFloat32x3 LoadPrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) +{ + return r_input_prev_color_post_alpha[iPxPos]; +} +#endif + +#if defined(FSR2_BIND_UAV_AUTOREACTIVE) +#if defined(FSR2_BIND_UAV_AUTOCOMPOSITION) +void StoreAutoReactive(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F2 fReactive) +{ + rw_output_autoreactive[iPxPos] = fReactive.x; + + rw_output_autocomposition[iPxPos] = fReactive.y; +} +#endif +#endif + +#if defined(FSR2_BIND_UAV_PREV_PRE_ALPHA_COLOR) +void StorePrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) +{ + rw_output_prev_color_pre_alpha[iPxPos] = color; + +} +#endif + +#if defined(FSR2_BIND_UAV_PREV_POST_ALPHA_COLOR) +void StorePrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) +{ + rw_output_prev_color_post_alpha[iPxPos] = color; +} +#endif + +FfxFloat32x2 SPD_LoadExposureBuffer() +{ +#if defined FSR2_BIND_UAV_AUTO_EXPOSURE + return rw_auto_exposure[FfxInt32x2(0, 0)]; +#else + return FfxFloat32x2(0.f, 0.f); +#endif // #if defined FSR2_BIND_UAV_AUTO_EXPOSURE +} + +void SPD_SetExposureBuffer(FfxFloat32x2 value) +{ +#if defined FSR2_BIND_UAV_AUTO_EXPOSURE + rw_auto_exposure[FfxInt32x2(0, 0)] = value; +#endif // #if defined FSR2_BIND_UAV_AUTO_EXPOSURE +} + +FfxFloat32x4 SPD_LoadMipmap5(FfxInt32x2 iPxPos) +{ +#if defined FSR2_BIND_UAV_EXPOSURE_MIP_5 + return FfxFloat32x4(rw_img_mip_5[iPxPos], 0, 0, 0); +#else + return FfxFloat32x4(0.f, 0.f, 0.f, 0.f); +#endif // #if defined FSR2_BIND_UAV_EXPOSURE_MIP_5 +} + +void SPD_SetMipmap(FfxInt32x2 iPxPos, FfxUInt32 slice, FfxFloat32 value) +{ + switch (slice) + { + case FFX_FSR2_SHADING_CHANGE_MIP_LEVEL: +#if defined FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE + rw_img_mip_shading_change[iPxPos] = value; +#endif // #if defined FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE + break; + case 5: +#if defined FSR2_BIND_UAV_EXPOSURE_MIP_5 + rw_img_mip_5[iPxPos] = value; +#endif // #if defined FSR2_BIND_UAV_EXPOSURE_MIP_5 + break; + default: + + // avoid flattened side effect +#if defined(FSR2_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE) + rw_img_mip_shading_change[iPxPos] = rw_img_mip_shading_change[iPxPos]; +#elif defined(FSR2_BIND_UAV_EXPOSURE_MIP_5) + rw_img_mip_5[iPxPos] = rw_img_mip_5[iPxPos]; +#endif // #if defined FSR2_BIND_UAV_EXPOSURE_MIP_5 + break; + } +} + +void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter) +{ +#if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC + InterlockedAdd(rw_spd_global_atomic[FfxInt32x2(0, 0)], 1, spdCounter); +#endif // #if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC +} + +void SPD_ResetAtomicCounter() +{ +#if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC + rw_spd_global_atomic[FfxInt32x2(0, 0)] = 0; +#endif // #if defined FSR2_BIND_UAV_SPD_GLOBAL_ATOMIC +} + +#endif // #if defined(FFX_GPU) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h.meta new file mode 100644 index 0000000..f044e59 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_callbacks_hlsl.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: ee531e22337dd714aab845c65c30ab94 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h new file mode 100644 index 0000000..e46b66c --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h @@ -0,0 +1,566 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#if !defined(FFX_FSR2_COMMON_H) +#define FFX_FSR2_COMMON_H + +#if defined(FFX_CPU) || defined(FFX_GPU) +//Locks +#define LOCK_LIFETIME_REMAINING 0 +#define LOCK_TEMPORAL_LUMA 1 +#endif // #if defined(FFX_CPU) || defined(FFX_GPU) + +#if defined(FFX_GPU) +FFX_STATIC const FfxFloat32 FSR2_FP16_MIN = 6.10e-05f; +FFX_STATIC const FfxFloat32 FSR2_FP16_MAX = 65504.0f; +FFX_STATIC const FfxFloat32 FSR2_EPSILON = 1e-03f; +FFX_STATIC const FfxFloat32 FSR2_TONEMAP_EPSILON = 1.0f / FSR2_FP16_MAX; +FFX_STATIC const FfxFloat32 FSR2_FLT_MAX = 3.402823466e+38f; +FFX_STATIC const FfxFloat32 FSR2_FLT_MIN = 1.175494351e-38f; + +// treat vector truncation warnings as errors +#pragma warning(error: 3206) + +// suppress warnings +#pragma warning(disable: 3205) // conversion from larger type to smaller +#pragma warning(disable: 3571) // in ffxPow(f, e), f could be negative + +// Reconstructed depth usage +FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = 0.01f; + +// Accumulation +FFX_STATIC const FfxFloat32 fUpsampleLanczosWeightScale = 1.0f / 12.0f; +FFX_STATIC const FfxFloat32 fMaxAccumulationLanczosWeight = 1.0f; +FFX_STATIC const FfxFloat32 fAverageLanczosWeightPerFrame = 0.74f * fUpsampleLanczosWeightScale; // Average lanczos weight for jitter accumulated samples +FFX_STATIC const FfxFloat32 fAccumulationMaxOnMotion = 3.0f * fUpsampleLanczosWeightScale; + +// Auto exposure +FFX_STATIC const FfxFloat32 resetAutoExposureAverageSmoothing = 1e8f; + +struct AccumulationPassCommonParams +{ + FfxInt32x2 iPxHrPos; + FfxFloat32x2 fHrUv; + FfxFloat32x2 fLrUv_HwSampler; + FfxFloat32x2 fMotionVector; + FfxFloat32x2 fReprojectedHrUv; + FfxFloat32 fHrVelocity; + FfxFloat32 fDepthClipFactor; + FfxFloat32 fDilatedReactiveFactor; + FfxFloat32 fAccumulationMask; + + FfxBoolean bIsResetFrame; + FfxBoolean bIsExistingSample; + FfxBoolean bIsNewSample; +}; + +struct LockState +{ + FfxBoolean NewLock; //Set for both unique new and re-locked new + FfxBoolean WasLockedPrevFrame; //Set to identify if the pixel was already locked (relock) +}; + +void InitializeNewLockSample(FFX_PARAMETER_OUT FfxFloat32x2 fLockStatus) +{ + fLockStatus = FfxFloat32x2(0, 0); +} + +#if FFX_HALF +void InitializeNewLockSample(FFX_PARAMETER_OUT FFX_MIN16_F2 fLockStatus) +{ + fLockStatus = FFX_MIN16_F2(0, 0); +} +#endif + + +void KillLock(FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus) +{ + fLockStatus[LOCK_LIFETIME_REMAINING] = 0; +} + +#if FFX_HALF +void KillLock(FFX_PARAMETER_INOUT FFX_MIN16_F2 fLockStatus) +{ + fLockStatus[LOCK_LIFETIME_REMAINING] = FFX_MIN16_F(0); +} +#endif + +struct RectificationBox +{ + FfxFloat32x3 boxCenter; + FfxFloat32x3 boxVec; + FfxFloat32x3 aabbMin; + FfxFloat32x3 aabbMax; + FfxFloat32 fBoxCenterWeight; +}; +#if FFX_HALF +struct RectificationBoxMin16 +{ + FFX_MIN16_F3 boxCenter; + FFX_MIN16_F3 boxVec; + FFX_MIN16_F3 aabbMin; + FFX_MIN16_F3 aabbMax; + FFX_MIN16_F fBoxCenterWeight; +}; +#endif + +void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBox rectificationBox) +{ + rectificationBox.fBoxCenterWeight = FfxFloat32(0); + + rectificationBox.boxCenter = FfxFloat32x3(0, 0, 0); + rectificationBox.boxVec = FfxFloat32x3(0, 0, 0); + rectificationBox.aabbMin = FfxFloat32x3(FSR2_FLT_MAX, FSR2_FLT_MAX, FSR2_FLT_MAX); + rectificationBox.aabbMax = -FfxFloat32x3(FSR2_FLT_MAX, FSR2_FLT_MAX, FSR2_FLT_MAX); +} +#if FFX_HALF +void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) +{ + rectificationBox.fBoxCenterWeight = FFX_MIN16_F(0); + + rectificationBox.boxCenter = FFX_MIN16_F3(0, 0, 0); + rectificationBox.boxVec = FFX_MIN16_F3(0, 0, 0); + rectificationBox.aabbMin = FFX_MIN16_F3(FSR2_FP16_MAX, FSR2_FP16_MAX, FSR2_FP16_MAX); + rectificationBox.aabbMax = -FFX_MIN16_F3(FSR2_FP16_MAX, FSR2_FP16_MAX, FSR2_FP16_MAX); +} +#endif + +void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) +{ + rectificationBox.aabbMin = colorSample; + rectificationBox.aabbMax = colorSample; + + FfxFloat32x3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter = weightedSample; + rectificationBox.boxVec = colorSample * weightedSample; + rectificationBox.fBoxCenterWeight = fSampleWeight; +} + +void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) +{ + if (bInitialSample) { + RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); + } else { + rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); + rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); + + FfxFloat32x3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter += weightedSample; + rectificationBox.boxVec += colorSample * weightedSample; + rectificationBox.fBoxCenterWeight += fSampleWeight; + } +} +#if FFX_HALF +void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) +{ + rectificationBox.aabbMin = colorSample; + rectificationBox.aabbMax = colorSample; + + FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter = weightedSample; + rectificationBox.boxVec = colorSample * weightedSample; + rectificationBox.fBoxCenterWeight = fSampleWeight; +} + +void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) +{ + if (bInitialSample) { + RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); + } else { + rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); + rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); + + FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter += weightedSample; + rectificationBox.boxVec += colorSample * weightedSample; + rectificationBox.fBoxCenterWeight += fSampleWeight; + } +} +#endif + +void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBox rectificationBox) +{ + rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FfxFloat32(FSR2_EPSILON) ? rectificationBox.fBoxCenterWeight : FfxFloat32(1.f)); + rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; + rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; + FfxFloat32x3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); + rectificationBox.boxVec = stdDev; +} +#if FFX_HALF +void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) +{ + rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FFX_MIN16_F(FSR2_EPSILON) ? rectificationBox.fBoxCenterWeight : FFX_MIN16_F(1.f)); + rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; + rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; + FFX_MIN16_F3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); + rectificationBox.boxVec = stdDev; +} +#endif + +FfxFloat32x3 SafeRcp3(FfxFloat32x3 v) +{ + return (all(FFX_NOT_EQUAL(v, FfxFloat32x3(0, 0, 0)))) ? (FfxFloat32x3(1, 1, 1) / v) : FfxFloat32x3(0, 0, 0); +} +#if FFX_HALF +FFX_MIN16_F3 SafeRcp3(FFX_MIN16_F3 v) +{ + return (all(FFX_NOT_EQUAL(v, FFX_MIN16_F3(0, 0, 0)))) ? (FFX_MIN16_F3(1, 1, 1) / v) : FFX_MIN16_F3(0, 0, 0); +} +#endif + +FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1) +{ + const FfxFloat32 m = ffxMax(v0, v1); + return m != 0 ? ffxMin(v0, v1) / m : 0; +} + +#if FFX_HALF +FFX_MIN16_F MinDividedByMax(const FFX_MIN16_F v0, const FFX_MIN16_F v1) +{ + const FFX_MIN16_F m = ffxMax(v0, v1); + return m != FFX_MIN16_F(0) ? ffxMin(v0, v1) / m : FFX_MIN16_F(0); +} +#endif + +FfxFloat32x3 YCoCgToRGB(FfxFloat32x3 fYCoCg) +{ + FfxFloat32x3 fRgb; + + fRgb = FfxFloat32x3( + fYCoCg.x + fYCoCg.y - fYCoCg.z, + fYCoCg.x + fYCoCg.z, + fYCoCg.x - fYCoCg.y - fYCoCg.z); + + return fRgb; +} +#if FFX_HALF +FFX_MIN16_F3 YCoCgToRGB(FFX_MIN16_F3 fYCoCg) +{ + FFX_MIN16_F3 fRgb; + + fRgb = FFX_MIN16_F3( + fYCoCg.x + fYCoCg.y - fYCoCg.z, + fYCoCg.x + fYCoCg.z, + fYCoCg.x - fYCoCg.y - fYCoCg.z); + + return fRgb; +} +#endif + +FfxFloat32x3 RGBToYCoCg(FfxFloat32x3 fRgb) +{ + FfxFloat32x3 fYCoCg; + + fYCoCg = FfxFloat32x3( + 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b, + 0.5f * fRgb.r - 0.5f * fRgb.b, + -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b); + + return fYCoCg; +} +#if FFX_HALF +FFX_MIN16_F3 RGBToYCoCg(FFX_MIN16_F3 fRgb) +{ + FFX_MIN16_F3 fYCoCg; + + fYCoCg = FFX_MIN16_F3( + 0.25 * fRgb.r + 0.5 * fRgb.g + 0.25 * fRgb.b, + 0.5 * fRgb.r - 0.5 * fRgb.b, + -0.25 * fRgb.r + 0.5 * fRgb.g - 0.25 * fRgb.b); + + return fYCoCg; +} +#endif + +FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb) +{ + return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f)); +} +#if FFX_HALF +FFX_MIN16_F RGBToLuma(FFX_MIN16_F3 fLinearRgb) +{ + return dot(fLinearRgb, FFX_MIN16_F3(0.2126f, 0.7152f, 0.0722f)); +} +#endif + +FfxFloat32 RGBToPerceivedLuma(FfxFloat32x3 fLinearRgb) +{ + FfxFloat32 fLuminance = RGBToLuma(fLinearRgb); + + FfxFloat32 fPercievedLuminance = 0; + if (fLuminance <= 216.0f / 24389.0f) { + fPercievedLuminance = fLuminance * (24389.0f / 27.0f); + } + else { + fPercievedLuminance = ffxPow(fLuminance, 1.0f / 3.0f) * 116.0f - 16.0f; + } + + return fPercievedLuminance * 0.01f; +} +#if FFX_HALF +FFX_MIN16_F RGBToPerceivedLuma(FFX_MIN16_F3 fLinearRgb) +{ + FFX_MIN16_F fLuminance = RGBToLuma(fLinearRgb); + + FFX_MIN16_F fPercievedLuminance = FFX_MIN16_F(0); + if (fLuminance <= FFX_MIN16_F(216.0f / 24389.0f)) { + fPercievedLuminance = fLuminance * FFX_MIN16_F(24389.0f / 27.0f); + } + else { + fPercievedLuminance = ffxPow(fLuminance, FFX_MIN16_F(1.0f / 3.0f)) * FFX_MIN16_F(116.0f) - FFX_MIN16_F(16.0f); + } + + return fPercievedLuminance * FFX_MIN16_F(0.01f); +} +#endif + +FfxFloat32x3 Tonemap(FfxFloat32x3 fRgb) +{ + return fRgb / (ffxMax(ffxMax(0.f, fRgb.r), ffxMax(fRgb.g, fRgb.b)) + 1.f).xxx; +} + +FfxFloat32x3 InverseTonemap(FfxFloat32x3 fRgb) +{ + return fRgb / ffxMax(FSR2_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; +} + +#if FFX_HALF +FFX_MIN16_F3 Tonemap(FFX_MIN16_F3 fRgb) +{ + return fRgb / (ffxMax(ffxMax(FFX_MIN16_F(0.f), fRgb.r), ffxMax(fRgb.g, fRgb.b)) + FFX_MIN16_F(1.f)).xxx; +} + +FFX_MIN16_F3 InverseTonemap(FFX_MIN16_F3 fRgb) +{ + return fRgb / ffxMax(FFX_MIN16_F(FSR2_TONEMAP_EPSILON), FFX_MIN16_F(1.f) - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; +} +#endif + +FfxInt32x2 ClampLoad(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) +{ + FfxInt32x2 result = iPxSample + iPxOffset; + result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; + result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; + result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; + result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; + return result; + + // return ffxMed3(iPxSample + iPxOffset, FfxInt32x2(0, 0), iTextureSize - FfxInt32x2(1, 1)); +} +#if FFX_HALF +FFX_MIN16_I2 ClampLoad(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) +{ + FFX_MIN16_I2 result = iPxSample + iPxOffset; + result.x = (iPxOffset.x < 0) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; + result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; + result.y = (iPxOffset.y < 0) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; + result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; + return result; + + // return ffxMed3Half(iPxSample + iPxOffset, FFX_MIN16_I2(0, 0), iTextureSize - FFX_MIN16_I2(1, 1)); +} +#endif + +FfxFloat32x2 ClampUv(FfxFloat32x2 fUv, FfxInt32x2 iTextureSize, FfxInt32x2 iResourceSize) +{ + const FfxFloat32x2 fSampleLocation = fUv * iTextureSize; + const FfxFloat32x2 fClampedLocation = ffxMax(FfxFloat32x2(0.5f, 0.5f), ffxMin(fSampleLocation, FfxFloat32x2(iTextureSize) - FfxFloat32x2(0.5f, 0.5f))); + const FfxFloat32x2 fClampedUv = fClampedLocation / FfxFloat32x2(iResourceSize); + + return fClampedUv; +} + +FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size) +{ + return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size))); +} +#if FFX_HALF +FfxBoolean IsOnScreen(FFX_MIN16_I2 pos, FFX_MIN16_I2 size) +{ + return all(FFX_LESS_THAN(FFX_MIN16_U2(pos), FFX_MIN16_U2(size))); +} +#endif + +FfxFloat32 ComputeAutoExposureFromLavg(FfxFloat32 Lavg) +{ + Lavg = exp(Lavg); + + const FfxFloat32 S = 100.0f; //ISO arithmetic speed + const FfxFloat32 K = 12.5f; + FfxFloat32 ExposureISO100 = log2((Lavg * S) / K); + + const FfxFloat32 q = 0.65f; + FfxFloat32 Lmax = (78.0f / (q * S)) * ffxPow(2.0f, ExposureISO100); + + return 1 / Lmax; +} +#if FFX_HALF +FFX_MIN16_F ComputeAutoExposureFromLavg(FFX_MIN16_F Lavg) +{ + Lavg = exp(Lavg); + + const FFX_MIN16_F S = FFX_MIN16_F(100.0f); //ISO arithmetic speed + const FFX_MIN16_F K = FFX_MIN16_F(12.5f); + const FFX_MIN16_F ExposureISO100 = log2((Lavg * S) / K); + + const FFX_MIN16_F q = FFX_MIN16_F(0.65f); + const FFX_MIN16_F Lmax = (FFX_MIN16_F(78.0f) / (q * S)) * ffxPow(FFX_MIN16_F(2.0f), ExposureISO100); + + return FFX_MIN16_F(1) / Lmax; +} +#endif + +FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos) +{ + FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter(); + FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * DisplaySize(); + FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr)); + return iPxHrPos; +} +#if FFX_HALF +FFX_MIN16_I2 ComputeHrPosFromLrPos(FFX_MIN16_I2 iPxLrPos) +{ + FFX_MIN16_F2 fSrcJitteredPos = FFX_MIN16_F2(iPxLrPos) + FFX_MIN16_F(0.5f) - FFX_MIN16_F2(Jitter()); + FFX_MIN16_F2 fLrPosInHr = (fSrcJitteredPos / FFX_MIN16_F2(RenderSize())) * FFX_MIN16_F2(DisplaySize()); + FFX_MIN16_I2 iPxHrPos = FFX_MIN16_I2(floor(fLrPosInHr)); + return iPxHrPos; +} +#endif + +FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize) +{ + return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f); +} + +FfxFloat32 GetViewSpaceDepth(FfxFloat32 fDeviceDepth) +{ + const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); + + // fDeviceToViewDepth details found in ffx_fsr2.cpp + return (fDeviceToViewDepth[1] / (fDeviceDepth - fDeviceToViewDepth[0])); +} + +FfxFloat32 GetViewSpaceDepthInMeters(FfxFloat32 fDeviceDepth) +{ + return GetViewSpaceDepth(fDeviceDepth) * ViewSpaceToMetersFactor(); +} + +FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) +{ + const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); + + const FfxFloat32 Z = GetViewSpaceDepth(fDeviceDepth); + + const FfxFloat32x2 fNdcPos = ComputeNdc(iViewportPos, iViewportSize); + const FfxFloat32 X = fDeviceToViewDepth[2] * fNdcPos.x * Z; + const FfxFloat32 Y = fDeviceToViewDepth[3] * fNdcPos.y * Z; + + return FfxFloat32x3(X, Y, Z); +} + +FfxFloat32x3 GetViewSpacePositionInMeters(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) +{ + return GetViewSpacePosition(iViewportPos, iViewportSize, fDeviceDepth) * ViewSpaceToMetersFactor(); +} + +FfxFloat32 GetMaxDistanceInMeters() +{ +#if FFX_FSR2_OPTION_INVERTED_DEPTH + return GetViewSpaceDepth(0.0f) * ViewSpaceToMetersFactor(); +#else + return GetViewSpaceDepth(1.0f) * ViewSpaceToMetersFactor(); +#endif +} + +FfxFloat32x3 PrepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure, FfxFloat32 fPreExposure) +{ + fRgb /= fPreExposure; + fRgb *= fExposure; + + fRgb = clamp(fRgb, 0.0f, FSR2_FP16_MAX); + + return fRgb; +} + +FfxFloat32x3 UnprepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure) +{ + fRgb /= fExposure; + fRgb *= PreExposure(); + + return fRgb; +} + + +struct BilinearSamplingData +{ + FfxInt32x2 iOffsets[4]; + FfxFloat32 fWeights[4]; + FfxInt32x2 iBasePos; +}; + +BilinearSamplingData GetBilinearSamplingData(FfxFloat32x2 fUv, FfxInt32x2 iSize) +{ + BilinearSamplingData data; + + FfxFloat32x2 fPxSample = (fUv * iSize) - FfxFloat32x2(0.5f, 0.5f); + data.iBasePos = FfxInt32x2(floor(fPxSample)); + FfxFloat32x2 fPxFrac = ffxFract(fPxSample); + + data.iOffsets[0] = FfxInt32x2(0, 0); + data.iOffsets[1] = FfxInt32x2(1, 0); + data.iOffsets[2] = FfxInt32x2(0, 1); + data.iOffsets[3] = FfxInt32x2(1, 1); + + data.fWeights[0] = (1 - fPxFrac.x) * (1 - fPxFrac.y); + data.fWeights[1] = (fPxFrac.x) * (1 - fPxFrac.y); + data.fWeights[2] = (1 - fPxFrac.x) * (fPxFrac.y); + data.fWeights[3] = (fPxFrac.x) * (fPxFrac.y); + + return data; +} + +struct PlaneData +{ + FfxFloat32x3 fNormal; + FfxFloat32 fDistanceFromOrigin; +}; + +PlaneData GetPlaneFromPoints(FfxFloat32x3 fP0, FfxFloat32x3 fP1, FfxFloat32x3 fP2) +{ + PlaneData plane; + + FfxFloat32x3 v0 = fP0 - fP1; + FfxFloat32x3 v1 = fP0 - fP2; + plane.fNormal = normalize(cross(v0, v1)); + plane.fDistanceFromOrigin = -dot(fP0, plane.fNormal); + + return plane; +} + +FfxFloat32 PointToPlaneDistance(PlaneData plane, FfxFloat32x3 fPoint) +{ + return abs(dot(plane.fNormal, fPoint) + plane.fDistanceFromOrigin); +} + +#endif // #if defined(FFX_GPU) + +#endif //!defined(FFX_FSR2_COMMON_H) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h.meta new file mode 100644 index 0000000..1357ddf --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_common.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 261b5f3ac23701a4aa6d399d43798324 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h new file mode 100644 index 0000000..d8d4820 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h @@ -0,0 +1,176 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +FFX_GROUPSHARED FfxUInt32 spdCounter; + +void SpdIncreaseAtomicCounter(FfxUInt32 slice) +{ + SPD_IncreaseAtomicCounter(spdCounter); +} + +FfxUInt32 SpdGetAtomicCounter() +{ + return spdCounter; +} + +void SpdResetAtomicCounter(FfxUInt32 slice) +{ + SPD_ResetAtomicCounter(); +} + +#ifndef SPD_PACKED_ONLY +FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16]; + +FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 tex, FfxUInt32 slice) +{ + FfxFloat32x2 fUv = (tex + 0.5f + Jitter()) / RenderSize(); + fUv = ClampUv(fUv, RenderSize(), InputColorResourceDimensions()); + FfxFloat32x3 fRgb = SampleInputColor(fUv); + + fRgb /= PreExposure(); + + //compute log luma + const FfxFloat32 fLogLuma = log(ffxMax(FSR2_EPSILON, RGBToLuma(fRgb))); + + // Make sure out of screen pixels contribute no value to the end result + const FfxFloat32 result = all(FFX_LESS_THAN(tex, RenderSize())) ? fLogLuma : 0.0f; + + return FfxFloat32x4(result, 0, 0, 0); +} + +FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice) +{ + return SPD_LoadMipmap5(tex); +} + +void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice) +{ + if (index == LumaMipLevelToUse() || index == 5) + { + SPD_SetMipmap(pix, index, outValue.r); + } + + if (index == MipCount() - 1) { //accumulate on 1x1 level + + if (all(FFX_EQUAL(pix, FfxInt32x2(0, 0)))) + { + FfxFloat32 prev = SPD_LoadExposureBuffer().y; + FfxFloat32 result = outValue.r; + + if (prev < resetAutoExposureAverageSmoothing) // Compare Lavg, so small or negative values + { + FfxFloat32 rate = 1.0f; + result = prev + (result - prev) * (1 - exp(-DeltaTime() * rate)); + } + FfxFloat32x2 spdOutput = FfxFloat32x2(ComputeAutoExposureFromLavg(result), result); + SPD_SetExposureBuffer(spdOutput); + } + } +} + +FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat32x4( + spdIntermediateR[x][y], + spdIntermediateG[x][y], + spdIntermediateB[x][y], + spdIntermediateA[x][y]); +} +void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) +{ + spdIntermediateR[x][y] = value.x; + spdIntermediateG[x][y] = value.y; + spdIntermediateB[x][y] = value.z; + spdIntermediateA[x][y] = value.w; +} +FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) +{ + return (v0 + v1 + v2 + v3) * 0.25f; +} +#endif + +// define fetch and store functions Packed +#if FFX_HALF + +FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16]; +FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16]; + +FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice) +{ + return FfxFloat16x4(0, 0, 0, 0); +} + +FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice) +{ + return FfxFloat16x4(0, 0, 0, 0); +} + +void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice) +{ +} + +FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat16x4( + spdIntermediateRG[x][y].x, + spdIntermediateRG[x][y].y, + spdIntermediateBA[x][y].x, + spdIntermediateBA[x][y].y); +} + +void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value) +{ + spdIntermediateRG[x][y] = value.xy; + spdIntermediateBA[x][y] = value.zw; +} + +FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3) +{ + return (v0 + v1 + v2 + v3) * FfxFloat16(0.25); +} +#endif + +#include "../spd/ffx_spd.h" + +void ComputeAutoExposure(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex) +{ +#if FFX_HALF + SpdDownsampleH( + FfxUInt32x2(WorkGroupId.xy), + FfxUInt32(LocalThreadIndex), + FfxUInt32(MipCount()), + FfxUInt32(NumWorkGroups()), + FfxUInt32(WorkGroupId.z), + FfxUInt32x2(WorkGroupOffset())); +#else + SpdDownsample( + FfxUInt32x2(WorkGroupId.xy), + FfxUInt32(LocalThreadIndex), + FfxUInt32(MipCount()), + FfxUInt32(NumWorkGroups()), + FfxUInt32(WorkGroupId.z), + FfxUInt32x2(WorkGroupOffset())); +#endif +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h.meta new file mode 100644 index 0000000..1ea247c --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_compute_luminance_pyramid.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 86bf712594bf26449924ac6bb393e498 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h new file mode 100644 index 0000000..873ff4b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h @@ -0,0 +1,259 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_DEPTH_CLIP_H +#define FFX_FSR2_DEPTH_CLIP_H + +FFX_STATIC const FfxFloat32 DepthClipBaseScale = 4.0f; + +FfxFloat32 ComputeDepthClip(FfxFloat32x2 fUvSample, FfxFloat32 fCurrentDepthSample) +{ + FfxFloat32 fCurrentDepthViewSpace = GetViewSpaceDepth(fCurrentDepthSample); + BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fUvSample, RenderSize()); + + FfxFloat32 fDilatedSum = 0.0f; + FfxFloat32 fDepth = 0.0f; + FfxFloat32 fWeightSum = 0.0f; + for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { + + const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; + const FfxInt32x2 iSamplePos = bilinearInfo.iBasePos + iOffset; + + if (IsOnScreen(iSamplePos, RenderSize())) { + const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; + if (fWeight > fReconstructedDepthBilinearWeightThreshold) { + + const FfxFloat32 fPrevDepthSample = LoadReconstructedPrevDepth(iSamplePos); + const FfxFloat32 fPrevNearestDepthViewSpace = GetViewSpaceDepth(fPrevDepthSample); + + const FfxFloat32 fDepthDiff = fCurrentDepthViewSpace - fPrevNearestDepthViewSpace; + + if (fDepthDiff > 0.0f) { + +#if FFX_FSR2_OPTION_INVERTED_DEPTH + const FfxFloat32 fPlaneDepth = ffxMin(fPrevDepthSample, fCurrentDepthSample); +#else + const FfxFloat32 fPlaneDepth = ffxMax(fPrevDepthSample, fCurrentDepthSample); +#endif + + const FfxFloat32x3 fCenter = GetViewSpacePosition(FfxInt32x2(RenderSize() * 0.5f), RenderSize(), fPlaneDepth); + const FfxFloat32x3 fCorner = GetViewSpacePosition(FfxInt32x2(0, 0), RenderSize(), fPlaneDepth); + + const FfxFloat32 fHalfViewportWidth = length(FfxFloat32x2(RenderSize())); + const FfxFloat32 fDepthThreshold = ffxMax(fCurrentDepthViewSpace, fPrevNearestDepthViewSpace); + + const FfxFloat32 Ksep = 1.37e-05f; + const FfxFloat32 Kfov = length(fCorner) / length(fCenter); + const FfxFloat32 fRequiredDepthSeparation = Ksep * Kfov * fHalfViewportWidth * fDepthThreshold; + + const FfxFloat32 fResolutionFactor = ffxSaturate(length(FfxFloat32x2(RenderSize())) / length(FfxFloat32x2(1920.0f, 1080.0f))); + const FfxFloat32 fPower = ffxLerp(1.0f, 3.0f, fResolutionFactor); + fDepth += ffxPow(ffxSaturate(FfxFloat32(fRequiredDepthSeparation / fDepthDiff)), fPower) * fWeight; + fWeightSum += fWeight; + } + } + } + } + + return (fWeightSum > 0) ? ffxSaturate(1.0f - fDepth / fWeightSum) : 0.0f; +} + +FfxFloat32 ComputeMotionDivergence(FfxInt32x2 iPxPos, FfxInt32x2 iPxInputMotionVectorSize) +{ + FfxFloat32 minconvergence = 1.0f; + + FfxFloat32x2 fMotionVectorNucleus = LoadInputMotionVector(iPxPos); + FfxFloat32 fNucleusVelocityLr = length(fMotionVectorNucleus * RenderSize()); + FfxFloat32 fMaxVelocityUv = length(fMotionVectorNucleus); + + const FfxFloat32 MotionVectorVelocityEpsilon = 1e-02f; + + if (fNucleusVelocityLr > MotionVectorVelocityEpsilon) { + for (FfxInt32 y = -1; y <= 1; ++y) { + for (FfxInt32 x = -1; x <= 1; ++x) { + + FfxInt32x2 sp = ClampLoad(iPxPos, FfxInt32x2(x, y), iPxInputMotionVectorSize); + + FfxFloat32x2 fMotionVector = LoadInputMotionVector(sp); + FfxFloat32 fVelocityUv = length(fMotionVector); + + fMaxVelocityUv = ffxMax(fVelocityUv, fMaxVelocityUv); + fVelocityUv = ffxMax(fVelocityUv, fMaxVelocityUv); + minconvergence = ffxMin(minconvergence, dot(fMotionVector / fVelocityUv, fMotionVectorNucleus / fVelocityUv)); + } + } + } + + return ffxSaturate(1.0f - minconvergence) * ffxSaturate(fMaxVelocityUv / 0.01f); +} + +FfxFloat32 ComputeDepthDivergence(FfxInt32x2 iPxPos) +{ + const FfxFloat32 fMaxDistInMeters = GetMaxDistanceInMeters(); + FfxFloat32 fDepthMax = 0.0f; + FfxFloat32 fDepthMin = fMaxDistInMeters; + + FfxInt32 iMaxDistFound = 0; + + for (FfxInt32 y = -1; y < 2; y++) { + for (FfxInt32 x = -1; x < 2; x++) { + + const FfxInt32x2 iOffset = FfxInt32x2(x, y); + const FfxInt32x2 iSamplePos = iPxPos + iOffset; + + const FfxFloat32 fOnScreenFactor = IsOnScreen(iSamplePos, RenderSize()) ? 1.0f : 0.0f; + FfxFloat32 fDepth = GetViewSpaceDepthInMeters(LoadDilatedDepth(iSamplePos)) * fOnScreenFactor; + + iMaxDistFound |= FfxInt32(fMaxDistInMeters == fDepth); + + fDepthMin = ffxMin(fDepthMin, fDepth); + fDepthMax = ffxMax(fDepthMax, fDepth); + } + } + + return (1.0f - fDepthMin / fDepthMax) * (FfxBoolean(iMaxDistFound) ? 0.0f : 1.0f); +} + +FfxFloat32 ComputeTemporalMotionDivergence(FfxInt32x2 iPxPos) +{ + const FfxFloat32x2 fUv = FfxFloat32x2(iPxPos + 0.5f) / RenderSize(); + + FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); + FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; + fReprojectedUv = ClampUv(fReprojectedUv, RenderSize(), MaxRenderSize()); + FfxFloat32x2 fPrevMotionVector = SamplePreviousDilatedMotionVector(fReprojectedUv); + + float fPxDistance = length(fMotionVector * DisplaySize()); + return fPxDistance > 1.0f ? ffxLerp(0.0f, 1.0f - ffxSaturate(length(fPrevMotionVector) / length(fMotionVector)), ffxSaturate(ffxPow(fPxDistance / 20.0f, 3.0f))) : 0; +} + +void PreProcessReactiveMasks(FfxInt32x2 iPxLrPos, FfxFloat32 fMotionDivergence) +{ + // Compensate for bilinear sampling in accumulation pass + + FfxFloat32x3 fReferenceColor = LoadInputColor(iPxLrPos).xyz; + FfxFloat32x2 fReactiveFactor = FfxFloat32x2(0.0f, fMotionDivergence); + + float fMasksSum = 0.0f; + + FfxFloat32x3 fColorSamples[9]; + FfxFloat32 fReactiveSamples[9]; + FfxFloat32 fTransparencyAndCompositionSamples[9]; + + FFX_UNROLL + for (FfxInt32 y = -1; y < 2; y++) { + FFX_UNROLL + for (FfxInt32 x = -1; x < 2; x++) { + + const FfxInt32x2 sampleCoord = ClampLoad(iPxLrPos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); + + FfxInt32 sampleIdx = (y + 1) * 3 + x + 1; + + FfxFloat32x3 fColorSample = LoadInputColor(sampleCoord).xyz; + FfxFloat32 fReactiveSample = LoadReactiveMask(sampleCoord); + FfxFloat32 fTransparencyAndCompositionSample = LoadTransparencyAndCompositionMask(sampleCoord); + + fColorSamples[sampleIdx] = fColorSample; + fReactiveSamples[sampleIdx] = fReactiveSample; + fTransparencyAndCompositionSamples[sampleIdx] = fTransparencyAndCompositionSample; + + fMasksSum += (fReactiveSample + fTransparencyAndCompositionSample); + } + } + + if (fMasksSum > 0) + { + for (FfxInt32 sampleIdx = 0; sampleIdx < 9; sampleIdx++) + { + FfxFloat32x3 fColorSample = fColorSamples[sampleIdx]; + FfxFloat32 fReactiveSample = fReactiveSamples[sampleIdx]; + FfxFloat32 fTransparencyAndCompositionSample = fTransparencyAndCompositionSamples[sampleIdx]; + + const FfxFloat32 fMaxLenSq = ffxMax(dot(fReferenceColor, fReferenceColor), dot(fColorSample, fColorSample)); + const FfxFloat32 fSimilarity = dot(fReferenceColor, fColorSample) / fMaxLenSq; + + // Increase power for non-similar samples + const FfxFloat32 fPowerBiasMax = 6.0f; + const FfxFloat32 fSimilarityPower = 1.0f + (fPowerBiasMax - fSimilarity * fPowerBiasMax); + const FfxFloat32 fWeightedReactiveSample = ffxPow(fReactiveSample, fSimilarityPower); + const FfxFloat32 fWeightedTransparencyAndCompositionSample = ffxPow(fTransparencyAndCompositionSample, fSimilarityPower); + + fReactiveFactor = ffxMax(fReactiveFactor, FfxFloat32x2(fWeightedReactiveSample, fWeightedTransparencyAndCompositionSample)); + } + } + + StoreDilatedReactiveMasks(iPxLrPos, fReactiveFactor); +} + +FfxFloat32x3 ComputePreparedInputColor(FfxInt32x2 iPxLrPos) +{ + //We assume linear data. if non-linear input (sRGB, ...), + //then we should convert to linear first and back to sRGB on output. + FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxLrPos)); + + fRgb = PrepareRgb(fRgb, Exposure(), PreExposure()); + + const FfxFloat32x3 fPreparedYCoCg = RGBToYCoCg(fRgb); + + return fPreparedYCoCg; +} + +FfxFloat32 EvaluateSurface(FfxInt32x2 iPxPos, FfxFloat32x2 fMotionVector) +{ + FfxFloat32 d0 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, -1))); + FfxFloat32 d1 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, 0))); + FfxFloat32 d2 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, 1))); + + return 1.0f - FfxFloat32(((d0 - d1) > (d1 * 0.01f)) && ((d1 - d2) > (d2 * 0.01f))); +} + +void DepthClip(FfxInt32x2 iPxPos) +{ + FfxFloat32x2 fDepthUv = (iPxPos + 0.5f) / RenderSize(); + FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); + + // Discard tiny mvs + fMotionVector *= FfxFloat32(length(fMotionVector * DisplaySize()) > 0.01f); + + const FfxFloat32x2 fDilatedUv = fDepthUv + fMotionVector; + const FfxFloat32 fDilatedDepth = LoadDilatedDepth(iPxPos); + const FfxFloat32 fCurrentDepthViewSpace = GetViewSpaceDepth(LoadInputDepth(iPxPos)); + + // Compute prepared input color and depth clip + FfxFloat32 fDepthClip = ComputeDepthClip(fDilatedUv, fDilatedDepth) * EvaluateSurface(iPxPos, fMotionVector); + FfxFloat32x3 fPreparedYCoCg = ComputePreparedInputColor(iPxPos); + StorePreparedInputColor(iPxPos, FfxFloat32x4(fPreparedYCoCg, fDepthClip)); + + // Compute dilated reactive mask +#if FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS + FfxInt32x2 iSamplePos = iPxPos; +#else + FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxPos); +#endif + + FfxFloat32 fMotionDivergence = ComputeMotionDivergence(iSamplePos, RenderSize()); + FfxFloat32 fTemporalMotionDifference = ffxSaturate(ComputeTemporalMotionDivergence(iPxPos) - ComputeDepthDivergence(iPxPos)); + + PreProcessReactiveMasks(iPxPos, ffxMax(fTemporalMotionDifference, fMotionDivergence)); +} + +#endif //!defined( FFX_FSR2_DEPTH_CLIPH ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h.meta new file mode 100644 index 0000000..49ed527 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_depth_clip.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 37ba9aabf1f29eb4abc9b37df9829fde +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h new file mode 100644 index 0000000..4a1f6d5 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h @@ -0,0 +1,116 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_LOCK_H +#define FFX_FSR2_LOCK_H + +void ClearResourcesForNextFrame(in FfxInt32x2 iPxHrPos) +{ + if (all(FFX_LESS_THAN(iPxHrPos, FfxInt32x2(RenderSize())))) + { +#if FFX_FSR2_OPTION_INVERTED_DEPTH + const FfxUInt32 farZ = 0x0; +#else + const FfxUInt32 farZ = 0x3f800000; +#endif + SetReconstructedDepth(iPxHrPos, farZ); + } +} + +FfxBoolean ComputeThinFeatureConfidence(FfxInt32x2 pos) +{ + const FfxInt32 RADIUS = 1; + + FfxFloat32 fNucleus = LoadLockInputLuma(pos); + + FfxFloat32 similar_threshold = 1.05f; + FfxFloat32 dissimilarLumaMin = FSR2_FLT_MAX; + FfxFloat32 dissimilarLumaMax = 0; + + /* + 0 1 2 + 3 4 5 + 6 7 8 + */ + + #define SETBIT(x) (1U << x) + + FfxUInt32 mask = SETBIT(4); //flag fNucleus as similar + + const FfxUInt32 uNumRejectionMasks = 4; + const FfxUInt32 uRejectionMasks[uNumRejectionMasks] = { + SETBIT(0) | SETBIT(1) | SETBIT(3) | SETBIT(4), //Upper left + SETBIT(1) | SETBIT(2) | SETBIT(4) | SETBIT(5), //Upper right + SETBIT(3) | SETBIT(4) | SETBIT(6) | SETBIT(7), //Lower left + SETBIT(4) | SETBIT(5) | SETBIT(7) | SETBIT(8), //Lower right + }; + + FfxInt32 idx = 0; + FFX_UNROLL + for (FfxInt32 y = -RADIUS; y <= RADIUS; y++) { + FFX_UNROLL + for (FfxInt32 x = -RADIUS; x <= RADIUS; x++, idx++) { + if (x == 0 && y == 0) continue; + + FfxInt32x2 samplePos = ClampLoad(pos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); + + FfxFloat32 sampleLuma = LoadLockInputLuma(samplePos); + FfxFloat32 difference = ffxMax(sampleLuma, fNucleus) / ffxMin(sampleLuma, fNucleus); + + if (difference > 0 && (difference < similar_threshold)) { + mask |= SETBIT(idx); + } else { + dissimilarLumaMin = ffxMin(dissimilarLumaMin, sampleLuma); + dissimilarLumaMax = ffxMax(dissimilarLumaMax, sampleLuma); + } + } + } + + FfxBoolean isRidge = fNucleus > dissimilarLumaMax || fNucleus < dissimilarLumaMin; + + if (FFX_FALSE == isRidge) { + + return false; + } + + FFX_UNROLL + for (FfxInt32 i = 0; i < 4; i++) { + + if ((mask & uRejectionMasks[i]) == uRejectionMasks[i]) { + return false; + } + } + + return true; +} + +void ComputeLock(FfxInt32x2 iPxLrPos) +{ + if (ComputeThinFeatureConfidence(iPxLrPos)) + { + StoreNewLocks(ComputeHrPosFromLrPos(iPxLrPos), 1.f); + } + + // ClearResourcesForNextFrame(iPxLrPos); +} + +#endif // FFX_FSR2_LOCK_H diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h.meta new file mode 100644 index 0000000..bf702fd --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_lock.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 77b94974e206e5045afda96cf9048139 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h new file mode 100644 index 0000000..90ef344 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h @@ -0,0 +1,107 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_POSTPROCESS_LOCK_STATUS_H +#define FFX_FSR2_POSTPROCESS_LOCK_STATUS_H + +FfxFloat32x4 WrapShadingChangeLuma(FfxInt32x2 iPxSample) +{ + return FfxFloat32x4(LoadMipLuma(iPxSample, LumaMipLevelToUse()), 0, 0, 0); +} + +#if FFX_HALF +FFX_MIN16_F4 WrapShadingChangeLuma(FFX_MIN16_I2 iPxSample) +{ + return FFX_MIN16_F4(LoadMipLuma(iPxSample, LumaMipLevelToUse()), 0, 0, 0); +} +#endif + +#if FFX_FSR2_OPTION_POSTPROCESSLOCKSTATUS_SAMPLERS_USE_DATA_HALF && FFX_HALF +DeclareCustomFetchBilinearSamplesMin16(FetchShadingChangeLumaSamples, WrapShadingChangeLuma) +#else +DeclareCustomFetchBicubicSamples(FetchShadingChangeLumaSamples, WrapShadingChangeLuma) +#endif +DeclareCustomTextureSample(ShadingChangeLumaSample, Lanczos2, FetchShadingChangeLumaSamples) + +FfxFloat32 GetShadingChangeLuma(FfxInt32x2 iPxHrPos, FfxFloat32x2 fUvCoord) +{ + FfxFloat32 fShadingChangeLuma = 0; + +#if 0 + fShadingChangeLuma = Exposure() * exp(ShadingChangeLumaSample(fUvCoord, LumaMipDimensions()).x); +#else + + const FfxFloat32 fDiv = FfxFloat32(2 << LumaMipLevelToUse()); + FfxInt32x2 iMipRenderSize = FfxInt32x2(RenderSize() / fDiv); + + fUvCoord = ClampUv(fUvCoord, iMipRenderSize, LumaMipDimensions()); + fShadingChangeLuma = Exposure() * exp(FfxFloat32(SampleMipLuma(fUvCoord, LumaMipLevelToUse()))); +#endif + + fShadingChangeLuma = ffxPow(fShadingChangeLuma, 1.0f / 6.0f); + + return fShadingChangeLuma; +} + +void UpdateLockStatus(AccumulationPassCommonParams params, + FFX_PARAMETER_INOUT FfxFloat32 fReactiveFactor, LockState state, + FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus, + FFX_PARAMETER_OUT FfxFloat32 fLockContributionThisFrame, + FFX_PARAMETER_OUT FfxFloat32 fLuminanceDiff) { + + const FfxFloat32 fShadingChangeLuma = GetShadingChangeLuma(params.iPxHrPos, params.fHrUv); + + //init temporal shading change factor, init to -1 or so in reproject to know if "true new"? + fLockStatus[LOCK_TEMPORAL_LUMA] = (fLockStatus[LOCK_TEMPORAL_LUMA] == FfxFloat32(0.0f)) ? fShadingChangeLuma : fLockStatus[LOCK_TEMPORAL_LUMA]; + + FfxFloat32 fPreviousShadingChangeLuma = fLockStatus[LOCK_TEMPORAL_LUMA]; + + fLuminanceDiff = 1.0f - MinDividedByMax(fPreviousShadingChangeLuma, fShadingChangeLuma); + + if (state.NewLock) { + fLockStatus[LOCK_TEMPORAL_LUMA] = fShadingChangeLuma; + + fLockStatus[LOCK_LIFETIME_REMAINING] = (fLockStatus[LOCK_LIFETIME_REMAINING] != 0.0f) ? 2.0f : 1.0f; + } + else if(fLockStatus[LOCK_LIFETIME_REMAINING] <= 1.0f) { + fLockStatus[LOCK_TEMPORAL_LUMA] = ffxLerp(fLockStatus[LOCK_TEMPORAL_LUMA], FfxFloat32(fShadingChangeLuma), 0.5f); + } + else { + if (fLuminanceDiff > 0.1f) { + KillLock(fLockStatus); + } + } + + fReactiveFactor = ffxMax(fReactiveFactor, ffxSaturate((fLuminanceDiff - 0.1f) * 10.0f)); + fLockStatus[LOCK_LIFETIME_REMAINING] *= (1.0f - fReactiveFactor); + + fLockStatus[LOCK_LIFETIME_REMAINING] *= ffxSaturate(1.0f - params.fAccumulationMask); + fLockStatus[LOCK_LIFETIME_REMAINING] *= FfxFloat32(params.fDepthClipFactor < 0.1f); + + // Compute this frame lock contribution + const FfxFloat32 fLifetimeContribution = ffxSaturate(fLockStatus[LOCK_LIFETIME_REMAINING] - 1.0f); + const FfxFloat32 fShadingChangeContribution = ffxSaturate(MinDividedByMax(fLockStatus[LOCK_TEMPORAL_LUMA], fShadingChangeLuma)); + + fLockContributionThisFrame = ffxSaturate(ffxSaturate(fLifetimeContribution * 4.0f) * fShadingChangeContribution); +} + +#endif //!defined( FFX_FSR2_POSTPROCESS_LOCK_STATUS_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h.meta new file mode 100644 index 0000000..f92a8d3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_postprocess_lock_status.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 04dd68d20ea907f4381636f90aae5bc9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h new file mode 100644 index 0000000..fd5fd26 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h @@ -0,0 +1,67 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define GROUP_SIZE 8 +#define FSR_RCAS_DENOISE 1 + +#include "../ffx_core.h" + +void WriteUpscaledOutput(FFX_MIN16_U2 iPxHrPos, FfxFloat32x3 fUpscaledColor) +{ + StoreUpscaledOutput(FFX_MIN16_I2(iPxHrPos), fUpscaledColor); +} + +#define FSR_RCAS_F 1 +FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p) +{ + FfxFloat32x4 fColor = LoadRCAS_Input(p); + + fColor.rgb = PrepareRgb(fColor.rgb, Exposure(), PreExposure()); + + return fColor; +} +void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {} + +#include "../fsr1/ffx_fsr1.h" + +void CurrFilter(FFX_MIN16_U2 pos) +{ + FfxFloat32x3 c; + FsrRcasF(c.r, c.g, c.b, pos, RCASConfig()); + + c = UnprepareRgb(c, Exposure()); + + WriteUpscaledOutput(pos, c); +} + +void RCAS(FfxUInt32x3 LocalThreadId, FfxUInt32x3 WorkGroupId, FfxUInt32x3 Dtid) +{ + // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. + FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u); + CurrFilter(FFX_MIN16_U2(gxy)); + gxy.x += 8u; + CurrFilter(FFX_MIN16_U2(gxy)); + gxy.y += 8u; + CurrFilter(FFX_MIN16_U2(gxy)); + gxy.x -= 8u; + CurrFilter(FFX_MIN16_U2(gxy)); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h.meta new file mode 100644 index 0000000..c121733 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_rcas.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 1b6b9dc81790da347b42bf6a3a132479 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h new file mode 100644 index 0000000..1a4305d --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h @@ -0,0 +1,146 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H +#define FFX_FSR2_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H + +void ReconstructPrevDepth(FfxInt32x2 iPxPos, FfxFloat32 fDepth, FfxFloat32x2 fMotionVector, FfxInt32x2 iPxDepthSize) +{ + fMotionVector *= FfxFloat32(length(fMotionVector * DisplaySize()) > 0.1f); + + FfxFloat32x2 fUv = (iPxPos + FfxFloat32(0.5)) / iPxDepthSize; + FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; + + BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize()); + + // Project current depth into previous frame locations. + // Push to all pixels having some contribution if reprojection is using bilinear logic. + for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { + + const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; + FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; + + if (fWeight > fReconstructedDepthBilinearWeightThreshold) { + + FfxInt32x2 iStorePos = bilinearInfo.iBasePos + iOffset; + if (IsOnScreen(iStorePos, iPxDepthSize)) { + StoreReconstructedDepth(iStorePos, fDepth); + } + } + } +} + +void FindNearestDepth(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxInt32x2 iPxSize, FFX_PARAMETER_OUT FfxFloat32 fNearestDepth, FFX_PARAMETER_OUT FfxInt32x2 fNearestDepthCoord) +{ + const FfxInt32 iSampleCount = 9; + const FfxInt32x2 iSampleOffsets[iSampleCount] = { + FfxInt32x2(+0, +0), + FfxInt32x2(+1, +0), + FfxInt32x2(+0, +1), + FfxInt32x2(+0, -1), + FfxInt32x2(-1, +0), + FfxInt32x2(-1, +1), + FfxInt32x2(+1, +1), + FfxInt32x2(-1, -1), + FfxInt32x2(+1, -1), + }; + + // pull out the depth loads to allow SC to batch them + FfxFloat32 depth[9]; + FfxInt32 iSampleIndex = 0; + FFX_UNROLL + for (iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) { + + FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; + depth[iSampleIndex] = LoadInputDepth(iPos); + } + + // find closest depth + fNearestDepthCoord = iPxPos; + fNearestDepth = depth[0]; + FFX_UNROLL + for (iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex) { + + FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; + if (IsOnScreen(iPos, iPxSize)) { + + FfxFloat32 fNdDepth = depth[iSampleIndex]; +#if FFX_FSR2_OPTION_INVERTED_DEPTH + if (fNdDepth > fNearestDepth) { +#else + if (fNdDepth < fNearestDepth) { +#endif + fNearestDepthCoord = iPos; + fNearestDepth = fNdDepth; + } + } + } +} + +FfxFloat32 ComputeLockInputLuma(FfxInt32x2 iPxLrPos) +{ + //We assume linear data. if non-linear input (sRGB, ...), + //then we should convert to linear first and back to sRGB on output. + FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxLrPos)); + + // Use internal auto exposure for locking logic + fRgb /= PreExposure(); + fRgb *= Exposure(); + +#if FFX_FSR2_OPTION_HDR_COLOR_INPUT + fRgb = Tonemap(fRgb); +#endif + + //compute luma used to lock pixels, if used elsewhere the ffxPow must be moved! + const FfxFloat32 fLockInputLuma = ffxPow(RGBToPerceivedLuma(fRgb), FfxFloat32(1.0 / 6.0)); + + return fLockInputLuma; +} + +void ReconstructAndDilate(FfxInt32x2 iPxLrPos) +{ + FfxFloat32 fDilatedDepth; + FfxInt32x2 iNearestDepthCoord; + + FindNearestDepth(iPxLrPos, RenderSize(), fDilatedDepth, iNearestDepthCoord); + +#if FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS + FfxInt32x2 iSamplePos = iPxLrPos; + FfxInt32x2 iMotionVectorPos = iNearestDepthCoord; +#else + FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxLrPos); + FfxInt32x2 iMotionVectorPos = ComputeHrPosFromLrPos(iNearestDepthCoord); +#endif + + FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iMotionVectorPos); + + StoreDilatedDepth(iPxLrPos, fDilatedDepth); + StoreDilatedMotionVector(iPxLrPos, fDilatedMotionVector); + + ReconstructPrevDepth(iPxLrPos, fDilatedDepth, fDilatedMotionVector, RenderSize()); + + FfxFloat32 fLockInputLuma = ComputeLockInputLuma(iPxLrPos); + StoreLockInputLuma(iPxLrPos, fLockInputLuma); +} + + +#endif //!defined( FFX_FSR2_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h.meta new file mode 100644 index 0000000..3d37e71 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: c7d2ce16ffba7e5459f6e88351b86355 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h new file mode 100644 index 0000000..386b297 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h @@ -0,0 +1,137 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_REPROJECT_H +#define FFX_FSR2_REPROJECT_H + +#ifndef FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE +#define FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE 0 // Reference +#endif + +FfxFloat32x4 WrapHistory(FfxInt32x2 iPxSample) +{ + return LoadHistory(iPxSample); +} + +#if FFX_HALF +FFX_MIN16_F4 WrapHistory(FFX_MIN16_I2 iPxSample) +{ + return FFX_MIN16_F4(LoadHistory(iPxSample)); +} +#endif + + +#if FFX_FSR2_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF +DeclareCustomFetchBicubicSamplesMin16(FetchHistorySamples, WrapHistory) +DeclareCustomTextureSampleMin16(HistorySample, FFX_FSR2_GET_LANCZOS_SAMPLER1D(FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) +#else +DeclareCustomFetchBicubicSamples(FetchHistorySamples, WrapHistory) +DeclareCustomTextureSample(HistorySample, FFX_FSR2_GET_LANCZOS_SAMPLER1D(FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) +#endif + +FfxFloat32x4 WrapLockStatus(FfxInt32x2 iPxSample) +{ + FfxFloat32x4 fSample = FfxFloat32x4(LoadLockStatus(iPxSample), 0.0f, 0.0f); + return fSample; +} + +#if FFX_HALF +FFX_MIN16_F4 WrapLockStatus(FFX_MIN16_I2 iPxSample) +{ + FFX_MIN16_F4 fSample = FFX_MIN16_F4(LoadLockStatus(iPxSample), 0.0, 0.0); + + return fSample; +} +#endif + +#if 1 +#if FFX_FSR2_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF +DeclareCustomFetchBilinearSamplesMin16(FetchLockStatusSamples, WrapLockStatus) +DeclareCustomTextureSampleMin16(LockStatusSample, Bilinear, FetchLockStatusSamples) +#else +DeclareCustomFetchBilinearSamples(FetchLockStatusSamples, WrapLockStatus) +DeclareCustomTextureSample(LockStatusSample, Bilinear, FetchLockStatusSamples) +#endif +#else +#if FFX_FSR2_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF +DeclareCustomFetchBicubicSamplesMin16(FetchLockStatusSamples, WrapLockStatus) +DeclareCustomTextureSampleMin16(LockStatusSample, FFX_FSR2_GET_LANCZOS_SAMPLER1D(FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchLockStatusSamples) +#else +DeclareCustomFetchBicubicSamples(FetchLockStatusSamples, WrapLockStatus) +DeclareCustomTextureSample(LockStatusSample, FFX_FSR2_GET_LANCZOS_SAMPLER1D(FFX_FSR2_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchLockStatusSamples) +#endif +#endif + +FfxFloat32x2 GetMotionVector(FfxInt32x2 iPxHrPos, FfxFloat32x2 fHrUv) +{ +#if FFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS + FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(FFX_MIN16_I2(fHrUv * RenderSize())); +#else + FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iPxHrPos); +#endif + + return fDilatedMotionVector; +} + +FfxBoolean IsUvInside(FfxFloat32x2 fUv) +{ + return (fUv.x >= 0.0f && fUv.x <= 1.0f) && (fUv.y >= 0.0f && fUv.y <= 1.0f); +} + +void ComputeReprojectedUVs(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedHrUv, FFX_PARAMETER_OUT FfxBoolean bIsExistingSample) +{ + fReprojectedHrUv = params.fHrUv + params.fMotionVector; + + bIsExistingSample = IsUvInside(fReprojectedHrUv); +} + +void ReprojectHistoryColor(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x3 fHistoryColor, FFX_PARAMETER_OUT FfxFloat32 fTemporalReactiveFactor, FFX_PARAMETER_OUT FfxBoolean bInMotionLastFrame) +{ + FfxFloat32x4 fHistory = HistorySample(params.fReprojectedHrUv, DisplaySize()); + + fHistoryColor = PrepareRgb(fHistory.rgb, Exposure(), PreviousFramePreExposure()); + + fHistoryColor = RGBToYCoCg(fHistoryColor); + + //Compute temporal reactivity info + fTemporalReactiveFactor = ffxSaturate(abs(fHistory.w)); + bInMotionLastFrame = (fHistory.w < 0.0f); +} + +LockState ReprojectHistoryLockStatus(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedLockStatus) +{ + LockState state = { FFX_FALSE, FFX_FALSE }; + const FfxFloat32 fNewLockIntensity = LoadRwNewLocks(params.iPxHrPos); + state.NewLock = fNewLockIntensity > (127.0f / 255.0f); + + FfxFloat32 fInPlaceLockLifetime = state.NewLock ? fNewLockIntensity : 0; + + fReprojectedLockStatus = SampleLockStatus(params.fReprojectedHrUv); + + if (fReprojectedLockStatus[LOCK_LIFETIME_REMAINING] != FfxFloat32(0.0f)) { + state.WasLockedPrevFrame = true; + } + + return state; +} + +#endif //!defined( FFX_FSR2_REPROJECT_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h.meta new file mode 100644 index 0000000..d99b60e --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_reproject.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: c888fea71de852c468ab21c525ce2320 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h new file mode 100644 index 0000000..a597c5a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h @@ -0,0 +1,106 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_RESOURCES_H +#define FFX_FSR2_RESOURCES_H + +#if defined(FFX_CPU) || defined(FFX_GPU) +#define FFX_FSR2_RESOURCE_IDENTIFIER_NULL 0 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_OPAQUE_ONLY 1 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_COLOR 2 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 3 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_DEPTH 4 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_EXPOSURE 5 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_REACTIVE_MASK 6 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_TRANSPARENCY_AND_COMPOSITION_MASK 7 +#define FFX_FSR2_RESOURCE_IDENTIFIER_RECONSTRUCTED_PREVIOUS_NEAREST_DEPTH 8 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DILATED_MOTION_VECTORS 9 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DILATED_DEPTH 10 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR 11 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS 12 +#define FFX_FSR2_RESOURCE_IDENTIFIER_NEW_LOCKS 13 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREPARED_INPUT_COLOR 14 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LUMA_HISTORY 15 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DEBUG_OUTPUT 16 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LANCZOS_LUT 17 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SPD_ATOMIC_COUNT 18 +#define FFX_FSR2_RESOURCE_IDENTIFIER_UPSCALED_OUTPUT 19 +#define FFX_FSR2_RESOURCE_IDENTIFIER_RCAS_INPUT 20 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS_1 21 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS_2 22 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_1 23 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_2 24 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_REACTIVITY 25 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_TRANSPARENCY_AND_COMPOSITION 26 +#define FFX_FSR2_RESOURCE_IDENTITIER_UPSAMPLE_MAXIMUM_BIAS_LUT 27 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DILATED_REACTIVE_MASKS 28 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE 29 // same as FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 29 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_1 30 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_2 31 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_3 32 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 33 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_5 34 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_6 35 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_7 36 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_8 37 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_9 38 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_10 39 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_11 40 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12 41 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_EXPOSURE 42 +#define FFX_FSR2_RESOURCE_IDENTIFIER_AUTO_EXPOSURE 43 +#define FFX_FSR2_RESOURCE_IDENTIFIER_AUTOREACTIVE 44 +#define FFX_FSR2_RESOURCE_IDENTIFIER_AUTOCOMPOSITION 45 + +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR 46 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR 47 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_1 48 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_1 49 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_2 50 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_2 51 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREVIOUS_DILATED_MOTION_VECTORS 52 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DILATED_MOTION_VECTORS_1 53 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DILATED_MOTION_VECTORS_2 54 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LUMA_HISTORY_1 55 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LUMA_HISTORY_2 56 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_INPUT_LUMA 57 + +// Shading change detection mip level setting, value must be in the range [FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0, FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12] +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 +#define FFX_FSR2_SHADING_CHANGE_MIP_LEVEL (FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE - FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE) + +#define FFX_FSR2_RESOURCE_IDENTIFIER_COUNT 58 + +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_FSR2 0 +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_SPD 1 +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_RCAS 2 +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_GENREACTIVE 3 + +#define FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_TONEMAP 1 +#define FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP 2 +#define FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_THRESHOLD 4 +#define FFX_FSR2_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX 8 + +#endif // #if defined(FFX_CPU) || defined(FFX_GPU) + +#endif //!defined( FFX_FSR2_RESOURCES_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h.meta new file mode 100644 index 0000000..4269f87 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_resources.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: af595ef438ac2b4469f50e6f92485a6c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h new file mode 100644 index 0000000..b75f090 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h @@ -0,0 +1,606 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_SAMPLE_H +#define FFX_FSR2_SAMPLE_H + +// suppress warnings +#ifdef FFX_HLSL +#pragma warning(disable: 4008) // potentially divide by zero +#endif //FFX_HLSL + +struct FetchedBilinearSamples { + + FfxFloat32x4 fColor00; + FfxFloat32x4 fColor10; + + FfxFloat32x4 fColor01; + FfxFloat32x4 fColor11; +}; + +struct FetchedBicubicSamples { + + FfxFloat32x4 fColor00; + FfxFloat32x4 fColor10; + FfxFloat32x4 fColor20; + FfxFloat32x4 fColor30; + + FfxFloat32x4 fColor01; + FfxFloat32x4 fColor11; + FfxFloat32x4 fColor21; + FfxFloat32x4 fColor31; + + FfxFloat32x4 fColor02; + FfxFloat32x4 fColor12; + FfxFloat32x4 fColor22; + FfxFloat32x4 fColor32; + + FfxFloat32x4 fColor03; + FfxFloat32x4 fColor13; + FfxFloat32x4 fColor23; + FfxFloat32x4 fColor33; +}; + +#if FFX_HALF +struct FetchedBilinearSamplesMin16 { + + FFX_MIN16_F4 fColor00; + FFX_MIN16_F4 fColor10; + + FFX_MIN16_F4 fColor01; + FFX_MIN16_F4 fColor11; +}; + +struct FetchedBicubicSamplesMin16 { + + FFX_MIN16_F4 fColor00; + FFX_MIN16_F4 fColor10; + FFX_MIN16_F4 fColor20; + FFX_MIN16_F4 fColor30; + + FFX_MIN16_F4 fColor01; + FFX_MIN16_F4 fColor11; + FFX_MIN16_F4 fColor21; + FFX_MIN16_F4 fColor31; + + FFX_MIN16_F4 fColor02; + FFX_MIN16_F4 fColor12; + FFX_MIN16_F4 fColor22; + FFX_MIN16_F4 fColor32; + + FFX_MIN16_F4 fColor03; + FFX_MIN16_F4 fColor13; + FFX_MIN16_F4 fColor23; + FFX_MIN16_F4 fColor33; +}; +#else //FFX_HALF +#define FetchedBicubicSamplesMin16 FetchedBicubicSamples +#define FetchedBilinearSamplesMin16 FetchedBilinearSamples +#endif //FFX_HALF + +FfxFloat32x4 Linear(FfxFloat32x4 A, FfxFloat32x4 B, FfxFloat32 t) +{ + return A + (B - A) * t; +} + +FfxFloat32x4 Bilinear(FetchedBilinearSamples BilinearSamples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); + FfxFloat32x4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); + FfxFloat32x4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Linear(FFX_MIN16_F4 A, FFX_MIN16_F4 B, FFX_MIN16_F t) +{ + return A + (B - A) * t; +} + +FFX_MIN16_F4 Bilinear(FetchedBilinearSamplesMin16 BilinearSamples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); + return fColorXY; +} +#endif + +FfxFloat32 Lanczos2NoClamp(FfxFloat32 x) +{ + const FfxFloat32 PI = 3.141592653589793f; // TODO: share SDK constants + return abs(x) < FSR2_EPSILON ? 1.f : (sin(PI * x) / (PI * x)) * (sin(0.5f * PI * x) / (0.5f * PI * x)); +} + +FfxFloat32 Lanczos2(FfxFloat32 x) +{ + x = ffxMin(abs(x), 2.0f); + return Lanczos2NoClamp(x); +} + +#if FFX_HALF + +#if 0 +FFX_MIN16_F Lanczos2NoClamp(FFX_MIN16_F x) +{ + const FFX_MIN16_F PI = FFX_MIN16_F(3.141592653589793f); // TODO: share SDK constants + return abs(x) < FFX_MIN16_F(FSR2_EPSILON) ? FFX_MIN16_F(1.f) : (sin(PI * x) / (PI * x)) * (sin(FFX_MIN16_F(0.5f) * PI * x) / (FFX_MIN16_F(0.5f) * PI * x)); +} +#endif + +FFX_MIN16_F Lanczos2(FFX_MIN16_F x) +{ + x = ffxMin(abs(x), FFX_MIN16_F(2.0f)); + return FFX_MIN16_F(Lanczos2NoClamp(x)); +} +#endif //FFX_HALF + +// FSR1 lanczos approximation. Input is x*x and must be <= 4. +FfxFloat32 Lanczos2ApproxSqNoClamp(FfxFloat32 x2) +{ + FfxFloat32 a = (2.0f / 5.0f) * x2 - 1; + FfxFloat32 b = (1.0f / 4.0f) * x2 - 1; + return ((25.0f / 16.0f) * a * a - (25.0f / 16.0f - 1)) * (b * b); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2ApproxSqNoClamp(FFX_MIN16_F x2) +{ + FFX_MIN16_F a = FFX_MIN16_F(2.0f / 5.0f) * x2 - FFX_MIN16_F(1); + FFX_MIN16_F b = FFX_MIN16_F(1.0f / 4.0f) * x2 - FFX_MIN16_F(1); + return (FFX_MIN16_F(25.0f / 16.0f) * a * a - FFX_MIN16_F(25.0f / 16.0f - 1)) * (b * b); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2ApproxSq(FfxFloat32 x2) +{ + x2 = ffxMin(x2, 4.0f); + return Lanczos2ApproxSqNoClamp(x2); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2ApproxSq(FFX_MIN16_F x2) +{ + x2 = ffxMin(x2, FFX_MIN16_F(4.0f)); + return Lanczos2ApproxSqNoClamp(x2); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2ApproxNoClamp(FfxFloat32 x) +{ + return Lanczos2ApproxSqNoClamp(x * x); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2ApproxNoClamp(FFX_MIN16_F x) +{ + return Lanczos2ApproxSqNoClamp(x * x); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2Approx(FfxFloat32 x) +{ + return Lanczos2ApproxSq(x * x); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2Approx(FFX_MIN16_F x) +{ + return Lanczos2ApproxSq(x * x); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2_UseLUT(FfxFloat32 x) +{ + return SampleLanczos2Weight(abs(x)); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2_UseLUT(FFX_MIN16_F x) +{ + return FFX_MIN16_F(SampleLanczos2Weight(abs(x))); +} +#endif //FFX_HALF + +FfxFloat32x4 Lanczos2_UseLUT(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) +{ + FfxFloat32 fWeight0 = Lanczos2_UseLUT(-1.f - t); + FfxFloat32 fWeight1 = Lanczos2_UseLUT(-0.f - t); + FfxFloat32 fWeight2 = Lanczos2_UseLUT(+1.f - t); + FfxFloat32 fWeight3 = Lanczos2_UseLUT(+2.f - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} +#if FFX_HALF +FFX_MIN16_F4 Lanczos2_UseLUT(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) +{ + FFX_MIN16_F fWeight0 = Lanczos2_UseLUT(FFX_MIN16_F(-1.f) - t); + FFX_MIN16_F fWeight1 = Lanczos2_UseLUT(FFX_MIN16_F(-0.f) - t); + FFX_MIN16_F fWeight2 = Lanczos2_UseLUT(FFX_MIN16_F(+1.f) - t); + FFX_MIN16_F fWeight3 = Lanczos2_UseLUT(FFX_MIN16_F(+2.f) - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} +#endif + +FfxFloat32x4 Lanczos2(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) +{ + FfxFloat32 fWeight0 = Lanczos2(-1.f - t); + FfxFloat32 fWeight1 = Lanczos2(-0.f - t); + FfxFloat32 fWeight2 = Lanczos2(+1.f - t); + FfxFloat32 fWeight3 = Lanczos2(+2.f - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} + +FfxFloat32x4 Lanczos2(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FfxFloat32x4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FfxFloat32x4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FfxFloat32x4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FfxFloat32x4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FfxFloat32x4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; + FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { + + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) +{ + FFX_MIN16_F fWeight0 = Lanczos2(FFX_MIN16_F(-1.f) - t); + FFX_MIN16_F fWeight1 = Lanczos2(FFX_MIN16_F(-0.f) - t); + FFX_MIN16_F fWeight2 = Lanczos2(FFX_MIN16_F(+1.f) - t); + FFX_MIN16_F fWeight3 = Lanczos2(FFX_MIN16_F(+2.f) - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} + +FFX_MIN16_F4 Lanczos2(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FFX_MIN16_F4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FFX_MIN16_F4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FFX_MIN16_F4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; + FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} +#endif //FFX_HALF + + +FfxFloat32x4 Lanczos2LUT(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FfxFloat32x4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FfxFloat32x4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FfxFloat32x4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FfxFloat32x4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FfxFloat32x4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; + FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { + + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2LUT(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FFX_MIN16_F4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FFX_MIN16_F4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FFX_MIN16_F4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; + FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} +#endif //FFX_HALF + + + +FfxFloat32x4 Lanczos2Approx(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) +{ + FfxFloat32 fWeight0 = Lanczos2ApproxNoClamp(-1.f - t); + FfxFloat32 fWeight1 = Lanczos2ApproxNoClamp(-0.f - t); + FfxFloat32 fWeight2 = Lanczos2ApproxNoClamp(+1.f - t); + FfxFloat32 fWeight3 = Lanczos2ApproxNoClamp(+2.f - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2Approx(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) +{ + FFX_MIN16_F fWeight0 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-1.f) - t); + FFX_MIN16_F fWeight1 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-0.f) - t); + FFX_MIN16_F fWeight2 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+1.f) - t); + FFX_MIN16_F fWeight3 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+2.f) - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} +#endif //FFX_HALF + +FfxFloat32x4 Lanczos2Approx(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FfxFloat32x4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FfxFloat32x4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FfxFloat32x4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FfxFloat32x4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FfxFloat32x4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; + FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2Approx(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FFX_MIN16_F4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FFX_MIN16_F4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FFX_MIN16_F4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; + FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} +#endif + +// Clamp by offset direction. Assuming iPxSample is already in range and iPxOffset is compile time constant. +FfxInt32x2 ClampCoord(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) +{ + FfxInt32x2 result = iPxSample + iPxOffset; + result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; + result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; + result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; + result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; + return result; +} +#if FFX_HALF +FFX_MIN16_I2 ClampCoord(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) +{ + FFX_MIN16_I2 result = iPxSample + iPxOffset; + result.x = (iPxOffset.x < FFX_MIN16_I(0)) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; + result.x = (iPxOffset.x > FFX_MIN16_I(0)) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; + result.y = (iPxOffset.y < FFX_MIN16_I(0)) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; + result.y = (iPxOffset.y > FFX_MIN16_I(0)) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; + return result; +} +#endif //FFX_HALF + + +#define DeclareCustomFetchBicubicSamplesWithType(SampleType, TextureType, AddrType, Name, LoadTexture) \ + SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ + { \ + SampleType Samples; \ + \ + Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, -1), iTextureSize))); \ + Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, -1), iTextureSize))); \ + Samples.fColor20 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, -1), iTextureSize))); \ + Samples.fColor30 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, -1), iTextureSize))); \ + \ + Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +0), iTextureSize))); \ + Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ + Samples.fColor21 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ + Samples.fColor31 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +0), iTextureSize))); \ + \ + Samples.fColor02 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +1), iTextureSize))); \ + Samples.fColor12 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ + Samples.fColor22 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ + Samples.fColor32 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +1), iTextureSize))); \ + \ + Samples.fColor03 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +2), iTextureSize))); \ + Samples.fColor13 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +2), iTextureSize))); \ + Samples.fColor23 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +2), iTextureSize))); \ + Samples.fColor33 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +2), iTextureSize))); \ + \ + return Samples; \ + } + +#define DeclareCustomFetchBicubicSamples(Name, LoadTexture) \ + DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) + +#define DeclareCustomFetchBicubicSamplesMin16(Name, LoadTexture) \ + DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) + +#define DeclareCustomFetchBilinearSamplesWithType(SampleType, TextureType,AddrType, Name, LoadTexture) \ + SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ + { \ + SampleType Samples; \ + Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ + Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ + Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ + Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ + return Samples; \ + } + +#define DeclareCustomFetchBilinearSamples(Name, LoadTexture) \ + DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) + +#define DeclareCustomFetchBilinearSamplesMin16(Name, LoadTexture) \ + DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) + +// BE CAREFUL: there is some precision issues and (3253, 125) leading to (3252.9989778, 125.001102) +// is common, so iPxSample can "jitter" +#define DeclareCustomTextureSample(Name, InterpolateSamples, FetchSamples) \ + FfxFloat32x4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ + { \ + FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ + /* Clamp base coords */ \ + fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ + fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ + /* */ \ + FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ + FfxFloat32x2 fPxFrac = ffxFract(fPxSample); \ + FfxFloat32x4 fColorXY = FfxFloat32x4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ + return fColorXY; \ + } + +#define DeclareCustomTextureSampleMin16(Name, InterpolateSamples, FetchSamples) \ + FFX_MIN16_F4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ + { \ + FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ + /* Clamp base coords */ \ + fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ + fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ + /* */ \ + FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ + FFX_MIN16_F2 fPxFrac = FFX_MIN16_F2(ffxFract(fPxSample)); \ + FFX_MIN16_F4 fColorXY = FFX_MIN16_F4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ + return fColorXY; \ + } + +#define FFX_FSR2_CONCAT_ID(x, y) x ## y +#define FFX_FSR2_CONCAT(x, y) FFX_FSR2_CONCAT_ID(x, y) +#define FFX_FSR2_SAMPLER_1D_0 Lanczos2 +#define FFX_FSR2_SAMPLER_1D_1 Lanczos2LUT +#define FFX_FSR2_SAMPLER_1D_2 Lanczos2Approx + +#define FFX_FSR2_GET_LANCZOS_SAMPLER1D(x) FFX_FSR2_CONCAT(FFX_FSR2_SAMPLER_1D_, x) + +#endif //!defined( FFX_FSR2_SAMPLE_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h.meta new file mode 100644 index 0000000..5e24564 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_sample.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 4468441a6f8d1a54c95a33bca10569a2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h new file mode 100644 index 0000000..10970ef --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h @@ -0,0 +1,251 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define USE_YCOCG 1 + +#define fAutogenEpsilon 0.01f + +// EXPERIMENTAL + +FFX_MIN16_F ComputeAutoTC_01(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) +{ + FfxFloat32x3 colorPreAlpha = LoadOpaqueOnly(uDispatchThreadId); + FfxFloat32x3 colorPostAlpha = LoadInputColor(uDispatchThreadId); + FfxFloat32x3 colorPrevPreAlpha = LoadPrevPreAlpha(iPrevIdx); + FfxFloat32x3 colorPrevPostAlpha = LoadPrevPostAlpha(iPrevIdx); + +#if USE_YCOCG + colorPreAlpha = RGBToYCoCg(colorPreAlpha); + colorPostAlpha = RGBToYCoCg(colorPostAlpha); + colorPrevPreAlpha = RGBToYCoCg(colorPrevPreAlpha); + colorPrevPostAlpha = RGBToYCoCg(colorPrevPostAlpha); +#endif + + FfxFloat32x3 colorDeltaCurr = colorPostAlpha - colorPreAlpha; + FfxFloat32x3 colorDeltaPrev = colorPrevPostAlpha - colorPrevPreAlpha; + bool hasAlpha = any(FFX_GREATER_THAN(abs(colorDeltaCurr), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); + bool hadAlpha = any(FFX_GREATER_THAN(abs(colorDeltaPrev), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); + + FfxFloat32x3 X = colorPreAlpha; + FfxFloat32x3 Y = colorPostAlpha; + FfxFloat32x3 Z = colorPrevPreAlpha; + FfxFloat32x3 W = colorPrevPostAlpha; + + FFX_MIN16_F retVal = FFX_MIN16_F(ffxSaturate(dot(abs(abs(Y - X) - abs(W - Z)), FfxFloat32x3(1, 1, 1)))); + + // cleanup very small values + retVal = (retVal < TcThreshold()) ? FFX_MIN16_F(0.0f) : FFX_MIN16_F(1.f); + + return retVal; +} + +// works ok: thin edges +FFX_MIN16_F ComputeAutoTC_02(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) +{ + FfxFloat32x3 colorPreAlpha = LoadOpaqueOnly(uDispatchThreadId); + FfxFloat32x3 colorPostAlpha = LoadInputColor(uDispatchThreadId); + FfxFloat32x3 colorPrevPreAlpha = LoadPrevPreAlpha(iPrevIdx); + FfxFloat32x3 colorPrevPostAlpha = LoadPrevPostAlpha(iPrevIdx); + +#if USE_YCOCG + colorPreAlpha = RGBToYCoCg(colorPreAlpha); + colorPostAlpha = RGBToYCoCg(colorPostAlpha); + colorPrevPreAlpha = RGBToYCoCg(colorPrevPreAlpha); + colorPrevPostAlpha = RGBToYCoCg(colorPrevPostAlpha); +#endif + + FfxFloat32x3 colorDelta = colorPostAlpha - colorPreAlpha; + FfxFloat32x3 colorPrevDelta = colorPrevPostAlpha - colorPrevPreAlpha; + bool hasAlpha = any(FFX_GREATER_THAN(abs(colorDelta), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); + bool hadAlpha = any(FFX_GREATER_THAN(abs(colorPrevDelta), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); + + FfxFloat32x3 delta = colorPostAlpha - colorPreAlpha; //prev+1*d = post => d = color, alpha = + FfxFloat32x3 deltaPrev = colorPrevPostAlpha - colorPrevPreAlpha; + + FfxFloat32x3 X = colorPrevPreAlpha; + FfxFloat32x3 N = colorPreAlpha - colorPrevPreAlpha; + FfxFloat32x3 YAminusXA = colorPrevPostAlpha - colorPrevPreAlpha; + FfxFloat32x3 NminusNA = colorPostAlpha - colorPrevPostAlpha; + + FfxFloat32x3 A = (hasAlpha || hadAlpha) ? NminusNA / max(FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon), N) : FfxFloat32x3(0, 0, 0); + + FFX_MIN16_F retVal = FFX_MIN16_F( max(max(A.x, A.y), A.z) ); + + // only pixels that have significantly changed in color shuold be considered + retVal = ffxSaturate(retVal * FFX_MIN16_F(length(colorPostAlpha - colorPrevPostAlpha)) ); + + return retVal; +} + +// This function computes the TransparencyAndComposition mask: +// This mask indicates pixels that should discard locks and apply color clamping. +// +// Typically this is the case for translucent pixels (that don't write depth values) or pixels where the correctness of +// the MVs can not be guaranteed (e.g. procedutal movement or vegetation that does not have MVs to reduce the cost during rasterization) +// Also, large changes in color due to changed lighting should be marked to remove locks on pixels with "old" lighting. +// +// This function takes a opaque only and a final texture and uses internal copies of those textures from the last frame. +// The function tries to determine where the color changes between opaque only and final image to determine the pixels that use transparency. +// Also it uses the previous frames and detects where the use of transparency changed to mark those pixels. +// Additionally it marks pixels where the color changed significantly in the opaque only image, e.g. due to lighting or texture animation. +// +// In the final step it stores the current textures in internal textures for the next frame + +FFX_MIN16_F ComputeTransparencyAndComposition(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) +{ + FFX_MIN16_F retVal = ComputeAutoTC_02(uDispatchThreadId, iPrevIdx); + + // [branch] + if (retVal > FFX_MIN16_F(0.01f)) + { + retVal = ComputeAutoTC_01(uDispatchThreadId, iPrevIdx); + } + return retVal; +} + +float computeSolidEdge(FFX_MIN16_I2 curPos, FFX_MIN16_I2 prevPos) +{ + float lum[9]; + int i = 0; + for (int y = -1; y < 2; ++y) + { + for (int x = -1; x < 2; ++x) + { + FfxFloat32x3 curCol = LoadOpaqueOnly(curPos + FFX_MIN16_I2(x, y)).rgb; + FfxFloat32x3 prevCol = LoadPrevPreAlpha(prevPos + FFX_MIN16_I2(x, y)).rgb; + lum[i++] = length(curCol - prevCol); + } + } + + //float gradX = abs(lum[3] - lum[4]) + abs(lum[5] - lum[4]); + //float gradY = abs(lum[1] - lum[4]) + abs(lum[7] - lum[4]); + + //return sqrt(gradX * gradX + gradY * gradY); + + float gradX = abs(lum[3] - lum[4]) * abs(lum[5] - lum[4]); + float gradY = abs(lum[1] - lum[4]) * abs(lum[7] - lum[4]); + + return sqrt(sqrt(gradX * gradY)); +} + +float computeAlphaEdge(FFX_MIN16_I2 curPos, FFX_MIN16_I2 prevPos) +{ + float lum[9]; + int i = 0; + for (int y = -1; y < 2; ++y) + { + for (int x = -1; x < 2; ++x) + { + FfxFloat32x3 curCol = abs(LoadInputColor(curPos + FFX_MIN16_I2(x, y)).rgb - LoadOpaqueOnly(curPos + FFX_MIN16_I2(x, y)).rgb); + FfxFloat32x3 prevCol = abs(LoadPrevPostAlpha(prevPos + FFX_MIN16_I2(x, y)).rgb - LoadPrevPreAlpha(prevPos + FFX_MIN16_I2(x, y)).rgb); + lum[i++] = length(curCol - prevCol); + } + } + + //float gradX = abs(lum[3] - lum[4]) + abs(lum[5] - lum[4]); + //float gradY = abs(lum[1] - lum[4]) + abs(lum[7] - lum[4]); + + //return sqrt(gradX * gradX + gradY * gradY); + + float gradX = abs(lum[3] - lum[4]) * abs(lum[5] - lum[4]); + float gradY = abs(lum[1] - lum[4]) * abs(lum[7] - lum[4]); + + return sqrt(sqrt(gradX * gradY)); +} + +FFX_MIN16_F ComputeAabbOverlap(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) +{ + FFX_MIN16_F retVal = FFX_MIN16_F(0.f); + + FfxFloat32x2 fMotionVector = LoadInputMotionVector(uDispatchThreadId); + FfxFloat32x3 colorPreAlpha = LoadOpaqueOnly(uDispatchThreadId); + FfxFloat32x3 colorPostAlpha = LoadInputColor(uDispatchThreadId); + FfxFloat32x3 colorPrevPreAlpha = LoadPrevPreAlpha(iPrevIdx); + FfxFloat32x3 colorPrevPostAlpha = LoadPrevPostAlpha(iPrevIdx); + +#if USE_YCOCG + colorPreAlpha = RGBToYCoCg(colorPreAlpha); + colorPostAlpha = RGBToYCoCg(colorPostAlpha); + colorPrevPreAlpha = RGBToYCoCg(colorPrevPreAlpha); + colorPrevPostAlpha = RGBToYCoCg(colorPrevPostAlpha); +#endif + FfxFloat32x3 minPrev = FFX_MIN16_F3(+1000.f, +1000.f, +1000.f); + FfxFloat32x3 maxPrev = FFX_MIN16_F3(-1000.f, -1000.f, -1000.f); + for (int y = -1; y < 2; ++y) + { + for (int x = -1; x < 2; ++x) + { + FfxFloat32x3 W = LoadPrevPostAlpha(iPrevIdx + FFX_MIN16_I2(x, y)); + +#if USE_YCOCG + W = RGBToYCoCg(W); +#endif + minPrev = min(minPrev, W); + maxPrev = max(maxPrev, W); + } + } + // instead of computing the overlap: simply count how many samples are outside + // set reactive based on that + FFX_MIN16_F count = FFX_MIN16_F(0.f); + for (int y = -1; y < 2; ++y) + { + for (int x = -1; x < 2; ++x) + { + FfxFloat32x3 Y = LoadInputColor(uDispatchThreadId + FFX_MIN16_I2(x, y)); + +#if USE_YCOCG + Y = RGBToYCoCg(Y); +#endif + count += ((Y.x < minPrev.x) || (Y.x > maxPrev.x)) ? FFX_MIN16_F(1.f) : FFX_MIN16_F(0.f); + count += ((Y.y < minPrev.y) || (Y.y > maxPrev.y)) ? FFX_MIN16_F(1.f) : FFX_MIN16_F(0.f); + count += ((Y.z < minPrev.z) || (Y.z > maxPrev.z)) ? FFX_MIN16_F(1.f) : FFX_MIN16_F(0.f); + } + } + retVal = count / FFX_MIN16_F(27.f); + + return retVal; +} + + +// This function computes the Reactive mask: +// We want pixels marked where the alpha portion of the frame changes a lot between neighbours +// Those pixels are expected to change quickly between frames, too. (e.g. small particles, reflections on curved surfaces...) +// As a result history would not be trustworthy. +// On the other hand we don't want pixels marked where pre-alpha has a large differnce, since those would profit from accumulation +// For mirrors we may assume the pre-alpha is pretty uniform color. +// +// This works well generally, but also marks edge pixels +FFX_MIN16_F ComputeReactive(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) +{ + // we only get here if alpha has a significant contribution and has changed since last frame. + FFX_MIN16_F retVal = FFX_MIN16_F(0.f); + + // mark pixels with huge variance in alpha as reactive + FFX_MIN16_F alphaEdge = FFX_MIN16_F(computeAlphaEdge(uDispatchThreadId, iPrevIdx)); + FFX_MIN16_F opaqueEdge = FFX_MIN16_F(computeSolidEdge(uDispatchThreadId, iPrevIdx)); + retVal = ffxSaturate(alphaEdge - opaqueEdge); + + // the above also marks edge pixels due to jitter, so we need to cancel those out + + + return retVal; +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h.meta new file mode 100644 index 0000000..2195de0 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_tcr_autogen.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 8b62ba5e9686a6545b8ed6a8786322d2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h new file mode 100644 index 0000000..9287185 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h @@ -0,0 +1,191 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR2_UPSAMPLE_H +#define FFX_FSR2_UPSAMPLE_H + +FFX_STATIC const FfxUInt32 iLanczos2SampleCount = 16; + +void Deringing(RectificationBox clippingBox, FFX_PARAMETER_INOUT FfxFloat32x3 fColor) +{ + fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); +} +#if FFX_HALF +void Deringing(RectificationBoxMin16 clippingBox, FFX_PARAMETER_INOUT FFX_MIN16_F3 fColor) +{ + fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); +} +#endif + +#ifndef FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE +#define FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE 2 // Approximate +#endif + +FfxFloat32 GetUpsampleLanczosWeight(FfxFloat32x2 fSrcSampleOffset, FfxFloat32 fKernelWeight) +{ + FfxFloat32x2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; +#if FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE + FfxFloat32 fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); +#elif FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT + FfxFloat32 fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); +#elif FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE + FfxFloat32 fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); +#else +#error "Invalid Lanczos type" +#endif + return fSampleWeight; +} + +#if FFX_HALF +FFX_MIN16_F GetUpsampleLanczosWeight(FFX_MIN16_F2 fSrcSampleOffset, FFX_MIN16_F fKernelWeight) +{ + FFX_MIN16_F2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; +#if FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE + FFX_MIN16_F fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); +#elif FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT + FFX_MIN16_F fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); +#elif FFX_FSR2_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE + FFX_MIN16_F fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); + + // To Test: Save reciproqual sqrt compute + // FfxFloat32 fSampleWeight = Lanczos2Sq_UseLUT(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); +#else +#error "Invalid Lanczos type" +#endif + return fSampleWeight; +} +#endif + +FfxFloat32 ComputeMaxKernelWeight() { + const FfxFloat32 fKernelSizeBias = 1.0f; + + FfxFloat32 fKernelWeight = FfxFloat32(1) + (FfxFloat32(1.0f) / FfxFloat32x2(DownscaleFactor()) - FfxFloat32(1)).x * FfxFloat32(fKernelSizeBias); + + return ffxMin(FfxFloat32(1.99f), fKernelWeight); +} + +FfxFloat32x4 ComputeUpsampledColorAndWeight(const AccumulationPassCommonParams params, + FFX_PARAMETER_INOUT RectificationBox clippingBox, FfxFloat32 fReactiveFactor) +{ + #if FFX_FSR2_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF + #include "ffx_fsr2_force16_begin.h" + #endif + // We compute a sliced lanczos filter with 2 lobes (other slices are accumulated temporaly) + FfxFloat32x2 fDstOutputPos = FfxFloat32x2(params.iPxHrPos) + FFX_BROADCAST_FLOAT32X2(0.5f); // Destination resolution output pixel center position + FfxFloat32x2 fSrcOutputPos = fDstOutputPos * DownscaleFactor(); // Source resolution output pixel center position + FfxInt32x2 iSrcInputPos = FfxInt32x2(floor(fSrcOutputPos)); // TODO: what about weird upscale factors... + + #if FFX_FSR2_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF + #include "ffx_fsr2_force16_end.h" + #endif + + FfxFloat32x3 fSamples[iLanczos2SampleCount]; + + FfxFloat32x2 fSrcUnjitteredPos = (FfxFloat32x2(iSrcInputPos) + FfxFloat32x2(0.5f, 0.5f)) - Jitter(); // This is the un-jittered position of the sample at offset 0,0 + + FfxInt32x2 offsetTL; + offsetTL.x = (fSrcUnjitteredPos.x > fSrcOutputPos.x) ? FfxInt32(-2) : FfxInt32(-1); + offsetTL.y = (fSrcUnjitteredPos.y > fSrcOutputPos.y) ? FfxInt32(-2) : FfxInt32(-1); + + //Load samples + // If fSrcUnjitteredPos.y > fSrcOutputPos.y, indicates offsetTL.y = -2, sample offset Y will be [-2, 1], clipbox will be rows [1, 3]. + // Flip row# for sampling offset in this case, so first 0~2 rows in the sampled array can always be used for computing the clipbox. + // This reduces branch or cmove on sampled colors, but moving this overhead to sample position / weight calculation time which apply to less values. + const FfxBoolean bFlipRow = fSrcUnjitteredPos.y > fSrcOutputPos.y; + const FfxBoolean bFlipCol = fSrcUnjitteredPos.x > fSrcOutputPos.x; + + FfxFloat32x2 fOffsetTL = FfxFloat32x2(offsetTL); + + FFX_UNROLL + for (FfxInt32 row = 0; row < 3; row++) { + + FFX_UNROLL + for (FfxInt32 col = 0; col < 3; col++) { + FfxInt32 iSampleIndex = col + (row << 2); + + FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); + FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + offsetTL + sampleColRow; + + const FfxInt32x2 sampleCoord = ClampLoad(iSrcSamplePos, FfxInt32x2(0, 0), FfxInt32x2(RenderSize())); + + fSamples[iSampleIndex] = LoadPreparedInputColor(FfxInt32x2(sampleCoord)); + } + } + + FfxFloat32x4 fColorAndWeight = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f); + + FfxFloat32x2 fBaseSampleOffset = FfxFloat32x2(fSrcUnjitteredPos - fSrcOutputPos); + + // Identify how much of each upsampled color to be used for this frame + const FfxFloat32 fKernelReactiveFactor = ffxMax(fReactiveFactor, FfxFloat32(params.bIsNewSample)); + const FfxFloat32 fKernelBiasMax = ComputeMaxKernelWeight() * (1.0f - fKernelReactiveFactor); + + const FfxFloat32 fKernelBiasMin = ffxMax(1.0f, ((1.0f + fKernelBiasMax) * 0.3f)); + const FfxFloat32 fKernelBiasFactor = ffxMax(0.0f, ffxMax(0.25f * params.fDepthClipFactor, fKernelReactiveFactor)); + const FfxFloat32 fKernelBias = ffxLerp(fKernelBiasMax, fKernelBiasMin, fKernelBiasFactor); + + const FfxFloat32 fRectificationCurveBias = ffxLerp(-2.0f, -3.0f, ffxSaturate(params.fHrVelocity / 50.0f)); + + FFX_UNROLL + for (FfxInt32 row = 0; row < 3; row++) { + FFX_UNROLL + for (FfxInt32 col = 0; col < 3; col++) { + FfxInt32 iSampleIndex = col + (row << 2); + + const FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); + const FfxFloat32x2 fOffset = fOffsetTL + FfxFloat32x2(sampleColRow); + FfxFloat32x2 fSrcSampleOffset = fBaseSampleOffset + fOffset; + + FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + FfxInt32x2(offsetTL) + sampleColRow; + + const FfxFloat32 fOnScreenFactor = FfxFloat32(IsOnScreen(FfxInt32x2(iSrcSamplePos), FfxInt32x2(RenderSize()))); + FfxFloat32 fSampleWeight = fOnScreenFactor * FfxFloat32(GetUpsampleLanczosWeight(fSrcSampleOffset, fKernelBias)); + + fColorAndWeight += FfxFloat32x4(fSamples[iSampleIndex] * fSampleWeight, fSampleWeight); + + // Update rectification box + { + const FfxFloat32 fSrcSampleOffsetSq = dot(fSrcSampleOffset, fSrcSampleOffset); + const FfxFloat32 fBoxSampleWeight = exp(fRectificationCurveBias * fSrcSampleOffsetSq); + + const FfxBoolean bInitialSample = (row == 0) && (col == 0); + RectificationBoxAddSample(bInitialSample, clippingBox, fSamples[iSampleIndex], fBoxSampleWeight); + } + } + } + + RectificationBoxComputeVarianceBoxData(clippingBox); + + fColorAndWeight.w *= FfxFloat32(fColorAndWeight.w > FSR2_EPSILON); + + if (fColorAndWeight.w > FSR2_EPSILON) { + // Normalize for deringing (we need to compare colors) + fColorAndWeight.xyz = fColorAndWeight.xyz / fColorAndWeight.w; + fColorAndWeight.w *= fUpsampleLanczosWeightScale; + + Deringing(clippingBox, fColorAndWeight.xyz); + } + + return fColorAndWeight; +} + +#endif //!defined( FFX_FSR2_UPSAMPLE_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h.meta new file mode 100644 index 0000000..5c0e011 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr2/ffx_fsr2_upsample.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 02358daf003aa3d408e0922790429182 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler.meta new file mode 100644 index 0000000..b538189 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2c8d127ec4826a54cae3fe1e7e2c17b8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h new file mode 100644 index 0000000..766cba3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h @@ -0,0 +1,172 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +void Accumulate(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) +{ + // Avoid invalid values when accumulation and upsampled weight is 0 + data.fHistoryWeight *= FfxFloat32(data.fHistoryWeight > FSR3UPSCALER_FP16_MIN); + data.fHistoryWeight = ffxMax(FSR3UPSCALER_EPSILON, data.fHistoryWeight + data.fUpsampledWeight); + +#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT + //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation) + data.fUpsampledColor = RGBToYCoCg(Tonemap(YCoCgToRGB(data.fUpsampledColor))); + data.fHistoryColor = RGBToYCoCg(Tonemap(YCoCgToRGB(data.fHistoryColor))); +#endif + + const FfxFloat32 fAlpha = ffxSaturate(data.fUpsampledWeight / data.fHistoryWeight); + data.fHistoryColor = ffxLerp(data.fHistoryColor, data.fUpsampledColor, fAlpha); + data.fHistoryColor = YCoCgToRGB(data.fHistoryColor); + +#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT + data.fHistoryColor = InverseTonemap(data.fHistoryColor); +#endif +} + +void RectifyHistory( + const AccumulationPassCommonParams params, + FFX_PARAMETER_INOUT AccumulationPassData data +) +{ + const FfxFloat32 fVecolityFactor = ffxSaturate(params.f4KVelocity / 20.0f); + const FfxFloat32 fDistanceFactor = ffxSaturate(0.75f - params.fFarthestDepthInMeters / 20.0f); + const FfxFloat32 fAccumulationFactor = 1.0f - params.fAccumulation; + const FfxFloat32 fReactiveFactor = ffxPow(params.fReactiveMask, 1.0f / 2.0f); + const FfxFloat32 fShadingChangeFactor = params.fShadingChange; + const FfxFloat32 fBoxScaleT = ffxMax(fVecolityFactor, ffxMax(fDistanceFactor, ffxMax(fAccumulationFactor, ffxMax(fReactiveFactor, fShadingChangeFactor)))); + + const FfxFloat32 fBoxScale = ffxLerp(3.0f, 1.0f, fBoxScaleT); + + const FfxFloat32x3 fScaledBoxVec = data.clippingBox.boxVec * fBoxScale; + const FfxFloat32x3 fBoxMin = data.clippingBox.boxCenter - fScaledBoxVec; + const FfxFloat32x3 fBoxMax = data.clippingBox.boxCenter + fScaledBoxVec; + + if (any(FFX_GREATER_THAN(fBoxMin, data.fHistoryColor)) || any(FFX_GREATER_THAN(data.fHistoryColor, fBoxMax))) { + + const FfxFloat32x3 fClampedHistoryColor = clamp(data.fHistoryColor, fBoxMin, fBoxMax); + + const FfxFloat32 fHistoryContribution = ffxMax(params.fLumaInstabilityFactor, data.fLockContributionThisFrame) * params.fAccumulation * (1 - params.fDisocclusion); + + // Scale history color using rectification info, also using accumulation mask to avoid potential invalid color protection + data.fHistoryColor = ffxLerp(fClampedHistoryColor, data.fHistoryColor, ffxSaturate(fHistoryContribution)); + } +} + +void UpdateLockStatus(AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) +{ + data.fLock *= FfxFloat32(params.bIsNewSample == false); + + const FfxFloat32 fLifetimeDecreaseFactor = ffxMax(ffxSaturate(params.fShadingChange), ffxMax(params.fReactiveMask, params.fDisocclusion)); + data.fLock = ffxMax(0.0f, data.fLock - fLifetimeDecreaseFactor * fLockMax); + + // Compute this frame lock contribution + data.fLockContributionThisFrame = ffxSaturate(ffxSaturate(data.fLock - fLockThreshold) * (fLockMax - fLockThreshold)); + + const FfxFloat32 fNewLockIntensity = LoadRwNewLocks(params.iPxHrPos) * (1.0f - ffxMax(params.fShadingChange * 0, params.fReactiveMask)); + data.fLock = ffxMax(0.0f, ffxMin(data.fLock + fNewLockIntensity, fLockMax)); + + // Preparing for next frame + const FfxFloat32 fLifetimeDecrease = (0.1f / JitterSequenceLength()) * (1.0f - fLifetimeDecreaseFactor); + data.fLock = ffxMax(0.0f, data.fLock - fLifetimeDecrease); + + // we expect similar motion for next frame + // kill lock if that location is outside screen, avoid locks to be clamped to screen borders + const FfxFloat32x2 fEstimatedUvNextFrame = params.fHrUv - params.fMotionVector; + data.fLock *= FfxFloat32(IsUvInside(fEstimatedUvNextFrame) == true); +} + +void ComputeBaseAccumulationWeight(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) +{ + FfxFloat32 fBaseAccumulation = params.fAccumulation; + + fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, 0.15f, ffxSaturate(ffxMax(0.0f, params.f4KVelocity / 0.5f)))); + + data.fHistoryWeight = fBaseAccumulation; +} + +void InitPassData(FfxInt32x2 iPxHrPos, FFX_PARAMETER_INOUT AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) +{ + // Init constant params + params.iPxHrPos = iPxHrPos; + const FfxFloat32x2 fHrUv = (iPxHrPos + 0.5f) / UpscaleSize(); + params.fHrUv = fHrUv; + params.fLrUvJittered = fHrUv + Jitter() / RenderSize(); + params.fLrUv_HwSampler = ClampUv(params.fLrUvJittered, RenderSize(), MaxRenderSize()); + + params.fMotionVector = GetMotionVector(iPxHrPos, fHrUv); + params.f4KVelocity = Get4KVelocity(params.fMotionVector); + + ComputeReprojectedUVs(params, params.fReprojectedHrUv, params.bIsExistingSample); + + const FfxFloat32x2 fLumaInstabilityUv_HW = ClampUv(fHrUv, RenderSize(), MaxRenderSize()); + params.fLumaInstabilityFactor = SampleLumaInstability(fLumaInstabilityUv_HW); + + const FfxFloat32x2 fFarthestDepthUv = ClampUv(params.fLrUvJittered, RenderSize() / 2, GetFarthestDepthMip1ResourceDimensions()); + params.fFarthestDepthInMeters = SampleFarthestDepthMip1(fFarthestDepthUv); + params.bIsNewSample = (params.bIsExistingSample == false || 0 == FrameIndex()); + + const FfxFloat32x4 fDilatedReactiveMasks = SampleDilatedReactiveMasks(params.fLrUv_HwSampler); + params.fReactiveMask = ffxSaturate(fDilatedReactiveMasks[REACTIVE]); + params.fDisocclusion = ffxSaturate(fDilatedReactiveMasks[DISOCCLUSION]); + params.fShadingChange = ffxSaturate(fDilatedReactiveMasks[SHADING_CHANGE]); + params.fAccumulation = ffxSaturate(fDilatedReactiveMasks[ACCUMULAION]); + params.fAccumulation *= FfxFloat32(round(params.fAccumulation * 100.0f) > 1.0f); + + // Init variable data + data = (AccumulationPassData)0; + data.fUpsampledColor = FfxFloat32x3(0.0f, 0.0f, 0.0f); + data.fHistoryColor = FfxFloat32x3(0.0f, 0.0f, 0.0f); + data.fHistoryWeight = 1.0f; + data.fUpsampledWeight = 0.0f; + data.fLock = 0.0f; + data.fLockContributionThisFrame = 0.0f; +} + +void Accumulate(FfxInt32x2 iPxHrPos) +{ + AccumulationPassCommonParams params; + AccumulationPassData data; + InitPassData(iPxHrPos, params, data); + + if (params.bIsExistingSample && !params.bIsNewSample) { + ReprojectHistoryColor(params, data); + } + + UpdateLockStatus(params, data); + + ComputeBaseAccumulationWeight(params, data); + + ComputeUpsampledColorAndWeight(params, data); + + RectifyHistory(params, data); + + Accumulate(params, data); + + data.fHistoryColor /= Exposure(); + + StoreInternalColorAndWeight(iPxHrPos, FfxFloat32x4(data.fHistoryColor, data.fLock)); + + // Output final color when RCAS is disabled +#if FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING == 0 + StoreUpscaledOutput(iPxHrPos, data.fHistoryColor); +#endif + StoreNewLocks(iPxHrPos, 0); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta new file mode 100644 index 0000000..d84af26 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: cab06bfc45670ef42915e0e483e2c823 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h new file mode 100644 index 0000000..1c3fc99 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h @@ -0,0 +1,980 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#include "ffx_fsr3upscaler_resources.h" + +#if defined(FFX_GPU) +#ifdef __hlsl_dx_compiler +#pragma dxc diagnostic push +#pragma dxc diagnostic ignored "-Wambig-lit-shift" +#endif //__hlsl_dx_compiler +#include "../ffx_core.h" +#ifdef __hlsl_dx_compiler +#pragma dxc diagnostic pop +#endif //__hlsl_dx_compiler +#endif // #if defined(FFX_GPU) + +#if defined(FFX_GPU) +#ifndef FFX_PREFER_WAVE64 +#define FFX_PREFER_WAVE64 +#endif // FFX_PREFER_WAVE64 + +#pragma warning(disable: 3205) // conversion from larger type to smaller + +#define DECLARE_SRV_REGISTER(regIndex) t##regIndex +#define DECLARE_UAV_REGISTER(regIndex) u##regIndex +#define DECLARE_CB_REGISTER(regIndex) b##regIndex +#define FFX_FSR3UPSCALER_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) +#define FFX_FSR3UPSCALER_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) +#define FFX_FSR3UPSCALER_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) + +#if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) +cbuffer cbFSR3Upscaler : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) +{ + FfxInt32x2 iRenderSize; + FfxInt32x2 iPreviousFrameRenderSize; + + FfxInt32x2 iUpscaleSize; + FfxInt32x2 iPreviousFrameUpscaleSize; + + FfxInt32x2 iMaxRenderSize; + FfxInt32x2 iMaxUpscaleSize; + + FfxFloat32x4 fDeviceToViewDepth; + + FfxFloat32x2 fJitter; + FfxFloat32x2 fPreviousFrameJitter; + + FfxFloat32x2 fMotionVectorScale; + FfxFloat32x2 fDownscaleFactor; + + FfxFloat32x2 fMotionVectorJitterCancellation; + FfxFloat32 fTanHalfFOV; + FfxFloat32 fJitterSequenceLength; + + FfxFloat32 fDeltaTime; + FfxFloat32 fDeltaPreExposure; + FfxFloat32 fViewSpaceToMetersFactor; + FfxFloat32 fFrameIndex; +}; + +#define FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE (sizeof(cbFSR3Upscaler) / 4) // Number of 32-bit values. This must be kept in sync with the cbFSR3Upscaler size. + +/* Define getter functions in the order they are defined in the CB! */ +FfxInt32x2 RenderSize() +{ + return iRenderSize; +} + +FfxInt32x2 PreviousFrameRenderSize() +{ + return iPreviousFrameRenderSize; +} + +FfxInt32x2 MaxRenderSize() +{ + return iMaxRenderSize; +} + +FfxInt32x2 UpscaleSize() +{ + return iUpscaleSize; +} + +FfxInt32x2 PreviousFrameUpscaleSize() +{ + return iPreviousFrameUpscaleSize; +} + +FfxInt32x2 MaxUpscaleSize() +{ + return iMaxUpscaleSize; +} + +FfxFloat32x2 Jitter() +{ + return fJitter; +} + +FfxFloat32x2 PreviousFrameJitter() +{ + return fPreviousFrameJitter; +} + +FfxFloat32x4 DeviceToViewSpaceTransformFactors() +{ + return fDeviceToViewDepth; +} + +FfxFloat32x2 MotionVectorScale() +{ + return fMotionVectorScale; +} + +FfxFloat32x2 DownscaleFactor() +{ + return fDownscaleFactor; +} + +FfxFloat32x2 MotionVectorJitterCancellation() +{ + return fMotionVectorJitterCancellation; +} + +FfxFloat32 TanHalfFoV() +{ + return fTanHalfFOV; +} + +FfxFloat32 JitterSequenceLength() +{ + return fJitterSequenceLength; +} + +FfxFloat32 DeltaTime() +{ + return fDeltaTime; +} + +FfxFloat32 DeltaPreExposure() +{ + return fDeltaPreExposure; +} + +FfxFloat32 ViewSpaceToMetersFactor() +{ + return fViewSpaceToMetersFactor; +} + +FfxFloat32 FrameIndex() +{ + return fFrameIndex; +} + +#endif // #if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) + +#define FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(p) FFX_FSR3UPSCALER_ROOTSIG_STR(p) +#define FFX_FSR3UPSCALER_ROOTSIG_STR(p) #p +#define FFX_FSR3UPSCALER_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "CBV(b0), " \ + "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ + "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] + +#define FFX_FSR3UPSCALER_CONSTANT_BUFFER_2_SIZE 6 // Number of 32-bit values. This must be kept in sync with max( cbRCAS , cbSPD) size. + +#define FFX_FSR3UPSCALER_CB2_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ + "CBV(b0), " \ + "CBV(b1), " \ + "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ + "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ + "addressU = TEXTURE_ADDRESS_CLAMP, " \ + "addressV = TEXTURE_ADDRESS_CLAMP, " \ + "addressW = TEXTURE_ADDRESS_CLAMP, " \ + "comparisonFunc = COMPARISON_NEVER, " \ + "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] +#if defined(FFX_FSR3UPSCALER_EMBED_ROOTSIG) +#define FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT FFX_FSR3UPSCALER_ROOTSIG +#define FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT FFX_FSR3UPSCALER_CB2_ROOTSIG +#else +#define FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT +#define FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT +#endif // #if FFX_FSR3UPSCALER_EMBED_ROOTSIG + +#if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) +cbuffer cbGenerateReactive : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) +{ + FfxFloat32 fTcThreshold; // 0.1 is a good starting value, lower will result in more TC pixels + FfxFloat32 fTcScale; + FfxFloat32 fReactiveScale; + FfxFloat32 fReactiveMax; +}; + +FfxFloat32 TcThreshold() +{ + return fTcThreshold; +} + +FfxFloat32 TcScale() +{ + return fTcScale; +} + +FfxFloat32 ReactiveScale() +{ + return fReactiveScale; +} + +FfxFloat32 ReactiveMax() +{ + return fReactiveMax; +} +#endif // #if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) + +#if defined(FSR3UPSCALER_BIND_CB_RCAS) +cbuffer cbRCAS : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_RCAS) +{ + FfxUInt32x4 rcasConfig; +}; + +FfxUInt32x4 RCASConfig() +{ + return rcasConfig; +} +#endif // #if defined(FSR3UPSCALER_BIND_CB_RCAS) + + +#if defined(FSR3UPSCALER_BIND_CB_REACTIVE) +cbuffer cbGenerateReactive : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_REACTIVE) +{ + FfxFloat32 gen_reactive_scale; + FfxFloat32 gen_reactive_threshold; + FfxFloat32 gen_reactive_binaryValue; + FfxUInt32 gen_reactive_flags; +}; + +FfxFloat32 GenReactiveScale() +{ + return gen_reactive_scale; +} + +FfxFloat32 GenReactiveThreshold() +{ + return gen_reactive_threshold; +} + +FfxFloat32 GenReactiveBinaryValue() +{ + return gen_reactive_binaryValue; +} + +FfxUInt32 GenReactiveFlags() +{ + return gen_reactive_flags; +} +#endif // #if defined(FSR3UPSCALER_BIND_CB_REACTIVE) + +#if defined(FSR3UPSCALER_BIND_CB_SPD) +cbuffer cbSPD : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_SPD) { + + FfxUInt32 mips; + FfxUInt32 numWorkGroups; + FfxUInt32x2 workGroupOffset; + FfxUInt32x2 renderSize; +}; + +FfxUInt32 MipCount() +{ + return mips; +} + +FfxUInt32 NumWorkGroups() +{ + return numWorkGroups; +} + +FfxUInt32x2 WorkGroupOffset() +{ + return workGroupOffset; +} + +FfxUInt32x2 SPD_RenderSize() +{ + return renderSize; +} +#endif // #if defined(FSR3UPSCALER_BIND_CB_SPD) + +// Declare and sample camera buffers as regular textures, unless overridden +#if !defined(UNITY_FSR_TEX2D) +#define UNITY_FSR_TEX2D(type) Texture2D +#endif +#if !defined(UNITY_FSR_RWTEX2D) +#define UNITY_FSR_RWTEX2D(type) RWTexture2D +#endif +#if !defined(UNITY_FSR_POS) +#define UNITY_FSR_POS(pxPos) (pxPos) +#endif +#if !defined(UNITY_FSR_UV) +#define UNITY_FSR_UV(uv) (uv) +#endif +#if !defined(UNITY_FSR_GETDIMS) +#define UNITY_FSR_GETDIMS(tex, w, h) (tex).GetDimensions((w), (h)) +#endif + +SamplerState s_PointClamp : register(s0); +SamplerState s_LinearClamp : register(s1); + +#if defined(FSR3UPSCALER_BIND_SRV_SPD_MIPS) +Texture2D r_spd_mips : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_SPD_MIPS); + +FfxInt32x2 GetSPDMipDimensions(FfxUInt32 uMipLevel) +{ + FfxUInt32 uWidth; + FfxUInt32 uHeight; + FfxUInt32 uLevels; + r_spd_mips.GetDimensions(uMipLevel, uWidth, uHeight, uLevels); + + return FfxInt32x2(uWidth, uHeight); +} + +FfxFloat32x2 SampleSPDMipLevel(FfxFloat32x2 fUV, FfxUInt32 mipLevel) +{ + return r_spd_mips.SampleLevel(s_LinearClamp, fUV, mipLevel); +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH) +UNITY_FSR_TEX2D(FfxFloat32) r_input_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH); + +FfxFloat32 LoadInputDepth(FfxUInt32x2 iPxPos) +{ + return r_input_depth[UNITY_FSR_POS(iPxPos)]; +} + +FfxFloat32 SampleInputDepth(FfxFloat32x2 fUV) +{ + return r_input_depth.SampleLevel(s_LinearClamp, UNITY_FSR_UV(fUV), 0).x; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK) +UNITY_FSR_TEX2D(FfxFloat32) r_reactive_mask : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK); + +FfxFloat32 LoadReactiveMask(FfxUInt32x2 iPxPos) +{ + return r_reactive_mask[UNITY_FSR_POS(iPxPos)]; +} + +FfxInt32x2 GetReactiveMaskResourceDimensions() +{ + FfxUInt32 uWidth; + FfxUInt32 uHeight; + UNITY_FSR_GETDIMS(r_reactive_mask, uWidth, uHeight); + + return FfxInt32x2(uWidth, uHeight); +} + +FfxFloat32 SampleReactiveMask(FfxFloat32x2 fUV) +{ + return r_reactive_mask.SampleLevel(s_LinearClamp, UNITY_FSR_UV(fUV), 0).x; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK) +UNITY_FSR_TEX2D(FfxFloat32) r_transparency_and_composition_mask : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK); + +FfxFloat32 LoadTransparencyAndCompositionMask(FfxUInt32x2 iPxPos) +{ + return r_transparency_and_composition_mask[UNITY_FSR_POS(iPxPos)]; +} + +FfxInt32x2 GetTransparencyAndCompositionMaskResourceDimensions() +{ + FfxUInt32 uWidth; + FfxUInt32 uHeight; + UNITY_FSR_GETDIMS(r_transparency_and_composition_mask, uWidth, uHeight); + + return FfxInt32x2(uWidth, uHeight); +} + +FfxFloat32 SampleTransparencyAndCompositionMask(FfxFloat32x2 fUV) +{ + return r_transparency_and_composition_mask.SampleLevel(s_LinearClamp, UNITY_FSR_UV(fUV), 0).x; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_INPUT_COLOR) +UNITY_FSR_TEX2D(FfxFloat32x4) r_input_color_jittered : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_COLOR); + +FfxFloat32x3 LoadInputColor(FfxUInt32x2 iPxPos) +{ + return r_input_color_jittered[UNITY_FSR_POS(iPxPos)].rgb; +} + +FfxFloat32x3 SampleInputColor(FfxFloat32x2 fUV) +{ + return r_input_color_jittered.SampleLevel(s_LinearClamp, UNITY_FSR_UV(fUV), 0).rgb; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS) +UNITY_FSR_TEX2D(FfxFloat32x4) r_input_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS); + +FfxFloat32x2 LoadInputMotionVector(FfxUInt32x2 iPxDilatedMotionVectorPos) +{ + FfxFloat32x2 fSrcMotionVector = r_input_motion_vectors[UNITY_FSR_POS(iPxDilatedMotionVectorPos)].xy; + + FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale(); + +#if FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS + fUvMotionVector -= MotionVectorJitterCancellation(); +#endif + + return fUvMotionVector; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED) +Texture2D r_internal_upscaled_color : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED); + +FfxFloat32x4 LoadHistory(FfxUInt32x2 iPxHistory) +{ + return r_internal_upscaled_color[iPxHistory]; +} + +FfxFloat32x4 SampleHistory(FfxFloat32x2 fUV) +{ + return r_internal_upscaled_color.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY) +RWTexture2D rw_luma_history : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY); + +void StoreLumaHistory(FfxUInt32x2 iPxPos, FfxFloat32x4 fLumaHistory) +{ + rw_luma_history[iPxPos] = fLumaHistory; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY) +Texture2D r_luma_history : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY); + +FfxFloat32x4 LoadLumaHistory(FfxInt32x2 iPxPos) +{ + return r_luma_history[iPxPos]; +} + +FfxFloat32x4 SampleLumaHistory(FfxFloat32x2 fUV) +{ + return r_luma_history.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_RCAS_INPUT) +Texture2D r_rcas_input : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_RCAS_INPUT); + +FfxFloat32x4 LoadRCAS_Input(FfxInt32x2 iPxPos) +{ + return r_rcas_input[iPxPos]; +} + +FfxFloat32x3 SampleRCAS_Input(FfxFloat32x2 fUV) +{ + return r_rcas_input.SampleLevel(s_LinearClamp, fUV, 0).rgb; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED) +RWTexture2D rw_internal_upscaled_color : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED); + +void StoreReprojectedHistory(FfxUInt32x2 iPxHistory, FfxFloat32x4 fHistory) +{ + rw_internal_upscaled_color[iPxHistory] = fHistory; +} + +void StoreInternalColorAndWeight(FfxUInt32x2 iPxPos, FfxFloat32x4 fColorAndWeight) +{ + rw_internal_upscaled_color[iPxPos] = fColorAndWeight; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT) +UNITY_FSR_RWTEX2D(FfxFloat32x4) rw_upscaled_output : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT); + +void StoreUpscaledOutput(FfxUInt32x2 iPxPos, FfxFloat32x3 fColor) +{ + rw_upscaled_output[UNITY_FSR_POS(iPxPos)] = FfxFloat32x4(fColor, 1.f); +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_ACCUMULATION) +Texture2D r_accumulation : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_ACCUMULATION); + +FfxFloat32 SampleAccumulation(FfxFloat32x2 fUV) +{ + return r_accumulation.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_ACCUMULATION) +RWTexture2D rw_accumulation : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_ACCUMULATION); + +void StoreAccumulation(FfxUInt32x2 iPxPos, FfxFloat32 fAccumulation) +{ + rw_accumulation[iPxPos] = fAccumulation; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_SHADING_CHANGE) +Texture2D r_shading_change : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_SHADING_CHANGE); + +FfxFloat32 LoadShadingChange(FfxUInt32x2 iPxPos) +{ + return r_shading_change[iPxPos]; +} + +FfxFloat32 SampleShadingChange(FfxFloat32x2 fUV) +{ + return r_shading_change.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_SHADING_CHANGE) +RWTexture2D rw_shading_change : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SHADING_CHANGE); + +void StoreShadingChange(FfxUInt32x2 iPxPos, FfxFloat32 fShadingChange) +{ + rw_shading_change[iPxPos] = fShadingChange; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH) +Texture2D r_farthest_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH); + +FfxInt32x2 GetFarthestDepthResourceDimensions() +{ + FfxUInt32 uWidth; + FfxUInt32 uHeight; + r_farthest_depth.GetDimensions(uWidth, uHeight); + + return FfxInt32x2(uWidth, uHeight); +} + +FfxFloat32 LoadFarthestDepth(FfxUInt32x2 iPxPos) +{ + return r_farthest_depth[iPxPos]; +} + +FfxFloat32 SampleFarthestDepth(FfxFloat32x2 fUV) +{ + return r_farthest_depth.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH) +RWTexture2D rw_farthest_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH); + +void StoreFarthestDepth(FfxUInt32x2 iPxPos, FfxFloat32 fDepth) +{ + rw_farthest_depth[iPxPos] = fDepth; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH_MIP1) +Texture2D r_farthest_depth_mip1 : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_FARTHEST_DEPTH_MIP1); + +FfxInt32x2 GetFarthestDepthMip1ResourceDimensions() +{ + FfxUInt32 uWidth; + FfxUInt32 uHeight; + r_farthest_depth_mip1.GetDimensions(uWidth, uHeight); + + return FfxInt32x2(uWidth, uHeight); +} + +FfxFloat32 LoadFarthestDepthMip1(FfxUInt32x2 iPxPos) +{ + return r_farthest_depth_mip1[iPxPos]; +} + +FfxFloat32 SampleFarthestDepthMip1(FfxFloat32x2 fUV) +{ + return r_farthest_depth_mip1.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH_MIP1) +RWTexture2D rw_farthest_depth_mip1 : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_FARTHEST_DEPTH_MIP1); + +void StoreFarthestDepthMip1(FfxUInt32x2 iPxPos, FfxFloat32 fDepth) +{ + rw_farthest_depth_mip1[iPxPos] = fDepth; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_CURRENT_LUMA) +Texture2D r_current_luma : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_CURRENT_LUMA); + +FfxFloat32 LoadCurrentLuma(FfxUInt32x2 iPxPos) +{ + return r_current_luma[iPxPos]; +} + +FfxFloat32 SampleCurrentLuma(FfxFloat32x2 uv) +{ + return r_current_luma.SampleLevel(s_LinearClamp, uv, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_CURRENT_LUMA) +RWTexture2D rw_current_luma : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_CURRENT_LUMA); + +void StoreCurrentLuma(FfxUInt32x2 iPxPos, FfxFloat32 fLuma) +{ + rw_current_luma[iPxPos] = fLuma; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_LUMA_INSTABILITY) +Texture2D r_luma_instability : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LUMA_INSTABILITY); + +FfxFloat32 SampleLumaInstability(FfxFloat32x2 uv) +{ + return r_luma_instability.SampleLevel(s_LinearClamp, uv, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_LUMA_INSTABILITY) +RWTexture2D rw_luma_instability : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LUMA_INSTABILITY); + +void StoreLumaInstability(FfxUInt32x2 iPxPos, FfxFloat32 fLumaInstability) +{ + rw_luma_instability[iPxPos] = fLumaInstability; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_PREVIOUS_LUMA) +Texture2D r_previous_luma : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREVIOUS_LUMA); + +FfxFloat32 LoadPreviousLuma(FfxUInt32x2 iPxPos) +{ + return r_previous_luma[iPxPos]; +} + +FfxFloat32 SamplePreviousLuma(FfxFloat32x2 uv) +{ + return r_previous_luma.SampleLevel(s_LinearClamp, uv, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_NEW_LOCKS) +Texture2D r_new_locks : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_NEW_LOCKS); + +FfxFloat32 LoadNewLocks(FfxUInt32x2 iPxPos) +{ + return r_new_locks[iPxPos]; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_NEW_LOCKS) +RWTexture2D rw_new_locks : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_NEW_LOCKS); + +FfxFloat32 LoadRwNewLocks(FfxUInt32x2 iPxPos) +{ + return rw_new_locks[iPxPos]; +} + +void StoreNewLocks(FfxUInt32x2 iPxPos, FfxFloat32 newLock) +{ + rw_new_locks[iPxPos] = newLock; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH) +Texture2D r_reconstructed_previous_nearest_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH); + +FfxFloat32 LoadReconstructedPrevDepth(FfxUInt32x2 iPxPos) +{ + return asfloat(r_reconstructed_previous_nearest_depth[iPxPos]); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) +RWTexture2D rw_reconstructed_previous_nearest_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH); + +void StoreReconstructedDepth(FfxUInt32x2 iPxSample, FfxFloat32 fDepth) +{ + FfxUInt32 uDepth = asuint(fDepth); + +#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH + InterlockedMax(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); +#else + InterlockedMin(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); // min for standard, max for inverted depth +#endif +} + +void SetReconstructedDepth(FfxUInt32x2 iPxSample, const FfxUInt32 uValue) +{ + rw_reconstructed_previous_nearest_depth[iPxSample] = uValue; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH) +RWTexture2D rw_dilated_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH); + +void StoreDilatedDepth(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32 fDepth) +{ + rw_dilated_depth[iPxPos] = fDepth; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS) +RWTexture2D rw_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS); + +void StoreDilatedMotionVector(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fMotionVector) +{ + rw_dilated_motion_vectors[iPxPos] = fMotionVector; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS) +Texture2D r_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS); + +FfxFloat32x2 LoadDilatedMotionVector(FfxUInt32x2 iPxInput) +{ + return r_dilated_motion_vectors[iPxInput]; +} + +FfxFloat32x2 SampleDilatedMotionVector(FfxFloat32x2 fUV) +{ + return r_dilated_motion_vectors.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH) +Texture2D r_dilated_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH); + +FfxFloat32 LoadDilatedDepth(FfxUInt32x2 iPxInput) +{ + return r_dilated_depth[iPxInput]; +} + +FfxFloat32 SampleDilatedDepth(FfxFloat32x2 fUV) +{ + return r_dilated_depth.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE) +Texture2D r_input_exposure : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE); + +FfxFloat32 Exposure() +{ + FfxFloat32 exposure = r_input_exposure[FfxUInt32x2(0, 0)].x; + + if (exposure == 0.0f) { + exposure = 1.0f; + } + + return exposure; +} +#endif + +// BEGIN: FSR3UPSCALER_BIND_SRV_LANCZOS_LUT +#if defined(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT) +Texture2D r_lanczos_lut : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT); +#endif + +FfxFloat32 SampleLanczos2Weight(FfxFloat32 x) +{ +#if defined(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT) + return r_lanczos_lut.SampleLevel(s_LinearClamp, FfxFloat32x2(x / 2, 0.5f), 0); +#else + return 0.f; +#endif +} +// END: FSR3UPSCALER_BIND_SRV_LANCZOS_LUT + +#if defined(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS) +Texture2D r_dilated_reactive_masks : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS); + +FfxFloat32x4 SampleDilatedReactiveMasks(FfxFloat32x2 fUV) +{ + return r_dilated_reactive_masks.SampleLevel(s_LinearClamp, fUV, 0); +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS) +RWTexture2D rw_dilated_reactive_masks : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS); + +void StoreDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x4 fDilatedReactiveMasks) +{ + rw_dilated_reactive_masks[iPxPos] = fDilatedReactiveMasks; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY) +UNITY_FSR_TEX2D(FfxFloat32x4) r_input_opaque_only : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY); + +FfxFloat32x3 LoadOpaqueOnly(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) +{ + return r_input_opaque_only[UNITY_FSR_POS(iPxPos)].xyz; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR) +Texture2D r_input_prev_color_pre_alpha : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR); + +FfxFloat32x3 LoadPrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) +{ + return r_input_prev_color_pre_alpha[iPxPos]; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR) +Texture2D r_input_prev_color_post_alpha : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR); + +FfxFloat32x3 LoadPrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) +{ + return r_input_prev_color_post_alpha[iPxPos]; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE) && \ + defined(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION) + +RWTexture2D rw_output_autoreactive : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE); +RWTexture2D rw_output_autocomposition : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION); + +void StoreAutoReactive(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F2 fReactive) +{ + rw_output_autoreactive[iPxPos] = fReactive.x; + + rw_output_autocomposition[iPxPos] = fReactive.y; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR) +RWTexture2D rw_output_prev_color_pre_alpha : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR); + +void StorePrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) +{ + rw_output_prev_color_pre_alpha[iPxPos] = color; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR) +RWTexture2D rw_output_prev_color_post_alpha : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR); + +void StorePrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) +{ + rw_output_prev_color_post_alpha[iPxPos] = color; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_FRAME_INFO) +RWTexture2D rw_frame_info : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_FRAME_INFO); + +FfxFloat32x4 LoadFrameInfo() +{ + return rw_frame_info[FfxInt32x2(0, 0)]; +} + +void StoreFrameInfo(FfxFloat32x4 fInfo) +{ + rw_frame_info[FfxInt32x2(0, 0)] = fInfo; +} +#endif + +#if defined(FSR3UPSCALER_BIND_SRV_FRAME_INFO) +Texture2D r_frame_info : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_FRAME_INFO); + +FfxFloat32x4 FrameInfo() +{ + return r_frame_info[FfxInt32x2(0, 0)]; +} +#endif + +#if defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_0) && \ + defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_1) && \ + defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_2) && \ + defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_3) && \ + defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_4) && \ + defined(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_5) + +RWTexture2D rw_spd_mip0 : FFX_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_0); +RWTexture2D rw_spd_mip1 : FFX_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_1); +RWTexture2D rw_spd_mip2 : FFX_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_2); +RWTexture2D rw_spd_mip3 : FFX_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_3); +RWTexture2D rw_spd_mip4 : FFX_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_4); +globallycoherent RWTexture2D rw_spd_mip5 : FFX_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_MIPS_LEVEL_5); + +FfxFloat32x2 RWLoadPyramid(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxUInt32 index) +{ +#define LOAD(idx) \ + if (index == idx) \ + { \ + return rw_spd_mip##idx[iPxPos]; \ + } + LOAD(0); + LOAD(1); + LOAD(2); + LOAD(3); + LOAD(4); + LOAD(5); + + return 0; + +#undef LOAD +} + +void StorePyramid(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 outValue, FFX_PARAMETER_IN FfxUInt32 index) +{ +#define STORE(idx) \ + if (index == idx) \ + { \ + rw_spd_mip##idx[iPxPos] = outValue; \ + } + + STORE(0); + STORE(1); + STORE(2); + STORE(3); + STORE(4); + STORE(5); + +#undef STORE +} +#endif + +#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC +globallycoherent RWTexture2D rw_spd_global_atomic : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC); + +void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter) +{ + InterlockedAdd(rw_spd_global_atomic[FfxInt32x2(0, 0)], 1, spdCounter); +} + +void SPD_ResetAtomicCounter() +{ + rw_spd_global_atomic[FfxInt32x2(0, 0)] = 0; +} +#endif + +#endif // #if defined(FFX_GPU) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta new file mode 100644 index 0000000..aca1bc0 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: aef945ff764c453428c8c4759706228d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h new file mode 100644 index 0000000..dd479b1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h @@ -0,0 +1,403 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#if !defined(FFX_FSR3UPSCALER_COMMON_H) +#define FFX_FSR3UPSCALER_COMMON_H + +#if defined(FFX_GPU) +#pragma warning(error : 3206) // treat vector truncation warnings as errors +#pragma warning(disable : 3205) // conversion from larger type to smaller +#pragma warning(disable : 3571) // in ffxPow(f, e), f could be negative + +FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MIN = 6.10e-05f; +FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MAX = 65504.0f; +FFX_STATIC const FfxFloat32 FSR3UPSCALER_EPSILON = FSR3UPSCALER_FP16_MIN; +FFX_STATIC const FfxFloat32 FSR3UPSCALER_TONEMAP_EPSILON = FSR3UPSCALER_FP16_MIN; +FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP32_MAX = 3.402823466e+38f; +FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP32_MIN = 1.175494351e-38f; + +// Reconstructed depth usage +FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = FSR3UPSCALER_EPSILON * 10; + +FfxFloat32 ReconstructedDepthMvPxThreshold(FfxFloat32 fNearestDepthInMeters) +{ + return ffxLerp(0.25f, 0.75f, ffxSaturate(fNearestDepthInMeters / 100.0f)); +} + +// Accumulation +FFX_STATIC const FfxFloat32 fUpsampleLanczosWeightScale = 1.0f / 16.0f; +FFX_STATIC const FfxFloat32 fAverageLanczosWeightPerFrame = 0.74f * fUpsampleLanczosWeightScale; // Average lanczos weight for jitter accumulated samples +FFX_STATIC const FfxFloat32 fAccumulationMaxOnMotion = 3.0f * fUpsampleLanczosWeightScale; + +#define SHADING_CHANGE_SET_SIZE 5 +FFX_STATIC const FfxInt32 iShadingChangeMipStart = 0; +FFX_STATIC const FfxFloat32 fShadingChangeSamplePow = 1.0f / 1.0f; + + +FFX_STATIC const FfxFloat32 fLockThreshold = 1.0f; +FFX_STATIC const FfxFloat32 fLockMax = 2.0f; + +FFX_STATIC const FfxInt32 REACTIVE = 0; +FFX_STATIC const FfxInt32 DISOCCLUSION = 1; +FFX_STATIC const FfxInt32 SHADING_CHANGE = 2; +FFX_STATIC const FfxInt32 ACCUMULAION = 3; + +FFX_STATIC const FfxInt32 FRAME_INFO_EXPOSURE = 0; +FFX_STATIC const FfxInt32 FRAME_INFO_LOG_LUMA = 1; +FFX_STATIC const FfxInt32 FRAME_INFO_SCENE_AVERAGE_LUMA = 2; + +FfxBoolean TonemapFirstFrame() +{ + const FfxBoolean bEnabled = true; + return FrameIndex() == 0 && bEnabled; +} + +FfxFloat32 AverageLanczosWeightPerFrame() +{ + return 0.74f; +} + +FfxInt32x2 ShadingChangeRenderSize() +{ + return FfxInt32x2(RenderSize() * 0.5f); +} + +FfxInt32x2 ShadingChangeMaxRenderSize() +{ + return FfxInt32x2(MaxRenderSize() * 0.5f); +} + +FfxInt32x2 PreviousFrameShadingChangeRenderSize() +{ + return FfxInt32x2(PreviousFrameRenderSize() * 0.5f); +} + +#if defined(FSR3UPSCALER_BIND_SRV_FRAME_INFO) +FfxFloat32 SceneAverageLuma() +{ + return FrameInfo()[FRAME_INFO_SCENE_AVERAGE_LUMA]; +} +#endif + +// Auto exposure +FFX_STATIC const FfxFloat32 resetAutoExposureAverageSmoothing = 1e8f; + +struct AccumulationPassCommonParams +{ + FfxInt32x2 iPxHrPos; + FfxFloat32x2 fHrUv; + FfxFloat32x2 fLrUvJittered; + FfxFloat32x2 fLrUv_HwSampler; + FfxFloat32x2 fMotionVector; + FfxFloat32x2 fReprojectedHrUv; + FfxFloat32 f4KVelocity; + FfxFloat32 fDisocclusion; + FfxFloat32 fReactiveMask; + FfxFloat32 fShadingChange; + FfxFloat32 fAccumulation; + FfxFloat32 fLumaInstabilityFactor; + FfxFloat32 fFarthestDepthInMeters; + + FfxBoolean bIsExistingSample; + FfxBoolean bIsNewSample; +}; + +FfxFloat32 Get4KVelocity(FfxFloat32x2 fMotionVector) +{ + return length(fMotionVector * FfxFloat32x2(3840.0f, 2160.0f)); +} + +struct RectificationBox +{ + FfxFloat32x3 boxCenter; + FfxFloat32x3 boxVec; + FfxFloat32x3 aabbMin; + FfxFloat32x3 aabbMax; + FfxFloat32 fBoxCenterWeight; +}; + +struct AccumulationPassData +{ + RectificationBox clippingBox; + FfxFloat32x3 fUpsampledColor; + FfxFloat32 fUpsampledWeight; + FfxFloat32x3 fHistoryColor; + FfxFloat32 fHistoryWeight; + FfxFloat32 fLock; + FfxFloat32 fLockContributionThisFrame; +}; + +void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) +{ + rectificationBox.aabbMin = colorSample; + rectificationBox.aabbMax = colorSample; + + FfxFloat32x3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter = weightedSample; + rectificationBox.boxVec = colorSample * weightedSample; + rectificationBox.fBoxCenterWeight = fSampleWeight; +} + +void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) +{ + if (bInitialSample) { + RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); + } else { + rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); + rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); + + FfxFloat32x3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter += weightedSample; + rectificationBox.boxVec += colorSample * weightedSample; + rectificationBox.fBoxCenterWeight += fSampleWeight; + } +} + +void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBox rectificationBox) +{ + rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FfxFloat32(FSR3UPSCALER_FP32_MIN) ? rectificationBox.fBoxCenterWeight : FfxFloat32(1.f)); + rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; + rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; + FfxFloat32x3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); + rectificationBox.boxVec = stdDev; +} + +FfxFloat32x3 SafeRcp3(FfxFloat32x3 v) +{ + return (all(FFX_NOT_EQUAL(v, FfxFloat32x3(0, 0, 0)))) ? (FfxFloat32x3(1, 1, 1) / v) : FfxFloat32x3(0, 0, 0); +} + +FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1, const FfxFloat32 fOnZeroReturnValue) +{ + const FfxFloat32 m = ffxMax(v0, v1); + return m != 0 ? ffxMin(v0, v1) / m : fOnZeroReturnValue; +} + +FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1) +{ + const FfxFloat32 m = ffxMax(v0, v1); + return m != 0 ? ffxMin(v0, v1) / m : 0; +} + +FfxFloat32x3 YCoCgToRGB(FfxFloat32x3 fYCoCg) +{ + FfxFloat32x3 fRgb; + + fRgb = FfxFloat32x3( + fYCoCg.x + fYCoCg.y - fYCoCg.z, + fYCoCg.x + fYCoCg.z, + fYCoCg.x - fYCoCg.y - fYCoCg.z); + + return fRgb; +} + +FfxFloat32x3 RGBToYCoCg(FfxFloat32x3 fRgb) +{ + FfxFloat32x3 fYCoCg; + + fYCoCg = FfxFloat32x3( + 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b, + 0.5f * fRgb.r - 0.5f * fRgb.b, + -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b); + + return fYCoCg; +} + +FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb) +{ + return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f)); +} + +FfxFloat32 RGBToPerceivedLuma(FfxFloat32x3 fLinearRgb) +{ + FfxFloat32 fLuminance = RGBToLuma(fLinearRgb); + + FfxFloat32 fPercievedLuminance = 0; + if (fLuminance <= 216.0f / 24389.0f) { + fPercievedLuminance = fLuminance * (24389.0f / 27.0f); + } + else { + fPercievedLuminance = ffxPow(fLuminance, 1.0f / 3.0f) * 116.0f - 16.0f; + } + + return fPercievedLuminance * 0.01f; +} + +FfxFloat32x3 Tonemap(FfxFloat32x3 fRgb) +{ + return fRgb / (ffxMax(ffxMax(0.f, fRgb.r), ffxMax(fRgb.g, fRgb.b)) + 1.f).xxx; +} + +FfxFloat32x3 InverseTonemap(FfxFloat32x3 fRgb) +{ + return fRgb / ffxMax(FSR3UPSCALER_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; +} + +FfxBoolean IsUvInside(FfxFloat32x2 fUv) +{ + return (fUv.x >= 0.0f && fUv.x <= 1.0f) && (fUv.y >= 0.0f && fUv.y <= 1.0f); +} + +FfxInt32x2 ClampLoad(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) +{ + FfxInt32x2 result = iPxSample + iPxOffset; + result.x = ffxMax(0, ffxMin(result.x, iTextureSize.x - 1)); + result.y = ffxMax(0, ffxMin(result.y, iTextureSize.y - 1)); + return result; +} + +FfxFloat32x2 ClampUv(FfxFloat32x2 fUv, FfxInt32x2 iTextureSize, FfxInt32x2 iResourceSize) +{ + const FfxFloat32x2 fSampleLocation = fUv * iTextureSize; + const FfxFloat32x2 fClampedLocation = ffxMax(FfxFloat32x2(0.5f, 0.5f), ffxMin(fSampleLocation, FfxFloat32x2(iTextureSize) - FfxFloat32x2(0.5f, 0.5f))); + const FfxFloat32x2 fClampedUv = fClampedLocation / FfxFloat32x2(iResourceSize); + + return fClampedUv; +} + +FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size) +{ + return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size))); +} + +FfxFloat32 ComputeAutoExposureFromLavg(FfxFloat32 Lavg) +{ + Lavg = exp(Lavg); + + const FfxFloat32 S = 100.0f; //ISO arithmetic speed + const FfxFloat32 K = 12.5f; + FfxFloat32 ExposureISO100 = log2((Lavg * S) / K); + + const FfxFloat32 q = 0.65f; + FfxFloat32 Lmax = (78.0f / (q * S)) * ffxPow(2.0f, ExposureISO100); + + return 1.0f / Lmax; +} + +FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos) +{ + FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter(); + FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * UpscaleSize(); + FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr)); + return iPxHrPos; +} + +FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize) +{ + return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f); +} + +FfxFloat32 GetViewSpaceDepth(FfxFloat32 fDeviceDepth) +{ + const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); + + // fDeviceToViewDepth details found in ffx_fsr3upscaler.cpp + return (fDeviceToViewDepth[1] / (fDeviceDepth - fDeviceToViewDepth[0])); +} + +FfxFloat32 GetViewSpaceDepthInMeters(FfxFloat32 fDeviceDepth) +{ + return GetViewSpaceDepth(fDeviceDepth) * ViewSpaceToMetersFactor(); +} + +FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) +{ + const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); + + const FfxFloat32 Z = GetViewSpaceDepth(fDeviceDepth); + + const FfxFloat32x2 fNdcPos = ComputeNdc(iViewportPos, iViewportSize); + const FfxFloat32 X = fDeviceToViewDepth[2] * fNdcPos.x * Z; + const FfxFloat32 Y = fDeviceToViewDepth[3] * fNdcPos.y * Z; + + return FfxFloat32x3(X, Y, Z); +} + +FfxFloat32x3 GetViewSpacePositionInMeters(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) +{ + return GetViewSpacePosition(iViewportPos, iViewportSize, fDeviceDepth) * ViewSpaceToMetersFactor(); +} + +FfxFloat32 GetMaxDistanceInMeters() +{ +#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH + return GetViewSpaceDepth(0.0f) * ViewSpaceToMetersFactor(); +#else + return GetViewSpaceDepth(1.0f) * ViewSpaceToMetersFactor(); +#endif +} + + +struct BilinearSamplingData +{ + FfxInt32x2 iOffsets[4]; + FfxFloat32 fWeights[4]; + FfxInt32x2 iBasePos; +}; + +BilinearSamplingData GetBilinearSamplingData(FfxFloat32x2 fUv, FfxInt32x2 iSize) +{ + BilinearSamplingData data; + + FfxFloat32x2 fPxSample = (fUv * iSize) - FfxFloat32x2(0.5f, 0.5f); + data.iBasePos = FfxInt32x2(floor(fPxSample)); + FfxFloat32x2 fPxFrac = ffxFract(fPxSample); + + data.iOffsets[0] = FfxInt32x2(0, 0); + data.iOffsets[1] = FfxInt32x2(1, 0); + data.iOffsets[2] = FfxInt32x2(0, 1); + data.iOffsets[3] = FfxInt32x2(1, 1); + + data.fWeights[0] = (1 - fPxFrac.x) * (1 - fPxFrac.y); + data.fWeights[1] = (fPxFrac.x) * (1 - fPxFrac.y); + data.fWeights[2] = (1 - fPxFrac.x) * (fPxFrac.y); + data.fWeights[3] = (fPxFrac.x) * (fPxFrac.y); + + return data; +} + +struct PlaneData +{ + FfxFloat32x3 fNormal; + FfxFloat32 fDistanceFromOrigin; +}; + +PlaneData GetPlaneFromPoints(FfxFloat32x3 fP0, FfxFloat32x3 fP1, FfxFloat32x3 fP2) +{ + PlaneData plane; + + FfxFloat32x3 v0 = fP0 - fP1; + FfxFloat32x3 v1 = fP0 - fP2; + plane.fNormal = normalize(cross(v0, v1)); + plane.fDistanceFromOrigin = -dot(fP0, plane.fNormal); + + return plane; +} + +FfxFloat32 PointToPlaneDistance(PlaneData plane, FfxFloat32x3 fPoint) +{ + return abs(dot(plane.fNormal, fPoint) + plane.fDistanceFromOrigin); +} + +#endif // #if defined(FFX_GPU) + +#endif //!defined(FFX_FSR3UPSCALER_COMMON_H) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta new file mode 100644 index 0000000..c735997 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 16bb355abb68c044983f42086cf3eb7e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h new file mode 100644 index 0000000..6f4fa33 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h @@ -0,0 +1,159 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +struct FfxDebugViewport +{ + FfxInt32x2 offset; + FfxInt32x2 size; +}; + +// Macro to cull and draw debug viewport +#define DRAW_VIEWPORT(function, pos, vp) \ + { \ + if (pointIsInsideViewport(pos, vp)) \ + { \ + function(pos, vp); \ + } \ + } + +FfxFloat32x2 getTransformedUv(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = (FfxFloat32x2(iPxPos - vp.offset) + 0.5f) / vp.size; + + return fUv; +} + +FfxFloat32x3 getMotionVectorColor(FfxFloat32x2 fMotionVector) +{ + return FfxFloat32x3(0.5f + fMotionVector * RenderSize() * 0.5f, 0.5f); +} + +FfxFloat32x4 getUnusedIndicationColor(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxInt32x2 basePos = iPxPos - vp.offset; + + FfxFloat32 ar = FfxFloat32(vp.size.x) / FfxFloat32(vp.size.y); + + return FfxFloat32x4(basePos.x == FfxInt32(basePos.y * ar), 0, 0, 1); +} + +void drawDilatedMotionVectors(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = getTransformedUv(iPxPos, vp); + + FfxFloat32x2 fUv_HW = ClampUv(fUv, RenderSize(), MaxRenderSize()); + + FfxFloat32x2 fMotionVector = SampleDilatedMotionVector(fUv_HW); + + StoreUpscaledOutput(iPxPos, getMotionVectorColor(fMotionVector)); +} + +void drawDisocclusionMask(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = getTransformedUv(iPxPos, vp); + + FfxFloat32x2 fUv_HW = ClampUv(fUv, RenderSize(), MaxRenderSize()); + + FfxFloat32 fDisocclusionFactor = ffxSaturate(SampleDilatedReactiveMasks(fUv_HW)[DISOCCLUSION]); + + StoreUpscaledOutput(iPxPos, FfxFloat32x3(0, fDisocclusionFactor, 0)); +} + +void drawDetailProtectionTakedown(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = getTransformedUv(iPxPos, vp); + + FfxFloat32x2 fUv_HW = ClampUv(fUv, RenderSize(), MaxRenderSize()); + + FfxFloat32 fProtectionTakedown = ffxSaturate(SampleDilatedReactiveMasks(fUv_HW)[REACTIVE]); + + StoreUpscaledOutput(iPxPos, FfxFloat32x3(0, fProtectionTakedown, 0)); +} + +void drawReactiveness(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = getTransformedUv(iPxPos, vp); + + FfxFloat32x2 fUv_HW = ClampUv(fUv, RenderSize(), MaxRenderSize()); + + FfxFloat32 fShadingChange = ffxSaturate(SampleDilatedReactiveMasks(fUv_HW)[SHADING_CHANGE]); + + StoreUpscaledOutput(iPxPos, FfxFloat32x3(0, fShadingChange, 0)); +} + +void drawProtectedAreas(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = getTransformedUv(iPxPos, vp); + + FfxFloat32 fProtection = ffxSaturate(SampleHistory(fUv).w - fLockThreshold); + + StoreUpscaledOutput(iPxPos, FfxFloat32x3(fProtection, 0, 0)); +} + +void drawDilatedDepthInMeters(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxFloat32x2 fUv = getTransformedUv(iPxPos, vp); + + FfxFloat32x2 fUv_HW = ClampUv(fUv, RenderSize(), MaxRenderSize()); + + const FfxFloat32 fDilatedDepth = SampleDilatedDepth(fUv_HW); + const FfxFloat32 fDepthInMeters = GetViewSpaceDepthInMeters(fDilatedDepth); + + StoreUpscaledOutput(iPxPos, FfxFloat32x3(ffxSaturate(fDepthInMeters / 25.0f), 0, 0)); +} + +FfxBoolean pointIsInsideViewport(FfxInt32x2 iPxPos, FfxDebugViewport vp) +{ + FfxInt32x2 extent = vp.offset + vp.size; + + return (iPxPos.x >= vp.offset.x && iPxPos.x < extent.x) && (iPxPos.y >= vp.offset.y && iPxPos.y < extent.y); +} + +void DebugView(FfxInt32x2 iPxPos) +{ +#define VIEWPORT_GRID_SIZE_X 3 +#define VIEWPORT_GRID_SIZE_Y 3 + + FfxFloat32x2 fViewportScale = FfxFloat32x2(1.0f / VIEWPORT_GRID_SIZE_X, 1.0f / VIEWPORT_GRID_SIZE_Y); + FfxInt32x2 iViewportSize = FfxInt32x2(UpscaleSize() * fViewportScale); + + // compute grid [y][x] for easier placement of viewports + FfxDebugViewport vp[VIEWPORT_GRID_SIZE_Y][VIEWPORT_GRID_SIZE_X]; + for (FfxInt32 y = 0; y < VIEWPORT_GRID_SIZE_Y; y++) + { + for (FfxInt32 x = 0; x < VIEWPORT_GRID_SIZE_X; x++) + { + vp[y][x].offset = iViewportSize * FfxInt32x2(x, y); + vp[y][x].size = iViewportSize; + } + } + + // top row + DRAW_VIEWPORT(drawDilatedMotionVectors, iPxPos, vp[0][0]); + DRAW_VIEWPORT(drawProtectedAreas, iPxPos, vp[0][1]); + DRAW_VIEWPORT(drawDilatedDepthInMeters, iPxPos, vp[0][2]); + + // bottom row + DRAW_VIEWPORT(drawDisocclusionMask, iPxPos, vp[2][0]); + DRAW_VIEWPORT(drawReactiveness, iPxPos, vp[2][1]); + DRAW_VIEWPORT(drawDetailProtectionTakedown, iPxPos, vp[2][2]); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h.meta new file mode 100644 index 0000000..3ec32db --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_debug_view.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 9c5c756c93605b6428eaae6c8aaabbe5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h new file mode 100644 index 0000000..624b7ca --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h @@ -0,0 +1,115 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +struct LumaInstabilityFactorData +{ + FfxFloat32x4 fLumaHistory; + FfxFloat32 fLumaInstabilityFactor; +}; + +LumaInstabilityFactorData ComputeLumaInstabilityFactor(LumaInstabilityFactorData data, FfxFloat32 fCurrentFrameLuma, FfxFloat32 fFarthestDepthInMeters) +{ + const FfxInt32 N_MINUS_1 = 0; + const FfxInt32 N_MINUS_2 = 1; + const FfxInt32 N_MINUS_3 = 2; + const FfxInt32 N_MINUS_4 = 3; + + FfxFloat32 fLumaInstability = 0.0f; + const FfxFloat32 fDiffs0 = (fCurrentFrameLuma - data.fLumaHistory[N_MINUS_1]); + const FfxFloat32 fSimilarity0 = MinDividedByMax(fCurrentFrameLuma, data.fLumaHistory[N_MINUS_1], 1.0f); + + FfxFloat32 fMaxSimilarity = fSimilarity0; + + if (fSimilarity0 < 1.0f) { + for (int i = N_MINUS_2; i <= N_MINUS_4; i++) { + const FfxFloat32 fDiffs1 = (fCurrentFrameLuma - data.fLumaHistory[i]); + const FfxFloat32 fSimilarity1 = MinDividedByMax(fCurrentFrameLuma, data.fLumaHistory[i]); + + if (sign(fDiffs0) == sign(fDiffs1)) { + + fMaxSimilarity = ffxMax(fMaxSimilarity, fSimilarity1); + } + } + + fLumaInstability = FfxFloat32(fMaxSimilarity > fSimilarity0); + } + + // Shift history + data.fLumaHistory[N_MINUS_4] = data.fLumaHistory[N_MINUS_3]; + data.fLumaHistory[N_MINUS_3] = data.fLumaHistory[N_MINUS_2]; + data.fLumaHistory[N_MINUS_2] = data.fLumaHistory[N_MINUS_1]; + data.fLumaHistory[N_MINUS_1] = fCurrentFrameLuma; + + data.fLumaHistory /= Exposure(); + + data.fLumaInstabilityFactor = fLumaInstability * FfxFloat32(data.fLumaHistory[N_MINUS_4] != 0); + + return data; +} + +void LumaInstability(FfxInt32x2 iPxPos) +{ + LumaInstabilityFactorData data; + data.fLumaInstabilityFactor = 0.0f; + data.fLumaHistory = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f); + + const FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(iPxPos); + const FfxFloat32x2 fUv = (iPxPos + 0.5f) / RenderSize(); + const FfxFloat32x2 fUvCurrFrameJittered = fUv + Jitter() / RenderSize(); + const FfxFloat32x2 fUvPrevFrameJittered = fUv + PreviousFrameJitter() / PreviousFrameRenderSize(); + const FfxFloat32x2 fReprojectedUv = fUvPrevFrameJittered + fDilatedMotionVector; + + if (IsUvInside(fReprojectedUv)) + { + const FfxFloat32x2 fUvReactive_HW = ClampUv(fUvCurrFrameJittered, RenderSize(), MaxRenderSize()); + + const FfxFloat32x4 fDilatedReactiveMasks = SampleDilatedReactiveMasks(fUvReactive_HW); + const FfxFloat32 fReactiveMask = ffxSaturate(fDilatedReactiveMasks[REACTIVE]); + const FfxFloat32 fDisocclusion = ffxSaturate(fDilatedReactiveMasks[DISOCCLUSION]); + const FfxFloat32 fShadingChange = ffxSaturate(fDilatedReactiveMasks[SHADING_CHANGE]); + const FfxFloat32 fAccumulation = ffxSaturate(fDilatedReactiveMasks[ACCUMULAION]); + + const FfxBoolean bAccumulationFactor = fAccumulation > 0.9f; + + const FfxBoolean bComputeInstability = bAccumulationFactor; + + if (bComputeInstability) { + + const FfxFloat32x2 fUv_HW = ClampUv(fUvCurrFrameJittered, RenderSize(), MaxRenderSize()); + const FfxFloat32 fCurrentFrameLuma = SampleCurrentLuma(fUv_HW) * Exposure(); + + const FfxFloat32x2 fReprojectedUv_HW = ClampUv(fReprojectedUv, PreviousFrameRenderSize(), MaxRenderSize()); + data.fLumaHistory = SampleLumaHistory(fReprojectedUv_HW) * DeltaPreExposure() * Exposure(); + + const FfxFloat32x2 fFarthestDepthUv_HW = ClampUv(fUvCurrFrameJittered, RenderSize() / 2, GetFarthestDepthMip1ResourceDimensions()); + const FfxFloat32 fFarthestDepthInMeters = SampleFarthestDepthMip1(fFarthestDepthUv_HW); + + data = ComputeLumaInstabilityFactor(data, fCurrentFrameLuma, fFarthestDepthInMeters); + + const FfxFloat32 fVelocityWeight = 1.0f - ffxSaturate(Get4KVelocity(fDilatedMotionVector) / 20.0f); + data.fLumaInstabilityFactor *= fVelocityWeight * (1.0f - fDisocclusion) * (1.0f - fReactiveMask) * (1.0f - fShadingChange); + } + } + + StoreLumaHistory(iPxPos, data.fLumaHistory); + StoreLumaInstability(iPxPos, data.fLumaInstabilityFactor); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h.meta new file mode 100644 index 0000000..7df8e8b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_instability.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: c34dbb67d00358d4b9de0a9fb0e0ace2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h new file mode 100644 index 0000000..e8a8c49 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h @@ -0,0 +1,192 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +FFX_GROUPSHARED FfxUInt32 spdCounter; + +void SpdIncreaseAtomicCounter(FfxUInt32 slice) +{ + SPD_IncreaseAtomicCounter(spdCounter); +} + +FfxUInt32 SpdGetAtomicCounter() +{ + return spdCounter; +} + +void SpdResetAtomicCounter(FfxUInt32 slice) +{ + SPD_ResetAtomicCounter(); +} + +#ifndef SPD_PACKED_ONLY +FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16]; + +FFX_STATIC const FfxInt32 LOG_LUMA = 0; +FFX_STATIC const FfxInt32 LUMA = 1; +FFX_STATIC const FfxInt32 DEPTH_IN_METERS = 2; + +FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 iPxPos, FfxUInt32 slice) +{ + //We assume linear data. if non-linear input (sRGB, ...), + //then we should convert to linear first and back to sRGB on output. + const FfxInt32x2 iPxSamplePos = ClampLoad(FfxInt32x2(iPxPos), FfxInt32x2(0, 0), FfxInt32x2(RenderSize())); + + const FfxFloat32 fLuma = LoadCurrentLuma(iPxSamplePos); + const FfxFloat32 fLogLuma = ffxMax(FSR3UPSCALER_EPSILON, log(fLuma)); + const FfxFloat32 fFarthestDepthInMeters = LoadFarthestDepth(iPxSamplePos); + + FfxFloat32x4 fOutput = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f); + fOutput[LOG_LUMA] = fLogLuma; + fOutput[LUMA] = fLuma; + fOutput[DEPTH_IN_METERS] = fFarthestDepthInMeters; + + return fOutput; +} + +FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice) +{ + return FfxFloat32x4(RWLoadPyramid(tex, 5), 0, 0); +} + +FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) +{ + return (v0 + v1 + v2 + v3) * 0.25f; +} + +void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice) +{ + if (index == 5) + { + StorePyramid(pix, outValue.xy, index); + } + else if (index == 0) { + StoreFarthestDepthMip1(pix, outValue[DEPTH_IN_METERS]); + } + + if (index == MipCount() - 1) { //accumulate on 1x1 level + + if (all(FFX_EQUAL(pix, FfxInt32x2(0, 0)))) + { + FfxFloat32x4 frameInfo = LoadFrameInfo(); + const FfxFloat32 fSceneAvgLuma = outValue[LUMA]; + const FfxFloat32 fPrevLogLuma = frameInfo[FRAME_INFO_LOG_LUMA]; + FfxFloat32 fLogLuma = outValue[LOG_LUMA]; + + if (fPrevLogLuma < resetAutoExposureAverageSmoothing) // Compare Lavg, so small or negative values + { + fLogLuma = fPrevLogLuma + (fLogLuma - fPrevLogLuma) * (1.0f - exp(-DeltaTime())); + fLogLuma = ffxMax(0.0f, fLogLuma); + } + + frameInfo[FRAME_INFO_EXPOSURE] = ComputeAutoExposureFromLavg(fLogLuma); + frameInfo[FRAME_INFO_LOG_LUMA] = fLogLuma; + frameInfo[FRAME_INFO_SCENE_AVERAGE_LUMA] = fSceneAvgLuma; + + StoreFrameInfo(frameInfo); + } + } +} + +FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat32x4( + spdIntermediateR[x][y], + spdIntermediateG[x][y], + spdIntermediateB[x][y], + spdIntermediateA[x][y]); +} +void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) +{ + spdIntermediateR[x][y] = value.x; + spdIntermediateG[x][y] = value.y; + spdIntermediateB[x][y] = value.z; + spdIntermediateA[x][y] = value.w; +} + +#endif + +// define fetch and store functions Packed +#if FFX_HALF + +FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16]; +FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16]; + +FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice) +{ + return FfxFloat16x4(0, 0, 0, 0); +} + +FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice) +{ + return FfxFloat16x4(0, 0, 0, 0); +} + +void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice) +{ +} + +FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat16x4( + spdIntermediateRG[x][y].x, + spdIntermediateRG[x][y].y, + spdIntermediateBA[x][y].x, + spdIntermediateBA[x][y].y); +} + +void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value) +{ + spdIntermediateRG[x][y] = value.xy; + spdIntermediateBA[x][y] = value.zw; +} + +FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3) +{ + return (v0 + v1 + v2 + v3) * FfxFloat16(0.25); +} +#endif + +#include "../spd/ffx_spd.h" + +void ComputeAutoExposure(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex) +{ +#if FFX_HALF + SpdDownsampleH( + FfxUInt32x2(WorkGroupId.xy), + FfxUInt32(LocalThreadIndex), + FfxUInt32(MipCount()), + FfxUInt32(NumWorkGroups()), + FfxUInt32(WorkGroupId.z), + FfxUInt32x2(WorkGroupOffset())); +#else + SpdDownsample( + FfxUInt32x2(WorkGroupId.xy), + FfxUInt32(LocalThreadIndex), + FfxUInt32(MipCount()), + FfxUInt32(NumWorkGroups()), + FfxUInt32(WorkGroupId.z), + FfxUInt32x2(WorkGroupOffset())); +#endif +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h.meta new file mode 100644 index 0000000..72f45f7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_luma_pyramid.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 5138e351a00fc914f973d2495f9aa07f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h new file mode 100644 index 0000000..59c765b --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h @@ -0,0 +1,152 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +void ReconstructPrevDepth(FfxInt32x2 iPxPos, FfxFloat32 fDepth, FfxFloat32x2 fMotionVector) +{ + const FfxFloat32 fNearestDepthInMeters = ffxMin(GetViewSpaceDepthInMeters(fDepth), FSR3UPSCALER_FP16_MAX); + const FfxFloat32 fReconstructedDeptMvThreshold = ReconstructedDepthMvPxThreshold(fNearestDepthInMeters); + + // Discard small mvs + fMotionVector *= FfxFloat32(Get4KVelocity(fMotionVector) > fReconstructedDeptMvThreshold); + + const FfxFloat32x2 fUv = (iPxPos + FfxFloat32(0.5)) / RenderSize(); + const FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; + const BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize()); + + // Project current depth into previous frame locations. + // Push to all pixels having some contribution if reprojection is using bilinear logic. + for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { + + const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; + const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; + + if (fWeight > fReconstructedDepthBilinearWeightThreshold) { + + const FfxInt32x2 iStorePos = bilinearInfo.iBasePos + iOffset; + if (IsOnScreen(iStorePos, RenderSize())) { + StoreReconstructedDepth(iStorePos, fDepth); + } + } + } +} + +struct DepthExtents +{ + FfxFloat32 fNearest; + FfxInt32x2 fNearestCoord; + FfxFloat32 fFarthest; +}; + +DepthExtents FindDepthExtents(FFX_PARAMETER_IN FfxInt32x2 iPxPos) +{ + DepthExtents extents; + const FfxInt32 iSampleCount = 9; + const FfxInt32x2 iSampleOffsets[iSampleCount] = { + FfxInt32x2(+0, +0), + FfxInt32x2(+1, +0), + FfxInt32x2(+0, +1), + FfxInt32x2(+0, -1), + FfxInt32x2(-1, +0), + FfxInt32x2(-1, +1), + FfxInt32x2(+1, +1), + FfxInt32x2(-1, -1), + FfxInt32x2(+1, -1), + }; + + // pull out the depth loads to allow SC to batch them + FfxFloat32 depth[9]; + FfxInt32 iSampleIndex = 0; + FFX_UNROLL + for (iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) { + + FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; + depth[iSampleIndex] = LoadInputDepth(iPos); + } + + // find closest depth + extents.fNearestCoord = iPxPos; + extents.fNearest = depth[0]; + extents.fFarthest = depth[0]; + FFX_UNROLL + for (iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex) { + + const FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; + if (IsOnScreen(iPos, RenderSize())) { + + FfxFloat32 fNdDepth = depth[iSampleIndex]; +#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH + if (fNdDepth > extents.fNearest) { + extents.fFarthest = ffxMin(extents.fFarthest, fNdDepth); +#else + if (fNdDepth < extents.fNearest) { + extents.fFarthest = ffxMax(extents.fFarthest, fNdDepth); +#endif + extents.fNearestCoord = iPos; + extents.fNearest = fNdDepth; + } + } + } + + return extents; +} + +FfxFloat32x2 DilateMotionVector(FfxInt32x2 iPxPos, const DepthExtents depthExtents) +{ +#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS + const FfxInt32x2 iSamplePos = iPxPos; + const FfxInt32x2 iMotionVectorPos = depthExtents.fNearestCoord; +#else + const FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxPos); + const FfxInt32x2 iMotionVectorPos = ComputeHrPosFromLrPos(depthExtents.fNearestCoord); +#endif + + const FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iMotionVectorPos); + + return fDilatedMotionVector; +} + +FfxFloat32 GetCurrentFrameLuma(FfxInt32x2 iPxPos) +{ + //We assume linear data. if non-linear input (sRGB, ...), + //then we should convert to linear first and back to sRGB on output. + const FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxPos)); + const FfxFloat32 fLuma = RGBToLuma(fRgb); + + return fLuma; +} + +void PrepareInputs(FfxInt32x2 iPxPos) +{ + const DepthExtents depthExtents = FindDepthExtents(iPxPos); + const FfxFloat32x2 fDilatedMotionVector = DilateMotionVector(iPxPos, depthExtents); + + ReconstructPrevDepth(iPxPos, depthExtents.fNearest, fDilatedMotionVector); + + StoreDilatedMotionVector(iPxPos, fDilatedMotionVector); + StoreDilatedDepth(iPxPos, depthExtents.fNearest); + + const FfxFloat32 fFarthestDepthInMeters = ffxMin(GetViewSpaceDepthInMeters(depthExtents.fFarthest), FSR3UPSCALER_FP16_MAX); + StoreFarthestDepth(iPxPos, fFarthestDepthInMeters); + + const FfxFloat32 fLuma = GetCurrentFrameLuma(iPxPos); + StoreCurrentLuma(iPxPos, fLuma); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h.meta new file mode 100644 index 0000000..9e7a5db --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_inputs.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: b08f1fd2bbf082242b930cb94652872c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h new file mode 100644 index 0000000..fa9571d --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h @@ -0,0 +1,270 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +FfxFloat32 ComputeDisocclusions(FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxFloat32 fCurrentDepthViewSpace) +{ + const FfxFloat32 fNearestDepthInMeters = ffxMin(fCurrentDepthViewSpace * ViewSpaceToMetersFactor(), FSR3UPSCALER_FP16_MAX); + const FfxFloat32 fReconstructedDeptMvThreshold = ReconstructedDepthMvPxThreshold(fNearestDepthInMeters); + + fMotionVector *= FfxFloat32(Get4KVelocity(fMotionVector) > fReconstructedDeptMvThreshold); + + const FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; + const BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize()); + + FfxFloat32 fDisocclusion = 0.0f; + FfxFloat32 fWeightSum = 0.0f; + FfxBoolean bPotentialDisocclusion = true; + + for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4 && bPotentialDisocclusion; iSampleIndex++) + { + + const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; + const FfxInt32x2 iSamplePos = ClampLoad(bilinearInfo.iBasePos, iOffset, FfxInt32x2(RenderSize())); + + if (IsOnScreen(iSamplePos, RenderSize())) { + const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; + if (fWeight > fReconstructedDepthBilinearWeightThreshold) { + + const FfxFloat32 fPrevNearestDepthViewSpace = GetViewSpaceDepth(LoadReconstructedPrevDepth(iSamplePos)); + const FfxFloat32 fDepthDifference = fCurrentDepthViewSpace - fPrevNearestDepthViewSpace; + + bPotentialDisocclusion = bPotentialDisocclusion && (fDepthDifference > FSR3UPSCALER_FP32_MIN); + + if (bPotentialDisocclusion) { + const FfxFloat32 fHalfViewportWidth = length(FfxFloat32x2(RenderSize()) * 0.5f); + const FfxFloat32 fDepthThreshold = ffxMax(fCurrentDepthViewSpace, fPrevNearestDepthViewSpace); + + const FfxFloat32 Ksep = 1.37e-05f; + const FfxFloat32 fRequiredDepthSeparation = Ksep * fHalfViewportWidth * fDepthThreshold; + + fDisocclusion += ffxSaturate(FfxFloat32(fRequiredDepthSeparation / fDepthDifference)) * fWeight; + fWeightSum += fWeight; + } + } + } + } + + fDisocclusion = (bPotentialDisocclusion && fWeightSum > 0) ? ffxSaturate(1.0f - fDisocclusion / fWeightSum) : 0.0f; + + return fDisocclusion; +} + +FfxFloat32 ComputeMotionDivergence(FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxFloat32 fCurrentDepthSample) +{ + const FfxInt32x2 iPxReprojectedPos = FfxInt32x2((fUv + fMotionVector) * RenderSize()); + const FfxFloat32 fReprojectedDepth = LoadDilatedDepth(iPxReprojectedPos); + const FfxFloat32x2 fReprojectedMotionVector = LoadDilatedMotionVector(iPxReprojectedPos); + + const FfxFloat32 fReprojectedVelocity = Get4KVelocity(fReprojectedMotionVector); + const FfxFloat32 f4KVelocity = Get4KVelocity(fMotionVector); + + const FfxFloat32 fMaxLen = max(length(fMotionVector), length(fReprojectedMotionVector)); + + const FfxFloat32 fNucleusDepthInMeters = GetViewSpaceDepthInMeters(fReprojectedDepth); + const FfxFloat32 fCurrentDepthInMeters = GetViewSpaceDepthInMeters(fCurrentDepthSample); + + const FfxFloat32 fDistanceFactor = MinDividedByMax(fNucleusDepthInMeters, fCurrentDepthInMeters); + const FfxFloat32 fVelocityFactor = ffxSaturate(f4KVelocity / 10.0f); + const FfxFloat32 fMotionVectorFieldConfidence = (1.0f - ffxSaturate(fReprojectedVelocity / f4KVelocity)) * fDistanceFactor * fVelocityFactor; + + return fMotionVectorFieldConfidence; +} + +FfxFloat32 DilateReactiveMasks(FfxInt32x2 iPxPos, FfxFloat32x2 fUv) +{ + FfxFloat32 fDilatedReactiveMasks = 0.0f; + + FFX_UNROLL + for (FfxInt32 y = -1; y <=1; y++) + { + FFX_UNROLL + for (FfxInt32 x = -1; x <= 1; x++) + { + const FfxInt32x2 sampleCoord = ClampLoad(iPxPos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); + fDilatedReactiveMasks = ffxMax(fDilatedReactiveMasks, LoadReactiveMask(sampleCoord)); + } + } + + return fDilatedReactiveMasks; +} + +FfxFloat32 DilateTransparencyAndCompositionMasks(FfxInt32x2 iPxPos, FfxFloat32x2 fUv) +{ + const FfxFloat32x2 fUvTransparencyAndCompositionMask = ClampUv(fUv, RenderSize(), GetTransparencyAndCompositionMaskResourceDimensions()); + return SampleTransparencyAndCompositionMask(fUvTransparencyAndCompositionMask); +} + +FfxFloat32 ComputeThinFeatureConfidence(FfxInt32x2 iPxPos) +{ + /* + 1 2 3 + 4 0 5 + 6 7 8 + */ + + const FfxInt32 iNucleusIndex = 0; + const FfxInt32 iSampleCount = 9; + const FfxInt32x2 iSampleOffsets[iSampleCount] = { + FfxInt32x2(+0, +0), + FfxInt32x2(-1, -1), + FfxInt32x2(+0, -1), + FfxInt32x2(+1, -1), + FfxInt32x2(-1, +0), + FfxInt32x2(+1, +0), + FfxInt32x2(-1, +1), + FfxInt32x2(+0, +1), + FfxInt32x2(+1, +1), + }; + + FfxFloat32 fSamples[iSampleCount]; + + FfxFloat32 fLumaMin = FSR3UPSCALER_FP32_MAX; + FfxFloat32 fLumaMax = FSR3UPSCALER_FP32_MIN; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) { + const FfxInt32x2 iPxSamplePos = ClampLoad(iPxPos, iSampleOffsets[iSampleIndex], FfxInt32x2(RenderSize())); + fSamples[iSampleIndex] = LoadCurrentLuma(iPxSamplePos) * Exposure(); + + fLumaMin = ffxMin(fLumaMin, fSamples[iSampleIndex]); + fLumaMax = ffxMax(fLumaMax, fSamples[iSampleIndex]); + } + + const FfxFloat32 fThreshold = 0.9f; + FfxFloat32 fDissimilarLumaMin = FSR3UPSCALER_FP32_MAX; + FfxFloat32 fDissimilarLumaMax = 0; + +#define SETBIT(x) (1U << x) + + FfxUInt32 uPatternMask = SETBIT(iNucleusIndex); // Flag nucleus as similar + + const FfxUInt32 uNumRejectionMasks = 4; + const FfxUInt32 uRejectionMasks[uNumRejectionMasks] = { + SETBIT(1) | SETBIT(2) | SETBIT(4) | SETBIT(iNucleusIndex), // Upper left + SETBIT(2) | SETBIT(3) | SETBIT(5) | SETBIT(iNucleusIndex), // Upper right + SETBIT(4) | SETBIT(6) | SETBIT(7) | SETBIT(iNucleusIndex), // Lower left + SETBIT(5) | SETBIT(7) | SETBIT(8) | SETBIT(iNucleusIndex) // Lower right + }; + + FfxInt32 iBitIndex = 1; + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex, ++iBitIndex) { + + const FfxFloat32 fDifference = abs(fSamples[iSampleIndex] - fSamples[iNucleusIndex]) / (fLumaMax - fLumaMin); + + if (fDifference < fThreshold) + { + uPatternMask |= SETBIT(iBitIndex); + } + else + { + fDissimilarLumaMin = ffxMin(fDissimilarLumaMin, fSamples[iSampleIndex]); + fDissimilarLumaMax = ffxMax(fDissimilarLumaMax, fSamples[iSampleIndex]); + } + } + + const FfxBoolean bIsRidge = fSamples[iNucleusIndex] > fDissimilarLumaMax || fSamples[iNucleusIndex] < fDissimilarLumaMin; + + if (FFX_FALSE == bIsRidge) + { + return 0.0f; + } + + FFX_UNROLL + for (FfxInt32 i = 0; i < uNumRejectionMasks; i++) + { + if ((uPatternMask & uRejectionMasks[i]) == uRejectionMasks[i]) + { + return 0.0f; + } + } + + return 1.0f - fLumaMin / fLumaMax; +} + +FfxFloat32 UpdateAccumulation(FfxInt32x2 iPxPos, FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector, FfxFloat32 fDisocclusion, FfxFloat32 fShadingChange) +{ + const FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; + FfxFloat32 fAccumulation = 0.0f; + + if (IsUvInside(fReprojectedUv)) { + const FfxFloat32x2 fReprojectedUv_HW = ClampUv(fReprojectedUv, PreviousFrameRenderSize(), MaxRenderSize()); + fAccumulation = ffxSaturate(SampleAccumulation(fReprojectedUv_HW)); + } + + fAccumulation = ffxLerp(fAccumulation, 0.0f, fShadingChange); + fAccumulation = ffxLerp(fAccumulation, ffxMin(fAccumulation, 0.25f), fDisocclusion); + + fAccumulation *= FfxFloat32(round(fAccumulation * 100.0f) > 1.0f); + + // Update for next frame, normalize to store in unorm + const FfxFloat32 fAccumulatedFramesMax = 3.0f; + const FfxFloat32 fAccumulatedFramesToStore = ffxSaturate(fAccumulation + (1.0f / fAccumulatedFramesMax)); + StoreAccumulation(iPxPos, fAccumulatedFramesToStore); + + return fAccumulation; +} + +FfxFloat32 ComputeShadingChange(FfxFloat32x2 fUv) +{ + // NOTE: Here we re-apply jitter, will be reverted again when sampled in accumulation pass + const FfxFloat32x2 fShadingChangeUv = ClampUv(fUv - Jitter() / RenderSize(), ShadingChangeRenderSize(), ShadingChangeMaxRenderSize()); + const FfxFloat32 fShadingChange = ffxSaturate(SampleShadingChange(fShadingChangeUv)); + + return fShadingChange; +} + +void PrepareReactivity(FfxInt32x2 iPxPos) +{ + const FfxFloat32x2 fUv = (iPxPos + 0.5f) / RenderSize(); + const FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); + + // Discard small mvs + const FfxFloat32 f4KVelocity = Get4KVelocity(fMotionVector); + + const FfxFloat32x2 fDilatedUv = fUv + fMotionVector; + const FfxFloat32 fDilatedDepth = LoadDilatedDepth(iPxPos); + const FfxFloat32 fDepthInMeters = GetViewSpaceDepthInMeters(fDilatedDepth); + + const FfxFloat32 fDisocclusion = ComputeDisocclusions(fUv, fMotionVector, GetViewSpaceDepth(fDilatedDepth)); + const FfxFloat32 fShadingChange = ffxMax(DilateReactiveMasks(iPxPos, fUv), ComputeShadingChange(fUv)); + + const FfxFloat32 fMotionDivergence = ComputeMotionDivergence(fUv, fMotionVector, fDilatedDepth); + const FfxFloat32 fDilatedTransparencyAndComposition = DilateTransparencyAndCompositionMasks(iPxPos, fUv); + const FfxFloat32 fFinalReactiveness = ffxMax(fMotionDivergence, fDilatedTransparencyAndComposition); + + const FfxFloat32 fAccumulation = UpdateAccumulation(iPxPos, fUv, fMotionVector, fDisocclusion, fShadingChange); + + FfxFloat32x4 fOutput; + fOutput[REACTIVE] = fFinalReactiveness; + fOutput[DISOCCLUSION] = fDisocclusion; + fOutput[SHADING_CHANGE] = fShadingChange; + fOutput[ACCUMULAION] = fAccumulation; + + StoreDilatedReactiveMasks(iPxPos, fOutput); + + const FfxFloat32 fLockStrength = ComputeThinFeatureConfidence(iPxPos); + if (fLockStrength > (1.0f / 100.0f)) + { + StoreNewLocks(ComputeHrPosFromLrPos(FfxInt32x2(iPxPos)), fLockStrength); + } +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h.meta new file mode 100644 index 0000000..46fcfc6 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_prepare_reactivity.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 0070b6300195a7649a899fe0ed099c82 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h new file mode 100644 index 0000000..90a85b3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h @@ -0,0 +1,67 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#define GROUP_SIZE 8 +#define FSR_RCAS_DENOISE 1 + +#include "../ffx_core.h" + +void WriteUpscaledOutput(FFX_MIN16_U2 iPxHrPos, FfxFloat32x3 fUpscaledColor) +{ + StoreUpscaledOutput(FFX_MIN16_I2(iPxHrPos), fUpscaledColor); +} + +#define FSR_RCAS_F 1 +FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p) +{ + FfxFloat32x4 fColor = LoadRCAS_Input(p); + + fColor.rgb *= Exposure(); + + return fColor; +} +void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {} + +#include "../fsr1/ffx_fsr1.h" + +void CurrFilter(FFX_MIN16_U2 pos) +{ + FfxFloat32x3 c; + FsrRcasF(c.r, c.g, c.b, pos, RCASConfig()); + + c /= Exposure(); + + WriteUpscaledOutput(pos, c); +} + +void RCAS(FfxUInt32x3 LocalThreadId, FfxUInt32x3 WorkGroupId, FfxUInt32x3 Dtid) +{ + // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. + FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u); + CurrFilter(FFX_MIN16_U2(gxy)); + gxy.x += 8u; + CurrFilter(FFX_MIN16_U2(gxy)); + gxy.y += 8u; + CurrFilter(FFX_MIN16_U2(gxy)); + gxy.x -= 8u; + CurrFilter(FFX_MIN16_U2(gxy)); +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta new file mode 100644 index 0000000..f21aeb7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: dc2e80d2251e46c4d9af5696c3061fab +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h new file mode 100644 index 0000000..153a9b7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h @@ -0,0 +1,64 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE +#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 0 // Reference +#endif + +FfxFloat32x4 WrapHistory(FfxInt32x2 iPxSample) +{ + return LoadHistory(iPxSample); +} + +DeclareCustomFetchBicubicSamples(FetchHistorySamples, WrapHistory) +DeclareCustomTextureSample(HistorySample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) + +FfxFloat32x2 GetMotionVector(FfxInt32x2 iPxHrPos, FfxFloat32x2 fHrUv) +{ +#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS + const FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(FFX_MIN16_I2(fHrUv * RenderSize())); +#else + const FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iPxHrPos); +#endif + + return fDilatedMotionVector; +} + +void ComputeReprojectedUVs(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedHrUv, FFX_PARAMETER_OUT FfxBoolean bIsExistingSample) +{ + fReprojectedHrUv = params.fHrUv + params.fMotionVector; + + bIsExistingSample = IsUvInside(fReprojectedHrUv); +} + +void ReprojectHistoryColor(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) +{ + const FfxFloat32x4 fReprojectedHistory = HistorySample(params.fReprojectedHrUv, PreviousFrameUpscaleSize()); + + data.fHistoryColor = fReprojectedHistory.rgb; + data.fHistoryColor *= DeltaPreExposure(); + data.fHistoryColor *= Exposure(); + + data.fHistoryColor = RGBToYCoCg(data.fHistoryColor); + + data.fLock = fReprojectedHistory.w; +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta new file mode 100644 index 0000000..cb9c6d7 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 2bcf78cbd908b664cb9c656e28b430d4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h new file mode 100644 index 0000000..b3d8ddb --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h @@ -0,0 +1,100 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR3UPSCALER_RESOURCES_H +#define FFX_FSR3UPSCALER_RESOURCES_H + +#if defined(FFX_CPU) || defined(FFX_GPU) +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NULL 0 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_OPAQUE_ONLY 1 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_COLOR 2 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 3 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_DEPTH 4 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_EXPOSURE 5 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_REACTIVE_MASK 6 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_TRANSPARENCY_AND_COMPOSITION_MASK 7 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RECONSTRUCTED_PREVIOUS_NEAREST_DEPTH 8 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_MOTION_VECTORS 9 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_DEPTH 10 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR 11 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_ACCUMULATION 12 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NEW_LOCKS 13 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY 14 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DEBUG_OUTPUT 15 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LANCZOS_LUT 16 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_ATOMIC_COUNT 17 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_UPSCALED_OUTPUT 18 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RCAS_INPUT 19 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_ACCUMULATION_1 20 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_ACCUMULATION_2 21 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_1 22 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_2 23 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_REACTIVITY 24 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_TRANSPARENCY_AND_COMPOSITION 25 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_REACTIVE_MASKS 26 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS 27 // same as FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_0 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_0 27 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_1 28 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_2 29 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_3 30 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_4 31 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_MIPS_LEVEL_5 32 + +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_EXPOSURE 33 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_FRAME_INFO 34 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOREACTIVE 35 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOCOMPOSITION_DEPRECATED 36 + +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_1 37 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_2 38 + +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_1 40 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_2 41 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SHADING_CHANGE 42 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_FARTHEST_DEPTH 43 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_FARTHEST_DEPTH_MIP1 44 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_CURRENT_LUMA 45 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREVIOUS_LUMA 46 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_INSTABILITY 48 +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERMEDIATE_FP16x1 49 + + +// Shading change detection mip level setting, value must be in the range [FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0, FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12] +//#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 +//#define FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL (FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE - FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE) + +#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT 60 + +#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_FSR3UPSCALER 0 +#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_SPD 1 +#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_RCAS 2 +#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_GENREACTIVE 3 +#define FFX_FSR3UPSCALER_CONSTANTBUFFER_COUNT 4 + +#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP 1 +#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP 2 +#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD 4 +#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX 8 + +#endif // #if defined(FFX_CPU) || defined(FFX_GPU) + +#endif //!defined( FFX_FSR3UPSCALER_RESOURCES_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta new file mode 100644 index 0000000..591d1d8 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 0a6f299994574d641a6a2f864d6b78b7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h new file mode 100644 index 0000000..5f727b1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h @@ -0,0 +1,602 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +#ifndef FFX_FSR3UPSCALER_SAMPLE_H +#define FFX_FSR3UPSCALER_SAMPLE_H + +// suppress warnings +#ifdef FFX_HLSL +#pragma warning(disable: 4008) // potentially divide by zero +#endif //FFX_HLSL + +struct FetchedBilinearSamples { + + FfxFloat32x4 fColor00; + FfxFloat32x4 fColor10; + + FfxFloat32x4 fColor01; + FfxFloat32x4 fColor11; +}; + +struct FetchedBicubicSamples { + + FfxFloat32x4 fColor00; + FfxFloat32x4 fColor10; + FfxFloat32x4 fColor20; + FfxFloat32x4 fColor30; + + FfxFloat32x4 fColor01; + FfxFloat32x4 fColor11; + FfxFloat32x4 fColor21; + FfxFloat32x4 fColor31; + + FfxFloat32x4 fColor02; + FfxFloat32x4 fColor12; + FfxFloat32x4 fColor22; + FfxFloat32x4 fColor32; + + FfxFloat32x4 fColor03; + FfxFloat32x4 fColor13; + FfxFloat32x4 fColor23; + FfxFloat32x4 fColor33; +}; + +#if FFX_HALF +struct FetchedBilinearSamplesMin16 { + + FFX_MIN16_F4 fColor00; + FFX_MIN16_F4 fColor10; + + FFX_MIN16_F4 fColor01; + FFX_MIN16_F4 fColor11; +}; + +struct FetchedBicubicSamplesMin16 { + + FFX_MIN16_F4 fColor00; + FFX_MIN16_F4 fColor10; + FFX_MIN16_F4 fColor20; + FFX_MIN16_F4 fColor30; + + FFX_MIN16_F4 fColor01; + FFX_MIN16_F4 fColor11; + FFX_MIN16_F4 fColor21; + FFX_MIN16_F4 fColor31; + + FFX_MIN16_F4 fColor02; + FFX_MIN16_F4 fColor12; + FFX_MIN16_F4 fColor22; + FFX_MIN16_F4 fColor32; + + FFX_MIN16_F4 fColor03; + FFX_MIN16_F4 fColor13; + FFX_MIN16_F4 fColor23; + FFX_MIN16_F4 fColor33; +}; +#else //FFX_HALF +#define FetchedBicubicSamplesMin16 FetchedBicubicSamples +#define FetchedBilinearSamplesMin16 FetchedBilinearSamples +#endif //FFX_HALF + +FfxFloat32x4 Linear(FfxFloat32x4 A, FfxFloat32x4 B, FfxFloat32 t) +{ + return A + (B - A) * t; +} + +FfxFloat32x4 Bilinear(FetchedBilinearSamples BilinearSamples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); + FfxFloat32x4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); + FfxFloat32x4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Linear(FFX_MIN16_F4 A, FFX_MIN16_F4 B, FFX_MIN16_F t) +{ + return A + (B - A) * t; +} + +FFX_MIN16_F4 Bilinear(FetchedBilinearSamplesMin16 BilinearSamples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); + return fColorXY; +} +#endif + +FfxFloat32 Lanczos2NoClamp(FfxFloat32 x) +{ + const FfxFloat32 PI = 3.141592653589793f; // TODO: share SDK constants + return abs(x) < FSR3UPSCALER_EPSILON ? 1.f : (sin(PI * x) / (PI * x)) * (sin(0.5f * PI * x) / (0.5f * PI * x)); +} + +FfxFloat32 Lanczos2(FfxFloat32 x) +{ + x = ffxMin(abs(x), 2.0f); + return Lanczos2NoClamp(x); +} + +#if FFX_HALF + +#if 0 +FFX_MIN16_F Lanczos2NoClamp(FFX_MIN16_F x) +{ + const FFX_MIN16_F PI = FFX_MIN16_F(3.141592653589793f); // TODO: share SDK constants + return abs(x) < FFX_MIN16_F(FSR3UPSCALER_EPSILON) ? FFX_MIN16_F(1.f) : (sin(PI * x) / (PI * x)) * (sin(FFX_MIN16_F(0.5f) * PI * x) / (FFX_MIN16_F(0.5f) * PI * x)); +} +#endif + +FFX_MIN16_F Lanczos2(FFX_MIN16_F x) +{ + x = ffxMin(abs(x), FFX_MIN16_F(2.0f)); + return FFX_MIN16_F(Lanczos2NoClamp(x)); +} +#endif //FFX_HALF + +// FSR1 lanczos approximation. Input is x*x and must be <= 4. +FfxFloat32 Lanczos2ApproxSqNoClamp(FfxFloat32 x2) +{ + FfxFloat32 a = (2.0f / 5.0f) * x2 - 1; + FfxFloat32 b = (1.0f / 4.0f) * x2 - 1; + return ((25.0f / 16.0f) * a * a - (25.0f / 16.0f - 1)) * (b * b); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2ApproxSqNoClamp(FFX_MIN16_F x2) +{ + FFX_MIN16_F a = FFX_MIN16_F(2.0f / 5.0f) * x2 - FFX_MIN16_F(1); + FFX_MIN16_F b = FFX_MIN16_F(1.0f / 4.0f) * x2 - FFX_MIN16_F(1); + return (FFX_MIN16_F(25.0f / 16.0f) * a * a - FFX_MIN16_F(25.0f / 16.0f - 1)) * (b * b); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2ApproxSq(FfxFloat32 x2) +{ + x2 = ffxMin(x2, 4.0f); + return Lanczos2ApproxSqNoClamp(x2); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2ApproxSq(FFX_MIN16_F x2) +{ + x2 = ffxMin(x2, FFX_MIN16_F(4.0f)); + return Lanczos2ApproxSqNoClamp(x2); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2ApproxNoClamp(FfxFloat32 x) +{ + return Lanczos2ApproxSqNoClamp(x * x); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2ApproxNoClamp(FFX_MIN16_F x) +{ + return Lanczos2ApproxSqNoClamp(x * x); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2Approx(FfxFloat32 x) +{ + return Lanczos2ApproxSq(x * x); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2Approx(FFX_MIN16_F x) +{ + return Lanczos2ApproxSq(x * x); +} +#endif //FFX_HALF + +FfxFloat32 Lanczos2_UseLUT(FfxFloat32 x) +{ + return SampleLanczos2Weight(abs(x)); +} + +#if FFX_HALF +FFX_MIN16_F Lanczos2_UseLUT(FFX_MIN16_F x) +{ + return FFX_MIN16_F(SampleLanczos2Weight(abs(x))); +} +#endif //FFX_HALF + +FfxFloat32x4 Lanczos2_UseLUT(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) +{ + FfxFloat32 fWeight0 = Lanczos2_UseLUT(-1.f - t); + FfxFloat32 fWeight1 = Lanczos2_UseLUT(-0.f - t); + FfxFloat32 fWeight2 = Lanczos2_UseLUT(+1.f - t); + FfxFloat32 fWeight3 = Lanczos2_UseLUT(+2.f - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} +#if FFX_HALF +FFX_MIN16_F4 Lanczos2_UseLUT(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) +{ + FFX_MIN16_F fWeight0 = Lanczos2_UseLUT(FFX_MIN16_F(-1.f) - t); + FFX_MIN16_F fWeight1 = Lanczos2_UseLUT(FFX_MIN16_F(-0.f) - t); + FFX_MIN16_F fWeight2 = Lanczos2_UseLUT(FFX_MIN16_F(+1.f) - t); + FFX_MIN16_F fWeight3 = Lanczos2_UseLUT(FFX_MIN16_F(+2.f) - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} +#endif + +FfxFloat32x4 Lanczos2(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) +{ + FfxFloat32 fWeight0 = Lanczos2(-1.f - t); + FfxFloat32 fWeight1 = Lanczos2(-0.f - t); + FfxFloat32 fWeight2 = Lanczos2(+1.f - t); + FfxFloat32 fWeight3 = Lanczos2(+2.f - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} + +FfxFloat32x4 Lanczos2(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FfxFloat32x4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FfxFloat32x4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FfxFloat32x4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FfxFloat32x4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FfxFloat32x4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; + FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { + + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) +{ + FFX_MIN16_F fWeight0 = Lanczos2(FFX_MIN16_F(-1.f) - t); + FFX_MIN16_F fWeight1 = Lanczos2(FFX_MIN16_F(-0.f) - t); + FFX_MIN16_F fWeight2 = Lanczos2(FFX_MIN16_F(+1.f) - t); + FFX_MIN16_F fWeight3 = Lanczos2(FFX_MIN16_F(+2.f) - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} + +FFX_MIN16_F4 Lanczos2(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FFX_MIN16_F4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FFX_MIN16_F4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FFX_MIN16_F4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; + FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} +#endif //FFX_HALF + + +FfxFloat32x4 Lanczos2LUT(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FfxFloat32x4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FfxFloat32x4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FfxFloat32x4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FfxFloat32x4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FfxFloat32x4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; + FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { + + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2LUT(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FFX_MIN16_F4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FFX_MIN16_F4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FFX_MIN16_F4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; + FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} +#endif //FFX_HALF + + + +FfxFloat32x4 Lanczos2Approx(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) +{ + FfxFloat32 fWeight0 = Lanczos2ApproxNoClamp(-1.f - t); + FfxFloat32 fWeight1 = Lanczos2ApproxNoClamp(-0.f - t); + FfxFloat32 fWeight2 = Lanczos2ApproxNoClamp(+1.f - t); + FfxFloat32 fWeight3 = Lanczos2ApproxNoClamp(+2.f - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2Approx(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) +{ + FFX_MIN16_F fWeight0 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-1.f) - t); + FFX_MIN16_F fWeight1 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-0.f) - t); + FFX_MIN16_F fWeight2 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+1.f) - t); + FFX_MIN16_F fWeight3 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+2.f) - t); + return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); +} +#endif //FFX_HALF + +FfxFloat32x4 Lanczos2Approx(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) +{ + FfxFloat32x4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FfxFloat32x4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FfxFloat32x4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FfxFloat32x4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FfxFloat32x4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FfxFloat32x4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; + FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} + +#if FFX_HALF +FFX_MIN16_F4 Lanczos2Approx(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) +{ + FFX_MIN16_F4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); + FFX_MIN16_F4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); + FFX_MIN16_F4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); + FFX_MIN16_F4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); + FFX_MIN16_F4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); + + // Deringing + + // TODO: only use 4 by checking jitter + const FfxInt32 iDeringingSampleCount = 4; + const FFX_MIN16_F4 fDeringingSamples[4] = { + Samples.fColor11, + Samples.fColor21, + Samples.fColor12, + Samples.fColor22, + }; + + FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; + FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) + { + fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); + fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); + } + + fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); + + return fColorXY; +} +#endif + +// Clamp by offset direction. Assuming iPxSample is already in range and iPxOffset is compile time constant. +FfxInt32x2 ClampCoord(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) +{ + FfxInt32x2 result = iPxSample + iPxOffset; + result.x = ffxMax(1, ffxMin(result.x, iTextureSize.x - 2)); + result.y = ffxMax(1, ffxMin(result.y, iTextureSize.y - 2)); + return result; +} +#if FFX_HALF +FFX_MIN16_I2 ClampCoord(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) +{ + FFX_MIN16_I2 result = iPxSample + iPxOffset; + result.x = ffxMax(FFX_MIN16_I(1), ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(2))); + result.y = ffxMax(FFX_MIN16_I(1), ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(2))); + return result; +} +#endif //FFX_HALF + + +#define DeclareCustomFetchBicubicSamplesWithType(SampleType, TextureType, AddrType, Name, LoadTexture) \ + SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ + { \ + SampleType Samples; \ + \ + Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, -1), iTextureSize))); \ + Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, -1), iTextureSize))); \ + Samples.fColor20 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, -1), iTextureSize))); \ + Samples.fColor30 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, -1), iTextureSize))); \ + \ + Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +0), iTextureSize))); \ + Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ + Samples.fColor21 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ + Samples.fColor31 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +0), iTextureSize))); \ + \ + Samples.fColor02 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +1), iTextureSize))); \ + Samples.fColor12 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ + Samples.fColor22 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ + Samples.fColor32 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +1), iTextureSize))); \ + \ + Samples.fColor03 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +2), iTextureSize))); \ + Samples.fColor13 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +2), iTextureSize))); \ + Samples.fColor23 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +2), iTextureSize))); \ + Samples.fColor33 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +2), iTextureSize))); \ + \ + return Samples; \ + } + +#define DeclareCustomFetchBicubicSamples(Name, LoadTexture) \ + DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) + +#define DeclareCustomFetchBicubicSamplesMin16(Name, LoadTexture) \ + DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) + +#define DeclareCustomFetchBilinearSamplesWithType(SampleType, TextureType,AddrType, Name, LoadTexture) \ + SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ + { \ + SampleType Samples; \ + Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ + Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ + Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ + Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ + return Samples; \ + } + +#define DeclareCustomFetchBilinearSamples(Name, LoadTexture) \ + DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) + +#define DeclareCustomFetchBilinearSamplesMin16(Name, LoadTexture) \ + DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) + +// BE CAREFUL: there is some precision issues and (3253, 125) leading to (3252.9989778, 125.001102) +// is common, so iPxSample can "jitter" +#define DeclareCustomTextureSample(Name, InterpolateSamples, FetchSamples) \ + FfxFloat32x4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ + { \ + FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ + FfxFloat32x2 fPxFrac = ffxFract(fPxSample); \ + /* Clamp base coords */ \ + fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x-1), fPxSample.x)); \ + fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y-1), fPxSample.y)); \ + /* */ \ + FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ + FfxFloat32x4 fColorXY = FfxFloat32x4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ + return fColorXY; \ + } + +#define DeclareCustomTextureSampleMin16(Name, InterpolateSamples, FetchSamples) \ + FFX_MIN16_F4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ + { \ + FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ + FFX_MIN16_F2 fPxFrac = FFX_MIN16_F2(ffxFract(fPxSample)); \ + /* Clamp base coords */ \ + fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ + fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ + /* */ \ + FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ + FFX_MIN16_F4 fColorXY = FFX_MIN16_F4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ + return fColorXY; \ + } + +#define FFX_FSR3UPSCALER_CONCAT_ID(x, y) x ## y +#define FFX_FSR3UPSCALER_CONCAT(x, y) FFX_FSR3UPSCALER_CONCAT_ID(x, y) +#define FFX_FSR3UPSCALER_SAMPLER_1D_0 Lanczos2 +#define FFX_FSR3UPSCALER_SAMPLER_1D_1 Lanczos2LUT +#define FFX_FSR3UPSCALER_SAMPLER_1D_2 Lanczos2Approx + +#define FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(x) FFX_FSR3UPSCALER_CONCAT(FFX_FSR3UPSCALER_SAMPLER_1D_, x) + +#endif //!defined( FFX_FSR3UPSCALER_SAMPLE_H ) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta new file mode 100644 index 0000000..1e2fde8 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 0c406ac147d40644fab8636a35029185 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h new file mode 100644 index 0000000..2eb23aa --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h @@ -0,0 +1,68 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +FFX_STATIC const FfxInt32 s_MipLevelsToUse = 3; + +struct ShadingChangeLumaInfo +{ + FfxFloat32 fSamples[s_MipLevelsToUse]; +}; + +ShadingChangeLumaInfo ComputeShadingChangeLuma(FfxInt32x2 iPxPos, FfxFloat32x2 fUv, const FfxInt32x2 iCurrentSize) +{ + ShadingChangeLumaInfo info; + + const FfxFloat32x2 fMipUv = ClampUv(fUv, ShadingChangeRenderSize(), GetSPDMipDimensions(0)); + + FFX_UNROLL + for (FfxInt32 iMipLevel = iShadingChangeMipStart; iMipLevel < s_MipLevelsToUse; iMipLevel++) { + + const FfxFloat32x2 fSample = SampleSPDMipLevel(fMipUv, iMipLevel); + + info.fSamples[iMipLevel] = abs(fSample.x * fSample.y); + } + + return info; +} + +void ShadingChange(FfxInt32x2 iPxPos) +{ + if (IsOnScreen(FfxInt32x2(iPxPos), ShadingChangeRenderSize())) { + + const FfxFloat32x2 fUv = (iPxPos + 0.5f) / ShadingChangeRenderSize(); + const FfxFloat32x2 fUvJittered = fUv + Jitter() / RenderSize(); + + const ShadingChangeLumaInfo info = ComputeShadingChangeLuma(iPxPos, fUvJittered, ShadingChangeRenderSize()); + + const FfxFloat32 fScale = 1.0f + iShadingChangeMipStart / s_MipLevelsToUse; + FfxFloat32 fShadingChange = 0.0f; + FFX_UNROLL + for (int iMipLevel = iShadingChangeMipStart; iMipLevel < s_MipLevelsToUse; iMipLevel++) + { + if (info.fSamples[iMipLevel] > 0) { + fShadingChange = ffxMax(fShadingChange, info.fSamples[iMipLevel]) * fScale; + } + } + + StoreShadingChange(iPxPos, ffxSaturate(fShadingChange)); + } +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h.meta new file mode 100644 index 0000000..b2c234a --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: d20f9266c072698448f3b51d3d478520 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h new file mode 100644 index 0000000..651c5b3 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h @@ -0,0 +1,299 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +FFX_GROUPSHARED FfxUInt32 spdCounter; + +void SpdIncreaseAtomicCounter(FfxUInt32 slice) +{ + SPD_IncreaseAtomicCounter(spdCounter); +} + +FfxUInt32 SpdGetAtomicCounter() +{ + return spdCounter; +} + +void SpdResetAtomicCounter(FfxUInt32 slice) +{ + SPD_ResetAtomicCounter(); +} + +#ifndef SPD_PACKED_ONLY +FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16]; +FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16]; + +FFX_STATIC const FfxInt32 DIFFERENCE = 0; +FFX_STATIC const FfxInt32 SIGN_SUM = 1; +FFX_STATIC const FfxInt32 MIP0_INDICATOR = 2; + +FfxFloat32x2 Sort2(FfxFloat32x2 v) +{ + return FfxFloat32x2(ffxMin(v.x, v.y), ffxMax(v.x, v.y)); +} + +struct SampleSet +{ + FfxFloat32 fSamples[SHADING_CHANGE_SET_SIZE]; +}; + +#define CompareSwap(i, j) \ +{ \ +FfxFloat32 fTmp = ffxMin(fSet.fSamples[i], fSet.fSamples[j]);\ +fSet.fSamples[j] = ffxMax(fSet.fSamples[i], fSet.fSamples[j]);\ +fSet.fSamples[i] = fTmp;\ +} + +#if SHADING_CHANGE_SET_SIZE == 5 +FFX_STATIC const FfxInt32x2 iSampleOffsets[5] = {FfxInt32x2(+0, +0), FfxInt32x2(-1, +0), FfxInt32x2(+1, +0), FfxInt32x2(+0, -1), FfxInt32x2(+0, +1)}; + +void SortSet(FFX_PARAMETER_INOUT SampleSet fSet) +{ + CompareSwap(0, 3); + CompareSwap(1, 4); + CompareSwap(0, 2); + CompareSwap(1, 3); + CompareSwap(0, 1); + CompareSwap(2, 4); + CompareSwap(1, 2); + CompareSwap(3, 4); + CompareSwap(2, 3); +} +#endif + +FfxFloat32 ComputeMinimumDifference(FfxInt32x2 iPxPos, SampleSet fSet0, SampleSet fSet1) +{ + FfxFloat32 fMinDiff = FSR3UPSCALER_FP16_MAX - 1; + FfxInt32 a = 0; + FfxInt32 b = 0; + + SortSet(fSet0); + SortSet(fSet1); + + const FfxFloat32 fMax = ffxMin(fSet0.fSamples[SHADING_CHANGE_SET_SIZE-1], fSet1.fSamples[SHADING_CHANGE_SET_SIZE-1]); + + if (fMax > FSR3UPSCALER_FP32_MIN) { + + FFX_UNROLL + for (FfxInt32 i = 0; i < SHADING_CHANGE_SET_SIZE && (fMinDiff < FSR3UPSCALER_FP16_MAX); i++) { + + FfxFloat32 fDiff = fSet0.fSamples[a] - fSet1.fSamples[b]; + + if (abs(fDiff) > FSR3UPSCALER_FP16_MIN) { + + fDiff = sign(fDiff) * (1.0f - MinDividedByMax(fSet0.fSamples[a], fSet1.fSamples[b])); + + fMinDiff = (abs(fDiff) < abs(fMinDiff)) ? fDiff : fMinDiff; + + a += FfxInt32(fSet0.fSamples[a] < fSet1.fSamples[b]); + b += FfxInt32(fSet0.fSamples[a] >= fSet1.fSamples[b]); + } + else + { + fMinDiff = FSR3UPSCALER_FP16_MAX; + } + } + } + + return fMinDiff * FfxFloat32(fMinDiff < (FSR3UPSCALER_FP16_MAX - 1)); +} + +SampleSet GetCurrentLumaBilinearSamples(FfxFloat32x2 fUv) +{ + const FfxFloat32x2 fUvJittered = fUv + Jitter() / RenderSize(); + const FfxInt32x2 iBasePos = FfxInt32x2(floor(fUvJittered * RenderSize())); + + SampleSet fSet; + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 0; iSampleIndex < SHADING_CHANGE_SET_SIZE; iSampleIndex++) { + const FfxInt32x2 iSamplePos = ClampLoad(iBasePos, iSampleOffsets[iSampleIndex], RenderSize()); + fSet.fSamples[iSampleIndex] = LoadCurrentLuma(iSamplePos) * Exposure(); + fSet.fSamples[iSampleIndex] = ffxPow(fSet.fSamples[iSampleIndex], fShadingChangeSamplePow); + fSet.fSamples[iSampleIndex] = ffxMax(fSet.fSamples[iSampleIndex], FSR3UPSCALER_EPSILON); + } + + return fSet; +} + +struct PreviousLumaBilinearSamplesData +{ + SampleSet fSet; + FfxBoolean bIsExistingSample; +}; + +PreviousLumaBilinearSamplesData GetPreviousLumaBilinearSamples(FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector) +{ + PreviousLumaBilinearSamplesData data = (PreviousLumaBilinearSamplesData)0; + + const FfxFloat32x2 fUvJittered = fUv + PreviousFrameJitter() / PreviousFrameRenderSize(); + const FfxFloat32x2 fReprojectedUv = fUvJittered + fMotionVector; + + data.bIsExistingSample = IsUvInside(fReprojectedUv); + + if (data.bIsExistingSample) { + + const FfxInt32x2 iBasePos = FfxInt32x2(floor(fReprojectedUv * PreviousFrameRenderSize())); + + FFX_UNROLL + for (FfxInt32 iSampleIndex = 0; iSampleIndex < SHADING_CHANGE_SET_SIZE; iSampleIndex++) { + + const FfxInt32x2 iSamplePos = ClampLoad(iBasePos, iSampleOffsets[iSampleIndex], PreviousFrameRenderSize()); + data.fSet.fSamples[iSampleIndex] = LoadPreviousLuma(iSamplePos) * DeltaPreExposure() * Exposure(); + data.fSet.fSamples[iSampleIndex] = ffxPow(data.fSet.fSamples[iSampleIndex], fShadingChangeSamplePow); + data.fSet.fSamples[iSampleIndex] = ffxMax(data.fSet.fSamples[iSampleIndex], FSR3UPSCALER_EPSILON); + } + } + + return data; +} + +FfxFloat32 ComputeDiff(FfxInt32x2 iPxPos, FfxFloat32x2 fUv, FfxFloat32x2 fMotionVector) +{ + FfxFloat32 fMinDiff = 0.0f; + + const SampleSet fCurrentSamples = GetCurrentLumaBilinearSamples(fUv); + const PreviousLumaBilinearSamplesData previousData = GetPreviousLumaBilinearSamples(fUv, fMotionVector); + + if (previousData.bIsExistingSample) { + fMinDiff = ComputeMinimumDifference(iPxPos, fCurrentSamples, previousData.fSet); + } + + return fMinDiff; +} + +FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 iPxPos, FfxUInt32 slice) +{ + const FfxInt32x2 iPxSamplePos = ClampLoad(FfxInt32x2(iPxPos), FfxInt32x2(0, 0), FfxInt32x2(RenderSize())); + const FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(iPxSamplePos); + const FfxFloat32x2 fUv = (iPxSamplePos + 0.5f) / RenderSize(); + + const FfxFloat32 fScaledAndSignedLumaDiff = ComputeDiff(iPxSamplePos, fUv, fDilatedMotionVector); + + FfxFloat32x4 fOutput = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f); + fOutput[DIFFERENCE] = fScaledAndSignedLumaDiff; + fOutput[SIGN_SUM] = (fScaledAndSignedLumaDiff != 0.0f) ? sign(fScaledAndSignedLumaDiff) : 0.0f; + fOutput[MIP0_INDICATOR] = 1.0f; + + return fOutput; +} + +FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice) +{ + return FfxFloat32x4(RWLoadPyramid(tex, 5), 0, 0); +} + +FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) +{ + return (v0 + v1 + v2 + v3) * 0.25f; +} + +void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice) +{ + if (index >= iShadingChangeMipStart) + { + StorePyramid(pix, outValue.xy, index); + } +} + +FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat32x4( + spdIntermediateR[x][y], + spdIntermediateG[x][y], + spdIntermediateB[x][y], + spdIntermediateA[x][y]); +} +void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) +{ + spdIntermediateR[x][y] = value.x; + spdIntermediateG[x][y] = value.y; + spdIntermediateB[x][y] = value.z; + spdIntermediateA[x][y] = value.w; +} + +#endif + +// define fetch and store functions Packed +#if FFX_HALF + +FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16]; +FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16]; + +FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice) +{ + return FfxFloat16x4(0, 0, 0, 0); +} + +FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice) +{ + return FfxFloat16x4(0, 0, 0, 0); +} + +void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice) +{ +} + +FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat16x4( + spdIntermediateRG[x][y].x, + spdIntermediateRG[x][y].y, + spdIntermediateBA[x][y].x, + spdIntermediateBA[x][y].y); +} + +void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value) +{ + spdIntermediateRG[x][y] = value.xy; + spdIntermediateBA[x][y] = value.zw; +} + +FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3) +{ + return (v0 + v1 + v2 + v3) * FfxFloat16(0.25); +} +#endif + +#include "../spd/ffx_spd.h" + +void ComputeShadingChangePyramid(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex) +{ +#if FFX_HALF + SpdDownsampleH( + FfxUInt32x2(WorkGroupId.xy), + FfxUInt32(LocalThreadIndex), + FfxUInt32(MipCount()), + FfxUInt32(NumWorkGroups()), + FfxUInt32(WorkGroupId.z), + FfxUInt32x2(WorkGroupOffset())); +#else + SpdDownsample( + FfxUInt32x2(WorkGroupId.xy), + FfxUInt32(LocalThreadIndex), + FfxUInt32(MipCount()), + FfxUInt32(NumWorkGroups()), + FfxUInt32(WorkGroupId.z), + FfxUInt32x2(WorkGroupOffset())); +#endif +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h.meta new file mode 100644 index 0000000..94175f1 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_shading_change_pyramid.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: fac4b69395377df409c7677a54515cd8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h similarity index 100% rename from Assets/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h rename to Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta new file mode 100644 index 0000000..67b7d76 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 21f8219a8876f77478e60862240a41f8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h new file mode 100644 index 0000000..2d587f0 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h @@ -0,0 +1,184 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +void Deringing(RectificationBox clippingBox, FFX_PARAMETER_INOUT FfxFloat32x3 fColor) +{ + fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); +} + +#ifndef FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE +#define FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE 2 // Approximate +#endif + +FfxFloat32 GetUpsampleLanczosWeight(FfxFloat32x2 fSrcSampleOffset, FfxFloat32 fKernelWeight) +{ + FfxFloat32x2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; +#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE + FfxFloat32 fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); +#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT + FfxFloat32 fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); +#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE + FfxFloat32 fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); +#else +#error "Invalid Lanczos type" +#endif + return fSampleWeight; +} + +FfxFloat32 ComputeMaxKernelWeight(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) { + + const FfxFloat32 fKernelSizeBias = 1.0f + (1.0f / FfxFloat32x2(DownscaleFactor()) - 1.0f).x; + + return ffxMin(FfxFloat32(1.99f), fKernelSizeBias); +} + +FfxFloat32x3 LoadPreparedColor(FfxInt32x2 iSamplePos) +{ + const FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iSamplePos)) * Exposure(); + const FfxFloat32x3 fPreparedYCoCg = RGBToYCoCg(fRgb); + + return fPreparedYCoCg; +} + +void ComputeUpsampledColorAndWeight(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT AccumulationPassData data) +{ + // We compute a sliced lanczos filter with 2 lobes (other slices are accumulated temporaly) + const FfxFloat32x2 fDstOutputPos = FfxFloat32x2(params.iPxHrPos) + FFX_BROADCAST_FLOAT32X2(0.5f); + const FfxFloat32x2 fSrcOutputPos = fDstOutputPos * DownscaleFactor(); + const FfxInt32x2 iSrcInputPos = FfxInt32x2(floor(fSrcOutputPos)); + const FfxFloat32x2 fSrcUnjitteredPos = (FfxFloat32x2(iSrcInputPos) + FfxFloat32x2(0.5f, 0.5f)) - Jitter(); // This is the un-jittered position of the sample at offset 0,0 + const FfxFloat32x2 fBaseSampleOffset = FfxFloat32x2(fSrcUnjitteredPos - fSrcOutputPos); + + FfxInt32x2 offsetTL; + offsetTL.x = (fSrcUnjitteredPos.x > fSrcOutputPos.x) ? FfxInt32(-2) : FfxInt32(-1); + offsetTL.y = (fSrcUnjitteredPos.y > fSrcOutputPos.y) ? FfxInt32(-2) : FfxInt32(-1); + + //Load samples + // If fSrcUnjitteredPos.y > fSrcOutputPos.y, indicates offsetTL.y = -2, sample offset Y will be [-2, 1], clipbox will be rows [1, 3]. + // Flip row# for sampling offset in this case, so first 0~2 rows in the sampled array can always be used for computing the clipbox. + // This reduces branch or cmove on sampled colors, but moving this overhead to sample position / weight calculation time which apply to less values. + const FfxBoolean bFlipRow = fSrcUnjitteredPos.y > fSrcOutputPos.y; + const FfxBoolean bFlipCol = fSrcUnjitteredPos.x > fSrcOutputPos.x; + const FfxFloat32x2 fOffsetTL = FfxFloat32x2(offsetTL); + + const FfxBoolean bIsInitialSample = (params.fAccumulation == 0.0f); + + FfxFloat32x3 fSamples[9]; + FfxInt32 iSampleIndex = 0; + + FFX_UNROLL + for (FfxInt32 row = 0; row < 3; row++) { + FFX_UNROLL + for (FfxInt32 col = 0; col < 3; col++) { + const FfxInt32x2 iSampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); + const FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + offsetTL + iSampleColRow; + const FfxInt32x2 iSampleCoord = ClampLoad(iSrcSamplePos, FfxInt32x2(0, 0), FfxInt32x2(RenderSize())); + + fSamples[iSampleIndex] = LoadPreparedColor(iSampleCoord); + + ++iSampleIndex; + } + } + +#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT + if (bIsInitialSample) + { + for (iSampleIndex = 0; iSampleIndex < 9; ++iSampleIndex) + { + //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation) + fSamples[iSampleIndex] = RGBToYCoCg(Tonemap(YCoCgToRGB(fSamples[iSampleIndex]))); + } + } +#endif + + // Identify how much of each upsampled color to be used for this frame + const FfxFloat32 fKernelBiasMax = ComputeMaxKernelWeight(params, data); + const FfxFloat32 fKernelBiasMin = ffxMax(1.0f, ((1.0f + fKernelBiasMax) * 0.3f)); + + const FfxFloat32 fKernelBiasWeight = + ffxMin(1.0f - params.fDisocclusion * 0.5f, + ffxMin(1.0f - params.fShadingChange, + ffxSaturate(data.fHistoryWeight * 5.0f) + )); + + const FfxFloat32 fKernelBias = ffxLerp(fKernelBiasMin, fKernelBiasMax, fKernelBiasWeight); + + + iSampleIndex = 0; + + FFX_UNROLL + for (FfxInt32 row = 0; row < 3; row++) + { + FFX_UNROLL + for (FfxInt32 col = 0; col < 3; col++) + { + const FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); + const FfxFloat32x2 fOffset = fOffsetTL + FfxFloat32x2(sampleColRow); + const FfxFloat32x2 fSrcSampleOffset = fBaseSampleOffset + fOffset; + + const FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + FfxInt32x2(offsetTL) + sampleColRow; + const FfxFloat32 fOnScreenFactor = FfxFloat32(IsOnScreen(FfxInt32x2(iSrcSamplePos), FfxInt32x2(RenderSize()))); + + if (!bIsInitialSample) + { + const FfxFloat32 fSampleWeight = fOnScreenFactor * FfxFloat32(GetUpsampleLanczosWeight(fSrcSampleOffset, fKernelBias)); + + data.fUpsampledColor += fSamples[iSampleIndex] * fSampleWeight; + data.fUpsampledWeight += fSampleWeight; + } + + // Update rectification box + { + const FfxFloat32 fRectificationCurveBias = -2.3f; + const FfxFloat32 fSrcSampleOffsetSq = dot(fSrcSampleOffset, fSrcSampleOffset); + const FfxFloat32 fBoxSampleWeight = exp(fRectificationCurveBias * fSrcSampleOffsetSq) * fOnScreenFactor; + + const FfxBoolean bInitialSample = (row == 0) && (col == 0); + RectificationBoxAddSample(bInitialSample, data.clippingBox, fSamples[iSampleIndex], fBoxSampleWeight); + } + ++iSampleIndex; + } + } + + RectificationBoxComputeVarianceBoxData(data.clippingBox); + + data.fUpsampledWeight *= FfxFloat32(data.fUpsampledWeight > FSR3UPSCALER_EPSILON); + + if (data.fUpsampledWeight > FSR3UPSCALER_EPSILON) { + // Normalize for deringing (we need to compare colors) + data.fUpsampledColor = data.fUpsampledColor / data.fUpsampledWeight; + data.fUpsampledWeight *= fAverageLanczosWeightPerFrame; + + Deringing(data.clippingBox, data.fUpsampledColor); + } + + // Initial samples using tonemapped upsampling + if (bIsInitialSample) { +#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT + data.fUpsampledColor = RGBToYCoCg(InverseTonemap(YCoCgToRGB(data.clippingBox.boxCenter))); +#else + data.fUpsampledColor = data.clippingBox.boxCenter; +#endif + data.fUpsampledWeight = 1.0f; + data.fHistoryWeight = 0.0f; + } +} diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta new file mode 100644 index 0000000..7dd7a51 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: 06167cdbc8ec55e4ab6913f7b5f1abf8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/spd.meta b/Packages/fidelityfx.fsr/Shaders/shaders/spd.meta new file mode 100644 index 0000000..c564f28 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/spd.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bd7cab5c46d4cd740ae57c03c8789ca4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h b/Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h new file mode 100644 index 0000000..6d4f997 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h @@ -0,0 +1,1007 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// 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. + +/// @defgroup FfxGPUSpd FidelityFX SPD +/// FidelityFX Single Pass Downsampler 2.0 GPU documentation +/// +/// @ingroup FfxGPUEffects + +/// Setup required constant values for SPD (CPU). +/// +/// @param [out] dispatchThreadGroupCountXY CPU side: dispatch thread group count xy. z is number of slices of the input texture +/// @param [out] workGroupOffset GPU side: pass in as constant +/// @param [out] numWorkGroupsAndMips GPU side: pass in as constant +/// @param [in] rectInfo left, top, width, height +/// @param [in] mips optional: if -1, calculate based on rect width and height +/// +/// @ingroup FfxGPUSpd +#if defined(FFX_CPU) +FFX_STATIC void ffxSpdSetup(FfxUInt32x2 dispatchThreadGroupCountXY, + FfxUInt32x2 workGroupOffset, + FfxUInt32x2 numWorkGroupsAndMips, + FfxUInt32x4 rectInfo, + FfxInt32 mips) +{ + // determines the offset of the first tile to downsample based on + // left (rectInfo[0]) and top (rectInfo[1]) of the subregion. + workGroupOffset[0] = rectInfo[0] / 64; + workGroupOffset[1] = rectInfo[1] / 64; + + FfxUInt32 endIndexX = (rectInfo[0] + rectInfo[2] - 1) / 64; // rectInfo[0] = left, rectInfo[2] = width + FfxUInt32 endIndexY = (rectInfo[1] + rectInfo[3] - 1) / 64; // rectInfo[1] = top, rectInfo[3] = height + + // we only need to dispatch as many thread groups as tiles we need to downsample + // number of tiles per slice depends on the subregion to downsample + dispatchThreadGroupCountXY[0] = endIndexX + 1 - workGroupOffset[0]; + dispatchThreadGroupCountXY[1] = endIndexY + 1 - workGroupOffset[1]; + + // number of thread groups per slice + numWorkGroupsAndMips[0] = (dispatchThreadGroupCountXY[0]) * (dispatchThreadGroupCountXY[1]); + + if (mips >= 0) + { + numWorkGroupsAndMips[1] = FfxUInt32(mips); + } + else + { + // calculate based on rect width and height + FfxUInt32 resolution = ffxMax(rectInfo[2], rectInfo[3]); + numWorkGroupsAndMips[1] = FfxUInt32((ffxMin(floor(log2(FfxFloat32(resolution))), FfxFloat32(12)))); + } +} + +/// Setup required constant values for SPD (CPU). +/// +/// @param [out] dispatchThreadGroupCountXY CPU side: dispatch thread group count xy. z is number of slices of the input texture +/// @param [out] workGroupOffset GPU side: pass in as constant +/// @param [out] numWorkGroupsAndMips GPU side: pass in as constant +/// @param [in] rectInfo left, top, width, height +/// +/// @ingroup FfxGPUSpd +FFX_STATIC void ffxSpdSetup(FfxUInt32x2 dispatchThreadGroupCountXY, + FfxUInt32x2 workGroupOffset, + FfxUInt32x2 numWorkGroupsAndMips, + FfxUInt32x4 rectInfo) +{ + ffxSpdSetup(dispatchThreadGroupCountXY, workGroupOffset, numWorkGroupsAndMips, rectInfo, -1); +} +#endif // #if defined(FFX_CPU) + + +//============================================================================================================================== +// NON-PACKED VERSION +//============================================================================================================================== +#if defined(FFX_GPU) +#if defined(FFX_SPD_PACKED_ONLY) +// Avoid compiler errors by including default implementations of these callbacks. +FfxFloat32x4 SpdLoadSourceImage(FfxInt32x2 p, FfxUInt32 slice) +{ + return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); +} + +FfxFloat32x4 SpdLoad(FfxInt32x2 p, FfxUInt32 slice) +{ + return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); +} +void SpdStore(FfxInt32x2 p, FfxFloat32x4 value, FfxUInt32 mip, FfxUInt32 slice) +{ +} +FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) +{ + return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); +} +void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) +{ +} +FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) +{ + return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); +} +#endif // #if FFX_SPD_PACKED_ONLY + +//_____________________________________________________________/\_______________________________________________________________ + +void ffxSpdWorkgroupShuffleBarrier() +{ + FFX_GROUP_MEMORY_BARRIER; +} + +// Only last active workgroup should proceed +bool SpdExitWorkgroup(FfxUInt32 numWorkGroups, FfxUInt32 localInvocationIndex, FfxUInt32 slice) +{ + // global atomic counter + if (localInvocationIndex == 0) + { + SpdIncreaseAtomicCounter(slice); + } + + ffxSpdWorkgroupShuffleBarrier(); + return (SpdGetAtomicCounter() != (numWorkGroups - 1)); +} + +// User defined: FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3); +FfxFloat32x4 SpdReduceQuad(FfxFloat32x4 v) +{ +#if defined(FFX_GLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) + + FfxFloat32x4 v0 = v; + FfxFloat32x4 v1 = subgroupQuadSwapHorizontal(v); + FfxFloat32x4 v2 = subgroupQuadSwapVertical(v); + FfxFloat32x4 v3 = subgroupQuadSwapDiagonal(v); + return SpdReduce4(v0, v1, v2, v3); + +#elif defined(FFX_HLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) + + // requires SM6.0 + FfxFloat32x4 v0 = v; + FfxFloat32x4 v1 = QuadReadAcrossX(v); + FfxFloat32x4 v2 = QuadReadAcrossY(v); + FfxFloat32x4 v3 = QuadReadAcrossDiagonal(v); + return SpdReduce4(v0, v1, v2, v3); +/* + // if SM6.0 is not available, you can use the AMD shader intrinsics + // the AMD shader intrinsics are available in AMD GPU Services (AGS) library: + // https://gpuopen.com/amd-gpu-services-ags-library/ + // works for DX11 + FfxFloat32x4 v0 = v; + FfxFloat32x4 v1; + v1.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + v1.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + v1.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + v1.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + FfxFloat32x4 v2; + v2.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + v2.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + v2.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + v2.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + FfxFloat32x4 v3; + v3.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + v3.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + v3.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + v3.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + return SpdReduce4(v0, v1, v2, v3); + */ +#endif + return v; +} + +FfxFloat32x4 SpdReduceIntermediate(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3) +{ + FfxFloat32x4 v0 = SpdLoadIntermediate(i0.x, i0.y); + FfxFloat32x4 v1 = SpdLoadIntermediate(i1.x, i1.y); + FfxFloat32x4 v2 = SpdLoadIntermediate(i2.x, i2.y); + FfxFloat32x4 v3 = SpdLoadIntermediate(i3.x, i3.y); + return SpdReduce4(v0, v1, v2, v3); +} + +FfxFloat32x4 SpdReduceLoad4(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) +{ + FfxFloat32x4 v0 = SpdLoad(FfxInt32x2(i0), slice); + FfxFloat32x4 v1 = SpdLoad(FfxInt32x2(i1), slice); + FfxFloat32x4 v2 = SpdLoad(FfxInt32x2(i2), slice); + FfxFloat32x4 v3 = SpdLoad(FfxInt32x2(i3), slice); + return SpdReduce4(v0, v1, v2, v3); +} + +FfxFloat32x4 SpdReduceLoad4(FfxUInt32x2 base, FfxUInt32 slice) +{ + return SpdReduceLoad4(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); +} + +FfxFloat32x4 SpdReduceLoadSourceImage4(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) +{ + FfxFloat32x4 v0 = SpdLoadSourceImage(FfxInt32x2(i0), slice); + FfxFloat32x4 v1 = SpdLoadSourceImage(FfxInt32x2(i1), slice); + FfxFloat32x4 v2 = SpdLoadSourceImage(FfxInt32x2(i2), slice); + FfxFloat32x4 v3 = SpdLoadSourceImage(FfxInt32x2(i3), slice); + return SpdReduce4(v0, v1, v2, v3); +} + +FfxFloat32x4 SpdReduceLoadSourceImage(FfxUInt32x2 base, FfxUInt32 slice) +{ +#if defined(SPD_LINEAR_SAMPLER) + return SpdLoadSourceImage(FfxInt32x2(base), slice); +#else + return SpdReduceLoadSourceImage4(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); +#endif +} + +void SpdDownsampleMips_0_1_Intrinsics(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ + FfxFloat32x4 v[4]; + + FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); + FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); + v[0] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[0], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); + v[1] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[1], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); + v[2] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[2], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); + v[3] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[3], 0, slice); + + if (mip <= 1) + return; + + v[0] = SpdReduceQuad(v[0]); + v[1] = SpdReduceQuad(v[1]); + v[2] = SpdReduceQuad(v[2]); + v[3] = SpdReduceQuad(v[3]); + + if ((localInvocationIndex % 4) == 0) + { + SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2), v[0], 1, slice); + SpdStoreIntermediate(x / 2, y / 2, v[0]); + + SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2), v[1], 1, slice); + SpdStoreIntermediate(x / 2 + 8, y / 2, v[1]); + + SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2 + 8), v[2], 1, slice); + SpdStoreIntermediate(x / 2, y / 2 + 8, v[2]); + + SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); + SpdStoreIntermediate(x / 2 + 8, y / 2 + 8, v[3]); + } +} + +void SpdDownsampleMips_0_1_LDS(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ + FfxFloat32x4 v[4]; + + FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); + FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); + v[0] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[0], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); + v[1] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[1], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); + v[2] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[2], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); + v[3] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[3], 0, slice); + + if (mip <= 1) + return; + + for (FfxUInt32 i = 0; i < 4; i++) + { + SpdStoreIntermediate(x, y, v[i]); + ffxSpdWorkgroupShuffleBarrier(); + if (localInvocationIndex < 64) + { + v[i] = SpdReduceIntermediate(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); + SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); + } + ffxSpdWorkgroupShuffleBarrier(); + } + + if (localInvocationIndex < 64) + { + SpdStoreIntermediate(x + 0, y + 0, v[0]); + SpdStoreIntermediate(x + 8, y + 0, v[1]); + SpdStoreIntermediate(x + 0, y + 8, v[2]); + SpdStoreIntermediate(x + 8, y + 8, v[3]); + } +} + +void SpdDownsampleMips_0_1(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + SpdDownsampleMips_0_1_LDS(x, y, workGroupID, localInvocationIndex, mip, slice); +#else + SpdDownsampleMips_0_1_Intrinsics(x, y, workGroupID, localInvocationIndex, mip, slice); +#endif +} + + +void SpdDownsampleMip_2(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 64) + { + FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); + SpdStore(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x, y), v, mip, slice); + // store to LDS, try to reduce bank conflicts + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + // ... + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + SpdStoreIntermediate(x * 2 + y % 2, y * 2, v); + } +#else + FfxFloat32x4 v = SpdLoadIntermediate(x, y); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediate(x + (y / 2) % 2, y, v); + } +#endif +} + +void SpdDownsampleMip_3(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 16) + { + // x 0 x 0 + // 0 0 0 0 + // 0 x 0 x + // 0 0 0 0 + FfxFloat32x4 v = + SpdReduceIntermediate(FfxUInt32x2(x * 4 + 0 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 2 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 0 + 1, y * 4 + 2), FfxUInt32x2(x * 4 + 2 + 1, y * 4 + 2)); + SpdStore(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x, y), v, mip, slice); + // store to LDS + // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 + // ... + // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 + // ... + // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x + // ... + SpdStoreIntermediate(x * 4 + y, y * 4, v); + } +#else + if (localInvocationIndex < 64) + { + FfxFloat32x4 v = SpdLoadIntermediate(x * 2 + y % 2, y * 2); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediate(x * 2 + y / 2, y * 2, v); + } + } +#endif +} + +void SpdDownsampleMip_4(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 4) + { + // x 0 0 0 x 0 0 0 + // ... + // 0 x 0 0 0 x 0 0 + FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), + FfxUInt32x2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), + FfxUInt32x2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), + FfxUInt32x2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); + SpdStore(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x, y), v, mip, slice); + // store to LDS + // x x x x 0 ... + // 0 ... + SpdStoreIntermediate(x + y * 2, 0, v); + } +#else + if (localInvocationIndex < 16) + { + FfxFloat32x4 v = SpdLoadIntermediate(x * 4 + y, y * 4); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediate(x / 2 + y, 0, v); + } + } +#endif +} + +void SpdDownsampleMip_5(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 1) + { + // x x x x 0 ... + // 0 ... + FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(0, 0), FfxUInt32x2(1, 0), FfxUInt32x2(2, 0), FfxUInt32x2(3, 0)); + SpdStore(FfxInt32x2(workGroupID.xy), v, mip, slice); + } +#else + if (localInvocationIndex < 4) + { + FfxFloat32x4 v = SpdLoadIntermediate(localInvocationIndex, 0); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(FfxInt32x2(workGroupID.xy), v, mip, slice); + } + } +#endif +} + +void SpdDownsampleMips_6_7(FfxUInt32 x, FfxUInt32 y, FfxUInt32 mips, FfxUInt32 slice) +{ + FfxInt32x2 tex = FfxInt32x2(x * 4 + 0, y * 4 + 0); + FfxInt32x2 pix = FfxInt32x2(x * 2 + 0, y * 2 + 0); + FfxFloat32x4 v0 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v0, 6, slice); + + tex = FfxInt32x2(x * 4 + 2, y * 4 + 0); + pix = FfxInt32x2(x * 2 + 1, y * 2 + 0); + FfxFloat32x4 v1 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v1, 6, slice); + + tex = FfxInt32x2(x * 4 + 0, y * 4 + 2); + pix = FfxInt32x2(x * 2 + 0, y * 2 + 1); + FfxFloat32x4 v2 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v2, 6, slice); + + tex = FfxInt32x2(x * 4 + 2, y * 4 + 2); + pix = FfxInt32x2(x * 2 + 1, y * 2 + 1); + FfxFloat32x4 v3 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v3, 6, slice); + + if (mips <= 7) + return; + // no barrier needed, working on values only from the same thread + + FfxFloat32x4 v = SpdReduce4(v0, v1, v2, v3); + SpdStore(FfxInt32x2(x, y), v, 7, slice); + SpdStoreIntermediate(x, y, v); +} + +void SpdDownsampleNextFour(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 baseMip, FfxUInt32 mips, FfxUInt32 slice) +{ + if (mips <= baseMip) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_2(x, y, workGroupID, localInvocationIndex, baseMip, slice); + + if (mips <= baseMip + 1) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_3(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); + + if (mips <= baseMip + 2) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_4(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); + + if (mips <= baseMip + 3) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_5(workGroupID, localInvocationIndex, baseMip + 3, slice); +} + +/// Downsamples a 64x64 tile based on the work group id. +/// If after downsampling it's the last active thread group, computes the remaining MIP levels. +/// +/// @param [in] workGroupID index of the work group / thread group +/// @param [in] localInvocationIndex index of the thread within the thread group in 1D +/// @param [in] mips the number of total MIP levels to compute for the input texture +/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice +/// @param [in] slice the slice of the input texture +/// +/// @ingroup FfxGPUSpd +void SpdDownsample(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice) +{ + // compute MIP level 0 and 1 + FfxUInt32x2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); + FfxUInt32 x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); + FfxUInt32 y = sub_xy.y + 8 * ((localInvocationIndex >> 7)); + SpdDownsampleMips_0_1(x, y, workGroupID, localInvocationIndex, mips, slice); + + // compute MIP level 2, 3, 4, 5 + SpdDownsampleNextFour(x, y, workGroupID, localInvocationIndex, 2, mips, slice); + + if (mips <= 6) + return; + + // increase the global atomic counter for the given slice and check if it's the last remaining thread group: + // terminate if not, continue if yes. + if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) + return; + + // reset the global atomic counter back to 0 for the next spd dispatch + SpdResetAtomicCounter(slice); + + // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. + // compute MIP level 6 and 7 + SpdDownsampleMips_6_7(x, y, mips, slice); + + // compute MIP level 8, 9, 10, 11 + SpdDownsampleNextFour(x, y, FfxUInt32x2(0, 0), localInvocationIndex, 8, mips, slice); +} +/// Downsamples a 64x64 tile based on the work group id and work group offset. +/// If after downsampling it's the last active thread group, computes the remaining MIP levels. +/// +/// @param [in] workGroupID index of the work group / thread group +/// @param [in] localInvocationIndex index of the thread within the thread group in 1D +/// @param [in] mips the number of total MIP levels to compute for the input texture +/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice +/// @param [in] slice the slice of the input texture +/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. +/// +/// @ingroup FfxGPUSpd +void SpdDownsample(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice, FfxUInt32x2 workGroupOffset) +{ + SpdDownsample(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//============================================================================================================================== +// PACKED VERSION +//============================================================================================================================== + +#if FFX_HALF + +FfxFloat16x4 SpdReduceQuadH(FfxFloat16x4 v) +{ +#if defined(FFX_GLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) + FfxFloat16x4 v0 = v; + FfxFloat16x4 v1 = subgroupQuadSwapHorizontal(v); + FfxFloat16x4 v2 = subgroupQuadSwapVertical(v); + FfxFloat16x4 v3 = subgroupQuadSwapDiagonal(v); + return SpdReduce4H(v0, v1, v2, v3); +#elif defined(FFX_HLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) + // requires SM6.0 + FfxFloat16x4 v0 = v; + FfxFloat16x4 v1 = QuadReadAcrossX(v); + FfxFloat16x4 v2 = QuadReadAcrossY(v); + FfxFloat16x4 v3 = QuadReadAcrossDiagonal(v); + return SpdReduce4H(v0, v1, v2, v3); +/* + // if SM6.0 is not available, you can use the AMD shader intrinsics + // the AMD shader intrinsics are available in AMD GPU Services (AGS) library: + // https://gpuopen.com/amd-gpu-services-ags-library/ + // works for DX11 + FfxFloat16x4 v0 = v; + FfxFloat16x4 v1; + v1.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + v1.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + v1.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + v1.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); + FfxFloat16x4 v2; + v2.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + v2.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + v2.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + v2.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); + FfxFloat16x4 v3; + v3.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + v3.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + v3.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + v3.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); + return SpdReduce4H(v0, v1, v2, v3); + */ +#endif + return FfxFloat16x4(0.0, 0.0, 0.0, 0.0); +} + +FfxFloat16x4 SpdReduceIntermediateH(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3) +{ + FfxFloat16x4 v0 = SpdLoadIntermediateH(i0.x, i0.y); + FfxFloat16x4 v1 = SpdLoadIntermediateH(i1.x, i1.y); + FfxFloat16x4 v2 = SpdLoadIntermediateH(i2.x, i2.y); + FfxFloat16x4 v3 = SpdLoadIntermediateH(i3.x, i3.y); + return SpdReduce4H(v0, v1, v2, v3); +} + +FfxFloat16x4 SpdReduceLoad4H(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) +{ + FfxFloat16x4 v0 = SpdLoadH(FfxInt32x2(i0), slice); + FfxFloat16x4 v1 = SpdLoadH(FfxInt32x2(i1), slice); + FfxFloat16x4 v2 = SpdLoadH(FfxInt32x2(i2), slice); + FfxFloat16x4 v3 = SpdLoadH(FfxInt32x2(i3), slice); + return SpdReduce4H(v0, v1, v2, v3); +} + +FfxFloat16x4 SpdReduceLoad4H(FfxUInt32x2 base, FfxUInt32 slice) +{ + return SpdReduceLoad4H(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); +} + +FfxFloat16x4 SpdReduceLoadSourceImage4H(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) +{ + FfxFloat16x4 v0 = SpdLoadSourceImageH(FfxInt32x2(i0), slice); + FfxFloat16x4 v1 = SpdLoadSourceImageH(FfxInt32x2(i1), slice); + FfxFloat16x4 v2 = SpdLoadSourceImageH(FfxInt32x2(i2), slice); + FfxFloat16x4 v3 = SpdLoadSourceImageH(FfxInt32x2(i3), slice); + return SpdReduce4H(v0, v1, v2, v3); +} + +FfxFloat16x4 SpdReduceLoadSourceImageH(FfxUInt32x2 base, FfxUInt32 slice) +{ +#if defined(SPD_LINEAR_SAMPLER) + return SpdLoadSourceImageH(FfxInt32x2(base), slice); +#else + return SpdReduceLoadSourceImage4H(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); +#endif +} + +void SpdDownsampleMips_0_1_IntrinsicsH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) +{ + FfxFloat16x4 v[4]; + + FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); + FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); + v[0] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[0], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); + v[1] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[1], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); + v[2] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[2], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); + v[3] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[3], 0, slice); + + if (mips <= 1) + return; + + v[0] = SpdReduceQuadH(v[0]); + v[1] = SpdReduceQuadH(v[1]); + v[2] = SpdReduceQuadH(v[2]); + v[3] = SpdReduceQuadH(v[3]); + + if ((localInvocationIndex % 4) == 0) + { + SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2), v[0], 1, slice); + SpdStoreIntermediateH(x / 2, y / 2, v[0]); + + SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2), v[1], 1, slice); + SpdStoreIntermediateH(x / 2 + 8, y / 2, v[1]); + + SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2 + 8), v[2], 1, slice); + SpdStoreIntermediateH(x / 2, y / 2 + 8, v[2]); + + SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); + SpdStoreIntermediateH(x / 2 + 8, y / 2 + 8, v[3]); + } +} + +void SpdDownsampleMips_0_1_LDSH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) +{ + FfxFloat16x4 v[4]; + + FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); + FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); + v[0] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[0], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); + v[1] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[1], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); + v[2] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[2], 0, slice); + + tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); + pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); + v[3] = SpdReduceLoadSourceImageH(tex, slice); + SpdStoreH(pix, v[3], 0, slice); + + if (mips <= 1) + return; + + for (FfxInt32 i = 0; i < 4; i++) + { + SpdStoreIntermediateH(x, y, v[i]); + ffxSpdWorkgroupShuffleBarrier(); + if (localInvocationIndex < 64) + { + v[i] = SpdReduceIntermediateH(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); + SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); + } + ffxSpdWorkgroupShuffleBarrier(); + } + + if (localInvocationIndex < 64) + { + SpdStoreIntermediateH(x + 0, y + 0, v[0]); + SpdStoreIntermediateH(x + 8, y + 0, v[1]); + SpdStoreIntermediateH(x + 0, y + 8, v[2]); + SpdStoreIntermediateH(x + 8, y + 8, v[3]); + } +} + +void SpdDownsampleMips_0_1H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + SpdDownsampleMips_0_1_LDSH(x, y, workGroupID, localInvocationIndex, mips, slice); +#else + SpdDownsampleMips_0_1_IntrinsicsH(x, y, workGroupID, localInvocationIndex, mips, slice); +#endif +} + + +void SpdDownsampleMip_2H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 64) + { + FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); + SpdStoreH(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x, y), v, mip, slice); + // store to LDS, try to reduce bank conflicts + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + // ... + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + SpdStoreIntermediateH(x * 2 + y % 2, y * 2, v); + } +#else + FfxFloat16x4 v = SpdLoadIntermediateH(x, y); + v = SpdReduceQuadH(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStoreH(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediateH(x + (y / 2) % 2, y, v); + } +#endif +} + +void SpdDownsampleMip_3H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 16) + { + // x 0 x 0 + // 0 0 0 0 + // 0 x 0 x + // 0 0 0 0 + FfxFloat16x4 v = + SpdReduceIntermediateH(FfxUInt32x2(x * 4 + 0 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 2 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 0 + 1, y * 4 + 2), FfxUInt32x2(x * 4 + 2 + 1, y * 4 + 2)); + SpdStoreH(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x, y), v, mip, slice); + // store to LDS + // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 + // ... + // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 + // ... + // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x + // ... + SpdStoreIntermediateH(x * 4 + y, y * 4, v); + } +#else + if (localInvocationIndex < 64) + { + FfxFloat16x4 v = SpdLoadIntermediateH(x * 2 + y % 2, y * 2); + v = SpdReduceQuadH(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStoreH(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediateH(x * 2 + y / 2, y * 2, v); + } + } +#endif +} + +void SpdDownsampleMip_4H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 4) + { + // x 0 0 0 x 0 0 0 + // ... + // 0 x 0 0 0 x 0 0 + FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), + FfxUInt32x2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), + FfxUInt32x2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), + FfxUInt32x2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); + SpdStoreH(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x, y), v, mip, slice); + // store to LDS + // x x x x 0 ... + // 0 ... + SpdStoreIntermediateH(x + y * 2, 0, v); + } +#else + if (localInvocationIndex < 16) + { + FfxFloat16x4 v = SpdLoadIntermediateH(x * 4 + y, y * 4); + v = SpdReduceQuadH(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStoreH(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediateH(x / 2 + y, 0, v); + } + } +#endif +} + +void SpdDownsampleMip_5H(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) +{ +#if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 1) + { + // x x x x 0 ... + // 0 ... + FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(0, 0), FfxUInt32x2(1, 0), FfxUInt32x2(2, 0), FfxUInt32x2(3, 0)); + SpdStoreH(FfxInt32x2(workGroupID.xy), v, mip, slice); + } +#else + if (localInvocationIndex < 4) + { + FfxFloat16x4 v = SpdLoadIntermediateH(localInvocationIndex, 0); + v = SpdReduceQuadH(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStoreH(FfxInt32x2(workGroupID.xy), v, mip, slice); + } + } +#endif +} + +void SpdDownsampleMips_6_7H(FfxUInt32 x, FfxUInt32 y, FfxUInt32 mips, FfxUInt32 slice) +{ + FfxInt32x2 tex = FfxInt32x2(x * 4 + 0, y * 4 + 0); + FfxInt32x2 pix = FfxInt32x2(x * 2 + 0, y * 2 + 0); + FfxFloat16x4 v0 = SpdReduceLoad4H(tex, slice); + SpdStoreH(pix, v0, 6, slice); + + tex = FfxInt32x2(x * 4 + 2, y * 4 + 0); + pix = FfxInt32x2(x * 2 + 1, y * 2 + 0); + FfxFloat16x4 v1 = SpdReduceLoad4H(tex, slice); + SpdStoreH(pix, v1, 6, slice); + + tex = FfxInt32x2(x * 4 + 0, y * 4 + 2); + pix = FfxInt32x2(x * 2 + 0, y * 2 + 1); + FfxFloat16x4 v2 = SpdReduceLoad4H(tex, slice); + SpdStoreH(pix, v2, 6, slice); + + tex = FfxInt32x2(x * 4 + 2, y * 4 + 2); + pix = FfxInt32x2(x * 2 + 1, y * 2 + 1); + FfxFloat16x4 v3 = SpdReduceLoad4H(tex, slice); + SpdStoreH(pix, v3, 6, slice); + + if (mips < 8) + return; + // no barrier needed, working on values only from the same thread + + FfxFloat16x4 v = SpdReduce4H(v0, v1, v2, v3); + SpdStoreH(FfxInt32x2(x, y), v, 7, slice); + SpdStoreIntermediateH(x, y, v); +} + +void SpdDownsampleNextFourH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 baseMip, FfxUInt32 mips, FfxUInt32 slice) +{ + if (mips <= baseMip) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_2H(x, y, workGroupID, localInvocationIndex, baseMip, slice); + + if (mips <= baseMip + 1) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_3H(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); + + if (mips <= baseMip + 2) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_4H(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); + + if (mips <= baseMip + 3) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_5H(workGroupID, localInvocationIndex, baseMip + 3, slice); +} + +/// Downsamples a 64x64 tile based on the work group id and work group offset. +/// If after downsampling it's the last active thread group, computes the remaining MIP levels. +/// Uses half types. +/// +/// @param [in] workGroupID index of the work group / thread group +/// @param [in] localInvocationIndex index of the thread within the thread group in 1D +/// @param [in] mips the number of total MIP levels to compute for the input texture +/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice +/// @param [in] slice the slice of the input texture +/// +/// @ingroup FfxGPUSpd +void SpdDownsampleH(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice) +{ + FfxUInt32x2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); + FfxUInt32 x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); + FfxUInt32 y = sub_xy.y + 8 * ((localInvocationIndex >> 7)); + + // compute MIP level 0 and 1 + SpdDownsampleMips_0_1H(x, y, workGroupID, localInvocationIndex, mips, slice); + + // compute MIP level 2, 3, 4, 5 + SpdDownsampleNextFourH(x, y, workGroupID, localInvocationIndex, 2, mips, slice); + + if (mips < 7) + return; + + // increase the global atomic counter for the given slice and check if it's the last remaining thread group: + // terminate if not, continue if yes. + if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) + return; + + // reset the global atomic counter back to 0 for the next spd dispatch + SpdResetAtomicCounter(slice); + + // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. + // compute MIP level 6 and 7 + SpdDownsampleMips_6_7H(x, y, mips, slice); + + // compute MIP level 8, 9, 10, 11 + SpdDownsampleNextFourH(x, y, FfxUInt32x2(0, 0), localInvocationIndex, 8, mips, slice); +} + +/// Downsamples a 64x64 tile based on the work group id and work group offset. +/// If after downsampling it's the last active thread group, computes the remaining MIP levels. +/// Uses half types. +/// +/// @param [in] workGroupID index of the work group / thread group +/// @param [in] localInvocationIndex index of the thread within the thread group in 1D +/// @param [in] mips the number of total MIP levels to compute for the input texture +/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice +/// @param [in] slice the slice of the input texture +/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. +/// +/// @ingroup FfxGPUSpd +void SpdDownsampleH(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice, FfxUInt32x2 workGroupOffset) +{ + SpdDownsampleH(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); +} + +#endif // #if FFX_HALF +#endif // #if defined(FFX_GPU) diff --git a/Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h.meta b/Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h.meta new file mode 100644 index 0000000..2739093 --- /dev/null +++ b/Packages/fidelityfx.fsr/Shaders/shaders/spd/ffx_spd.h.meta @@ -0,0 +1,60 @@ +fileFormatVersion: 2 +guid: fe8e621d12c641c4297675f3015697fc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude GameCoreScarlett: 1 + Exclude GameCoreXboxOne: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/fidelityfx.fsr/package.json b/Packages/fidelityfx.fsr/package.json new file mode 100644 index 0000000..d672c3f --- /dev/null +++ b/Packages/fidelityfx.fsr/package.json @@ -0,0 +1,12 @@ +{ + "name": "fidelityfx.fsr", + "version": "1.0.0", + "displayName": "FidelityFX FSR", + "description": "FidelityFX Super Resolution 2/3 Upscaler core assets", + "unity": "2020.1", + "documentationUrl": "https://github.com/ndepoel/FSR2Unity", + "author": { + "name": "Nico de Poel", + "email": "ndepoel@gmail.com.com" + } +} \ No newline at end of file diff --git a/Packages/fidelityfx.fsr/package.json.meta b/Packages/fidelityfx.fsr/package.json.meta new file mode 100644 index 0000000..c3ad2ad --- /dev/null +++ b/Packages/fidelityfx.fsr/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 202b84e3e446cc544a82954c2235ec60 +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 4441e75..0daceeb 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -40,13 +40,13 @@ "url": "https://packages.unity.com" }, "com.unity.postprocessing": { - "version": "3.2.2", + "version": "file:com.unity.postprocessing", "depth": 0, - "source": "registry", + "source": "embedded", "dependencies": { - "com.unity.modules.physics": "1.0.0" - }, - "url": "https://packages.unity.com" + "com.unity.modules.physics": "1.0.0", + "fidelityfx.fsr": "1.0.0" + } }, "com.unity.test-framework": { "version": "1.1.31", @@ -99,6 +99,12 @@ }, "url": "https://packages.unity.com" }, + "fidelityfx.fsr": { + "version": "file:fidelityfx.fsr", + "depth": 0, + "source": "embedded", + "dependencies": {} + }, "com.unity.modules.ai": { "version": "1.0.0", "depth": 0, diff --git a/README.md b/README.md index c2fc3fd..fb3694d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# FSR 3.0 Upscaler for Unity +# FSR 3.1 Upscaler for Unity -FidelityFX Super Resolution 3 (FSR3) Upscaler is an advanced image upscaling and temporal reconstruction technique developed by AMD and [released as open source](https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/tree/release-FSR3-3.0.3) through the GPUOpen initiative. It provides both high-quality anti-aliasing and the ability for games to output at high resolutions while rendering at a lower internal resolution, improving performance. +FidelityFX Super Resolution 3 (FSR3) Upscaler is an advanced image upscaling and temporal reconstruction technique developed by AMD and [released as open source](https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK) through the GPUOpen initiative. It provides both high-quality anti-aliasing and the ability for games to output at high resolutions while rendering at a lower internal resolution, improving performance. This project aims to bring FSR3 upscaling to Unity as an alternative to Unity's existing FXAA, SMAA and TAA anti-aliasing solutions. Compared to Unity's TAA implementation, FSR3 Upscaler offers a sharper and more stable image, with a better sub-pixel detail resolve and better performance due to its reliance on upscaling. -![Comparison](images/comparison.png) +![Comparison](images/comparison-fsr3.1.png) -Rather than attempting to integrate AMD's open source C++ libraries for FSR3, this project instead reimplements the C++ backend in C# while adapting FSR3's HLSL shader code to work within Unity. This allows for maximum compatibility with the platforms and graphics APIs that Unity supports, including ones that normally wouldn't be supported by FSR3. +Rather than attempting to integrate AMD's native plugin libraries for FSR3, this project instead reimplements the C++ backend in C# using Unity's scripting APIs while adapting FSR3's HLSL shader code to work within Unity. This allows for maximum compatibility with the platforms and graphics APIs that Unity supports, including ones that normally wouldn't be supported by FSR3. -Focus of this project lies initially on making FSR3 Upscaler work with the traditional Unity built-in render pipeline. However the core FSR3 Upscaler classes are render pipeline-agnostic, so there is every possibility for URP and HDRP to be supported as well in the future. +Focus of this project lies initially on making FSR3 Upscaler work with the traditional Unity built-in render pipeline. However the core FSR3 Upscaler classes and shaders are render pipeline-agnostic, so there is every possibility for URP and HDRP integrations to be built as well. ## Table of Contents @@ -32,7 +32,7 @@ Focus of this project lies initially on making FSR3 Upscaler work with the tradi FSR3 Upscaler for Unity requires Unity 2020.1 or higher. This version of Unity added support for the `multi_compile` keyword in compute shaders, which the FSR3 Upscaler implementation makes use of. -Other than the above, requirements are no different from standard FSR3 Upscaler: it requires a GPU with compute shader support, typed UAV load and R16G16B16A16_UNORM support. In practice, almost every GPU made in the past decade will be able to run it. +Other than the above, requirements are no different from the standard FSR3 Upscaler libraries: it requires a GPU with compute shader support, typed UAV load and R16G16B16A16_UNORM support. In practice, almost every GPU made in the past decade will be able to run it. Platforms tested and confirmed working: - Windows @@ -171,6 +171,9 @@ Dynamic resolution works really well in combination with FSR3 Upscaler. Any run- - Texture mipmap bias adjustment is not working on MacOS Metal. This causes blurry textures as the internal render resolution is lowered. This is a Unity issue of some sort. Workaround: no known workaround yet. +- Using FP16 mode on Xbox with GDK October 2023 or later causes random jittering artifacts. + This seems to be a shader compilation issue introduced by this GDK update. + Workaround: disable FP16 on Xbox consoles. ## Details on implementation diff --git a/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs b/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs deleted file mode 100644 index 691483d..0000000 --- a/com.unity.postprocessing/PostProcessing/Editor/PostProcessLayerEditor.cs +++ /dev/null @@ -1,472 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.Rendering.PostProcessing; -using UnityEditorInternal; -using System.IO; - -namespace UnityEditor.Rendering.PostProcessing -{ - using SerializedBundleRef = PostProcessLayer.SerializedBundleRef; - using EXRFlags = Texture2D.EXRFlags; - - [CanEditMultipleObjects, CustomEditor(typeof(PostProcessLayer))] - sealed class PostProcessLayerEditor : BaseEditor - { - SerializedProperty m_StopNaNPropagation; -#pragma warning disable 414 - SerializedProperty m_DirectToCameraTarget; -#pragma warning restore 414 - SerializedProperty m_VolumeTrigger; - SerializedProperty m_VolumeLayer; - - SerializedProperty m_AntialiasingMode; - SerializedProperty m_TaaJitterSpread; - SerializedProperty m_TaaSharpness; - SerializedProperty m_TaaStationaryBlending; - SerializedProperty m_TaaMotionBlending; - SerializedProperty m_SmaaQuality; - SerializedProperty m_FxaaFastMode; - SerializedProperty m_FxaaKeepAlpha; - - SerializedProperty m_FsrQualityMode; - SerializedProperty m_FsrPerformSharpen; - SerializedProperty m_FsrSharpness; - SerializedProperty m_FsrEnableFP16; - SerializedProperty m_FsrExposureSource; - SerializedProperty m_FsrExposureTexture; - SerializedProperty m_FsrPreExposure; - SerializedProperty m_FsrAutoReactive; - SerializedProperty m_FsrAutoReactiveParams; - SerializedProperty m_FsrReactiveMaskTexture; - SerializedProperty m_FsrAutoTcr; - SerializedProperty m_FsrAutoTcrParams; - SerializedProperty m_FsrTcrMaskTexture; - - SerializedProperty m_FogEnabled; - SerializedProperty m_FogExcludeSkybox; - - SerializedProperty m_ShowToolkit; - SerializedProperty m_ShowCustomSorter; - - Dictionary m_CustomLists; - -#if UNITY_2017_3_OR_NEWER - Camera m_TargetCameraComponent; -#endif - - static GUIContent[] s_AntialiasingMethodNames = - { - new GUIContent("No Anti-aliasing"), - new GUIContent("Fast Approximate Anti-aliasing (FXAA)"), - new GUIContent("Subpixel Morphological Anti-aliasing (SMAA)"), - new GUIContent("Temporal Anti-aliasing (TAA)"), - new GUIContent("FidelityFX Super Resolution 3 (FSR3) Upscaler") - }; - - enum ExportMode - { - FullFrame, - DisablePost, - BreakBeforeColorGradingLinear, - BreakBeforeColorGradingLog - } - - void OnEnable() - { - m_StopNaNPropagation = FindProperty(x => x.stopNaNPropagation); - m_DirectToCameraTarget = FindProperty(x => x.finalBlitToCameraTarget); - m_VolumeTrigger = FindProperty(x => x.volumeTrigger); - m_VolumeLayer = FindProperty(x => x.volumeLayer); - - m_AntialiasingMode = FindProperty(x => x.antialiasingMode); - m_TaaJitterSpread = FindProperty(x => x.temporalAntialiasing.jitterSpread); - m_TaaSharpness = FindProperty(x => x.temporalAntialiasing.sharpness); - m_TaaStationaryBlending = FindProperty(x => x.temporalAntialiasing.stationaryBlending); - m_TaaMotionBlending = FindProperty(x => x.temporalAntialiasing.motionBlending); - m_SmaaQuality = FindProperty(x => x.subpixelMorphologicalAntialiasing.quality); - m_FxaaFastMode = FindProperty(x => x.fastApproximateAntialiasing.fastMode); - m_FxaaKeepAlpha = FindProperty(x => x.fastApproximateAntialiasing.keepAlpha); - - m_FsrQualityMode = FindProperty(x => x.superResolution.qualityMode); - m_FsrPerformSharpen = FindProperty(x => x.superResolution.performSharpenPass); - m_FsrSharpness = FindProperty(x => x.superResolution.sharpness); - m_FsrEnableFP16 = FindProperty(x => x.superResolution.enableFP16); - m_FsrExposureSource = FindProperty(x => x.superResolution.exposureSource); - m_FsrExposureTexture = FindProperty(x => x.superResolution.exposure); - m_FsrPreExposure = FindProperty(x => x.superResolution.preExposure); - m_FsrAutoReactive = FindProperty(x => x.superResolution.autoGenerateReactiveMask); - m_FsrAutoReactiveParams = FindProperty(x => x.superResolution.generateReactiveParameters); - m_FsrReactiveMaskTexture = FindProperty(x => x.superResolution.reactiveMask); - m_FsrAutoTcr = FindProperty(x => x.superResolution.autoGenerateTransparencyAndComposition); - m_FsrAutoTcrParams = FindProperty(x => x.superResolution.generateTransparencyAndCompositionParameters); - m_FsrTcrMaskTexture = FindProperty(x => x.superResolution.transparencyAndCompositionMask); - - m_FogEnabled = FindProperty(x => x.fog.enabled); - m_FogExcludeSkybox = FindProperty(x => x.fog.excludeSkybox); - - m_ShowToolkit = serializedObject.FindProperty("m_ShowToolkit"); - m_ShowCustomSorter = serializedObject.FindProperty("m_ShowCustomSorter"); - -#if UNITY_2017_3_OR_NEWER - m_TargetCameraComponent = m_Target.GetComponent(); -#endif - } - - void OnDisable() - { - m_CustomLists = null; - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - var camera = m_Target.GetComponent(); - - DoVolumeBlending(); - DoAntialiasing(); - DoFog(camera); - - EditorGUILayout.PropertyField(m_StopNaNPropagation, EditorUtilities.GetContent("Stop NaN Propagation|Automatically replaces NaN/Inf in shaders by a black pixel to avoid breaking some effects. This will slightly affect performances and should only be used if you experience NaN issues that you can't fix. Has no effect on GLES2 platforms.")); - -#if UNITY_2019_1_OR_NEWER - if (!RuntimeUtilities.scriptableRenderPipelineActive) - EditorGUILayout.PropertyField(m_DirectToCameraTarget, EditorUtilities.GetContent("Directly to Camera Target|Use the final blit to the camera render target for postprocessing. This has less overhead but breaks compatibility with legacy image effect that use OnRenderImage.")); -#endif - - EditorGUILayout.Space(); - - DoToolkit(); - DoCustomEffectSorter(); - - EditorUtilities.DrawSplitter(); - EditorGUILayout.Space(); - - serializedObject.ApplyModifiedProperties(); - } - - void DoVolumeBlending() - { - EditorGUILayout.LabelField(EditorUtilities.GetContent("Volume blending"), EditorStyles.boldLabel); - EditorGUI.indentLevel++; - { - // The layout system sort of break alignement when mixing inspector fields with - // custom layouted fields, do the layout manually instead - var indentOffset = EditorGUI.indentLevel * 15f; - var lineRect = GUILayoutUtility.GetRect(1, EditorGUIUtility.singleLineHeight); - var labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth - indentOffset, lineRect.height); - var fieldRect = new Rect(labelRect.xMax, lineRect.y, lineRect.width - labelRect.width - 60f, lineRect.height); - var buttonRect = new Rect(fieldRect.xMax, lineRect.y, 60f, lineRect.height); - - EditorGUI.PrefixLabel(labelRect, EditorUtilities.GetContent("Trigger|A transform that will act as a trigger for volume blending.")); - m_VolumeTrigger.objectReferenceValue = (Transform)EditorGUI.ObjectField(fieldRect, m_VolumeTrigger.objectReferenceValue, typeof(Transform), true); - if (GUI.Button(buttonRect, EditorUtilities.GetContent("This|Assigns the current GameObject as a trigger."), EditorStyles.miniButton)) - m_VolumeTrigger.objectReferenceValue = m_Target.transform; - - if (m_VolumeTrigger.objectReferenceValue == null) - EditorGUILayout.HelpBox("No trigger has been set, the camera will only be affected by global volumes.", MessageType.Info); - - EditorGUILayout.PropertyField(m_VolumeLayer, EditorUtilities.GetContent("Layer|This camera will only be affected by volumes in the selected scene-layers.")); - - int mask = m_VolumeLayer.intValue; - if (mask == 0) - EditorGUILayout.HelpBox("No layer has been set, the trigger will never be affected by volumes.", MessageType.Warning); - else if (mask == -1 || ((mask & 1) != 0)) - EditorGUILayout.HelpBox("Do not use \"Everything\" or \"Default\" as a layer mask as it will slow down the volume blending process! Put post-processing volumes in their own dedicated layer for best performances.", MessageType.Warning); - } - EditorGUI.indentLevel--; - - EditorGUILayout.Space(); - } - - void DoAntialiasing() - { - EditorGUILayout.LabelField(EditorUtilities.GetContent("Anti-aliasing"), EditorStyles.boldLabel); - EditorGUI.indentLevel++; - { - m_AntialiasingMode.intValue = EditorGUILayout.Popup(EditorUtilities.GetContent("Mode|The anti-aliasing method to use. FXAA is fast but low quality. SMAA works well for non-HDR scenes. TAA is a bit slower but higher quality and works well with HDR."), m_AntialiasingMode.intValue, s_AntialiasingMethodNames); - - if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.TemporalAntialiasing) - { -#if !UNITY_2017_3_OR_NEWER - if (RuntimeUtilities.isSinglePassStereoSelected) - EditorGUILayout.HelpBox("TAA requires Unity 2017.3+ for Single-pass stereo rendering support.", MessageType.Warning); -#endif -#if UNITY_2017_3_OR_NEWER - if (m_TargetCameraComponent != null && RuntimeUtilities.IsDynamicResolutionEnabled(m_TargetCameraComponent)) - EditorGUILayout.HelpBox("TAA is not supported with Dynamic Resolution.", MessageType.Warning); -#endif - - EditorGUILayout.PropertyField(m_TaaJitterSpread); - EditorGUILayout.PropertyField(m_TaaStationaryBlending); - EditorGUILayout.PropertyField(m_TaaMotionBlending); - EditorGUILayout.PropertyField(m_TaaSharpness); - } - else if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing) - { - if (RuntimeUtilities.isSinglePassStereoSelected) - EditorGUILayout.HelpBox("SMAA doesn't work with Single-pass stereo rendering.", MessageType.Warning); - - EditorGUILayout.PropertyField(m_SmaaQuality); - - if (m_SmaaQuality.intValue != (int)SubpixelMorphologicalAntialiasing.Quality.Low && EditorUtilities.isTargetingConsolesOrMobiles) - EditorGUILayout.HelpBox("For performance reasons it is recommended to use Low Quality on mobile and console platforms.", MessageType.Warning); - } - else if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.FastApproximateAntialiasing) - { - EditorGUILayout.PropertyField(m_FxaaFastMode); - EditorGUILayout.PropertyField(m_FxaaKeepAlpha); - - if (!m_FxaaFastMode.boolValue && EditorUtilities.isTargetingConsolesOrMobiles) - EditorGUILayout.HelpBox("For performance reasons it is recommended to use Fast Mode on mobile and console platforms.", MessageType.Warning); - } - else if (m_AntialiasingMode.intValue == (int)PostProcessLayer.Antialiasing.SuperResolution) - { - EditorGUILayout.PropertyField(m_FsrQualityMode); - EditorGUILayout.PropertyField(m_FsrPerformSharpen); - EditorGUILayout.PropertyField(m_FsrSharpness); - EditorGUILayout.PropertyField(m_FsrEnableFP16); - EditorGUILayout.PropertyField(m_FsrExposureSource); - if (m_FsrExposureSource.intValue == (int)SuperResolution.ExposureSource.Manual) EditorGUILayout.PropertyField(m_FsrExposureTexture); - EditorGUILayout.PropertyField(m_FsrPreExposure); - EditorGUILayout.PropertyField(m_FsrAutoReactive); - EditorGUILayout.PropertyField(m_FsrAutoReactive.boolValue ? m_FsrAutoReactiveParams : m_FsrReactiveMaskTexture); - EditorGUILayout.PropertyField(m_FsrAutoTcr); - EditorGUILayout.PropertyField(m_FsrAutoTcr.boolValue ? m_FsrAutoTcrParams : m_FsrTcrMaskTexture); - } - } - EditorGUI.indentLevel--; - - EditorGUILayout.Space(); - } - - void DoFog(Camera camera) - { - if (camera == null || camera.actualRenderingPath != RenderingPath.DeferredShading) - return; - - EditorGUILayout.LabelField(EditorUtilities.GetContent("Deferred Fog"), EditorStyles.boldLabel); - EditorGUI.indentLevel++; - { - EditorGUILayout.PropertyField(m_FogEnabled); - - if (m_FogEnabled.boolValue) - { - EditorGUILayout.PropertyField(m_FogExcludeSkybox); - EditorGUILayout.HelpBox("This adds fog compatibility to the deferred rendering path; actual fog settings should be set in the Lighting panel.", MessageType.Info); - } - } - EditorGUI.indentLevel--; - - EditorGUILayout.Space(); - } - - void DoToolkit() - { - EditorUtilities.DrawSplitter(); - m_ShowToolkit.boolValue = EditorUtilities.DrawHeader("Toolkit", m_ShowToolkit.boolValue); - - if (m_ShowToolkit.boolValue) - { - GUILayout.Space(2); - - if (GUILayout.Button(EditorUtilities.GetContent("Export frame to EXR..."), EditorStyles.miniButton)) - { - var menu = new GenericMenu(); - menu.AddItem(EditorUtilities.GetContent("Full Frame (as displayed)"), false, () => ExportFrameToExr(ExportMode.FullFrame)); - menu.AddItem(EditorUtilities.GetContent("Disable post-processing"), false, () => ExportFrameToExr(ExportMode.DisablePost)); - menu.AddItem(EditorUtilities.GetContent("Break before Color Grading (Linear)"), false, () => ExportFrameToExr(ExportMode.BreakBeforeColorGradingLinear)); - menu.AddItem(EditorUtilities.GetContent("Break before Color Grading (Log)"), false, () => ExportFrameToExr(ExportMode.BreakBeforeColorGradingLog)); - menu.ShowAsContext(); - } - - if (GUILayout.Button(EditorUtilities.GetContent("Select all layer volumes|Selects all the volumes that will influence this layer."), EditorStyles.miniButton)) - { - var volumes = RuntimeUtilities.GetAllSceneObjects() - .Where(x => (m_VolumeLayer.intValue & (1 << x.gameObject.layer)) != 0) - .Select(x => x.gameObject) - .Cast() - .ToArray(); - - if (volumes.Length > 0) - Selection.objects = volumes; - } - - if (GUILayout.Button(EditorUtilities.GetContent("Select all active volumes|Selects all volumes currently affecting the layer."), EditorStyles.miniButton)) - { - var volumes = new List(); - PostProcessManager.instance.GetActiveVolumes(m_Target, volumes); - - if (volumes.Count > 0) - { - Selection.objects = volumes - .Select(x => x.gameObject) - .Cast() - .ToArray(); - } - } - - GUILayout.Space(3); - } - } - - void DoCustomEffectSorter() - { - EditorUtilities.DrawSplitter(); - m_ShowCustomSorter.boolValue = EditorUtilities.DrawHeader("Custom Effect Sorting", m_ShowCustomSorter.boolValue); - - if (m_ShowCustomSorter.boolValue) - { - bool isInPrefab = false; - - // Init lists if needed - if (m_CustomLists == null) - { - // In some cases the editor will refresh before components which means - // components might not have been fully initialized yet. In this case we also - // need to make sure that we're not in a prefab as sorteBundles isn't a - // serializable object and won't exist until put on a scene. - if (m_Target.sortedBundles == null) - { - isInPrefab = string.IsNullOrEmpty(m_Target.gameObject.scene.name); - - if (!isInPrefab) - { - // sortedBundles will be initialized and ready to use on the next frame - Repaint(); - } - } - else - { - // Create a reorderable list for each injection event - m_CustomLists = new Dictionary(); - foreach (var evt in Enum.GetValues(typeof(PostProcessEvent)).Cast()) - { - var bundles = m_Target.sortedBundles[evt]; - var listName = ObjectNames.NicifyVariableName(evt.ToString()); - - var list = new ReorderableList(bundles, typeof(SerializedBundleRef), true, true, false, false); - - list.drawHeaderCallback = (rect) => - { - EditorGUI.LabelField(rect, listName); - }; - - list.drawElementCallback = (rect, index, isActive, isFocused) => - { - var sbr = (SerializedBundleRef)list.list[index]; - EditorGUI.LabelField(rect, sbr.bundle.attribute.menuItem); - }; - - list.onReorderCallback = (l) => - { - EditorUtility.SetDirty(m_Target); - }; - - m_CustomLists.Add(evt, list); - } - } - } - - GUILayout.Space(5); - - if (isInPrefab) - { - EditorGUILayout.HelpBox("Not supported in prefabs.", MessageType.Info); - GUILayout.Space(3); - return; - } - - bool anyList = false; - if (m_CustomLists != null) - { - foreach (var kvp in m_CustomLists) - { - var list = kvp.Value; - - // Skip empty lists to avoid polluting the inspector - if (list.count == 0) - continue; - - list.DoLayoutList(); - anyList = true; - } - } - - if (!anyList) - { - EditorGUILayout.HelpBox("No custom effect loaded.", MessageType.Info); - GUILayout.Space(3); - } - } - } - - void ExportFrameToExr(ExportMode mode) - { - string path = EditorUtility.SaveFilePanel("Export EXR...", "", "Frame", "exr"); - - if (string.IsNullOrEmpty(path)) - return; - - EditorUtility.DisplayProgressBar("Export EXR", "Rendering...", 0f); - - var camera = m_Target.GetComponent(); - var w = camera.pixelWidth; - var h = camera.pixelHeight; - - var texOut = new Texture2D(w, h, TextureFormat.RGBAFloat, false, true); - var target = RenderTexture.GetTemporary(w, h, 24, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); - - var lastActive = RenderTexture.active; - var lastTargetSet = camera.targetTexture; - var lastPostFXState = m_Target.enabled; - var lastBreakColorGradingState = m_Target.breakBeforeColorGrading; - - if (mode == ExportMode.DisablePost) - m_Target.enabled = false; - else if (mode == ExportMode.BreakBeforeColorGradingLinear || mode == ExportMode.BreakBeforeColorGradingLog) - m_Target.breakBeforeColorGrading = true; - - camera.targetTexture = target; - camera.Render(); - camera.targetTexture = lastTargetSet; - - EditorUtility.DisplayProgressBar("Export EXR", "Reading...", 0.25f); - - m_Target.enabled = lastPostFXState; - m_Target.breakBeforeColorGrading = lastBreakColorGradingState; - - if (mode == ExportMode.BreakBeforeColorGradingLog) - { - // Convert to log - var material = new Material(Shader.Find("Hidden/PostProcessing/Editor/ConvertToLog")); - var newTarget = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); - Graphics.Blit(target, newTarget, material, 0); - RenderTexture.ReleaseTemporary(target); - DestroyImmediate(material); - target = newTarget; - } - - RenderTexture.active = target; - texOut.ReadPixels(new Rect(0, 0, w, h), 0, 0); - texOut.Apply(); - RenderTexture.active = lastActive; - - EditorUtility.DisplayProgressBar("Export EXR", "Encoding...", 0.5f); - - var bytes = texOut.EncodeToEXR(EXRFlags.OutputAsFloat | EXRFlags.CompressZIP); - - EditorUtility.DisplayProgressBar("Export EXR", "Saving...", 0.75f); - - File.WriteAllBytes(path, bytes); - - EditorUtility.ClearProgressBar(); - AssetDatabase.Refresh(); - - RenderTexture.ReleaseTemporary(target); - DestroyImmediate(texOut); - } - } -} diff --git a/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef b/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef deleted file mode 100644 index a96f35c..0000000 --- a/com.unity.postprocessing/PostProcessing/Editor/Unity.Postprocessing.Editor.asmdef +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "Unity.Postprocessing.Editor", - "rootNamespace": "", - "references": [ - "Unity.Postprocessing.Runtime", - "Unity.XR.Management", - "Unity.XR.Management.Editor" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [ - { - "name": "com.unity.xr.management", - "expression": "4.0.1", - "define": "XR_MANAGEMENT_4_0_1_OR_NEWER" - }, - { - "name": "com.unity.modules.vr", - "expression": "1.0.0", - "define": "ENABLE_VR_MODULE" - }, - { - "name": "com.unity.modules.xr", - "expression": "1.0.0", - "define": "ENABLE_XR_MODULE" - } - ], - "noEngineReferences": false -} diff --git a/com.unity.postprocessing/PostProcessing/PostProcessResources.asset b/com.unity.postprocessing/PostProcessing/PostProcessResources.asset deleted file mode 100644 index 835a2eb..0000000 --- a/com.unity.postprocessing/PostProcessing/PostProcessResources.asset +++ /dev/null @@ -1,138 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 30f4b897495c7ad40b2d47143e02aaba, type: 3} - m_Name: PostProcessResources - m_EditorClassIdentifier: - blueNoise64: - - {fileID: 2800000, guid: 50b54341495978843a6f85583ed4417d, type: 3} - - {fileID: 2800000, guid: 3c2f1fb7e4b66e74191b7c328ada52d9, type: 3} - - {fileID: 2800000, guid: a469f920b21fc7c4fb5b950917ce2fb2, type: 3} - - {fileID: 2800000, guid: 373f9bf6b0841af4ebf26d25e4a3f4e2, type: 3} - - {fileID: 2800000, guid: 6fa5cf178eaaa5f42b820f636bb6e0bd, type: 3} - - {fileID: 2800000, guid: a1ae041906217ae44a774d4ca139af50, type: 3} - - {fileID: 2800000, guid: 79b86f3419b87f3429164a956da8cfab, type: 3} - - {fileID: 2800000, guid: 3ac02e7e783571c468f9c086d2384ba7, type: 3} - - {fileID: 2800000, guid: c55042318a938344ab23cd7f09dd0076, type: 3} - - {fileID: 2800000, guid: 71583cfd8899717428d5b1a95fa39cda, type: 3} - - {fileID: 2800000, guid: afe1e502240079342a0a980484b6da8b, type: 3} - - {fileID: 2800000, guid: 771903fe7b4674445829e52e91cff019, type: 3} - - {fileID: 2800000, guid: 980acadb960f8424c94307ec0e585b4e, type: 3} - - {fileID: 2800000, guid: 68613e6a221be1a4b9f31d7fa1c2d1bf, type: 3} - - {fileID: 2800000, guid: f6439b54b28f3884eb67579dec0b6f21, type: 3} - - {fileID: 2800000, guid: 2ee161d8945169243b5698fec114e1b7, type: 3} - - {fileID: 2800000, guid: 153f7d6dfbe713d4884df0f1e243ba92, type: 3} - - {fileID: 2800000, guid: bf95b6fdc179b0e4f890c841406193fc, type: 3} - - {fileID: 2800000, guid: 74aca53eb7273624baffc2bf5e5cc173, type: 3} - - {fileID: 2800000, guid: 729a3ae164bcb3b4380459386adcf331, type: 3} - - {fileID: 2800000, guid: 6dda07f1420a968449cf4c6620c44d9f, type: 3} - - {fileID: 2800000, guid: b7f000750830ddb4bbc80065b9314ce9, type: 3} - - {fileID: 2800000, guid: df01d03f056c6f445b4b8a0ae054207c, type: 3} - - {fileID: 2800000, guid: bfe953600e8fb1849a804ee08ace7b4c, type: 3} - - {fileID: 2800000, guid: 32c6a5f7143b86c44bd5cdee2ff3f8ad, type: 3} - - {fileID: 2800000, guid: f4b8ab78b57749d4e96d36f6d8a395d0, type: 3} - - {fileID: 2800000, guid: 09f6c01f98a3ded4daf1afc52a3c260f, type: 3} - - {fileID: 2800000, guid: bdd06fb88ef36ed4a85dd506352c2d80, type: 3} - - {fileID: 2800000, guid: 02c0a84bd64c6f044954d8bde9b46ec8, type: 3} - - {fileID: 2800000, guid: aa80dc44aa4fe4c43bb9d51d90cf2958, type: 3} - - {fileID: 2800000, guid: 0fa10b21877c61b4db40ba5708815f81, type: 3} - - {fileID: 2800000, guid: 6b0a189df0bd4d5448eaefb4e673ace8, type: 3} - - {fileID: 2800000, guid: 87a5e40cc271ea648b583616f6ebe7fe, type: 3} - - {fileID: 2800000, guid: b71bb466b71fd13449dd736f63caeb67, type: 3} - - {fileID: 2800000, guid: 319b8e66db3faa4438cf6982e9c89b2f, type: 3} - - {fileID: 2800000, guid: 0a79c155edf9b2d429d4736abee5acdb, type: 3} - - {fileID: 2800000, guid: 351e95d0e20a54849bd4ce5f9b498934, type: 3} - - {fileID: 2800000, guid: 1d6958e30e40a254dbe5a54c573eeb3c, type: 3} - - {fileID: 2800000, guid: 9660a4ca1ca8425408ac25c641932977, type: 3} - - {fileID: 2800000, guid: 547dbd5f858c74047ba3f213e4408307, type: 3} - - {fileID: 2800000, guid: 1a9ce5640cde5934aae0022f020464a6, type: 3} - - {fileID: 2800000, guid: cd9006dc442cc244e89b3f492384d46a, type: 3} - - {fileID: 2800000, guid: b266511438fae724f9d3ce6bd26583e8, type: 3} - - {fileID: 2800000, guid: 71bc1b6b66e8b784b972199b7e90204e, type: 3} - - {fileID: 2800000, guid: 15e54aa23a938444389469d53765d741, type: 3} - - {fileID: 2800000, guid: b9960364038cbfa4aa49d7b2032d3110, type: 3} - - {fileID: 2800000, guid: 8ecbbcae4cc747a4abbc4adce795d25e, type: 3} - - {fileID: 2800000, guid: 1378a33cdd085d64c9da863d2484ff21, type: 3} - - {fileID: 2800000, guid: aff59c63d25d43f4c938f248837c30fb, type: 3} - - {fileID: 2800000, guid: 3f7c3687170b90e4a8d2ee6b142670f4, type: 3} - - {fileID: 2800000, guid: d8c290e38ff0425409d0ae6a98c95e41, type: 3} - - {fileID: 2800000, guid: d5a51525b27e3ee4aadbeb39cbcf0750, type: 3} - - {fileID: 2800000, guid: d2e8e90fac2e6a341a38e1c3963c218d, type: 3} - - {fileID: 2800000, guid: c94b57b5a32a22d43ade66e09f6a4bd2, type: 3} - - {fileID: 2800000, guid: 936dea238abb0864ab3985a995e16a29, type: 3} - - {fileID: 2800000, guid: 5e542d0126a2c7848b66bffc428905fd, type: 3} - - {fileID: 2800000, guid: 70f23eaf7d8ae9147aa542d20e93733b, type: 3} - - {fileID: 2800000, guid: e138166e7a7c70f49943be7edda35d35, type: 3} - - {fileID: 2800000, guid: 85a45a6d8b2ffb84987d2b028ecfb220, type: 3} - - {fileID: 2800000, guid: d96974690c77f50489eb60ec84bd8dac, type: 3} - - {fileID: 2800000, guid: 404fa8def46b1c447817e1ebdaa7144e, type: 3} - - {fileID: 2800000, guid: 119591e0bb084e848835d237546b3882, type: 3} - - {fileID: 2800000, guid: a03c400b0e3959f428ee99dfc6cfc263, type: 3} - - {fileID: 2800000, guid: 4a11d65ce13d5f542a0ff136cc2f3fba, type: 3} - blueNoise256: - - {fileID: 2800000, guid: 6017f374382d64245a0a4aab668e6f38, type: 3} - - {fileID: 2800000, guid: 0f8fa14b3731cda4e947062e734d5e1e, type: 3} - - {fileID: 2800000, guid: 1abfe0e165ca1e9428b455ffc9a2d9ef, type: 3} - - {fileID: 2800000, guid: c072b653e98a06e40857d76ca8c7eecd, type: 3} - - {fileID: 2800000, guid: b52d5033b68309943a2386c270a90f44, type: 3} - - {fileID: 2800000, guid: acde5141d5f4f7a4188394bd52c4dc38, type: 3} - - {fileID: 2800000, guid: 999434725cbc2be4eb54043b36efd4a8, type: 3} - - {fileID: 2800000, guid: 70d0a1182b29d6347ac70374c3593bba, type: 3} - smaaLuts: - area: {fileID: 2800000, guid: 73ec4ae984a0a0f44a2be737e41a6f2f, type: 3} - search: {fileID: 2800000, guid: d99701099481a2f489610e977df6dcbc, type: 3} - shaders: - bloom: {fileID: 4800000, guid: c1e1d3119c6fd4646aea0b4b74cacc1a, type: 3} - copy: {fileID: 4800000, guid: cdbdb71de5f9c454b980f6d0e87f0afb, type: 3} - copyStd: {fileID: 4800000, guid: 4bf4cff0d0bac3d43894e2e8839feb40, type: 3} - copyStdFromTexArray: {fileID: 4800000, guid: 02d2da9bc88d25c4d878c1ed4e0b3854, type: 3} - copyStdFromDoubleWide: {fileID: 4800000, guid: e8ce9961912f3214586fe8709b9012c1, type: 3} - discardAlpha: {fileID: 4800000, guid: 5ab0816423f0dfe45841cab3b05ec9ef, type: 3} - depthOfField: {fileID: 4800000, guid: 0ef78d24e85a44f4da9d5b5eaa00e50b, type: 3} - finalPass: {fileID: 4800000, guid: f75014305794b3948a3c6d5ccd550e05, type: 3} - grainBaker: {fileID: 4800000, guid: 0d8afcb51cc9f0349a6d190da929b838, type: 3} - motionBlur: {fileID: 4800000, guid: 2c459b89a7c8b1a4fbefe0d81341651c, type: 3} - temporalAntialiasing: {fileID: 4800000, guid: 51bcf79c50dc92e47ba87821b61100c3, type: 3} - subpixelMorphologicalAntialiasing: {fileID: 4800000, guid: 81af42a93ade3dd46a9b583d4eec76d6, type: 3} - texture2dLerp: {fileID: 4800000, guid: 34a819c9e33402547a81619693adc8d5, type: 3} - uber: {fileID: 4800000, guid: 382151503e2a43a4ebb7366d1632731d, type: 3} - lut2DBaker: {fileID: 4800000, guid: 7ad194cbe7d006f4bace915156972026, type: 3} - lightMeter: {fileID: 4800000, guid: b34a29e523cb9d545881e193a079f2df, type: 3} - gammaHistogram: {fileID: 4800000, guid: f7ea35cfb33fcad4ab8f2429ec103bef, type: 3} - waveform: {fileID: 4800000, guid: 3020ac7ece79a7f4eb789a236f8bd6c5, type: 3} - vectorscope: {fileID: 4800000, guid: a71093f2a4fe26a40805c22739e10e4a, type: 3} - debugOverlays: {fileID: 4800000, guid: b958ad1c92bd3d64c9e61318b8681dab, type: 3} - deferredFog: {fileID: 4800000, guid: 4117fce9491711c4094d33a048e36e73, type: 3} - scalableAO: {fileID: 4800000, guid: d7640629310e79646af0f46eb55ae466, type: 3} - multiScaleAO: {fileID: 4800000, guid: 67f9497810829eb4791ec19e95781e51, type: 3} - screenSpaceReflections: {fileID: 4800000, guid: f997a3dc9254c44459323cced085150c, type: 3} - computeShaders: - autoExposure: {fileID: 7200000, guid: 34845e0ca016b7448842e965db5890a5, type: 3} - exposureHistogram: {fileID: 7200000, guid: 8c2fcbdf9bc58664f89917f7b9d79501, type: 3} - lut3DBaker: {fileID: 7200000, guid: 42496b74c071f5749950ca1abe33e945, type: 3} - texture3dLerp: {fileID: 7200000, guid: 31e9175024adfd44aba2530ff9b77494, type: 3} - gammaHistogram: {fileID: 7200000, guid: 18183ebfeeab97749b43e38b928604a7, type: 3} - waveform: {fileID: 7200000, guid: 92c63830cd50c0b4fbb8233613839958, type: 3} - vectorscope: {fileID: 7200000, guid: e1efca7c36fd01840aae0dd10378de5c, type: 3} - multiScaleAODownsample1: {fileID: 7200000, guid: 4c63bc487e6c29a4a99f85a6c47b292b, type: 3} - multiScaleAODownsample2: {fileID: 7200000, guid: e4d3e4779e48a374f91d48d4c0aedb7b, type: 3} - multiScaleAORender: {fileID: 7200000, guid: 34a460e8a2e66c243a9c12024e5a798d, type: 3} - multiScaleAOUpsample: {fileID: 7200000, guid: 600d6212b59bb40409d19d750b5fd1e9, type: 3} - gaussianDownsample: {fileID: 7200000, guid: 6dba4103d23a7904fbc49099355aff3e, type: 3} - superResolution: - computeLuminancePyramidPass: {fileID: 7200000, guid: d253be05abcdc80428503d3e4cce3a36, type: 3} - reconstructPreviousDepthPass: {fileID: 7200000, guid: 4f59e5b9179d74844ae06a30ae1e0629, type: 3} - depthClipPass: {fileID: 7200000, guid: 20e44016ed34b0d4b8de499d1b566c69, type: 3} - lockPass: {fileID: 7200000, guid: a135306e6d1857e43a86ef20db2a47fe, type: 3} - accumulatePass: {fileID: 7200000, guid: c9b45f0ae7673694ba57a4aadfe212e9, type: 3} - sharpenPass: {fileID: 7200000, guid: 7aaf5cfff022de2499e9b0412f947f6c, type: 3} - autoGenReactivePass: {fileID: 7200000, guid: 5716b91fdaa4e9e439df6b96a796fe6e, type: 3} - tcrAutoGenPass: {fileID: 7200000, guid: 75cdc6ef23f08ed498d4da511923fcea, type: 3} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs b/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs deleted file mode 100644 index 90c4f59..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/Effects/SuperResolution.cs +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.Experimental.Rendering; -using FidelityFX; - -namespace UnityEngine.Rendering.PostProcessing -{ - [UnityEngine.Scripting.Preserve] - [Serializable] - public class SuperResolution - { - public Func callbacksFactory { get; set; } = (context) => new Fsr3UpscalerCallbacksBase(); - - [Tooltip("Standard scaling ratio presets.")] - public Fsr3Upscaler.QualityMode qualityMode = Fsr3Upscaler.QualityMode.Quality; - - [Tooltip("Apply RCAS sharpening to the image after upscaling.")] - public bool performSharpenPass = true; - [Tooltip("Strength of the sharpening effect.")] - [Range(0, 1)] public float sharpness = 0.8f; - - [Tooltip("Allow the use of half precision compute operations, potentially improving performance if the platform supports it.")] - public bool enableFP16 = false; - - [Tooltip("Choose where to get the exposure value from. Use auto-exposure from either FSR3 or Unity, provide a manual exposure texture, or use a default value.")] - public ExposureSource exposureSource = ExposureSource.Auto; - [Tooltip("Value by which the input signal will be divided, to get back to the original signal produced by the game.")] - public float preExposure = 1.0f; - [Tooltip("Optional 1x1 texture containing the exposure value for the current frame.")] - public Texture exposure = null; - - public enum ExposureSource - { - Default, - Auto, - Unity, - Manual, - } - - [Tooltip("Optional texture to control the influence of the current frame on the reconstructed output. If unset, either an auto-generated or a default cleared reactive mask will be used.")] - public Texture reactiveMask = null; - [Tooltip("Optional texture for marking areas of specialist rendering which should be accounted for during the upscaling process. If unset, a default cleared mask will be used.")] - public Texture transparencyAndCompositionMask = null; - [Tooltip("Automatically generate a reactive mask based on the difference between opaque-only render output and the final render output including alpha transparencies.")] - public bool autoGenerateReactiveMask = true; - [Tooltip("Parameters to control the process of auto-generating a reactive mask.")] - public GenerateReactiveParameters generateReactiveParameters = new GenerateReactiveParameters(); - - [Serializable] - public class GenerateReactiveParameters - { - [Tooltip("A value to scale the output")] - [Range(0, 2)] public float scale = 0.5f; - [Tooltip("A threshold value to generate a binary reactive mask")] - [Range(0, 1)] public float cutoffThreshold = 0.2f; - [Tooltip("A value to set for the binary reactive mask")] - [Range(0, 1)] public float binaryValue = 0.9f; - [Tooltip("Flags to determine how to generate the reactive mask")] - public Fsr3Upscaler.GenerateReactiveFlags flags = Fsr3Upscaler.GenerateReactiveFlags.ApplyTonemap | Fsr3Upscaler.GenerateReactiveFlags.ApplyThreshold | Fsr3Upscaler.GenerateReactiveFlags.UseComponentsMax; - } - - [Tooltip("(Experimental) Automatically generate and use Reactive mask and Transparency & composition mask internally.")] - public bool autoGenerateTransparencyAndComposition = false; - [Tooltip("Parameters to control the process of auto-generating transparency and composition masks.")] - public GenerateTcrParameters generateTransparencyAndCompositionParameters = new GenerateTcrParameters(); - - [Serializable] - public class GenerateTcrParameters - { - [Tooltip("Setting this value too small will cause visual instability. Larger values can cause ghosting.")] - [Range(0, 1)] public float autoTcThreshold = 0.05f; - [Tooltip("Smaller values will increase stability at hard edges of translucent objects.")] - [Range(0, 2)] public float autoTcScale = 1.0f; - [Tooltip("Larger values result in more reactive pixels.")] - [Range(0, 10)] public float autoReactiveScale = 5.0f; - [Tooltip("Maximum value reactivity can reach.")] - [Range(0, 1)] public float autoReactiveMax = 0.9f; - } - - public Vector2 jitter { get; private set; } - public Vector2Int renderSize => _maxRenderSize; - public Vector2Int displaySize => _displaySize; - public RenderTargetIdentifier colorOpaqueOnly { get; set; } - - private Fsr3UpscalerContext _fsrContext; - private Vector2Int _maxRenderSize; - private Vector2Int _displaySize; - private bool _resetHistory; - - private IFsr3UpscalerCallbacks _callbacks; - - private readonly Fsr3Upscaler.DispatchDescription _dispatchDescription = new Fsr3Upscaler.DispatchDescription(); - private readonly Fsr3Upscaler.GenerateReactiveDescription _genReactiveDescription = new Fsr3Upscaler.GenerateReactiveDescription(); - - private Fsr3Upscaler.QualityMode _prevQualityMode; - private ExposureSource _prevExposureSource; - private Vector2Int _prevDisplaySize; - - private Rect _originalRect; - - public bool IsSupported() - { - return SystemInfo.supportsComputeShaders && SystemInfo.supportsMotionVectors; - } - - public DepthTextureMode GetCameraFlags() - { - return DepthTextureMode.Depth | DepthTextureMode.MotionVectors; - } - - public void Release() - { - DestroyFsrContext(); - } - - public void ResetHistory() - { - _resetHistory = true; - } - - public void ConfigureJitteredProjectionMatrix(PostProcessRenderContext context) - { - ApplyJitter(context.camera); - } - - public void ConfigureCameraViewport(PostProcessRenderContext context) - { - var camera = context.camera; - _originalRect = camera.rect; - - // Determine the desired rendering and display resolutions - _displaySize = new Vector2Int(camera.pixelWidth, camera.pixelHeight); - Fsr3Upscaler.GetRenderResolutionFromQualityMode(out int maxRenderWidth, out int maxRenderHeight, _displaySize.x, _displaySize.y, qualityMode); - _maxRenderSize = new Vector2Int(maxRenderWidth, maxRenderHeight); - - // Render to a smaller portion of the screen by manipulating the camera's viewport rect - camera.aspect = (float)_displaySize.x / _displaySize.y; - camera.rect = new Rect(0, 0, _originalRect.width * _maxRenderSize.x / _displaySize.x, _originalRect.height * _maxRenderSize.y / _displaySize.y); - } - - public void ResetCameraViewport(PostProcessRenderContext context) - { - context.camera.rect = _originalRect; - } - - public void Render(PostProcessRenderContext context) - { - var cmd = context.command; - cmd.BeginSample("FSR3 Upscaler"); - - // Monitor for any resolution changes and recreate the FSR3 Upscaler context if necessary - // We can't create an FSR3 Upscaler context without info from the post-processing context, so delay the initial setup until here - if (_fsrContext == null || _displaySize.x != _prevDisplaySize.x || _displaySize.y != _prevDisplaySize.y || qualityMode != _prevQualityMode || exposureSource != _prevExposureSource) - { - DestroyFsrContext(); - CreateFsrContext(context); - } - - SetupDispatchDescription(context); - - if (autoGenerateReactiveMask) - { - SetupAutoReactiveDescription(context); - - var scaledRenderSize = _genReactiveDescription.RenderSize; - cmd.GetTemporaryRT(Fsr3ShaderIDs.UavAutoReactive, scaledRenderSize.x, scaledRenderSize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); - _fsrContext.GenerateReactiveMask(_genReactiveDescription, cmd); - _dispatchDescription.Reactive = new ResourceView(Fsr3ShaderIDs.UavAutoReactive); - } - - _fsrContext.Dispatch(_dispatchDescription, cmd); - - cmd.EndSample("FSR3 Upscaler"); - - _resetHistory = false; - } - - private void CreateFsrContext(PostProcessRenderContext context) - { - _prevQualityMode = qualityMode; - _prevExposureSource = exposureSource; - _prevDisplaySize = _displaySize; - - // Initialize FSR3 Upscaler context - Fsr3Upscaler.InitializationFlags flags = 0; - if (context.camera.allowHDR) flags |= Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange; - if (enableFP16) flags |= Fsr3Upscaler.InitializationFlags.EnableFP16Usage; - if (exposureSource == ExposureSource.Auto) flags |= Fsr3Upscaler.InitializationFlags.EnableAutoExposure; - if (RuntimeUtilities.IsDynamicResolutionEnabled(context.camera)) flags |= Fsr3Upscaler.InitializationFlags.EnableDynamicResolution; - - _callbacks = callbacksFactory(context); - _fsrContext = Fsr3Upscaler.CreateContext(_displaySize, _maxRenderSize, context.resources.computeShaders.superResolution, flags); - - // Apply a mipmap bias so that textures retain their sharpness - float biasOffset = Fsr3Upscaler.GetMipmapBiasOffset(_maxRenderSize.x, _displaySize.x); - if (!float.IsNaN(biasOffset) && !float.IsInfinity(biasOffset)) - { - _callbacks.ApplyMipmapBias(biasOffset); - } - } - - private void DestroyFsrContext() - { - if (_fsrContext != null) - { - _fsrContext.Destroy(); - _fsrContext = null; - } - - if (_callbacks != null) - { - // Undo the current mipmap bias offset - _callbacks.UndoMipmapBias(); - _callbacks = null; - } - } - - private void ApplyJitter(Camera camera) - { - var scaledRenderSize = GetScaledRenderSize(camera); - - // Perform custom jittering of the camera's projection matrix according to FSR3's recipe - int jitterPhaseCount = Fsr3Upscaler.GetJitterPhaseCount(scaledRenderSize.x, _displaySize.x); - Fsr3Upscaler.GetJitterOffset(out float jitterX, out float jitterY, Time.frameCount, jitterPhaseCount); - - _dispatchDescription.JitterOffset = new Vector2(jitterX, jitterY); - - jitterX = 2.0f * jitterX / scaledRenderSize.x; - jitterY = 2.0f * jitterY / scaledRenderSize.y; - - var jitterTranslationMatrix = Matrix4x4.Translate(new Vector3(jitterX, jitterY, 0)); - camera.nonJitteredProjectionMatrix = camera.projectionMatrix; - camera.projectionMatrix = jitterTranslationMatrix * camera.nonJitteredProjectionMatrix; - camera.useJitteredProjectionMatrixForTransparentRendering = true; - - jitter = new Vector2(jitterX, jitterY); - } - - private void SetupDispatchDescription(PostProcessRenderContext context) - { - var camera = context.camera; - - // Set up the main FSR3 Upscaler dispatch parameters - _dispatchDescription.Color = new ResourceView(context.source); - _dispatchDescription.Depth = new ResourceView(BuiltinRenderTextureType.CameraTarget, RenderTextureSubElement.Depth); - _dispatchDescription.MotionVectors = new ResourceView(BuiltinRenderTextureType.MotionVectors); - _dispatchDescription.Exposure = ResourceView.Unassigned; - _dispatchDescription.Reactive = ResourceView.Unassigned; - _dispatchDescription.TransparencyAndComposition = ResourceView.Unassigned; - - if (exposureSource == ExposureSource.Manual && exposure != null) _dispatchDescription.Exposure = new ResourceView(exposure); - if (exposureSource == ExposureSource.Unity) _dispatchDescription.Exposure = new ResourceView(context.autoExposureTexture); - if (reactiveMask != null) _dispatchDescription.Reactive = new ResourceView(reactiveMask); - if (transparencyAndCompositionMask != null) _dispatchDescription.TransparencyAndComposition = new ResourceView(transparencyAndCompositionMask); - - var scaledRenderSize = GetScaledRenderSize(context.camera); - - _dispatchDescription.Output = new ResourceView(context.destination); - _dispatchDescription.PreExposure = preExposure; - _dispatchDescription.EnableSharpening = performSharpenPass; - _dispatchDescription.Sharpness = sharpness; - _dispatchDescription.MotionVectorScale.x = -scaledRenderSize.x; - _dispatchDescription.MotionVectorScale.y = -scaledRenderSize.y; - _dispatchDescription.RenderSize = scaledRenderSize; - _dispatchDescription.InputResourceSize = scaledRenderSize; - _dispatchDescription.FrameTimeDelta = Time.unscaledDeltaTime; - _dispatchDescription.CameraNear = camera.nearClipPlane; - _dispatchDescription.CameraFar = camera.farClipPlane; - _dispatchDescription.CameraFovAngleVertical = camera.fieldOfView * Mathf.Deg2Rad; - _dispatchDescription.ViewSpaceToMetersFactor = 1.0f; // 1 unit is 1 meter in Unity - _dispatchDescription.Reset = _resetHistory; - - // Set up the parameters for the optional experimental auto-TCR feature - _dispatchDescription.EnableAutoReactive = autoGenerateTransparencyAndComposition; - if (autoGenerateTransparencyAndComposition) - { - _dispatchDescription.ColorOpaqueOnly = new ResourceView(colorOpaqueOnly); - _dispatchDescription.AutoTcThreshold = generateTransparencyAndCompositionParameters.autoTcThreshold; - _dispatchDescription.AutoTcScale = generateTransparencyAndCompositionParameters.autoTcScale; - _dispatchDescription.AutoReactiveScale = generateTransparencyAndCompositionParameters.autoReactiveScale; - _dispatchDescription.AutoReactiveMax = generateTransparencyAndCompositionParameters.autoReactiveMax; - } - - if (SystemInfo.usesReversedZBuffer) - { - // Swap the near and far clip plane distances as FSR3 expects this when using inverted depth - (_dispatchDescription.CameraNear, _dispatchDescription.CameraFar) = (_dispatchDescription.CameraFar, _dispatchDescription.CameraNear); - } - } - - private void SetupAutoReactiveDescription(PostProcessRenderContext context) - { - // Set up the parameters to auto-generate a reactive mask - _genReactiveDescription.ColorOpaqueOnly = new ResourceView(colorOpaqueOnly); - _genReactiveDescription.ColorPreUpscale = new ResourceView(context.source); - _genReactiveDescription.OutReactive = new ResourceView(Fsr3ShaderIDs.UavAutoReactive); - _genReactiveDescription.RenderSize = GetScaledRenderSize(context.camera); - _genReactiveDescription.Scale = generateReactiveParameters.scale; - _genReactiveDescription.CutoffThreshold = generateReactiveParameters.cutoffThreshold; - _genReactiveDescription.BinaryValue = generateReactiveParameters.binaryValue; - _genReactiveDescription.Flags = generateReactiveParameters.flags; - } - - internal Vector2Int GetScaledRenderSize(Camera camera) - { - if (!RuntimeUtilities.IsDynamicResolutionEnabled(camera)) - return _maxRenderSize; - - return new Vector2Int(Mathf.CeilToInt(_maxRenderSize.x * ScalableBufferManager.widthScaleFactor), Mathf.CeilToInt(_maxRenderSize.y * ScalableBufferManager.heightScaleFactor)); - } - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3.meta deleted file mode 100644 index e12cfa8..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bfeca308812376e4a8e5e49e0d96c5c6 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs deleted file mode 100644 index 3a28843..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2023 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; - -namespace FidelityFX -{ - internal static class Fsr3ShaderIDs - { - // 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_dilated_depth"); - 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"); - internal static readonly int SrvPrevColorPreAlpha = Shader.PropertyToID("r_input_prev_color_pre_alpha"); - internal static readonly int SrvPrevColorPostAlpha = Shader.PropertyToID("r_input_prev_color_post_alpha"); - - // 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_dilated_depth"); - 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"); - internal static readonly int UavAutoComposition = Shader.PropertyToID("rw_output_autocomposition"); - internal static readonly int UavPrevColorPreAlpha = Shader.PropertyToID("rw_output_prev_color_pre_alpha"); - internal static readonly int UavPrevColorPostAlpha = Shader.PropertyToID("rw_output_prev_color_post_alpha"); - - // Constant buffer bindings - internal static readonly int CbFsr3Upscaler = Shader.PropertyToID("cbFSR3Upscaler"); - internal static readonly int CbSpd = Shader.PropertyToID("cbSPD"); - internal static readonly int CbRcas = Shader.PropertyToID("cbRCAS"); - internal static readonly int CbGenReactive = Shader.PropertyToID("cbGenerateReactive"); - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs.meta deleted file mode 100644 index 9012617..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3ShaderIDs.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: a0e0bcc2967836742b7864d1cafafbf0 -timeCreated: 1679060863 \ No newline at end of file diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs deleted file mode 100644 index c636c93..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Runtime.InteropServices; -using UnityEngine; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// A collection of helper functions and data structures required by the FSR3 Upscaler process. - /// - public static class Fsr3Upscaler - { - /// - /// Creates a new FSR3 Upscaler context with standard parameters that are appropriate for the current platform. - /// - public static Fsr3UpscalerContext CreateContext(Vector2Int displaySize, Vector2Int maxRenderSize, Fsr3UpscalerShaders shaders, InitializationFlags flags = 0) - { - if (SystemInfo.usesReversedZBuffer) - flags |= InitializationFlags.EnableDepthInverted; - else - flags &= ~InitializationFlags.EnableDepthInverted; - -#if UNITY_EDITOR || DEVELOPMENT_BUILD - flags |= InitializationFlags.EnableDebugChecking; -#endif - - Debug.Log($"Setting up FSR3 Upscaler with render size: {maxRenderSize.x}x{maxRenderSize.y}, display size: {displaySize.x}x{displaySize.y}, flags: {flags}"); - - var contextDescription = new ContextDescription - { - Flags = flags, - DisplaySize = displaySize, - MaxRenderSize = maxRenderSize, - Shaders = shaders, - }; - - var context = new Fsr3UpscalerContext(); - context.Create(contextDescription); - return context; - } - - public static float GetUpscaleRatioFromQualityMode(QualityMode qualityMode) - { - switch (qualityMode) - { - case QualityMode.NativeAA: - return 1.0f; - case QualityMode.UltraQuality: - return 1.2f; - case QualityMode.Quality: - return 1.5f; - case QualityMode.Balanced: - return 1.7f; - case QualityMode.Performance: - return 2.0f; - case QualityMode.UltraPerformance: - return 3.0f; - default: - return 1.0f; - } - } - - public static void GetRenderResolutionFromQualityMode( - out int renderWidth, out int renderHeight, - int displayWidth, int displayHeight, QualityMode qualityMode) - { - float ratio = GetUpscaleRatioFromQualityMode(qualityMode); - renderWidth = Mathf.RoundToInt(displayWidth / ratio); - renderHeight = Mathf.RoundToInt(displayHeight / ratio); - } - - public static float GetMipmapBiasOffset(int renderWidth, int displayWidth) - { - return Mathf.Log((float)renderWidth / displayWidth, 2.0f) - 1.0f; - } - - public static int GetJitterPhaseCount(int renderWidth, int displayWidth) - { - const float basePhaseCount = 8.0f; - int jitterPhaseCount = (int)(basePhaseCount * Mathf.Pow((float)displayWidth / renderWidth, 2.0f)); - return jitterPhaseCount; - } - - public static void GetJitterOffset(out float outX, out float outY, int index, int phaseCount) - { - outX = Halton((index % phaseCount) + 1, 2) - 0.5f; - outY = Halton((index % phaseCount) + 1, 3) - 0.5f; - } - - // Calculate halton number for index and base. - private static float Halton(int index, int @base) - { - float f = 1.0f, result = 0.0f; - - for (int currentIndex = index; currentIndex > 0;) { - - f /= @base; - result += f * (currentIndex % @base); - currentIndex = (int)Mathf.Floor((float)currentIndex / @base); - } - - return result; - } - - public static float Lanczos2(float value) - { - return Mathf.Abs(value) < Mathf.Epsilon ? 1.0f : Mathf.Sin(Mathf.PI * value) / (Mathf.PI * value) * (Mathf.Sin(0.5f * Mathf.PI * value) / (0.5f * Mathf.PI * value)); - } - -#if !UNITY_2021_1_OR_NEWER - internal static void SetBufferData(this CommandBuffer commandBuffer, ComputeBuffer computeBuffer, Array data) - { - commandBuffer.SetComputeBufferData(computeBuffer, data); - } -#endif - - public enum QualityMode - { - NativeAA = 0, - UltraQuality = 1, - Quality = 2, - Balanced = 3, - Performance = 4, - UltraPerformance = 5, - } - - [Flags] - public enum InitializationFlags - { - EnableHighDynamicRange = 1 << 0, - EnableDisplayResolutionMotionVectors = 1 << 1, - EnableMotionVectorsJitterCancellation = 1 << 2, - EnableDepthInverted = 1 << 3, - EnableDepthInfinite = 1 << 4, - EnableAutoExposure = 1 << 5, - EnableDynamicResolution = 1 << 6, - EnableFP16Usage = 1 << 7, - EnableDebugChecking = 1 << 8, - } - - /// - /// A structure encapsulating the parameters required to initialize FidelityFX Super Resolution 3 upscaling. - /// - public struct ContextDescription - { - public InitializationFlags Flags; - public Vector2Int MaxRenderSize; - public Vector2Int DisplaySize; - public Fsr3UpscalerShaders Shaders; - } - - /// - /// A structure encapsulating the parameters for dispatching the various passes of FidelityFX Super Resolution 3. - /// - public class DispatchDescription - { - public ResourceView Color; - public ResourceView Depth; - public ResourceView MotionVectors; - public ResourceView Exposure; // optional - public ResourceView Reactive; // optional - public ResourceView TransparencyAndComposition; // optional - public ResourceView Output; - public Vector2 JitterOffset; - public Vector2 MotionVectorScale; - public Vector2Int RenderSize; - public Vector2Int InputResourceSize; - public bool EnableSharpening; - public float Sharpness; - public float FrameTimeDelta; // in seconds - public float PreExposure; - public bool Reset; - public float CameraNear; - public float CameraFar; - public float CameraFovAngleVertical; - public float ViewSpaceToMetersFactor; - - // EXPERIMENTAL reactive mask generation parameters - public bool EnableAutoReactive; - public ResourceView ColorOpaqueOnly; - public float AutoTcThreshold = 0.05f; - public float AutoTcScale = 1.0f; - public float AutoReactiveScale = 5.0f; - public float AutoReactiveMax = 0.9f; - } - - /// - /// A structure encapsulating the parameters for automatic generation of a reactive mask. - /// The default values for Scale, CutoffThreshold, BinaryValue and Flags were taken from the FSR3 demo project. - /// - public class GenerateReactiveDescription - { - public ResourceView ColorOpaqueOnly; - public ResourceView ColorPreUpscale; - public ResourceView OutReactive; - public Vector2Int RenderSize; - public float Scale = 0.5f; - public float CutoffThreshold = 0.2f; - public float BinaryValue = 0.9f; - public GenerateReactiveFlags Flags = GenerateReactiveFlags.ApplyTonemap | GenerateReactiveFlags.ApplyThreshold | GenerateReactiveFlags.UseComponentsMax; - } - - [Flags] - public enum GenerateReactiveFlags - { - ApplyTonemap = 1 << 0, - ApplyInverseTonemap = 1 << 1, - ApplyThreshold = 1 << 2, - UseComponentsMax = 1 << 3, - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct UpscalerConstants - { - public Vector2Int renderSize; - public Vector2Int maxRenderSize; - public Vector2Int displaySize; - public Vector2Int inputColorResourceDimensions; - public Vector2Int lumaMipDimensions; - public int lumaMipLevelToUse; - public int frameIndex; - - public Vector4 deviceToViewDepth; - public Vector2 jitterOffset; - public Vector2 motionVectorScale; - public Vector2 downscaleFactor; - public Vector2 motionVectorJitterCancellation; - public float preExposure; - public float previousFramePreExposure; - public float tanHalfFOV; - public float jitterPhaseCount; - public float deltaTime; - public float dynamicResChangeFactor; - public float viewSpaceToMetersFactor; - - public int dummy; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct SpdConstants - { - public uint mips; - public uint numWorkGroups; - public uint workGroupOffsetX, workGroupOffsetY; - public uint renderSizeX, renderSizeY; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct GenerateReactiveConstants - { - public float scale; - public float threshold; - public float binaryValue; - public uint flags; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct GenerateReactiveConstants2 - { - public float autoTcThreshold; - public float autoTcScale; - public float autoReactiveScale; - public float autoReactiveMax; - } - - [Serializable, StructLayout(LayoutKind.Sequential)] - internal struct RcasConstants - { - public RcasConstants(uint sharpness, uint halfSharp) - { - this.sharpness = sharpness; - this.halfSharp = halfSharp; - dummy0 = dummy1 = 0; - } - - public readonly uint sharpness; - public readonly uint halfSharp; - public readonly uint dummy0; - public readonly uint dummy1; - } - } - - /// - /// An immutable structure wrapping all of the necessary information to bind a specific buffer or attachment of a render target to a compute shader. - /// - public readonly struct ResourceView - { - /// - /// This value is the equivalent of not setting any value at all; all struct fields will have their default values. - /// It does not refer to a valid texture, therefore any variable set to this value should be checked for IsValid and reassigned before being bound to a shader. - /// - public static readonly ResourceView Unassigned = new ResourceView(default); - - /// - /// This value contains a valid texture reference that can be bound to a shader, however it is just an empty placeholder texture. - /// Binding this to a shader can be seen as setting the texture variable inside the shader to null. - /// - public static readonly ResourceView None = new ResourceView(BuiltinRenderTextureType.None); - - public ResourceView(in RenderTargetIdentifier renderTarget, RenderTextureSubElement subElement = RenderTextureSubElement.Default, int mipLevel = 0) - { - RenderTarget = renderTarget; - SubElement = subElement; - MipLevel = mipLevel; - } - - public bool IsValid => !RenderTarget.Equals(default); - - public readonly RenderTargetIdentifier RenderTarget; - public readonly RenderTextureSubElement SubElement; - public readonly int MipLevel; - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs.meta deleted file mode 100644 index 6717df4..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3Upscaler.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: df8b18c192f2dc145b4b43e68fd3407d -timeCreated: 1673441954 \ No newline at end of file diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs deleted file mode 100644 index 3e4e24f..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) 2023 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; - -namespace FidelityFX -{ - /// - /// Scriptable object containing all shader resources required by FidelityFX Super Resolution 3 (FSR3) Upscaler. - /// These can be stored in an asset file and referenced from a scene or prefab, avoiding the need to load the shaders from a Resources folder. - /// - public class Fsr3UpscalerAssets : ScriptableObject - { - public Fsr3UpscalerShaders shaders; - -#if UNITY_EDITOR - private void Reset() - { - shaders = new Fsr3UpscalerShaders - { - computeLuminancePyramidPass = FindComputeShader("ffx_fsr3upscaler_compute_luminance_pyramid_pass"), - reconstructPreviousDepthPass = FindComputeShader("ffx_fsr3upscaler_reconstruct_previous_depth_pass"), - depthClipPass = FindComputeShader("ffx_fsr3upscaler_depth_clip_pass"), - lockPass = FindComputeShader("ffx_fsr3upscaler_lock_pass"), - accumulatePass = FindComputeShader("ffx_fsr3upscaler_accumulate_pass"), - sharpenPass = FindComputeShader("ffx_fsr3upscaler_rcas_pass"), - autoGenReactivePass = FindComputeShader("ffx_fsr3upscaler_autogen_reactive_pass"), - tcrAutoGenPass = FindComputeShader("ffx_fsr3upscaler_tcr_autogen_pass"), - }; - } - - private static ComputeShader FindComputeShader(string name) - { - string[] assetGuids = UnityEditor.AssetDatabase.FindAssets($"t:ComputeShader {name}"); - if (assetGuids == null || assetGuids.Length == 0) - return null; - - string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assetGuids[0]); - return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); - } -#endif - } - - /// - /// All the compute shaders used by the FSR3 Upscaler. - /// - [System.Serializable] - public class Fsr3UpscalerShaders - { - /// - /// The compute shader used by the luminance pyramid computation pass. - /// - public ComputeShader computeLuminancePyramidPass; - - /// - /// The compute shader used by the previous depth reconstruction pass. - /// - public ComputeShader reconstructPreviousDepthPass; - - /// - /// The compute shader used by the depth clip pass. - /// - public ComputeShader depthClipPass; - - /// - /// The compute shader used by the lock pass. - /// - public ComputeShader lockPass; - - /// - /// The compute shader used by the accumulation pass. - /// - public ComputeShader accumulatePass; - - /// - /// The compute shader used by the RCAS sharpening pass. - /// - public ComputeShader sharpenPass; - - /// - /// The compute shader used to auto-generate a reactive mask. - /// - public ComputeShader autoGenReactivePass; - - /// - /// The compute shader used to auto-generate a transparency & composition mask. - /// - public ComputeShader tcrAutoGenPass; - - /// - /// Returns a copy of this class and its contents. - /// - public Fsr3UpscalerShaders Clone() - { - return (Fsr3UpscalerShaders)MemberwiseClone(); - } - - /// - /// Returns a copy of this class with clones of all its shaders. - /// This can be useful if you're running multiple FSR3 Upscaler instances with different shader configurations. - /// Be sure to clean up these clones through Dispose once you're done with them. - /// - public Fsr3UpscalerShaders DeepCopy() - { - return new Fsr3UpscalerShaders - { - computeLuminancePyramidPass = Object.Instantiate(computeLuminancePyramidPass), - reconstructPreviousDepthPass = Object.Instantiate(reconstructPreviousDepthPass), - depthClipPass = Object.Instantiate(depthClipPass), - lockPass = Object.Instantiate(lockPass), - accumulatePass = Object.Instantiate(accumulatePass), - sharpenPass = Object.Instantiate(sharpenPass), - autoGenReactivePass = Object.Instantiate(autoGenReactivePass), - tcrAutoGenPass = Object.Instantiate(tcrAutoGenPass), - }; - } - - /// - /// Destroy all the shaders within this instance. - /// Use this only on clones created through DeepCopy. - /// - public void Dispose() - { - Object.Destroy(computeLuminancePyramidPass); - Object.Destroy(reconstructPreviousDepthPass); - Object.Destroy(depthClipPass); - Object.Destroy(lockPass); - Object.Destroy(accumulatePass); - Object.Destroy(sharpenPass); - Object.Destroy(autoGenReactivePass); - Object.Destroy(tcrAutoGenPass); - } - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta deleted file mode 100644 index de48032..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerAssets.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: aaeb3d821f826d44b84289a2dd23f90e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs deleted file mode 100644 index 5b2c89c..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2023 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; - -namespace FidelityFX -{ - /// - /// A collection of callbacks required by the FSR3 Upscaler process. - /// This allows some customization by the game dev on how to integrate FSR3 upscaling into their own game setup. - /// - public interface IFsr3UpscalerCallbacks - { - /// - /// Apply a mipmap bias to in-game textures to prevent them from becoming blurry as the internal rendering resolution lowers. - /// This will need to be customized on a per-game basis, as there is no clear universal way to determine what are "in-game" textures. - /// The default implementation will simply apply a mipmap bias to all 2D textures, which will include things like UI textures and which might miss things like terrain texture arrays. - /// - /// Depending on how your game organizes its assets, you will want to create a filter that more specifically selects the textures that need to have this mipmap bias applied. - /// You may also want to store the bias offset value and apply it to any assets that are loaded in on demand. - /// - void ApplyMipmapBias(float biasOffset); - - void UndoMipmapBias(); - } - - /// - /// Default implementation of IFsr3UpscalerCallbacks using simple Resources calls. - /// These are fine for testing but a proper game will want to extend and override these methods. - /// - public class Fsr3UpscalerCallbacksBase: IFsr3UpscalerCallbacks - { - protected float CurrentBiasOffset = 0; - - public virtual void ApplyMipmapBias(float biasOffset) - { - if (float.IsNaN(biasOffset) || float.IsInfinity(biasOffset)) - return; - - CurrentBiasOffset += biasOffset; - - if (Mathf.Approximately(CurrentBiasOffset, 0f)) - { - CurrentBiasOffset = 0f; - } - - foreach (var texture in Resources.FindObjectsOfTypeAll()) - { - if (texture.mipmapCount <= 1) - continue; - - texture.mipMapBias += biasOffset; - } - } - - public virtual void UndoMipmapBias() - { - if (CurrentBiasOffset == 0f) - return; - - ApplyMipmapBias(-CurrentBiasOffset); - } - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta deleted file mode 100644 index ae2ee1a..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerCallbacks.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e1b555daa29ec3043a8cf89b4db31a26 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs deleted file mode 100644 index da02d9f..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs +++ /dev/null @@ -1,610 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Runtime.InteropServices; -using UnityEngine; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// This class loosely matches the FfxFsr3UpscalerContext struct from the original FSR3 codebase. - /// It manages the various resources and compute passes required by the FSR3 Upscaler process. - /// Note that this class does not know anything about Unity render pipelines; all it knows is CommandBuffers and RenderTargetIdentifiers. - /// This should make it suitable for integration with any of the available Unity render pipelines. - /// - public class Fsr3UpscalerContext - { - private const int MaxQueuedFrames = 16; - - private Fsr3Upscaler.ContextDescription _contextDescription; - private CommandBuffer _commandBuffer; - - private Fsr3UpscalerPass _depthClipPass; - private Fsr3UpscalerPass _reconstructPreviousDepthPass; - private Fsr3UpscalerPass _lockPass; - private Fsr3UpscalerPass _accumulatePass; - private Fsr3UpscalerPass _sharpenPass; - private Fsr3UpscalerPass _computeLuminancePyramidPass; - private Fsr3UpscalerPass _generateReactivePass; - private Fsr3UpscalerPass _tcrAutogeneratePass; - - private readonly Fsr3UpscalerResources _resources = new Fsr3UpscalerResources(); - - private ComputeBuffer _upscalerConstantsBuffer; - private readonly Fsr3Upscaler.UpscalerConstants[] _upscalerConstantsArray = { new Fsr3Upscaler.UpscalerConstants() }; - private ref Fsr3Upscaler.UpscalerConstants UpscalerConsts => ref _upscalerConstantsArray[0]; - - private ComputeBuffer _spdConstantsBuffer; - private readonly Fsr3Upscaler.SpdConstants[] _spdConstantsArray = { new Fsr3Upscaler.SpdConstants() }; - private ref Fsr3Upscaler.SpdConstants SpdConsts => ref _spdConstantsArray[0]; - - private ComputeBuffer _rcasConstantsBuffer; - private readonly Fsr3Upscaler.RcasConstants[] _rcasConstantsArray = new Fsr3Upscaler.RcasConstants[1]; - private ref Fsr3Upscaler.RcasConstants RcasConsts => ref _rcasConstantsArray[0]; - - private ComputeBuffer _generateReactiveConstantsBuffer; - private readonly Fsr3Upscaler.GenerateReactiveConstants[] _generateReactiveConstantsArray = { new Fsr3Upscaler.GenerateReactiveConstants() }; - private ref Fsr3Upscaler.GenerateReactiveConstants GenReactiveConsts => ref _generateReactiveConstantsArray[0]; - - private ComputeBuffer _tcrAutogenerateConstantsBuffer; - private readonly Fsr3Upscaler.GenerateReactiveConstants2[] _tcrAutogenerateConstantsArray = { new Fsr3Upscaler.GenerateReactiveConstants2() }; - private ref Fsr3Upscaler.GenerateReactiveConstants2 TcrAutoGenConsts => ref _tcrAutogenerateConstantsArray[0]; - - private bool _firstExecution; - private Vector2 _previousJitterOffset; - private int _resourceFrameIndex; - - public void Create(Fsr3Upscaler.ContextDescription contextDescription) - { - _contextDescription = contextDescription; - _commandBuffer = new CommandBuffer { name = "FSR3 Upscaler" }; - - _upscalerConstantsBuffer = CreateConstantBuffer(); - _spdConstantsBuffer = CreateConstantBuffer(); - _rcasConstantsBuffer = CreateConstantBuffer(); - _generateReactiveConstantsBuffer = CreateConstantBuffer(); - _tcrAutogenerateConstantsBuffer = CreateConstantBuffer(); - - // Set defaults - _firstExecution = true; - _resourceFrameIndex = 0; - - UpscalerConsts.displaySize = _contextDescription.DisplaySize; - - _resources.Create(_contextDescription); - CreatePasses(); - } - - private void CreatePasses() - { - _computeLuminancePyramidPass = new Fsr3UpscalerComputeLuminancePyramidPass(_contextDescription, _resources, _upscalerConstantsBuffer, _spdConstantsBuffer); - _reconstructPreviousDepthPass = new Fsr3UpscalerReconstructPreviousDepthPass(_contextDescription, _resources, _upscalerConstantsBuffer); - _depthClipPass = new Fsr3UpscalerDepthClipPass(_contextDescription, _resources, _upscalerConstantsBuffer); - _lockPass = new Fsr3UpscalerLockPass(_contextDescription, _resources, _upscalerConstantsBuffer); - _accumulatePass = new Fsr3UpscalerAccumulatePass(_contextDescription, _resources, _upscalerConstantsBuffer); - _sharpenPass = new Fsr3UpscalerSharpenPass(_contextDescription, _resources, _upscalerConstantsBuffer, _rcasConstantsBuffer); - _generateReactivePass = new Fsr3UpscalerGenerateReactivePass(_contextDescription, _resources, _generateReactiveConstantsBuffer); - _tcrAutogeneratePass = new Fsr3UpscalerTcrAutogeneratePass(_contextDescription, _resources, _upscalerConstantsBuffer, _tcrAutogenerateConstantsBuffer); - } - - public void Destroy() - { - DestroyPass(ref _tcrAutogeneratePass); - DestroyPass(ref _generateReactivePass); - DestroyPass(ref _computeLuminancePyramidPass); - DestroyPass(ref _sharpenPass); - DestroyPass(ref _accumulatePass); - DestroyPass(ref _lockPass); - DestroyPass(ref _reconstructPreviousDepthPass); - DestroyPass(ref _depthClipPass); - - _resources.Destroy(); - - DestroyConstantBuffer(ref _tcrAutogenerateConstantsBuffer); - DestroyConstantBuffer(ref _generateReactiveConstantsBuffer); - DestroyConstantBuffer(ref _rcasConstantsBuffer); - DestroyConstantBuffer(ref _spdConstantsBuffer); - DestroyConstantBuffer(ref _upscalerConstantsBuffer); - - _commandBuffer.Dispose(); - _commandBuffer = null; - } - - public void Dispatch(Fsr3Upscaler.DispatchDescription dispatchParams) - { - _commandBuffer.Clear(); - Dispatch(dispatchParams, _commandBuffer); - Graphics.ExecuteCommandBuffer(_commandBuffer); - } - - public void Dispatch(Fsr3Upscaler.DispatchDescription dispatchParams, CommandBuffer commandBuffer) - { - if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDebugChecking) != 0) - { - DebugCheckDispatch(dispatchParams); - } - - if (_firstExecution) - { - commandBuffer.SetRenderTarget(_resources.LockStatus[0]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - commandBuffer.SetRenderTarget(_resources.LockStatus[1]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - } - - int frameIndex = _resourceFrameIndex % 2; - bool resetAccumulation = dispatchParams.Reset || _firstExecution; - _firstExecution = false; - - // If auto exposure is enabled use the auto exposure SRV, otherwise what the app sends - if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableAutoExposure) != 0) - dispatchParams.Exposure = new ResourceView(_resources.AutoExposure); - else if (!dispatchParams.Exposure.IsValid) - dispatchParams.Exposure = new ResourceView(_resources.DefaultExposure); - - if (dispatchParams.EnableAutoReactive) - { - // Create the auto-TCR resources only when we need them - if (_resources.AutoReactive == null) - _resources.CreateTcrAutogenResources(_contextDescription); - - if (resetAccumulation) - { - RenderTargetIdentifier opaqueOnly = dispatchParams.ColorOpaqueOnly.IsValid ? dispatchParams.ColorOpaqueOnly.RenderTarget : Fsr3ShaderIDs.SrvOpaqueOnly; - commandBuffer.Blit(_resources.PrevPreAlpha[frameIndex ^ 1], opaqueOnly); - } - } - else if (_resources.AutoReactive != null) - { - // Destroy the auto-TCR resources if we don't use the feature - _resources.DestroyTcrAutogenResources(); - } - - if (!dispatchParams.Reactive.IsValid) dispatchParams.Reactive = new ResourceView(_resources.DefaultReactive); - if (!dispatchParams.TransparencyAndComposition.IsValid) dispatchParams.TransparencyAndComposition = new ResourceView(_resources.DefaultReactive); - Fsr3UpscalerResources.CreateAliasableResources(commandBuffer, _contextDescription, dispatchParams); - - SetupConstants(dispatchParams, resetAccumulation); - - // Reactive mask bias - const int threadGroupWorkRegionDim = 8; - int dispatchSrcX = (UpscalerConsts.renderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchSrcY = (UpscalerConsts.renderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchDstX = (_contextDescription.DisplaySize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchDstY = (_contextDescription.DisplaySize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - // Clear reconstructed depth for max depth store - if (resetAccumulation) - { - commandBuffer.SetRenderTarget(_resources.LockStatus[frameIndex ^ 1]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - - commandBuffer.SetRenderTarget(_resources.InternalUpscaled[frameIndex ^ 1]); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - - commandBuffer.SetRenderTarget(_resources.SceneLuminance); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - - // Auto exposure always used to track luma changes in locking logic - commandBuffer.SetRenderTarget(_resources.AutoExposure); - commandBuffer.ClearRenderTarget(false, true, new Color(0f, 1e8f, 0f, 0f)); - - // Reset atomic counter to 0 - commandBuffer.SetRenderTarget(_resources.SpdAtomicCounter); - commandBuffer.ClearRenderTarget(false, true, Color.clear); - } - - // FSR3: need to clear here since we need the content of this surface for frame interpolation, so clearing in the lock pass is not an option - bool depthInverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) == Fsr3Upscaler.InitializationFlags.EnableDepthInverted; - commandBuffer.SetRenderTarget(Fsr3ShaderIDs.UavReconstructedPrevNearestDepth); - commandBuffer.ClearRenderTarget(false, true, depthInverted ? Color.clear : Color.white); - - // Auto exposure - SetupSpdConstants(dispatchParams, out var dispatchThreadGroupCount); - - // Initialize constant buffers data - commandBuffer.SetBufferData(_upscalerConstantsBuffer, _upscalerConstantsArray); - commandBuffer.SetBufferData(_spdConstantsBuffer, _spdConstantsArray); - - // Auto reactive - if (dispatchParams.EnableAutoReactive) - { - GenerateTransparencyCompositionReactive(dispatchParams, commandBuffer, frameIndex); - dispatchParams.Reactive = new ResourceView(_resources.AutoReactive); - dispatchParams.TransparencyAndComposition = new ResourceView(_resources.AutoComposition); - } - - // Compute luminance pyramid - _computeLuminancePyramidPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); - - // Reconstruct previous depth - _reconstructPreviousDepthPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - - // Depth clip - _depthClipPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - - // Create locks - _lockPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - - // Accumulate - _accumulatePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchDstX, dispatchDstY); - - if (dispatchParams.EnableSharpening) - { - // Compute the constants - SetupRcasConstants(dispatchParams); - commandBuffer.SetBufferData(_rcasConstantsBuffer, _rcasConstantsArray); - - // Dispatch RCAS - const int threadGroupWorkRegionDimRcas = 16; - int threadGroupsX = (Screen.width + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - int threadGroupsY = (Screen.height + threadGroupWorkRegionDimRcas - 1) / threadGroupWorkRegionDimRcas; - _sharpenPass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, threadGroupsX, threadGroupsY); - } - - _resourceFrameIndex = (_resourceFrameIndex + 1) % MaxQueuedFrames; - - Fsr3UpscalerResources.DestroyAliasableResources(commandBuffer); - } - - public void GenerateReactiveMask(Fsr3Upscaler.GenerateReactiveDescription dispatchParams) - { - _commandBuffer.Clear(); - GenerateReactiveMask(dispatchParams, _commandBuffer); - Graphics.ExecuteCommandBuffer(_commandBuffer); - } - - public void GenerateReactiveMask(Fsr3Upscaler.GenerateReactiveDescription dispatchParams, CommandBuffer commandBuffer) - { - const int threadGroupWorkRegionDim = 8; - int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - GenReactiveConsts.scale = dispatchParams.Scale; - GenReactiveConsts.threshold = dispatchParams.CutoffThreshold; - GenReactiveConsts.binaryValue = dispatchParams.BinaryValue; - GenReactiveConsts.flags = (uint)dispatchParams.Flags; - commandBuffer.SetBufferData(_generateReactiveConstantsBuffer, _generateReactiveConstantsArray); - - ((Fsr3UpscalerGenerateReactivePass)_generateReactivePass).ScheduleDispatch(commandBuffer, dispatchParams, dispatchSrcX, dispatchSrcY); - } - - private void GenerateTransparencyCompositionReactive(Fsr3Upscaler.DispatchDescription dispatchParams, CommandBuffer commandBuffer, int frameIndex) - { - const int threadGroupWorkRegionDim = 8; - int dispatchSrcX = (dispatchParams.RenderSize.x + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - int dispatchSrcY = (dispatchParams.RenderSize.y + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - - TcrAutoGenConsts.autoTcThreshold = dispatchParams.AutoTcThreshold; - TcrAutoGenConsts.autoTcScale = dispatchParams.AutoTcScale; - TcrAutoGenConsts.autoReactiveScale = dispatchParams.AutoReactiveScale; - TcrAutoGenConsts.autoReactiveMax = dispatchParams.AutoReactiveMax; - commandBuffer.SetBufferData(_tcrAutogenerateConstantsBuffer, _tcrAutogenerateConstantsArray); - - _tcrAutogeneratePass.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); - } - - private void SetupConstants(Fsr3Upscaler.DispatchDescription dispatchParams, bool resetAccumulation) - { - ref Fsr3Upscaler.UpscalerConstants constants = ref UpscalerConsts; - - constants.jitterOffset = dispatchParams.JitterOffset; - constants.renderSize = dispatchParams.RenderSize; - constants.maxRenderSize = _contextDescription.MaxRenderSize; - constants.inputColorResourceDimensions = dispatchParams.InputResourceSize; - - // Compute the horizontal FOV for the shader from the vertical one - float aspectRatio = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; - float cameraAngleHorizontal = Mathf.Atan(Mathf.Tan(dispatchParams.CameraFovAngleVertical / 2.0f) * aspectRatio) * 2.0f; - constants.tanHalfFOV = Mathf.Tan(cameraAngleHorizontal * 0.5f); - constants.viewSpaceToMetersFactor = (dispatchParams.ViewSpaceToMetersFactor > 0.0f) ? dispatchParams.ViewSpaceToMetersFactor : 1.0f; - - // Compute params to enable device depth to view space depth computation in shader - constants.deviceToViewDepth = SetupDeviceDepthToViewSpaceDepthParams(dispatchParams); - - // To be updated if resource is larger than the actual image size - constants.downscaleFactor = new Vector2((float)constants.renderSize.x / _contextDescription.DisplaySize.x, (float)constants.renderSize.y / _contextDescription.DisplaySize.y); - constants.previousFramePreExposure = constants.preExposure; - constants.preExposure = (dispatchParams.PreExposure != 0) ? dispatchParams.PreExposure : 1.0f; - - // Motion vector data - Vector2Int motionVectorsTargetSize = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) != 0 ? constants.displaySize : constants.renderSize; - constants.motionVectorScale = dispatchParams.MotionVectorScale / motionVectorsTargetSize; - - // Compute jitter cancellation - if ((_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) - { - constants.motionVectorJitterCancellation = (_previousJitterOffset - constants.jitterOffset) / motionVectorsTargetSize; - _previousJitterOffset = constants.jitterOffset; - } - - int jitterPhaseCount = Fsr3Upscaler.GetJitterPhaseCount(dispatchParams.RenderSize.x, _contextDescription.DisplaySize.x); - if (resetAccumulation || constants.jitterPhaseCount == 0) - { - constants.jitterPhaseCount = jitterPhaseCount; - } - else - { - int jitterPhaseCountDelta = (int)(jitterPhaseCount - constants.jitterPhaseCount); - if (jitterPhaseCountDelta > 0) - constants.jitterPhaseCount++; - else if (jitterPhaseCountDelta < 0) - constants.jitterPhaseCount--; - } - - // Convert delta time to seconds and clamp to [0, 1] - constants.deltaTime = Mathf.Clamp01(dispatchParams.FrameTimeDelta); - - if (resetAccumulation) - constants.frameIndex = 0; - else - constants.frameIndex++; - - // Shading change usage of the SPD mip levels - constants.lumaMipLevelToUse = Fsr3UpscalerPass.ShadingChangeMipLevel; - - float mipDiv = 2 << constants.lumaMipLevelToUse; - constants.lumaMipDimensions.x = (int)(constants.maxRenderSize.x / mipDiv); - constants.lumaMipDimensions.y = (int)(constants.maxRenderSize.y / mipDiv); - } - - private Vector4 SetupDeviceDepthToViewSpaceDepthParams(Fsr3Upscaler.DispatchDescription dispatchParams) - { - bool inverted = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0; - bool infinite = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInfinite) != 0; - - // make sure it has no impact if near and far plane values are swapped in dispatch params - // the flags "inverted" and "infinite" will decide what transform to use - float min = Mathf.Min(dispatchParams.CameraNear, dispatchParams.CameraFar); - float max = Mathf.Max(dispatchParams.CameraNear, dispatchParams.CameraFar); - - if (inverted) - { - (min, max) = (max, min); - } - - float q = max / (min - max); - float d = -1.0f; - - Vector4 matrixElemC = new Vector4(q, -1.0f - Mathf.Epsilon, q, 0.0f + Mathf.Epsilon); - Vector4 matrixElemE = new Vector4(q * min, -min - Mathf.Epsilon, q * min, max); - - // Revert x and y coords - float aspect = (float)dispatchParams.RenderSize.x / dispatchParams.RenderSize.y; - float cotHalfFovY = Mathf.Cos(0.5f * dispatchParams.CameraFovAngleVertical) / Mathf.Sin(0.5f * dispatchParams.CameraFovAngleVertical); - - int matrixIndex = (inverted ? 2 : 0) + (infinite ? 1 : 0); - return new Vector4( - d * matrixElemC[matrixIndex], - matrixElemE[matrixIndex], - aspect / cotHalfFovY, - 1.0f / cotHalfFovY); - } - - private void SetupRcasConstants(Fsr3Upscaler.DispatchDescription dispatchParams) - { - int sharpnessIndex = Mathf.RoundToInt(Mathf.Clamp01(dispatchParams.Sharpness) * (RcasConfigs.Length - 1)); - RcasConsts = RcasConfigs[sharpnessIndex]; - } - - private void SetupSpdConstants(Fsr3Upscaler.DispatchDescription dispatchParams, out Vector2Int dispatchThreadGroupCount) - { - RectInt rectInfo = new RectInt(0, 0, dispatchParams.RenderSize.x, dispatchParams.RenderSize.y); - SpdSetup(rectInfo, out dispatchThreadGroupCount, out var workGroupOffset, out var numWorkGroupsAndMips); - - // Downsample - ref Fsr3Upscaler.SpdConstants spdConstants = ref SpdConsts; - spdConstants.numWorkGroups = (uint)numWorkGroupsAndMips.x; - spdConstants.mips = (uint)numWorkGroupsAndMips.y; - spdConstants.workGroupOffsetX = (uint)workGroupOffset.x; - spdConstants.workGroupOffsetY = (uint)workGroupOffset.y; - spdConstants.renderSizeX = (uint)dispatchParams.RenderSize.x; - spdConstants.renderSizeY = (uint)dispatchParams.RenderSize.y; - } - - private static void SpdSetup(RectInt rectInfo, out Vector2Int dispatchThreadGroupCount, out Vector2Int workGroupOffset, out Vector2Int numWorkGroupsAndMips, int mips = -1) - { - workGroupOffset = new Vector2Int(rectInfo.x / 64, rectInfo.y / 64); - - int endIndexX = (rectInfo.x + rectInfo.width - 1) / 64; - int endIndexY = (rectInfo.y + rectInfo.height - 1) / 64; - - dispatchThreadGroupCount = new Vector2Int(endIndexX + 1 - workGroupOffset.x, endIndexY + 1 - workGroupOffset.y); - - numWorkGroupsAndMips = new Vector2Int(dispatchThreadGroupCount.x * dispatchThreadGroupCount.y, mips); - if (mips < 0) - { - float resolution = Math.Max(rectInfo.width, rectInfo.height); - numWorkGroupsAndMips.y = Math.Min(Mathf.FloorToInt(Mathf.Log(resolution, 2.0f)), 12); - } - } - - private void DebugCheckDispatch(Fsr3Upscaler.DispatchDescription dispatchParams) - { - if (!dispatchParams.Color.IsValid) - { - Debug.LogError("Color resource is null"); - } - - if (!dispatchParams.Depth.IsValid) - { - Debug.LogError("Depth resource is null"); - } - - if (!dispatchParams.MotionVectors.IsValid) - { - Debug.LogError("MotionVectors resource is null"); - } - - if (!dispatchParams.Output.IsValid) - { - Debug.LogError("Output resource is null"); - } - - if (dispatchParams.Exposure.IsValid && (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableAutoExposure) != 0) - { - Debug.LogWarning("Exposure resource provided, however auto exposure flag is present"); - } - - if (Mathf.Abs(dispatchParams.JitterOffset.x) > 1.0f || Mathf.Abs(dispatchParams.JitterOffset.y) > 1.0f) - { - Debug.LogWarning("JitterOffset contains value outside of expected range [-1.0, 1.0]"); - } - - if (dispatchParams.MotionVectorScale.x > _contextDescription.MaxRenderSize.x || dispatchParams.MotionVectorScale.y > _contextDescription.MaxRenderSize.y) - { - Debug.LogWarning("MotionVectorScale contains scale value greater than MaxRenderSize"); - } - - if (dispatchParams.MotionVectorScale.x == 0.0f || dispatchParams.MotionVectorScale.y == 0.0f) - { - Debug.LogWarning("MotionVectorScale contains zero scale value"); - } - - if (dispatchParams.RenderSize.x > _contextDescription.MaxRenderSize.x || dispatchParams.RenderSize.y > _contextDescription.MaxRenderSize.y) - { - Debug.LogWarning("RenderSize is greater than context MaxRenderSize"); - } - - if (dispatchParams.RenderSize.x == 0 || dispatchParams.RenderSize.y == 0) - { - Debug.LogWarning("RenderSize contains zero dimension"); - } - - if (dispatchParams.FrameTimeDelta > 1.0f) - { - Debug.LogWarning("FrameTimeDelta is greater than 1.0f - this value should be seconds (~0.0166 for 60fps)"); - } - - if (dispatchParams.PreExposure == 0.0f) - { - Debug.LogError("PreExposure provided as 0.0f which is invalid"); - } - - bool infiniteDepth = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInfinite) != 0; - bool inverseDepth = (_contextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0; - - if (inverseDepth) - { - if (dispatchParams.CameraNear < dispatchParams.CameraFar) - { - Debug.LogWarning("EnableDepthInverted flag is present yet CameraNear is less than CameraFar"); - } - - if (infiniteDepth) - { - if (dispatchParams.CameraNear < float.MaxValue) - { - Debug.LogWarning("EnableDepthInfinite and EnableDepthInverted present, yet CameraNear != float.MaxValue"); - } - } - - if (dispatchParams.CameraFar < 0.075f) - { - Debug.LogWarning("EnableDepthInverted present, CameraFar value is very low which may result in depth separation artefacting"); - } - } - else - { - if (dispatchParams.CameraNear > dispatchParams.CameraFar) - { - Debug.LogWarning("CameraNear is greater than CameraFar in non-inverted-depth context"); - } - - if (infiniteDepth) - { - if (dispatchParams.CameraFar < float.MaxValue) - { - Debug.LogWarning("EnableDepthInfinite present, yet CameraFar != float.MaxValue"); - } - } - - if (dispatchParams.CameraNear < 0.075f) - { - Debug.LogWarning("CameraNear value is very low which may result in depth separation artefacting"); - } - } - - if (dispatchParams.CameraFovAngleVertical <= 0.0f) - { - Debug.LogError("CameraFovAngleVertical is 0.0f - this value should be > 0.0f"); - } - - if (dispatchParams.CameraFovAngleVertical > Mathf.PI) - { - Debug.LogError("CameraFovAngleVertical is greater than 180 degrees/PI"); - } - } - - /// - /// The FSR3 C++ codebase uses floats bitwise converted to ints to pass sharpness parameters to the RCAS shader. - /// This is not possible in C# without enabling unsafe code compilation, so to avoid that we instead use a table of precomputed values. - /// - private static readonly Fsr3Upscaler.RcasConstants[] RcasConfigs = new [] - { - new Fsr3Upscaler.RcasConstants(1048576000u, 872428544u), - new Fsr3Upscaler.RcasConstants(1049178080u, 877212745u), - new Fsr3Upscaler.RcasConstants(1049823372u, 882390168u), - new Fsr3Upscaler.RcasConstants(1050514979u, 887895276u), - new Fsr3Upscaler.RcasConstants(1051256227u, 893859143u), - new Fsr3Upscaler.RcasConstants(1052050675u, 900216232u), - new Fsr3Upscaler.RcasConstants(1052902144u, 907032080u), - new Fsr3Upscaler.RcasConstants(1053814727u, 914306687u), - new Fsr3Upscaler.RcasConstants(1054792807u, 922105590u), - new Fsr3Upscaler.RcasConstants(1055841087u, 930494326u), - new Fsr3Upscaler.RcasConstants(1056964608u, 939538432u), - new Fsr3Upscaler.RcasConstants(1057566688u, 944322633u), - new Fsr3Upscaler.RcasConstants(1058211980u, 949500056u), - new Fsr3Upscaler.RcasConstants(1058903587u, 955005164u), - new Fsr3Upscaler.RcasConstants(1059644835u, 960969031u), - new Fsr3Upscaler.RcasConstants(1060439283u, 967326120u), - new Fsr3Upscaler.RcasConstants(1061290752u, 974141968u), - new Fsr3Upscaler.RcasConstants(1062203335u, 981416575u), - new Fsr3Upscaler.RcasConstants(1063181415u, 989215478u), - new Fsr3Upscaler.RcasConstants(1064229695u, 997604214u), - new Fsr3Upscaler.RcasConstants(1065353216u, 1006648320), - }; - - private static ComputeBuffer CreateConstantBuffer() where TConstants: struct - { - return new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); - } - - private static void DestroyConstantBuffer(ref ComputeBuffer bufferRef) - { - if (bufferRef == null) - return; - - bufferRef.Release(); - bufferRef = null; - } - - private static void DestroyPass(ref Fsr3UpscalerPass pass) - { - if (pass == null) - return; - - pass.Dispose(); - pass = null; - } - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs.meta deleted file mode 100644 index 673b2ef..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerContext.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 14c8dc4c7c3e4ac418e50a859cec0b2f -timeCreated: 1673442225 \ No newline at end of file diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs deleted file mode 100644 index e1dc225..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) 2023 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 System; -using System.Runtime.InteropServices; -using UnityEngine; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// Base class for all of the compute passes that make up the FSR3 Upscaler process. - /// This loosely matches the FfxPipelineState struct from the original FSR3 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 Fsr3UpscalerPass: IDisposable - { - internal const int ShadingChangeMipLevel = 4; // This matches the FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL define - - protected readonly Fsr3Upscaler.ContextDescription ContextDescription; - protected readonly Fsr3UpscalerResources Resources; - protected readonly ComputeBuffer Constants; - - protected ComputeShader ComputeShader; - protected int KernelIndex; - - protected Fsr3UpscalerPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - { - ContextDescription = contextDescription; - Resources = resources; - Constants = constants; - } - - public virtual void Dispose() - { - } - - public abstract void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); - - protected void InitComputeShader(string passName, ComputeShader shader) - { - InitComputeShader(passName, shader, ContextDescription.Flags); - } - - private void InitComputeShader(string passName, ComputeShader shader, Fsr3Upscaler.InitializationFlags flags) - { - if (shader == null) - { - throw new MissingReferenceException($"Shader for FSR3 Upscaler '{passName}' could not be loaded! Please ensure it is included in the project correctly."); - } - - ComputeShader = shader; - KernelIndex = ComputeShader.FindKernel("CS"); - - 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 & Fsr3Upscaler.InitializationFlags.EnableHighDynamicRange) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableMotionVectorsJitterCancellation) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableDepthInverted) != 0) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH"); - if (useLut) ComputeShader.EnableKeyword("FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE"); - if ((flags & Fsr3Upscaler.InitializationFlags.EnableFP16Usage) != 0) ComputeShader.EnableKeyword("FFX_HALF"); - - // Inform the shader which render pipeline we're currently using - var pipeline = GraphicsSettings.currentRenderPipeline; - if (pipeline != null && pipeline.GetType().Name.Contains("HDRenderPipeline")) - { - ComputeShader.EnableKeyword("UNITY_FSR3UPSCALER_HDRP"); - } - } - } - - internal class Fsr3UpscalerComputeLuminancePyramidPass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _spdConstants; - - public Fsr3UpscalerComputeLuminancePyramidPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer spdConstants) - : base(contextDescription, resources, constants) - { - _spdConstants = spdConstants; - - InitComputeShader("compute_luminance_pyramid_pass", contextDescription.Shaders.computeLuminancePyramidPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavSpdAtomicCount, Resources.SpdAtomicCounter); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavExposureMipLumaChange, Resources.SceneLuminance, ShadingChangeMipLevel); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavExposureMip5, Resources.SceneLuminance, 5); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoExposure, Resources.AutoExposure); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbSpd, _spdConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerReconstructPreviousDepthPass : Fsr3UpscalerPass - { - public Fsr3UpscalerReconstructPreviousDepthPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("reconstruct_previous_depth_pass", contextDescription.Shaders.reconstructPreviousDepthPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - ref var depth = ref dispatchParams.Depth; - ref var motionVectors = ref dispatchParams.MotionVectors; - ref var exposure = ref dispatchParams.Exposure; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerDepthClipPass : Fsr3UpscalerPass - { - public Fsr3UpscalerDepthClipPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("depth_clip_pass", contextDescription.Shaders.depthClipPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - ref var depth = ref dispatchParams.Depth; - ref var motionVectors = ref dispatchParams.MotionVectors; - ref var exposure = ref dispatchParams.Exposure; - ref var reactive = ref dispatchParams.Reactive; - ref var tac = ref dispatchParams.TransparencyAndComposition; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputDepth, depth.RenderTarget, depth.MipLevel, depth.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReconstructedPrevNearestDepth, Fsr3ShaderIDs.UavReconstructedPrevNearestDepth); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedDepth, Fsr3ShaderIDs.UavDilatedDepth); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex ^ 1]); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerLockPass : Fsr3UpscalerPass - { - public Fsr3UpscalerLockPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("lock_pass", contextDescription.Shaders.lockPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLockInputLuma, Fsr3ShaderIDs.UavLockInputLuma); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerAccumulatePass : Fsr3UpscalerPass - { - private const string SharpeningKeyword = "FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING"; - -#if UNITY_2021_2_OR_NEWER - private readonly LocalKeyword _sharpeningKeyword; -#endif - - public Fsr3UpscalerAccumulatePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants) - : base(contextDescription, resources, constants) - { - InitComputeShader("accumulate_pass", contextDescription.Shaders.accumulatePass); -#if UNITY_2021_2_OR_NEWER - _sharpeningKeyword = new LocalKeyword(ComputeShader, SharpeningKeyword); -#endif - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { -#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 ((ContextDescription.Flags & Fsr3Upscaler.InitializationFlags.EnableDisplayResolutionMotionVectors) == 0) - { - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedMotionVectors, Resources.DilatedMotionVectors[frameIndex]); - } - else - { - ref var motionVectors = ref dispatchParams.MotionVectors; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - } - - ref var exposure = ref dispatchParams.Exposure; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvDilatedReactiveMasks, Fsr3ShaderIDs.UavDilatedReactiveMasks); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInternalUpscaled, Resources.InternalUpscaled[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLockStatus, Resources.LockStatus[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPreparedInputColor, Fsr3ShaderIDs.UavPreparedInputColor); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLanczosLut, Resources.LanczosLut); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvUpscaleMaximumBiasLut, Resources.MaximumBiasLut); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvSceneLuminanceMips, Resources.SceneLuminance); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvAutoExposure, Resources.AutoExposure); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvLumaHistory, Resources.LumaHistory[frameIndex ^ 1]); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavInternalUpscaled, Resources.InternalUpscaled[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavLockStatus, Resources.LockStatus[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavLumaHistory, Resources.LumaHistory[frameIndex]); - - ref var output = ref dispatchParams.Output; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerSharpenPass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _rcasConstants; - - public Fsr3UpscalerSharpenPass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer rcasConstants) - : base(contextDescription, resources, constants) - { - _rcasConstants = rcasConstants; - - InitComputeShader("rcas_pass", contextDescription.Shaders.sharpenPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var exposure = ref dispatchParams.Exposure; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputExposure, exposure.RenderTarget, exposure.MipLevel, exposure.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvRcasInput, Resources.InternalUpscaled[frameIndex]); - - ref var output = ref dispatchParams.Output; - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavUpscaledOutput, output.RenderTarget, output.MipLevel, output.SubElement); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbRcas, _rcasConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerGenerateReactivePass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _generateReactiveConstants; - - public Fsr3UpscalerGenerateReactivePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer generateReactiveConstants) - : base(contextDescription, resources, null) - { - _generateReactiveConstants = generateReactiveConstants; - - InitComputeShader("autogen_reactive_pass", contextDescription.Shaders.autoGenReactivePass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - } - - public void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.GenerateReactiveDescription dispatchParams, int dispatchX, int dispatchY) - { - ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; - ref var color = ref dispatchParams.ColorPreUpscale; - ref var reactive = ref dispatchParams.OutReactive; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoReactive, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbGenReactive, _generateReactiveConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } - - internal class Fsr3UpscalerTcrAutogeneratePass : Fsr3UpscalerPass - { - private readonly ComputeBuffer _tcrAutogenerateConstants; - - public Fsr3UpscalerTcrAutogeneratePass(Fsr3Upscaler.ContextDescription contextDescription, Fsr3UpscalerResources resources, ComputeBuffer constants, ComputeBuffer tcrAutogenerateConstants) - : base(contextDescription, resources, constants) - { - _tcrAutogenerateConstants = tcrAutogenerateConstants; - - InitComputeShader("tcr_autogen_pass", contextDescription.Shaders.tcrAutoGenPass); - } - - public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr3Upscaler.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) - { - ref var color = ref dispatchParams.Color; - ref var motionVectors = ref dispatchParams.MotionVectors; - ref var opaqueOnly = ref dispatchParams.ColorOpaqueOnly; - ref var reactive = ref dispatchParams.Reactive; - ref var tac = ref dispatchParams.TransparencyAndComposition; - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvOpaqueOnly, opaqueOnly.RenderTarget, opaqueOnly.MipLevel, opaqueOnly.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvInputMotionVectors, motionVectors.RenderTarget, motionVectors.MipLevel, motionVectors.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex ^ 1]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvReactiveMask, reactive.RenderTarget, reactive.MipLevel, reactive.SubElement); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.SrvTransparencyAndCompositionMask, tac.RenderTarget, tac.MipLevel, tac.SubElement); - - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoReactive, Resources.AutoReactive); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavAutoComposition, Resources.AutoComposition); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex]); - commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr3ShaderIDs.UavPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex]); - - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbFsr3Upscaler, Constants, 0, Marshal.SizeOf()); - commandBuffer.SetComputeConstantBufferParam(ComputeShader, Fsr3ShaderIDs.CbGenReactive, _tcrAutogenerateConstants, 0, Marshal.SizeOf()); - - commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); - } - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs.meta deleted file mode 100644 index 8ffd58e..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerPass.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: cfd077da533b192458b0b548668776e7 -timeCreated: 1676885169 \ No newline at end of file diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs deleted file mode 100644 index 344a371..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2023 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 System; -using UnityEngine; -using UnityEngine.Experimental.Rendering; -using UnityEngine.Rendering; - -namespace FidelityFX -{ - /// - /// Helper class for bundling and managing persistent resources required by the FSR3 Upscaler process. - /// This includes lookup tables, default fallback resources and double-buffered resources that get swapped between frames. - /// - internal class Fsr3UpscalerResources - { - public Texture2D DefaultExposure; - public Texture2D DefaultReactive; - public Texture2D LanczosLut; - public Texture2D MaximumBiasLut; - public RenderTexture SpdAtomicCounter; - public RenderTexture AutoExposure; - public RenderTexture SceneLuminance; - public RenderTexture AutoReactive; - public RenderTexture AutoComposition; - public readonly RenderTexture[] DilatedMotionVectors = new RenderTexture[2]; - public readonly RenderTexture[] LockStatus = new RenderTexture[2]; - public readonly RenderTexture[] InternalUpscaled = new RenderTexture[2]; - public readonly RenderTexture[] LumaHistory = new RenderTexture[2]; - public readonly RenderTexture[] PrevPreAlpha = new RenderTexture[2]; - public readonly RenderTexture[] PrevPostAlpha = new RenderTexture[2]; - - public void Create(Fsr3Upscaler.ContextDescription contextDescription) - { - // Generate the data for the LUT - const int lanczos2LutWidth = 128; - float[] lanczos2Weights = new float[lanczos2LutWidth]; - for (int currentLanczosWidthIndex = 0; currentLanczosWidthIndex < lanczos2LutWidth; ++currentLanczosWidthIndex) - { - float x = 2.0f * currentLanczosWidthIndex / (lanczos2LutWidth - 1); - float y = Fsr3Upscaler.Lanczos2(x); - lanczos2Weights[currentLanczosWidthIndex] = y; - } - - float[] maximumBias = new float[MaximumBiasTextureWidth * MaximumBiasTextureHeight]; - for (int i = 0; i < maximumBias.Length; ++i) - { - maximumBias[i] = MaximumBias[i] / 2.0f; - } - - // Resource FSR3UPSCALER_LanczosLutData: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE - // R16_SNorm textures are not supported by Unity on most platforms, strangely enough. So instead we use R32_SFloat and upload pre-normalized float data. - LanczosLut = new Texture2D(lanczos2LutWidth, 1, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_LanczosLutData" }; - LanczosLut.SetPixelData(lanczos2Weights, 0); - LanczosLut.Apply(); - - // Resource FSR3UPSCALER_MaximumUpsampleBias: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R16_SNORM, FFX_RESOURCE_FLAGS_NONE - MaximumBiasLut = new Texture2D(MaximumBiasTextureWidth, MaximumBiasTextureHeight, GraphicsFormat.R32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_MaximumUpsampleBias" }; - MaximumBiasLut.SetPixelData(maximumBias, 0); - MaximumBiasLut.Apply(); - - // Resource FSR3UPSCALER_DefaultExposure: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE - DefaultExposure = new Texture2D(1, 1, GraphicsFormat.R32G32_SFloat, TextureCreationFlags.None) { name = "FSR3UPSCALER_DefaultExposure" }; - DefaultExposure.SetPixel(0, 0, Color.clear); - DefaultExposure.Apply(); - - // Resource FSR3UPSCALER_DefaultReactivityMask: FFX_RESOURCE_USAGE_READ_ONLY, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE - DefaultReactive = new Texture2D(1, 1, GraphicsFormat.R8_UNorm, TextureCreationFlags.None) { name = "FSR3UPSCALER_DefaultReactivityMask" }; - DefaultReactive.SetPixel(0, 0, Color.clear); - DefaultReactive.Apply(); - - // Resource FSR3UPSCALER_SpdAtomicCounter: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE - // Despite what the original FSR3 codebase says, this resource really isn't aliasable. Resetting this counter to 0 every frame breaks auto-exposure on MacOS Metal. - SpdAtomicCounter = new RenderTexture(1, 1, 0, GraphicsFormat.R32_UInt) { name = "FSR3UPSCALER_SpdAtomicCounter", enableRandomWrite = true }; - SpdAtomicCounter.Create(); - - // Resource FSR3UPSCALER_AutoExposure: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32G32_FLOAT, FFX_RESOURCE_FLAGS_NONE - AutoExposure = new RenderTexture(1, 1, 0, GraphicsFormat.R32G32_SFloat) { name = "FSR3UPSCALER_AutoExposure", enableRandomWrite = true }; - AutoExposure.Create(); - - // Resource FSR3UPSCALER_ExposureMips: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - // This is a rather special case: it's an aliasable resource, but because we require a mipmap chain and bind specific mip levels per shader, we can't easily use temporary RTs for this. - int w = contextDescription.MaxRenderSize.x / 2, h = contextDescription.MaxRenderSize.y / 2; - int mipCount = 1 + Mathf.FloorToInt(Mathf.Log(Math.Max(w, h), 2.0f)); - SceneLuminance = new RenderTexture(w, h, 0, GraphicsFormat.R16_SFloat, mipCount) { name = "FSR3UPSCALER_ExposureMips", enableRandomWrite = true, useMipMap = true, autoGenerateMips = false }; - SceneLuminance.Create(); - - // Resources FSR3UPSCALER_InternalDilatedVelocity1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(DilatedMotionVectors, "FSR3UPSCALER_InternalDilatedVelocity", contextDescription.MaxRenderSize, GraphicsFormat.R16G16_SFloat); - - // Resources FSR3UPSCALER_LockStatus1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(LockStatus, "FSR3UPSCALER_LockStatus", contextDescription.DisplaySize, GraphicsFormat.R16G16_SFloat); - - // Resources FSR3UPSCALER_InternalUpscaled1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(InternalUpscaled, "FSR3UPSCALER_InternalUpscaled", contextDescription.DisplaySize, GraphicsFormat.R16G16B16A16_SFloat); - - // Resources FSR3UPSCALER_LumaHistory1/2: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8B8A8_UNORM, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(LumaHistory, "FSR3UPSCALER_LumaHistory", contextDescription.DisplaySize, GraphicsFormat.R8G8B8A8_UNorm); - } - - public void CreateTcrAutogenResources(Fsr3Upscaler.ContextDescription contextDescription) - { - // Resource FSR3UPSCALER_AutoReactive: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE - AutoReactive = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR3UPSCALER_AutoReactive", enableRandomWrite = true }; - AutoReactive.Create(); - - // Resource FSR3UPSCALER_AutoComposition: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_NONE - AutoComposition = new RenderTexture(contextDescription.MaxRenderSize.x, contextDescription.MaxRenderSize.y, 0, GraphicsFormat.R8_UNorm) { name = "FSR3UPSCALER_AutoComposition", enableRandomWrite = true }; - AutoComposition.Create(); - - // Resources FSR3UPSCALER_PrevPreAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(PrevPreAlpha, "FSR3UPSCALER_PrevPreAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); - - // Resources FSR3UPSCALER_PrevPostAlpha0/1: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R11G11B10_FLOAT, FFX_RESOURCE_FLAGS_NONE - CreateDoubleBufferedResource(PrevPostAlpha, "FSR3UPSCALER_PrevPostAlpha", contextDescription.MaxRenderSize, GraphicsFormat.B10G11R11_UFloatPack32); - } - - // 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 - public static void CreateAliasableResources(CommandBuffer commandBuffer, Fsr3Upscaler.ContextDescription contextDescription, Fsr3Upscaler.DispatchDescription dispatchParams) - { - Vector2Int displaySize = contextDescription.DisplaySize; - Vector2Int maxRenderSize = contextDescription.MaxRenderSize; - - // FSR3UPSCALER_ReconstructedPrevNearestDepth: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_UINT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavReconstructedPrevNearestDepth, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R32_UInt, 1, true); - - // FSR3UPSCALER_DilatedDepth: FFX_RESOURCE_USAGE_RENDERTARGET | FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R32_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavDilatedDepth, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R32_SFloat, 1, true); - - // FSR3UPSCALER_LockInputLuma: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavLockInputLuma, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16_SFloat, 1, true); - - // FSR3UPSCALER_DilatedReactiveMasks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8G8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavDilatedReactiveMasks, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R8G8_UNorm, 1, true); - - // FSR3UPSCALER_PreparedInputColor: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavPreparedInputColor, maxRenderSize.x, maxRenderSize.y, 0, default, GraphicsFormat.R16G16B16A16_SFloat, 1, true); - - // FSR3UPSCALER_NewLocks: FFX_RESOURCE_USAGE_UAV, FFX_SURFACE_FORMAT_R8_UNORM, FFX_RESOURCE_FLAGS_ALIASABLE - commandBuffer.GetTemporaryRT(Fsr3ShaderIDs.UavNewLocks, displaySize.x, displaySize.y, 0, default, GraphicsFormat.R8_UNorm, 1, true); - } - - public static void DestroyAliasableResources(CommandBuffer commandBuffer) - { - // Release all of the aliasable resources used this frame - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavReconstructedPrevNearestDepth); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavDilatedDepth); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavLockInputLuma); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavDilatedReactiveMasks); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavPreparedInputColor); - commandBuffer.ReleaseTemporaryRT(Fsr3ShaderIDs.UavNewLocks); - } - - private static void CreateDoubleBufferedResource(RenderTexture[] resource, string name, Vector2Int size, GraphicsFormat format) - { - for (int i = 0; i < 2; ++i) - { - resource[i] = new RenderTexture(size.x, size.y, 0, format) { name = name + (i + 1), enableRandomWrite = true }; - resource[i].Create(); - } - } - - public void Destroy() - { - DestroyTcrAutogenResources(); - - DestroyResource(LumaHistory); - DestroyResource(InternalUpscaled); - DestroyResource(LockStatus); - DestroyResource(DilatedMotionVectors); - DestroyResource(ref SceneLuminance); - DestroyResource(ref AutoExposure); - DestroyResource(ref DefaultReactive); - DestroyResource(ref DefaultExposure); - DestroyResource(ref MaximumBiasLut); - DestroyResource(ref LanczosLut); - } - - public void DestroyTcrAutogenResources() - { - DestroyResource(PrevPostAlpha); - DestroyResource(PrevPreAlpha); - DestroyResource(ref AutoComposition); - DestroyResource(ref AutoReactive); - } - - private static void DestroyResource(ref Texture2D resource) - { - if (resource == null) - return; - -#if UNITY_EDITOR - if (Application.isPlaying && !UnityEditor.EditorApplication.isPaused) - UnityEngine.Object.Destroy(resource); - else - UnityEngine.Object.DestroyImmediate(resource); -#else - UnityEngine.Object.Destroy(resource); -#endif - resource = null; - } - - private static void DestroyResource(ref RenderTexture resource) - { - if (resource == null) - return; - - resource.Release(); - resource = null; - } - - private static void DestroyResource(RenderTexture[] resource) - { - for (int i = 0; i < resource.Length; ++i) - DestroyResource(ref resource[i]); - } - - private const int MaximumBiasTextureWidth = 16; - private const int MaximumBiasTextureHeight = 16; - private static readonly float[] MaximumBias = - { - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.876f, 1.809f, 1.772f, 1.753f, 1.748f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.869f, 1.801f, 1.764f, 1.745f, 1.739f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.976f, 1.841f, 1.774f, 1.737f, 1.716f, 1.71f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.914f, 1.784f, 1.716f, 1.673f, 1.649f, 1.641f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.793f, 1.676f, 1.604f, 1.562f, 1.54f, 1.533f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.619f, 1.536f, 1.492f, 1.467f, 1.454f, 1.449f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.575f, 1.496f, 1.456f, 1.432f, 1.416f, 1.408f, 1.405f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.555f, 1.479f, 1.438f, 1.413f, 1.398f, 1.387f, 1.381f, 1.379f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.555f, 1.474f, 1.43f, 1.404f, 1.387f, 1.376f, 1.368f, 1.363f, 1.362f, - 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.575f, 1.479f, 1.43f, 1.401f, 1.382f, 1.369f, 1.36f, 1.354f, 1.351f, 1.35f, - 2.0f, 2.0f, 1.976f, 1.914f, 1.793f, 1.619f, 1.496f, 1.438f, 1.404f, 1.382f, 1.367f, 1.357f, 1.349f, 1.344f, 1.341f, 1.34f, - 1.876f, 1.869f, 1.841f, 1.784f, 1.676f, 1.536f, 1.456f, 1.413f, 1.387f, 1.369f, 1.357f, 1.347f, 1.341f, 1.336f, 1.333f, 1.332f, - 1.809f, 1.801f, 1.774f, 1.716f, 1.604f, 1.492f, 1.432f, 1.398f, 1.376f, 1.36f, 1.349f, 1.341f, 1.335f, 1.33f, 1.328f, 1.327f, - 1.772f, 1.764f, 1.737f, 1.673f, 1.562f, 1.467f, 1.416f, 1.387f, 1.368f, 1.354f, 1.344f, 1.336f, 1.33f, 1.326f, 1.323f, 1.323f, - 1.753f, 1.745f, 1.716f, 1.649f, 1.54f, 1.454f, 1.408f, 1.381f, 1.363f, 1.351f, 1.341f, 1.333f, 1.328f, 1.323f, 1.321f, 1.32f, - 1.748f, 1.739f, 1.71f, 1.641f, 1.533f, 1.449f, 1.405f, 1.379f, 1.362f, 1.35f, 1.34f, 1.332f, 1.327f, 1.323f, 1.32f, 1.319f, - }; - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs.meta b/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs.meta deleted file mode 100644 index bf03a07..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/FSR3/Fsr3UpscalerResources.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: ec6c0c34c7b11f041885ddee4aa72818 -timeCreated: 1677236102 \ No newline at end of file diff --git a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs b/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs deleted file mode 100644 index 35293ef..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/PostProcessResources.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System; -using FidelityFX; - -namespace UnityEngine.Rendering.PostProcessing -{ - /// - /// This asset is used to store references to shaders and other resources we might need at - /// runtime without having to use a `Resources` folder. This allows for better memory management, - /// better dependency tracking and better interoperability with asset bundles. - /// - public sealed class PostProcessResources : ScriptableObject - { - /// - /// All the shaders used by post-processing. - /// - [Serializable] - public sealed class Shaders - { - /// - /// The shader used for the bloom effect. - /// - public Shader bloom; - - /// - /// The shader used for internal blit copies. - /// - public Shader copy; - - /// - /// The shader used for built-in blit copies in the built-in pipeline. - /// - public Shader copyStd; - - /// - /// The shader used for built-in blit copies in the built-in pipeline when using a - /// texture array (for stereo rendering). - /// - public Shader copyStdFromTexArray; - - /// - /// The shader used for built-in blit copies in the built-in pipeline when using a - /// double-wide texture (for stereo rendering). - /// - public Shader copyStdFromDoubleWide; - - /// - /// The shader used to kill the alpha. - /// - public Shader discardAlpha; - - /// - /// The shader used for the depth of field effect. - /// - public Shader depthOfField; - - /// - /// The shader used for the final pass. - /// - public Shader finalPass; - - /// - /// The shader used to generate the grain texture. - /// - public Shader grainBaker; - - /// - /// The shader used for the motion blur effect. - /// - public Shader motionBlur; - - /// - /// The shader used for the temporal anti-aliasing effect. - /// - public Shader temporalAntialiasing; - - /// - /// The shader used for the sub-pixel morphological anti-aliasing effect. - /// - public Shader subpixelMorphologicalAntialiasing; - - /// - /// The shader use by the volume manager to interpolate between two 2D textures. - /// - public Shader texture2dLerp; - - /// - /// The uber shader that combine several effects into one. - /// - public Shader uber; - - /// - /// The shader used to bake the 2D lookup table for color grading. - /// - public Shader lut2DBaker; - - /// - /// The shader used to draw the light meter monitor. - /// - public Shader lightMeter; - - /// - /// The shader used to draw the histogram monitor. - /// - public Shader gammaHistogram; - - /// - /// The shader used to draw the waveform monitor. - /// - public Shader waveform; - - /// - /// The shader used to draw the vectorscope monitor. - /// - public Shader vectorscope; - - /// - /// The shader used to draw debug overlays. - /// - public Shader debugOverlays; - - /// - /// The shader used for the deferred fog effect. - /// - public Shader deferredFog; - - /// - /// The shader used for the scalable ambient occlusion effect. - /// - public Shader scalableAO; - - /// - /// The shader used for the multi-scale ambient occlusion effect. - /// - public Shader multiScaleAO; - - /// - /// The shader used for the screen-space reflection effect. - /// - public Shader screenSpaceReflections; - - /// - /// Returns a copy of this class and its content. - /// - /// A copy of this class and its content. - public Shaders Clone() - { - return (Shaders)MemberwiseClone(); - } - } - - /// - /// All the compute shaders used by post-processing. - /// - [Serializable] - public sealed class ComputeShaders - { - /// - /// The compute shader used for the auto-exposure effect. - /// - public ComputeShader autoExposure; - - /// - /// The compute shader used to compute an histogram of the current frame. - /// - public ComputeShader exposureHistogram; - - /// - /// The compute shader used to bake the 3D lookup table for color grading. - /// - public ComputeShader lut3DBaker; - - /// - /// The compute shader used by the volume manager to interpolate between two 3D textures. - /// - public ComputeShader texture3dLerp; - - /// - /// The compute shader used to compute the histogram monitor. - /// - public ComputeShader gammaHistogram; - - /// - /// The compute shader used to compute the waveform monitor. - /// - public ComputeShader waveform; - - /// - /// The compute shader used to compute the vectorscope monitor. - /// - public ComputeShader vectorscope; - - /// - /// The compute shader used for the first downsampling pass of MSVO. - /// - public ComputeShader multiScaleAODownsample1; - - /// - /// The compute shader used for the second downsampling pass of MSVO. - /// - public ComputeShader multiScaleAODownsample2; - - /// - /// The compute shader used for the render pass of MSVO. - /// - public ComputeShader multiScaleAORender; - - /// - /// The compute shader used for the upsampling pass of MSVO. - /// - public ComputeShader multiScaleAOUpsample; - - /// - /// The compute shader used to a fast gaussian downsample. - /// - public ComputeShader gaussianDownsample; - - /// - /// Compute shaders used by the FidelityFX Super Resolution 3 (FSR3) Upscaler. - /// - public Fsr3UpscalerShaders superResolution; - - /// - /// Returns a copy of this class and its content. - /// - /// A copy of this class and its content. - public ComputeShaders Clone() - { - return (ComputeShaders)MemberwiseClone(); - } - } - - /// - /// A set of textures needed by the sub-pixel morphological anti-aliasing effect. - /// - [Serializable] - public sealed class SMAALuts - { - /// - /// The area lookup table. - /// - public Texture2D area; - - /// - /// The search lookup table. - /// - public Texture2D search; - } - - /// - /// A set of 64x64, single-channel blue noise textures. - /// - public Texture2D[] blueNoise64; - - /// - /// A set of 256x256, single-channel blue noise textures. - /// - public Texture2D[] blueNoise256; - - /// - /// Lookup tables used by the sub-pixel morphological anti-aliasing effect. - /// - public SMAALuts smaaLuts; - - /// - /// All the shaders used by post-processing. - /// - public Shaders shaders; - - /// - /// All the compute shaders used by post-processing. - /// - public ComputeShaders computeShaders; - -#if UNITY_EDITOR - /// - /// A delegate used to track resource changes. - /// - public delegate void ChangeHandler(); - - /// - /// Set this callback to be notified of resource changes. - /// - public ChangeHandler changeHandler; - - void OnValidate() - { - if (changeHandler != null) - changeHandler(); - } - -#endif - } -} diff --git a/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef b/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef deleted file mode 100644 index 91449de..0000000 --- a/com.unity.postprocessing/PostProcessing/Runtime/Unity.Postprocessing.Runtime.asmdef +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "Unity.Postprocessing.Runtime", - "references": [], - "optionalUnityReferences": [], - "includePlatforms": [], - "excludePlatforms": [], - "versionDefines": [ - { - "name": "com.unity.render-pipelines.lightweight", - "expression": "1.0.0", - "define": "LWRP_1_0_0_OR_NEWER" - }, - { - "name": "com.unity.render-pipelines.universal", - "expression": "1.0.0", - "define": "UNIVERSAL_1_0_0_OR_NEWER" - }, - { - "name": "com.unity.modules.vr", - "expression": "1.0.0", - "define": "ENABLE_VR_MODULE" - }, - { - "name": "com.unity.modules.xr", - "expression": "1.0.0", - "define": "ENABLE_XR_MODULE" - } - ] -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3.meta deleted file mode 100644 index 3e30dae..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 37c436021e67c00459f44b59099c024a -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute deleted file mode 100644 index 5cbfb80..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -// Ensure the correct value is defined for this keyword, as it is used to select one of multiple sampler functions -#ifdef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#undef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 1 -#endif - -#include "shaders/ffx_fsr3upscaler_accumulate_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute.meta deleted file mode 100644 index dbe5282..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_accumulate_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c9b45f0ae7673694ba57a4aadfe212e9 -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute deleted file mode 100644 index e13c001..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta deleted file mode 100644 index 1df041b..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_autogen_reactive_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 5716b91fdaa4e9e439df6b96a796fe6e -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute deleted file mode 100644 index d5903c0..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -// Wave operations require shader model 6.0; this can only be enabled when using DXC on D3D12 -// These pragmas are commented out by default as Unity will sometimes ignore the #if's and try to enable these features anyway. -// Uncomment the below lines if you intend to try wave operations on DX12 with the DXC compiler. -//#if defined(UNITY_COMPILER_DXC) && defined(SHADER_API_D3D12) -//#pragma require WaveBasic // Required for WaveGetLaneIndex -//#pragma require WaveBallot // Required for WaveReadLaneAt -//#else -#define FFX_SPD_NO_WAVE_OPERATIONS -//#endif - -#include "shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta deleted file mode 100644 index 9e002c0..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_compute_luminance_pyramid_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: d253be05abcdc80428503d3e4cce3a36 -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute deleted file mode 100644 index 0ccd388..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta deleted file mode 100644 index d695f48..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_depth_clip_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 20e44016ed34b0d4b8de499d1b566c69 -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute deleted file mode 100644 index e38ad99..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_lock_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta deleted file mode 100644 index c01e009..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_lock_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a135306e6d1857e43a86ef20db2a47fe -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute deleted file mode 100644 index be7bbb5..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_rcas_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute.meta deleted file mode 100644 index cd12641..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_rcas_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7aaf5cfff022de2499e9b0412f947f6c -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute deleted file mode 100644 index ee2f276..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta deleted file mode 100644 index 1053c34..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_reconstruct_previous_depth_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4f59e5b9179d74844ae06a30ae1e0629 -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute deleted file mode 100644 index 6338918..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2023 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. - -#pragma kernel CS - -#pragma multi_compile_local __ FFX_HALF -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS -#pragma multi_compile_local __ FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - -#pragma multi_compile_local __ UNITY_FSR3UPSCALER_HDRP - -#include "ffx_fsr3upscaler_unity_common.cginc" - -#include "shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl" diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta deleted file mode 100644 index ad42fbb..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_tcr_autogen_pass.compute.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 75cdc6ef23f08ed498d4da511923fcea -ComputeShaderImporter: - externalObjects: {} - preprocessorOverride: 0 - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc deleted file mode 100644 index 758bb0c..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2023 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. - -// Suppress a few warnings produced by FFX's HLSL code -#pragma warning(disable: 3078) // Loop control variable conflicts -#pragma warning(disable: 3203) // Signed/unsigned mismatch - -#define FFX_GPU // Compiling for GPU -#define FFX_HLSL // Compile for plain HLSL - -// Use the DXC shader compiler on modern graphics APIs to enable a few advanced features -// The DXC-related pragmas are disabled by default, as DXC doesn't support all platforms yet and will break on some platforms when enabled. -// Consider this to be an experimental feature. If you want to benefit from 16-bit floating point and wave operations, and don't care about supporting older graphics APIs, then it's worth a try. -//#if defined(SHADER_API_D3D12) || defined(SHADER_API_VULKAN) || defined(SHADER_API_METAL) -//#pragma use_dxc // Using DXC will currently break DX11 support since DX11 and DX12 share the same shader bytecode in Unity. -//#endif - -// Enable half precision data types on platforms that support it -//#if defined(UNITY_COMPILER_DXC) && defined(FFX_HALF) -//#pragma require Native16Bit -//#endif - -// Hack to work around the lack of texture atomics on Metal -#if defined(SHADER_API_METAL) -#define InterlockedAdd(dest, val, orig) { (orig) = (dest); (dest) += (val); } -#define InterlockedMin(dest, val) { (dest) = min((dest), (val)); } -#define InterlockedMax(dest, val) { (dest) = max((dest), (val)); } -#endif - -// Workaround for HDRP using texture arrays for its camera buffers on some platforms -// The below defines are copied from: Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/TextureXR.hlsl -#if defined(UNITY_FSR3UPSCALER_HDRP) - // Must be in sync with C# with property useTexArray in TextureXR.cs - #if ((defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12)) && !defined(SHADER_API_XBOXONE) && !defined(SHADER_API_GAMECORE)) || defined(SHADER_API_PSSL) || defined(SHADER_API_VULKAN) - #define UNITY_TEXTURE2D_X_ARRAY_SUPPORTED - #endif - - // Control if TEXTURE2D_X macros will expand to texture arrays - #if defined(UNITY_TEXTURE2D_X_ARRAY_SUPPORTED) && !defined(DISABLE_TEXTURE2D_X_ARRAY) - #define USE_TEXTURE2D_X_AS_ARRAY - #endif - - // Early defines for single-pass instancing - #if defined(STEREO_INSTANCING_ON) && defined(UNITY_TEXTURE2D_X_ARRAY_SUPPORTED) - #define UNITY_STEREO_INSTANCING_ENABLED - #endif - - // Helper macros to handle XR single-pass with Texture2DArray - #if defined(USE_TEXTURE2D_X_AS_ARRAY) - - // Only single-pass stereo instancing used array indexing - #if defined(UNITY_STEREO_INSTANCING_ENABLED) - #define SLICE_ARRAY_INDEX unity_StereoEyeIndex - #else - #define SLICE_ARRAY_INDEX 0 - #endif - - // Declare and sample camera buffers as texture arrays - #define UNITY_FSR3_TEX2D(type) Texture2DArray - #define UNITY_FSR3_RWTEX2D(type) RWTexture2DArray - #define UNITY_FSR3_POS(pxPos) FfxUInt32x3(pxPos, SLICE_ARRAY_INDEX) - #define UNITY_FSR3_UV(uv) FfxFloat32x3(uv, SLICE_ARRAY_INDEX) - - #endif -#endif diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta deleted file mode 100644 index 5a68b6c..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/ffx_fsr3upscaler_unity_common.cginc.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 3ce00ba677bb7e14bb91772fd68bfe6b -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders.meta deleted file mode 100644 index 8a4ff2b..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8364d4f86c613ec4d999d062f5f773b8 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl deleted file mode 100644 index d2f1b32..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 -#define FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS 1 -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS -#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 2 -#else -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 2 -#endif -#define FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED 3 -#define FSR3UPSCALER_BIND_SRV_LOCK_STATUS 4 -#define FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR 5 -#define FSR3UPSCALER_BIND_SRV_LANCZOS_LUT 6 -#define FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT 7 -#define FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS 8 -#define FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE 9 -#define FSR3UPSCALER_BIND_SRV_LUMA_HISTORY 10 - -#define FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED 0 -#define FSR3UPSCALER_BIND_UAV_LOCK_STATUS 1 -#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 2 -#define FSR3UPSCALER_BIND_UAV_NEW_LOCKS 3 -#define FSR3UPSCALER_BIND_UAV_LUMA_HISTORY 4 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_upsample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h" -#include "fsr3upscaler/ffx_fsr3upscaler_reproject.h" -#include "fsr3upscaler/ffx_fsr3upscaler_accumulate.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - const uint GroupRows = (uint(DisplaySize().y) + FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT - 1) / FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT; - uGroupId.y = GroupRows - uGroupId.y - 1; - - uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; - - Accumulate(uDispatchThreadId); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta deleted file mode 100644 index 80f209e..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_accumulate_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 507ab779c38eddb429cdcedf9c108d1b -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl deleted file mode 100644 index 0d6e2eb..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl +++ /dev/null @@ -1,77 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY 0 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 1 - -#define FSR3UPSCALER_BIND_UAV_AUTOREACTIVE 0 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_REACTIVE 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; - - float3 ColorPreAlpha = LoadOpaqueOnly( FFX_MIN16_I2(uDispatchThreadId) ).rgb; - float3 ColorPostAlpha = LoadInputColor(uDispatchThreadId).rgb; - - if (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP) - { - ColorPreAlpha = Tonemap(ColorPreAlpha); - ColorPostAlpha = Tonemap(ColorPostAlpha); - } - - if (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP) - { - ColorPreAlpha = InverseTonemap(ColorPreAlpha); - ColorPostAlpha = InverseTonemap(ColorPostAlpha); - } - - float out_reactive_value = 0.f; - float3 delta = abs(ColorPostAlpha - ColorPreAlpha); - - out_reactive_value = (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX) ? max(delta.x, max(delta.y, delta.z)) : length(delta); - out_reactive_value *= GenReactiveScale(); - - out_reactive_value = (GenReactiveFlags() & FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD) ? (out_reactive_value < GenReactiveThreshold() ? 0 : GenReactiveBinaryValue()) : out_reactive_value; - - rw_output_autoreactive[uDispatchThreadId] = out_reactive_value; -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta deleted file mode 100644 index c55f004..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_autogen_reactive_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 52cdb7a7c30cb614984908593ed19082 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl deleted file mode 100644 index 93b7332..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 0 - -#define FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC 0 -#define FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE 1 -#define FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 2 -#define FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE 3 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_SPD 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 256 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT -void CS(uint3 WorkGroupId : SV_GroupID, uint LocalThreadIndex : SV_GroupIndex) -{ - ComputeAutoExposure(WorkGroupId, LocalThreadIndex); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta deleted file mode 100644 index 508b43e..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_compute_luminance_pyramid_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 2d149b52ba0f5bb468a94a71dbbcb66f -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl deleted file mode 100644 index 70cc7ba..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 -#define FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS 1 -#define FSR3UPSCALER_BIND_SRV_DILATED_DEPTH 2 -#define FSR3UPSCALER_BIND_SRV_REACTIVE_MASK 3 -#define FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 4 -#define FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS 5 -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 6 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 7 -#define FSR3UPSCALER_BIND_SRV_INPUT_DEPTH 8 -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 9 - -#define FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS 0 -#define FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR 1 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_depth_clip.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS( - int2 iGroupId : SV_GroupID, - int2 iDispatchThreadId : SV_DispatchThreadID, - int2 iGroupThreadId : SV_GroupThreadID, - int iGroupIndex : SV_GroupIndex) -{ - DepthClip(iDispatchThreadId); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta deleted file mode 100644 index cde3a5e..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_depth_clip_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: da435b71cf57e2247b80ae0f0f86d1f8 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl deleted file mode 100644 index 26b28f0..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl +++ /dev/null @@ -1,56 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA 0 - -#define FSR3UPSCALER_BIND_UAV_NEW_LOCKS 0 -#define FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 1 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_lock.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - uint2 uDispatchThreadId = uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId; - - ComputeLock(uDispatchThreadId); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta deleted file mode 100644 index 45c99dc..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_lock_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 98d2cbbda5e90dd4ebd1d70abbb63a09 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl deleted file mode 100644 index bebdeb3..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl +++ /dev/null @@ -1,53 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 0 -#define FSR3UPSCALER_BIND_SRV_RCAS_INPUT 1 - -#define FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT 0 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_RCAS 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_rcas.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 64 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT -void CS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID) -{ - RCAS(LocalThreadId, WorkGroupId, Dtid); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta deleted file mode 100644 index fb9bfe2..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_rcas_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 9a15fc73170a9bc478801c8fa4d8d574 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl deleted file mode 100644 index f277fd1..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 0 -#define FSR3UPSCALER_BIND_SRV_INPUT_DEPTH 1 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 2 -#define FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE 3 - -#define FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH 0 -#define FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS 1 -#define FSR3UPSCALER_BIND_UAV_DILATED_DEPTH 2 -#define FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA 3 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_sample.h" -#include "fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_PREFER_WAVE64 -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS( - int2 iGroupId : SV_GroupID, - int2 iDispatchThreadId : SV_DispatchThreadID, - int2 iGroupThreadId : SV_GroupThreadID, - int iGroupIndex : SV_GroupIndex -) -{ - ReconstructAndDilate(iDispatchThreadId); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta deleted file mode 100644 index 6489d6d..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_reconstruct_previous_depth_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: bafb3726a76b97a49bb343d8a4323754 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl deleted file mode 100644 index 6180885..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl +++ /dev/null @@ -1,90 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - -#define FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY 0 -#define FSR3UPSCALER_BIND_SRV_INPUT_COLOR 1 -#define FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS 2 -#define FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR 3 -#define FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR 4 -#define FSR3UPSCALER_BIND_SRV_REACTIVE_MASK 4 -#define FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 5 - -#define FSR3UPSCALER_BIND_UAV_AUTOREACTIVE 0 -#define FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION 1 -#define FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR 2 -#define FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR 3 - -#define FSR3UPSCALER_BIND_CB_FSR3UPSCALER 0 -#define FSR3UPSCALER_BIND_CB_AUTOREACTIVE 1 - -#include "fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h" -#include "fsr3upscaler/ffx_fsr3upscaler_common.h" -#include "fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h" - -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH 8 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#define FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT 8 -#endif // FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT -#ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#define FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH 1 -#endif // #ifndef FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH -#ifndef FFX_FSR3UPSCALER_NUM_THREADS -#define FFX_FSR3UPSCALER_NUM_THREADS [numthreads(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT, FFX_FSR3UPSCALER_THREAD_GROUP_DEPTH)] -#endif // #ifndef FFX_FSR3UPSCALER_NUM_THREADS - -FFX_FSR3UPSCALER_NUM_THREADS -FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -void CS(uint2 uGroupId : SV_GroupID, uint2 uGroupThreadId : SV_GroupThreadID) -{ - FFX_MIN16_I2 uDispatchThreadId = FFX_MIN16_I2(uGroupId * uint2(FFX_FSR3UPSCALER_THREAD_GROUP_WIDTH, FFX_FSR3UPSCALER_THREAD_GROUP_HEIGHT) + uGroupThreadId); - - // ToDo: take into account jitter (i.e. add delta of previous jitter and current jitter to previous UV - // fetch pre- and post-alpha color values - FFX_MIN16_F2 fUv = ( FFX_MIN16_F2(uDispatchThreadId) + FFX_MIN16_F2(0.5f, 0.5f) ) / FFX_MIN16_F2( RenderSize() ); - FFX_MIN16_F2 fPrevUV = fUv + FFX_MIN16_F2( LoadInputMotionVector(uDispatchThreadId) ); - FFX_MIN16_I2 iPrevIdx = FFX_MIN16_I2(fPrevUV * FFX_MIN16_F2(RenderSize()) - 0.5f); - - FFX_MIN16_F3 colorPreAlpha = FFX_MIN16_F3( LoadOpaqueOnly( uDispatchThreadId ) ); - FFX_MIN16_F3 colorPostAlpha = FFX_MIN16_F3( LoadInputColor( uDispatchThreadId ) ); - - FFX_MIN16_F2 outReactiveMask = 0; - - outReactiveMask.y = ComputeTransparencyAndComposition(uDispatchThreadId, iPrevIdx); - - if (outReactiveMask.y > 0.5f) - { - outReactiveMask.x = ComputeReactive(uDispatchThreadId, iPrevIdx); - outReactiveMask.x *= FFX_MIN16_F(fReactiveScale); - outReactiveMask.x = outReactiveMask.x < fReactiveMax ? outReactiveMask.x : FFX_MIN16_F( fReactiveMax ); - } - - outReactiveMask.y *= FFX_MIN16_F(fTcScale ); - - outReactiveMask.x = max( outReactiveMask.x, FFX_MIN16_F( LoadReactiveMask(uDispatchThreadId) ) ); - outReactiveMask.y = max( outReactiveMask.y, FFX_MIN16_F( LoadTransparencyAndCompositionMask(uDispatchThreadId) ) ); - - StoreAutoReactive(uDispatchThreadId, outReactiveMask); - - StorePrevPreAlpha(uDispatchThreadId, colorPreAlpha); - StorePrevPostAlpha(uDispatchThreadId, colorPostAlpha); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta deleted file mode 100644 index 02c5f46..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/ffx_fsr3upscaler_tcr_autogen_pass.hlsl.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 712d171118b59fc4fb28d0d487060d42 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler.meta deleted file mode 100644 index 2626a2e..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: ae9c6d015ae76544f9e8117e79ea862b -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h deleted file mode 100644 index f0b62ab..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h +++ /dev/null @@ -1,616 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_COMMON_TYPES_H -#define FFX_COMMON_TYPES_H - -#if defined(FFX_CPU) -#define FFX_PARAMETER_IN -#define FFX_PARAMETER_OUT -#define FFX_PARAMETER_INOUT -#define FFX_PARAMETER_UNIFORM -#elif defined(FFX_HLSL) -#define FFX_PARAMETER_IN in -#define FFX_PARAMETER_OUT out -#define FFX_PARAMETER_INOUT inout -#define FFX_PARAMETER_UNIFORM uniform -#elif defined(FFX_GLSL) -#define FFX_PARAMETER_IN in -#define FFX_PARAMETER_OUT out -#define FFX_PARAMETER_INOUT inout -#define FFX_PARAMETER_UNIFORM const //[cacao_placeholder] until a better fit is found! -#endif // #if defined(FFX_CPU) - -#if defined(FFX_CPU) -/// A typedef for a boolean value. -/// -/// @ingroup CPUTypes -typedef bool FfxBoolean; - -/// A typedef for a unsigned 8bit integer. -/// -/// @ingroup CPUTypes -typedef uint8_t FfxUInt8; - -/// A typedef for a unsigned 16bit integer. -/// -/// @ingroup CPUTypes -typedef uint16_t FfxUInt16; - -/// A typedef for a unsigned 32bit integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32; - -/// A typedef for a unsigned 64bit integer. -/// -/// @ingroup CPUTypes -typedef uint64_t FfxUInt64; - -/// A typedef for a signed 8bit integer. -/// -/// @ingroup CPUTypes -typedef int8_t FfxInt8; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup CPUTypes -typedef int16_t FfxInt16; - -/// A typedef for a signed 32bit integer. -/// -/// @ingroup CPUTypes -typedef int32_t FfxInt32; - -/// A typedef for a signed 64bit integer. -/// -/// @ingroup CPUTypes -typedef int64_t FfxInt64; - -/// A typedef for a floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32; - -/// A typedef for a 2-dimensional floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32x2[2]; - -/// A typedef for a 3-dimensional floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32x3[3]; - -/// A typedef for a 4-dimensional floating point value. -/// -/// @ingroup CPUTypes -typedef float FfxFloat32x4[4]; - -/// A typedef for a 2-dimensional 32bit unsigned integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32x2[2]; - -/// A typedef for a 3-dimensional 32bit unsigned integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32x3[3]; - -/// A typedef for a 4-dimensional 32bit unsigned integer. -/// -/// @ingroup CPUTypes -typedef uint32_t FfxUInt32x4[4]; -#endif // #if defined(FFX_CPU) - -#if defined(FFX_HLSL) - -#define FfxFloat32Mat4 matrix -#define FfxFloat32Mat3 matrix - -/// A typedef for a boolean value. -/// -/// @ingroup HLSLTypes -typedef bool FfxBoolean; - -#if FFX_HLSL_SM>=62 - -/// @defgroup HLSL62Types HLSL 6.2 And Above Types -/// HLSL 6.2 and above type defines for all commonly used variables -/// -/// @ingroup HLSLTypes - -/// A typedef for a floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t FfxFloat32; - -/// A typedef for a 2-dimensional floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t2 FfxFloat32x2; - -/// A typedef for a 3-dimensional floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t3 FfxFloat32x3; - -/// A typedef for a 4-dimensional floating point value. -/// -/// @ingroup HLSL62Types -typedef float32_t4 FfxFloat32x4; - -/// A [cacao_placeholder] typedef for matrix type until confirmed. -typedef float4x4 FfxFloat32x4x4; -typedef float3x3 FfxFloat32x3x3; -typedef float2x2 FfxFloat32x2x2; - -/// A typedef for a unsigned 32bit integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t FfxUInt32; - -/// A typedef for a 2-dimensional 32bit unsigned integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t2 FfxUInt32x2; - -/// A typedef for a 3-dimensional 32bit unsigned integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t3 FfxUInt32x3; - -/// A typedef for a 4-dimensional 32bit unsigned integer. -/// -/// @ingroup HLSL62Types -typedef uint32_t4 FfxUInt32x4; - -/// A typedef for a signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t FfxInt32; - -/// A typedef for a 2-dimensional signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t2 FfxInt32x2; - -/// A typedef for a 3-dimensional signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t3 FfxInt32x3; - -/// A typedef for a 4-dimensional signed 32bit integer. -/// -/// @ingroup HLSL62Types -typedef int32_t4 FfxInt32x4; - -#else // #if FFX_HLSL_SM>=62 - -/// @defgroup HLSLBaseTypes HLSL 6.1 And Below Types -/// HLSL 6.1 and below type defines for all commonly used variables -/// -/// @ingroup HLSLTypes - -#define FfxFloat32 float -#define FfxFloat32x2 float2 -#define FfxFloat32x3 float3 -#define FfxFloat32x4 float4 - -/// A [cacao_placeholder] typedef for matrix type until confirmed. -#define FfxFloat32x4x4 float4x4 -#define FfxFloat32x3x3 float3x3 -#define FfxFloat32x2x2 float2x2 - -/// A typedef for a unsigned 32bit integer. -/// -/// @ingroup GPU -typedef uint FfxUInt32; -typedef uint2 FfxUInt32x2; -typedef uint3 FfxUInt32x3; -typedef uint4 FfxUInt32x4; - -typedef int FfxInt32; -typedef int2 FfxInt32x2; -typedef int3 FfxInt32x3; -typedef int4 FfxInt32x4; - -#endif // #if FFX_HLSL_SM>=62 - -#if FFX_HALF - -#if FFX_HLSL_SM >= 62 - -typedef float16_t FfxFloat16; -typedef float16_t2 FfxFloat16x2; -typedef float16_t3 FfxFloat16x3; -typedef float16_t4 FfxFloat16x4; - -/// A typedef for an unsigned 16bit integer. -/// -/// @ingroup HLSLTypes -typedef uint16_t FfxUInt16; -typedef uint16_t2 FfxUInt16x2; -typedef uint16_t3 FfxUInt16x3; -typedef uint16_t4 FfxUInt16x4; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup HLSLTypes -typedef int16_t FfxInt16; -typedef int16_t2 FfxInt16x2; -typedef int16_t3 FfxInt16x3; -typedef int16_t4 FfxInt16x4; -#elif SHADER_API_PSSL -#pragma argument(realtypes) // Enable true 16-bit types - -typedef half FfxFloat16; -typedef half2 FfxFloat16x2; -typedef half3 FfxFloat16x3; -typedef half4 FfxFloat16x4; - -/// A typedef for an unsigned 16bit integer. -/// -/// @ingroup GPU -typedef ushort FfxUInt16; -typedef ushort2 FfxUInt16x2; -typedef ushort3 FfxUInt16x3; -typedef ushort4 FfxUInt16x4; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup GPU -typedef short FfxInt16; -typedef short2 FfxInt16x2; -typedef short3 FfxInt16x3; -typedef short4 FfxInt16x4; -#else // #if FFX_HLSL_SM>=62 -typedef min16float FfxFloat16; -typedef min16float2 FfxFloat16x2; -typedef min16float3 FfxFloat16x3; -typedef min16float4 FfxFloat16x4; - -/// A typedef for an unsigned 16bit integer. -/// -/// @ingroup HLSLTypes -typedef min16uint FfxUInt16; -typedef min16uint2 FfxUInt16x2; -typedef min16uint3 FfxUInt16x3; -typedef min16uint4 FfxUInt16x4; - -/// A typedef for a signed 16bit integer. -/// -/// @ingroup HLSLTypes -typedef min16int FfxInt16; -typedef min16int2 FfxInt16x2; -typedef min16int3 FfxInt16x3; -typedef min16int4 FfxInt16x4; -#endif // #if FFX_HLSL_SM>=62 - -#endif // FFX_HALF - -#endif // #if defined(FFX_HLSL) - -#if defined(FFX_GLSL) - -#define FfxFloat32Mat4 mat4 -#define FfxFloat32Mat3 mat3 - -/// A typedef for a boolean value. -/// -/// @ingroup GLSLTypes -#define FfxBoolean bool -#define FfxFloat32 float -#define FfxFloat32x2 vec2 -#define FfxFloat32x3 vec3 -#define FfxFloat32x4 vec4 -#define FfxUInt32 uint -#define FfxUInt32x2 uvec2 -#define FfxUInt32x3 uvec3 -#define FfxUInt32x4 uvec4 -#define FfxInt32 int -#define FfxInt32x2 ivec2 -#define FfxInt32x3 ivec3 -#define FfxInt32x4 ivec4 - -/// A [cacao_placeholder] typedef for matrix type until confirmed. -#define FfxFloat32x4x4 mat4 -#define FfxFloat32x3x3 mat3 -#define FfxFloat32x2x2 mat2 - -#if FFX_HALF -#define FfxFloat16 float16_t -#define FfxFloat16x2 f16vec2 -#define FfxFloat16x3 f16vec3 -#define FfxFloat16x4 f16vec4 -#define FfxUInt16 uint16_t -#define FfxUInt16x2 u16vec2 -#define FfxUInt16x3 u16vec3 -#define FfxUInt16x4 u16vec4 -#define FfxInt16 int16_t -#define FfxInt16x2 i16vec2 -#define FfxInt16x3 i16vec3 -#define FfxInt16x4 i16vec4 -#endif // FFX_HALF -#endif // #if defined(FFX_GLSL) - -// Global toggles: -// #define FFX_HALF (1) -// #define FFX_HLSL_SM (62) - -#if FFX_HALF && !defined(SHADER_API_PSSL) - -#if FFX_HLSL_SM >= 62 - -#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType##16_t TypeName; -#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType##16_t TypeName; -#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#else //FFX_HLSL_SM>=62 - -#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef min16##BaseComponentType TypeName; -#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) FFX_MIN16_SCALAR( TypeName, BaseComponentType ); -#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ); -#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ); - -#endif //FFX_HLSL_SM>=62 - -#else //FFX_HALF - -#define FFX_MIN16_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType TypeName; -#define FFX_MIN16_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_MIN16_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#define FFX_16BIT_SCALAR( TypeName, BaseComponentType ) typedef BaseComponentType TypeName; -#define FFX_16BIT_VECTOR( TypeName, BaseComponentType, COL ) typedef vector TypeName; -#define FFX_16BIT_MATRIX( TypeName, BaseComponentType, ROW, COL ) typedef matrix TypeName; - -#endif //FFX_HALF - -#if defined(FFX_GPU) -// Common typedefs: -#if defined(FFX_HLSL) && !defined(SHADER_API_PSSL) -FFX_MIN16_SCALAR( FFX_MIN16_F , float ); -FFX_MIN16_VECTOR( FFX_MIN16_F2, float, 2 ); -FFX_MIN16_VECTOR( FFX_MIN16_F3, float, 3 ); -FFX_MIN16_VECTOR( FFX_MIN16_F4, float, 4 ); - -FFX_MIN16_SCALAR( FFX_MIN16_I, int ); -FFX_MIN16_VECTOR( FFX_MIN16_I2, int, 2 ); -FFX_MIN16_VECTOR( FFX_MIN16_I3, int, 3 ); -FFX_MIN16_VECTOR( FFX_MIN16_I4, int, 4 ); - -FFX_MIN16_SCALAR( FFX_MIN16_U, uint ); -FFX_MIN16_VECTOR( FFX_MIN16_U2, uint, 2 ); -FFX_MIN16_VECTOR( FFX_MIN16_U3, uint, 3 ); -FFX_MIN16_VECTOR( FFX_MIN16_U4, uint, 4 ); - -FFX_16BIT_SCALAR( FFX_F16_t , float ); -FFX_16BIT_VECTOR( FFX_F16_t2, float, 2 ); -FFX_16BIT_VECTOR( FFX_F16_t3, float, 3 ); -FFX_16BIT_VECTOR( FFX_F16_t4, float, 4 ); - -FFX_16BIT_SCALAR( FFX_I16_t, int ); -FFX_16BIT_VECTOR( FFX_I16_t2, int, 2 ); -FFX_16BIT_VECTOR( FFX_I16_t3, int, 3 ); -FFX_16BIT_VECTOR( FFX_I16_t4, int, 4 ); - -FFX_16BIT_SCALAR( FFX_U16_t, uint ); -FFX_16BIT_VECTOR( FFX_U16_t2, uint, 2 ); -FFX_16BIT_VECTOR( FFX_U16_t3, uint, 3 ); -FFX_16BIT_VECTOR( FFX_U16_t4, uint, 4 ); - -#define TYPEDEF_MIN16_TYPES(Prefix) \ -typedef FFX_MIN16_F Prefix##_F; \ -typedef FFX_MIN16_F2 Prefix##_F2; \ -typedef FFX_MIN16_F3 Prefix##_F3; \ -typedef FFX_MIN16_F4 Prefix##_F4; \ -typedef FFX_MIN16_I Prefix##_I; \ -typedef FFX_MIN16_I2 Prefix##_I2; \ -typedef FFX_MIN16_I3 Prefix##_I3; \ -typedef FFX_MIN16_I4 Prefix##_I4; \ -typedef FFX_MIN16_U Prefix##_U; \ -typedef FFX_MIN16_U2 Prefix##_U2; \ -typedef FFX_MIN16_U3 Prefix##_U3; \ -typedef FFX_MIN16_U4 Prefix##_U4; - -#define TYPEDEF_16BIT_TYPES(Prefix) \ -typedef FFX_16BIT_F Prefix##_F; \ -typedef FFX_16BIT_F2 Prefix##_F2; \ -typedef FFX_16BIT_F3 Prefix##_F3; \ -typedef FFX_16BIT_F4 Prefix##_F4; \ -typedef FFX_16BIT_I Prefix##_I; \ -typedef FFX_16BIT_I2 Prefix##_I2; \ -typedef FFX_16BIT_I3 Prefix##_I3; \ -typedef FFX_16BIT_I4 Prefix##_I4; \ -typedef FFX_16BIT_U Prefix##_U; \ -typedef FFX_16BIT_U2 Prefix##_U2; \ -typedef FFX_16BIT_U3 Prefix##_U3; \ -typedef FFX_16BIT_U4 Prefix##_U4; - -#define TYPEDEF_FULL_PRECISION_TYPES(Prefix) \ -typedef FfxFloat32 Prefix##_F; \ -typedef FfxFloat32x2 Prefix##_F2; \ -typedef FfxFloat32x3 Prefix##_F3; \ -typedef FfxFloat32x4 Prefix##_F4; \ -typedef FfxInt32 Prefix##_I; \ -typedef FfxInt32x2 Prefix##_I2; \ -typedef FfxInt32x3 Prefix##_I3; \ -typedef FfxInt32x4 Prefix##_I4; \ -typedef FfxUInt32 Prefix##_U; \ -typedef FfxUInt32x2 Prefix##_U2; \ -typedef FfxUInt32x3 Prefix##_U3; \ -typedef FfxUInt32x4 Prefix##_U4; -#endif // #if defined(FFX_HLSL) - -#if defined(SHADER_API_PSSL) - -#define unorm -#define globallycoherent - -#if FFX_HALF - -#define FFX_MIN16_F half -#define FFX_MIN16_F2 half2 -#define FFX_MIN16_F3 half3 -#define FFX_MIN16_F4 half4 - -#define FFX_MIN16_I short -#define FFX_MIN16_I2 short2 -#define FFX_MIN16_I3 short3 -#define FFX_MIN16_I4 short4 - -#define FFX_MIN16_U ushort -#define FFX_MIN16_U2 ushort2 -#define FFX_MIN16_U3 ushort3 -#define FFX_MIN16_U4 ushort4 - -#define FFX_16BIT_F half -#define FFX_16BIT_F2 half2 -#define FFX_16BIT_F3 half3 -#define FFX_16BIT_F4 half4 - -#define FFX_16BIT_I short -#define FFX_16BIT_I2 short2 -#define FFX_16BIT_I3 short3 -#define FFX_16BIT_I4 short4 - -#define FFX_16BIT_U ushort -#define FFX_16BIT_U2 ushort2 -#define FFX_16BIT_U3 ushort3 -#define FFX_16BIT_U4 ushort4 - -#else // FFX_HALF - -#define FFX_MIN16_F float -#define FFX_MIN16_F2 float2 -#define FFX_MIN16_F3 float3 -#define FFX_MIN16_F4 float4 - -#define FFX_MIN16_I int -#define FFX_MIN16_I2 int2 -#define FFX_MIN16_I3 int3 -#define FFX_MIN16_I4 int4 - -#define FFX_MIN16_U uint -#define FFX_MIN16_U2 uint2 -#define FFX_MIN16_U3 uint3 -#define FFX_MIN16_U4 uint4 - -#define FFX_16BIT_F float -#define FFX_16BIT_F2 float2 -#define FFX_16BIT_F3 float3 -#define FFX_16BIT_F4 float4 - -#define FFX_16BIT_I int -#define FFX_16BIT_I2 int2 -#define FFX_16BIT_I3 int3 -#define FFX_16BIT_I4 int4 - -#define FFX_16BIT_U uint -#define FFX_16BIT_U2 uint2 -#define FFX_16BIT_U3 uint3 -#define FFX_16BIT_U4 uint4 - -#endif // FFX_HALF - -#endif // #if defined(SHADER_API_PSSL) - -#if defined(FFX_GLSL) - -#if FFX_HALF - -#define FFX_MIN16_F float16_t -#define FFX_MIN16_F2 f16vec2 -#define FFX_MIN16_F3 f16vec3 -#define FFX_MIN16_F4 f16vec4 - -#define FFX_MIN16_I int16_t -#define FFX_MIN16_I2 i16vec2 -#define FFX_MIN16_I3 i16vec3 -#define FFX_MIN16_I4 i16vec4 - -#define FFX_MIN16_U uint16_t -#define FFX_MIN16_U2 u16vec2 -#define FFX_MIN16_U3 u16vec3 -#define FFX_MIN16_U4 u16vec4 - -#define FFX_16BIT_F float16_t -#define FFX_16BIT_F2 f16vec2 -#define FFX_16BIT_F3 f16vec3 -#define FFX_16BIT_F4 f16vec4 - -#define FFX_16BIT_I int16_t -#define FFX_16BIT_I2 i16vec2 -#define FFX_16BIT_I3 i16vec3 -#define FFX_16BIT_I4 i16vec4 - -#define FFX_16BIT_U uint16_t -#define FFX_16BIT_U2 u16vec2 -#define FFX_16BIT_U3 u16vec3 -#define FFX_16BIT_U4 u16vec4 - -#else // FFX_HALF - -#define FFX_MIN16_F float -#define FFX_MIN16_F2 vec2 -#define FFX_MIN16_F3 vec3 -#define FFX_MIN16_F4 vec4 - -#define FFX_MIN16_I int -#define FFX_MIN16_I2 ivec2 -#define FFX_MIN16_I3 ivec3 -#define FFX_MIN16_I4 ivec4 - -#define FFX_MIN16_U uint -#define FFX_MIN16_U2 uvec2 -#define FFX_MIN16_U3 uvec3 -#define FFX_MIN16_U4 uvec4 - -#define FFX_16BIT_F float -#define FFX_16BIT_F2 vec2 -#define FFX_16BIT_F3 vec3 -#define FFX_16BIT_F4 vec4 - -#define FFX_16BIT_I int -#define FFX_16BIT_I2 ivec2 -#define FFX_16BIT_I3 ivec3 -#define FFX_16BIT_I4 ivec4 - -#define FFX_16BIT_U uint -#define FFX_16BIT_U2 uvec2 -#define FFX_16BIT_U3 uvec3 -#define FFX_16BIT_U4 uvec4 - -#endif // FFX_HALF - -#endif // #if defined(FFX_GLSL) - -#endif // #if defined(FFX_GPU) -#endif // #ifndef FFX_COMMON_TYPES_H diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta deleted file mode 100644 index 0570595..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_common_types.h.meta +++ /dev/null @@ -1,60 +0,0 @@ -fileFormatVersion: 2 -guid: 7974b728d5c1b6d4a8a8e3965d03f96d -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude WebGL: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h deleted file mode 100644 index 02f6b3f..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h +++ /dev/null @@ -1,80 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup FfxGPU GPU -/// The FidelityFX SDK GPU References -/// -/// @ingroup ffxSDK - -/// @defgroup FfxHLSL HLSL References -/// FidelityFX SDK HLSL GPU References -/// -/// @ingroup FfxGPU - -/// @defgroup FfxGLSL GLSL References -/// FidelityFX SDK GLSL GPU References -/// -/// @ingroup FfxGPU - -/// @defgroup FfxGPUEffects FidelityFX GPU References -/// FidelityFX Effect GPU Reference Documentation -/// -/// @ingroup FfxGPU - -/// @defgroup GPUCore GPU Core -/// GPU defines and functions -/// -/// @ingroup FfxGPU - -#if !defined(FFX_CORE_H) -#define FFX_CORE_H - -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic push -#pragma dxc diagnostic ignored "-Wambig-lit-shift" -#endif //__hlsl_dx_compiler - -#include "ffx_common_types.h" - -#if defined(FFX_CPU) - #include "ffx_core_cpu.h" -#endif // #if defined(FFX_CPU) - -#if defined(FFX_GLSL) && defined(FFX_GPU) - #include "ffx_core_glsl.h" -#endif // #if defined(FFX_GLSL) && defined(FFX_GPU) - -#if defined(FFX_HLSL) && defined(FFX_GPU) - #include "ffx_core_hlsl.h" -#endif // #if defined(FFX_HLSL) && defined(FFX_GPU) - -#if defined(FFX_GPU) - #include "ffx_core_gpu_common.h" - #include "ffx_core_gpu_common_half.h" - #include "ffx_core_portability.h" -#endif // #if defined(FFX_GPU) - -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic pop -#endif //__hlsl_dx_compiler - -#endif // #if !defined(FFX_CORE_H) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta deleted file mode 100644 index 8533462..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: b91c5f52b89ff554dacb51045a802ed8 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h deleted file mode 100644 index 865258d..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h +++ /dev/null @@ -1,338 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// A define for a true value in a boolean expression. -/// -/// @ingroup CPUTypes -#define FFX_TRUE (1) - -/// A define for a false value in a boolean expression. -/// -/// @ingroup CPUTypes -#define FFX_FALSE (0) - -#if !defined(FFX_STATIC) -/// A define to abstract declaration of static variables and functions. -/// -/// @ingroup CPUTypes -#define FFX_STATIC static -#endif // #if !defined(FFX_STATIC) - -/// @defgroup CPUCore CPU Core -/// Core CPU-side defines and functions -/// -/// @ingroup ffxHost - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wunused-variable" -#endif - -/// Interpret the bit layout of an IEEE-754 floating point value as an unsigned integer. -/// -/// @param [in] x A 32bit floating value. -/// -/// @returns -/// An unsigned 32bit integer value containing the bit pattern of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxUInt32 ffxAsUInt32(FfxFloat32 x) -{ - union - { - FfxFloat32 f; - FfxUInt32 u; - } bits; - - bits.f = x; - return bits.u; -} - -FFX_STATIC FfxFloat32 ffxDot2(FfxFloat32x2 a, FfxFloat32x2 b) -{ - return a[0] * b[0] + a[1] * b[1]; -} - -FFX_STATIC FfxFloat32 ffxDot3(FfxFloat32x3 a, FfxFloat32x3 b) -{ - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - -FFX_STATIC FfxFloat32 ffxDot4(FfxFloat32x4 a, FfxFloat32x4 b) -{ - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the GLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxLerp(FfxFloat32 x, FfxFloat32 y, FfxFloat32 t) -{ - return y * t + (-x * t + x); -} - -/// Compute the reciprocal of a value. -/// -/// @param [in] x The value to compute the reciprocal for. -/// -/// @returns -/// The reciprocal value of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxReciprocal(FfxFloat32 x) -{ - return 1.0f / x; -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxSqrt(FfxFloat32 x) -{ - return sqrt(x); -} - -FFX_STATIC FfxUInt32 AShrSU1(FfxUInt32 a, FfxUInt32 b) -{ - return FfxUInt32(FfxInt32(a) >> FfxInt32(b)); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxFract(FfxFloat32 x) -{ - return x - floor(x); -} - -/// Compute the reciprocal square root of a value. -/// -/// @param [in] x The value to compute the reciprocal for. -/// -/// @returns -/// The reciprocal square root value of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 rsqrt(FfxFloat32 x) -{ - return ffxReciprocal(ffxSqrt(x)); -} - -FFX_STATIC FfxFloat32 ffxMin(FfxFloat32 x, FfxFloat32 y) -{ - return x < y ? x : y; -} - -FFX_STATIC FfxUInt32 ffxMin(FfxUInt32 x, FfxUInt32 y) -{ - return x < y ? x : y; -} - -FFX_STATIC FfxFloat32 ffxMax(FfxFloat32 x, FfxFloat32 y) -{ - return x > y ? x : y; -} - -FFX_STATIC FfxUInt32 ffxMax(FfxUInt32 x, FfxUInt32 y) -{ - return x > y ? x : y; -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup CPUCore -FFX_STATIC FfxFloat32 ffxSaturate(FfxFloat32 x) -{ - return ffxMin(1.0f, ffxMax(0.0f, x)); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -FFX_STATIC void opAAddOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d[0] = a[0] + b; - d[1] = a[1] + b; - d[2] = a[2] + b; - return; -} - -FFX_STATIC void opACpyF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d[0] = a[0]; - d[1] = a[1]; - d[2] = a[2]; - return; -} - -FFX_STATIC void opAMulF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32x3 b) -{ - d[0] = a[0] * b[0]; - d[1] = a[1] * b[1]; - d[2] = a[2] * b[2]; - return; -} - -FFX_STATIC void opAMulOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d[0] = a[0] * b; - d[1] = a[1] * b; - d[2] = a[2] * b; - return; -} - -FFX_STATIC void opARcpF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d[0] = ffxReciprocal(a[0]); - d[1] = ffxReciprocal(a[1]); - d[2] = ffxReciprocal(a[2]); - return; -} - -/// Convert FfxFloat32 to half (in lower 16-bits of output). -/// -/// This function implements the same fast technique that is documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf -/// -/// The function supports denormals. -/// -/// Some conversion rules are to make computations possibly "safer" on the GPU, -/// -INF & -NaN -> -65504 -/// +INF & +NaN -> +65504 -/// -/// @param [in] f The 32bit floating point value to convert. -/// -/// @returns -/// The closest 16bit floating point value to f. -/// -/// @ingroup CPUCore -FFX_STATIC FfxUInt32 f32tof16(FfxFloat32 f) -{ - static FfxUInt16 base[512] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, - 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00, 0x5000, - 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, - 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, - 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, - 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, - 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, - 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff - }; - - static FfxUInt8 shift[512] = { - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 - }; - - union - { - FfxFloat32 f; - FfxUInt32 u; - } bits; - - bits.f = f; - FfxUInt32 u = bits.u; - FfxUInt32 i = u >> 23; - return (FfxUInt32)(base[i]) + ((u & 0x7fffff) >> shift[i]); -} - -/// Pack 2x32-bit floating point values in a single 32bit value. -/// -/// This function first converts each component of value into their nearest 16-bit floating -/// point representation, and then stores the X and Y components in the lower and upper 16 bits of the -/// 32bit unsigned integer respectively. -/// -/// @param [in] x A 2-dimensional floating point value to convert and pack. -/// -/// @returns -/// A packed 32bit value containing 2 16bit floating point values. -/// -/// @ingroup CPUCore -FFX_STATIC FfxUInt32 packHalf2x16(FfxFloat32x2 x) -{ - return f32tof16(x[0]) + (f32tof16(x[1]) << 16); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta deleted file mode 100644 index f6508bc..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_cpu.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 4c88c0b7a4dec1e479272449c19ca981 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h deleted file mode 100644 index 2f687df..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h +++ /dev/null @@ -1,2784 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// A define for a true value in a boolean expression. -/// -/// @ingroup GPUCore -#define FFX_TRUE (true) - -/// A define for a false value in a boolean expression. -/// -/// @ingroup GPUCore -#define FFX_FALSE (false) - -/// A define value for positive infinity. -/// -/// @ingroup GPUCore -#define FFX_POSITIVE_INFINITY_FLOAT ffxAsFloat(0x7f800000u) - -/// A define value for negative infinity. -/// -/// @ingroup GPUCore -#define FFX_NEGATIVE_INFINITY_FLOAT ffxAsFloat(0xff800000u) - -/// A define value for PI. -/// -/// @ingroup GPUCore -#define FFX_PI (3.14159) - -FFX_STATIC const FfxFloat32 FFX_FP16_MIN = 6.10e-05f; -FFX_STATIC const FfxFloat32 FFX_FP16_MAX = 65504.0f; -FFX_STATIC const FfxFloat32 FFX_TONEMAP_EPSILON = 1.0f / FFX_FP16_MAX; - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32 ffxReciprocal(FfxFloat32 value) -{ - return rcp(value); -} - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxReciprocal(FfxFloat32x2 value) -{ - return rcp(value); -} - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxReciprocal(FfxFloat32x3 value) -{ - return rcp(value); -} - -/// Compute the reciprocal of value. -/// -/// @param [in] value The value to compute the reciprocal of. -/// -/// @returns -/// The 1 / value. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxReciprocal(FfxFloat32x4 value) -{ - return rcp(value); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32 ffxMin(FfxFloat32 x, FfxFloat32 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxMin(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxMin(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxMin(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32 ffxMin(FfxInt32 x, FfxInt32 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x2 ffxMin(FfxInt32x2 x, FfxInt32x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x3 ffxMin(FfxInt32x3 x, FfxInt32x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x4 ffxMin(FfxInt32x4 x, FfxInt32x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32 ffxMin(FfxUInt32 x, FfxUInt32 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxMin(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxMin(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxMin(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return min(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32 ffxMax(FfxFloat32 x, FfxFloat32 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxMax(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxMax(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxMax(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32 ffxMax(FfxInt32 x, FfxInt32 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x2 ffxMax(FfxInt32x2 x, FfxInt32x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x3 ffxMax(FfxInt32x3 x, FfxInt32x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt32x4 ffxMax(FfxInt32x4 x, FfxInt32x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32 ffxMax(FfxUInt32 x, FfxUInt32 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxMax(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxMax(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxMax(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return max(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32 ffxPow(FfxFloat32 x, FfxFloat32 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxPow(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxPow(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxPow(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return pow(x, y); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32 ffxSqrt(FfxFloat32 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxSqrt(FfxFloat32x2 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxSqrt(FfxFloat32x3 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxSqrt(FfxFloat32x4 x) -{ - return sqrt(x); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32 ffxCopySignBit(FfxFloat32 d, FfxFloat32 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & FfxUInt32(0x80000000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxCopySignBit(FfxFloat32x2 d, FfxFloat32x2 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast2(0x80000000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxCopySignBit(FfxFloat32x3 d, FfxFloat32x3 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast3(0x80000000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxCopySignBit(FfxFloat32x4 d, FfxFloat32x4 s) -{ - return ffxAsFloat(ffxAsUInt32(d) | (ffxAsUInt32(s) & ffxBroadcast4(0x80000000u))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat32 ffxIsSigned(FfxFloat32 m) -{ - return ffxSaturate(m * FfxFloat32(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxIsSigned(FfxFloat32x2 m) -{ - return ffxSaturate(m * ffxBroadcast2(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxIsSigned(FfxFloat32x3 m) -{ - return ffxSaturate(m * ffxBroadcast3(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against for have the sign set. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or positive. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxIsSigned(FfxFloat32x4 m) -{ - return ffxSaturate(m * ffxBroadcast4(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32 ffxIsGreaterThanZero(FfxFloat32 m) -{ - return ffxSaturate(m * FfxFloat32(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxIsGreaterThanZero(FfxFloat32x2 m) -{ - return ffxSaturate(m * ffxBroadcast2(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxIsGreaterThanZero(FfxFloat32x3 m) -{ - return ffxSaturate(m * ffxBroadcast3(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxIsGreaterThanZero(FfxFloat32x4 m) -{ - return ffxSaturate(m * ffxBroadcast4(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Convert a 32bit floating point value to sortable integer. -/// -/// - If sign bit=0, flip the sign bit (positives). -/// - If sign bit=1, flip all bits (negatives). -/// -/// The function has the side effects that: -/// - Larger integers are more positive values. -/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). -/// -/// @param [in] value The floating point value to make sortable. -/// -/// @returns -/// The sortable integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxFloatToSortableInteger(FfxUInt32 value) -{ - return value ^ ((AShrSU1(value, FfxUInt32(31))) | FfxUInt32(0x80000000)); -} - -/// Convert a sortable integer to a 32bit floating point value. -/// -/// The function has the side effects that: -/// - If sign bit=1, flip the sign bit (positives). -/// - If sign bit=0, flip all bits (negatives). -/// -/// @param [in] value The floating point value to make sortable. -/// -/// @returns -/// The sortable integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxSortableIntegerToFloat(FfxUInt32 value) -{ - return value ^ ((~AShrSU1(value, FfxUInt32(31))) | FfxUInt32(0x80000000)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateSqrt(FfxFloat32 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> FfxUInt32(1)) + FfxUInt32(0x1fbc4639)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateReciprocal(FfxFloat32 value) -{ - return ffxAsFloat(FfxUInt32(0x7ef07ebb) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateReciprocalMedium(FfxFloat32 value) -{ - FfxFloat32 b = ffxAsFloat(FfxUInt32(0x7ef19fff) - ffxAsUInt32(value)); - return b * (-b * value + FfxFloat32(2.0)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal square root for. -/// -/// @returns -/// An approximation of the reciprocal square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateReciprocalSquareRoot(FfxFloat32 value) -{ - return ffxAsFloat(FfxUInt32(0x5f347d74) - (ffxAsUInt32(value) >> FfxUInt32(1))); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateSqrt(FfxFloat32x2 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast2(1u)) + ffxBroadcast2(0x1fbc4639u)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateReciprocal(FfxFloat32x2 value) -{ - return ffxAsFloat(ffxBroadcast2(0x7ef07ebbu) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateReciprocalMedium(FfxFloat32x2 value) -{ - FfxFloat32x2 b = ffxAsFloat(ffxBroadcast2(0x7ef19fffu) - ffxAsUInt32(value)); - return b * (-b * value + ffxBroadcast2(2.0f)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateReciprocalSquareRoot(FfxFloat32x2 value) -{ - return ffxAsFloat(ffxBroadcast2(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast2(1u))); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateSqrt(FfxFloat32x3 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast3(1u)) + ffxBroadcast3(0x1fbc4639u)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateReciprocal(FfxFloat32x3 value) -{ - return ffxAsFloat(ffxBroadcast3(0x7ef07ebbu) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateReciprocalMedium(FfxFloat32x3 value) -{ - FfxFloat32x3 b = ffxAsFloat(ffxBroadcast3(0x7ef19fffu) - ffxAsUInt32(value)); - return b * (-b * value + ffxBroadcast3(2.0f)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateReciprocalSquareRoot(FfxFloat32x3 value) -{ - return ffxAsFloat(ffxBroadcast3(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast3(1u))); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateSqrt(FfxFloat32x4 value) -{ - return ffxAsFloat((ffxAsUInt32(value) >> ffxBroadcast4(1u)) + ffxBroadcast4(0x1fbc4639u)); -} - -/// Calculate a low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateReciprocal(FfxFloat32x4 value) -{ - return ffxAsFloat(ffxBroadcast4(0x7ef07ebbu) - ffxAsUInt32(value)); -} - -/// Calculate a medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateReciprocalMedium(FfxFloat32x4 value) -{ - FfxFloat32x4 b = ffxAsFloat(ffxBroadcast4(0x7ef19fffu) - ffxAsUInt32(value)); - return b * (-b * value + ffxBroadcast4(2.0f)); -} - -/// Calculate a low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] value The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateReciprocalSquareRoot(FfxFloat32x4 value) -{ - return ffxAsFloat(ffxBroadcast4(0x5f347d74u) - (ffxAsUInt32(value) >> ffxBroadcast4(1u))); -} - -/// Calculate dot product of 'a' and 'b'. -/// -/// @param [in] a First vector input. -/// @param [in] b Second vector input. -/// -/// @returns -/// The value of a dot b. -/// -/// @ingroup GPUCore -FfxFloat32 ffxDot2(FfxFloat32x2 a, FfxFloat32x2 b) -{ - return dot(a, b); -} - -/// Calculate dot product of 'a' and 'b'. -/// -/// @param [in] a First vector input. -/// @param [in] b Second vector input. -/// -/// @returns -/// The value of a dot b. -/// -/// @ingroup GPUCore -FfxFloat32 ffxDot3(FfxFloat32x3 a, FfxFloat32x3 b) -{ - return dot(a, b); -} - -/// Calculate dot product of 'a' and 'b'. -/// -/// @param [in] a First vector input. -/// @param [in] b Second vector input. -/// -/// @returns -/// The value of a dot b. -/// -/// @ingroup GPUCore -FfxFloat32 ffxDot4(FfxFloat32x4 a, FfxFloat32x4 b) -{ - return dot(a, b); -} - - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximatePQToGamma2Medium(FfxFloat32 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximatePQToLinear(FfxFloat32 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateGamma2ToPQ(FfxFloat32 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(2)) + FfxUInt32(0x2F9A4E46)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateGamma2ToPQMedium(FfxFloat32 a) -{ - FfxFloat32 b = ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(2)) + FfxUInt32(0x2F9A4E46)); - FfxFloat32 b4 = b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateGamma2ToPQHigh(FfxFloat32 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateLinearToPQ(FfxFloat32 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(3)) + FfxUInt32(0x378D8723)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateLinearToPQMedium(FfxFloat32 a) -{ - FfxFloat32 b = ffxAsFloat((ffxAsUInt32(a) >> FfxUInt32(3)) + FfxUInt32(0x378D8723)); - FfxFloat32 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32 ffxApproximateLinearToPQHigh(FfxFloat32 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximatePQToGamma2Medium(FfxFloat32x2 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximatePQToLinear(FfxFloat32x2 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateGamma2ToPQ(FfxFloat32x2 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(2u)) + ffxBroadcast2(0x2F9A4E46u)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateGamma2ToPQMedium(FfxFloat32x2 a) -{ - FfxFloat32x2 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(2u)) + ffxBroadcast2(0x2F9A4E46u)); - FfxFloat32x2 b4 = b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateGamma2ToPQHigh(FfxFloat32x2 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateLinearToPQ(FfxFloat32x2 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(3u)) + ffxBroadcast2(0x378D8723u)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateLinearToPQMedium(FfxFloat32x2 a) -{ - FfxFloat32x2 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast2(3u)) + ffxBroadcast2(0x378D8723u)); - FfxFloat32x2 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxApproximateLinearToPQHigh(FfxFloat32x2 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximatePQToGamma2Medium(FfxFloat32x3 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximatePQToLinear(FfxFloat32x3 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateGamma2ToPQ(FfxFloat32x3 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(2u)) + ffxBroadcast3(0x2F9A4E46u)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateGamma2ToPQMedium(FfxFloat32x3 a) -{ - FfxFloat32x3 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(2u)) + ffxBroadcast3(0x2F9A4E46u)); - FfxFloat32x3 b4 = b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateGamma2ToPQHigh(FfxFloat32x3 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateLinearToPQ(FfxFloat32x3 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(3u)) + ffxBroadcast3(0x378D8723u)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateLinearToPQMedium(FfxFloat32x3 a) -{ - FfxFloat32x3 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast3(3u)) + ffxBroadcast3(0x378D8723u)); - FfxFloat32x3 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxApproximateLinearToPQHigh(FfxFloat32x3 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -/// Compute an approximate conversion from PQ to Gamma2 space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and Gamma2. -/// -/// @returns -/// The value a converted into Gamma2. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximatePQToGamma2Medium(FfxFloat32x4 a) -{ - return a * a * a * a; -} - -/// Compute an approximate conversion from PQ to linear space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between PQ and linear. -/// -/// @returns -/// The value a converted into linear. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximatePQToLinear(FfxFloat32x4 a) -{ - return a * a * a * a * a * a * a * a; -} - -/// Compute an approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateGamma2ToPQ(FfxFloat32x4 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(2u)) + ffxBroadcast4(0x2F9A4E46u)); -} - -/// Compute a more accurate approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateGamma2ToPQMedium(FfxFloat32x4 a) -{ - FfxFloat32x4 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(2u)) + ffxBroadcast4(0x2F9A4E46u)); - FfxFloat32x4 b4 = b * b * b * b * b * b * b * b; - return b - b * (b4 - a) / (FfxFloat32(4.0) * b4); -} - -/// Compute a high accuracy approximate conversion from gamma2 to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between gamma2 and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateGamma2ToPQHigh(FfxFloat32x4 a) -{ - return ffxSqrt(ffxSqrt(a)); -} - -/// Compute an approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateLinearToPQ(FfxFloat32x4 a) -{ - return ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(3u)) + ffxBroadcast4(0x378D8723u)); -} - -/// Compute a more accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateLinearToPQMedium(FfxFloat32x4 a) -{ - FfxFloat32x4 b = ffxAsFloat((ffxAsUInt32(a) >> ffxBroadcast4(3u)) + ffxBroadcast4(0x378D8723u)); - FfxFloat32x4 b8 = b * b * b * b * b * b * b * b; - return b - b * (b8 - a) / (FfxFloat32(8.0) * b8); -} - -/// Compute a very accurate approximate conversion from linear to PQ space. -/// -/// PQ is very close to x^(1/8). The functions below Use the fast FfxFloat32 approximation method to do -/// PQ conversions to and from Gamma2 (4th power and fast 4th root), and PQ to and from Linear -/// (8th power and fast 8th root). The maximum error is approximately 0.2%. -/// -/// @param a The value to convert between linear and PQ. -/// -/// @returns -/// The value a converted into PQ. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxApproximateLinearToPQHigh(FfxFloat32x4 a) -{ - return ffxSqrt(ffxSqrt(ffxSqrt(a))); -} - -// An approximation of sine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate sine for. -// -// @returns -// The approximate sine of value. -FfxFloat32 ffxParabolicSin(FfxFloat32 value) -{ - return value * abs(value) - value; -} - -// An approximation of sine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate sine for. -// -// @returns -// The approximate sine of value. -FfxFloat32x2 ffxParabolicSin(FfxFloat32x2 x) -{ - return x * abs(x) - x; -} - -// An approximation of cosine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate cosine for. -// -// @returns -// The approximate cosine of value. -FfxFloat32 ffxParabolicCos(FfxFloat32 x) -{ - x = ffxFract(x * FfxFloat32(0.5) + FfxFloat32(0.75)); - x = x * FfxFloat32(2.0) - FfxFloat32(1.0); - return ffxParabolicSin(x); -} - -// An approximation of cosine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate cosine for. -// -// @returns -// The approximate cosine of value. -FfxFloat32x2 ffxParabolicCos(FfxFloat32x2 x) -{ - x = ffxFract(x * ffxBroadcast2(0.5f) + ffxBroadcast2(0.75f)); - x = x * ffxBroadcast2(2.0f) - ffxBroadcast2(1.0f); - return ffxParabolicSin(x); -} - -// An approximation of both sine and cosine. -// -// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -// is {-1/4 to 1/4} representing {-1 to 1}. -// -// @param [in] value The value to calculate approximate cosine for. -// -// @returns -// A FfxFloat32x2 containing approximations of both sine and cosine of value. -FfxFloat32x2 ffxParabolicSinCos(FfxFloat32 x) -{ - FfxFloat32 y = ffxFract(x * FfxFloat32(0.5) + FfxFloat32(0.75)); - y = y * FfxFloat32(2.0) - FfxFloat32(1.0); - return ffxParabolicSin(FfxFloat32x2(x, y)); -} - -/// Conditional free logic AND operation using values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneAnd(FfxUInt32 x, FfxUInt32 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneAnd(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneAnd(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneAnd(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return min(x, y); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneAnd(FfxUInt32 x) -{ - return x ^ FfxUInt32(1); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneAnd(FfxUInt32x2 x) -{ - return x ^ ffxBroadcast2(1u); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneAnd(FfxUInt32x3 x) -{ - return x ^ ffxBroadcast3(1u); -} - -/// Conditional free logic NOT operation using two values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneAnd(FfxUInt32x4 x) -{ - return x ^ ffxBroadcast4(1u); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneOr(FfxUInt32 x, FfxUInt32 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneOr(FfxUInt32x2 x, FfxUInt32x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneOr(FfxUInt32x3 x, FfxUInt32x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneOr(FfxUInt32x4 x, FfxUInt32x4 y) -{ - return max(x, y); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32 ffxZeroOneAndToU1(FfxFloat32 x) -{ - return FfxUInt32(FfxFloat32(1.0) - x); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxZeroOneAndToU2(FfxFloat32x2 x) -{ - return FfxUInt32x2(ffxBroadcast2(1.0) - x); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x3 ffxZeroOneAndToU3(FfxFloat32x3 x) -{ - return FfxUInt32x3(ffxBroadcast3(1.0) - x); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxUInt32x4 ffxZeroOneAndToU4(FfxFloat32x4 x) -{ - return FfxUInt32x4(ffxBroadcast4(1.0) - x); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneAndOr(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return ffxSaturate(x * y + z); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneAndOr(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return ffxSaturate(x * y + z); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneAndOr(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return ffxSaturate(x * y + z); -} - -/// Conditional free logic AND operation using two values followed by a NOT operation -/// using the resulting value and a third value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneAndOr(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return ffxSaturate(x * y + z); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneIsGreaterThanZero(FfxFloat32 x) -{ - return ffxSaturate(x * FfxFloat32(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneIsGreaterThanZero(FfxFloat32x2 x) -{ - return ffxSaturate(x * ffxBroadcast2(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneIsGreaterThanZero(FfxFloat32x3 x) -{ - return ffxSaturate(x * ffxBroadcast3(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneIsGreaterThanZero(FfxFloat32x4 x) -{ - return ffxSaturate(x * ffxBroadcast4(FFX_POSITIVE_INFINITY_FLOAT)); -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneAnd(FfxFloat32 x) -{ - return FfxFloat32(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneAnd(FfxFloat32x2 x) -{ - return ffxBroadcast2(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneAnd(FfxFloat32x3 x) -{ - return ffxBroadcast3(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneAnd(FfxFloat32x4 x) -{ - return ffxBroadcast4(1.0) - x; -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneOr(FfxFloat32 x, FfxFloat32 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneOr(FfxFloat32x2 x, FfxFloat32x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneOr(FfxFloat32x3 x, FfxFloat32x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneOr(FfxFloat32x4 x, FfxFloat32x4 y) -{ - return max(x, y); -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneSelect(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - FfxFloat32 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneSelect(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - FfxFloat32x2 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneSelect(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - FfxFloat32x3 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneSelect(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - FfxFloat32x4 r = (-x) * z + z; - return x * y + r; -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32 ffxZeroOneIsSigned(FfxFloat32 x) -{ - return ffxSaturate(x * FfxFloat32(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxZeroOneIsSigned(FfxFloat32x2 x) -{ - return ffxSaturate(x * ffxBroadcast2(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxZeroOneIsSigned(FfxFloat32x3 x) -{ - return ffxSaturate(x * ffxBroadcast3(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Given a value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat32x4 ffxZeroOneIsSigned(FfxFloat32x4 x) -{ - return ffxSaturate(x * ffxBroadcast4(FFX_NEGATIVE_INFINITY_FLOAT)); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] color The color to convert to Rec. 709. -/// -/// @returns -/// The color in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxRec709FromLinear(FfxFloat32 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); - return clamp(j.x, color * j.y, pow(color, j.z) * k.x + k.y); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] color The color to convert to Rec. 709. -/// -/// @returns -/// The color in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxRec709FromLinear(FfxFloat32x2 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); - return clamp(j.xx, color * j.yy, pow(color, j.zz) * k.xx + k.yy); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] color The color to convert to Rec. 709. -/// -/// @returns -/// The color in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxRec709FromLinear(FfxFloat32x3 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.099, -0.099); - return clamp(j.xxx, color * j.yyy, pow(color, j.zzz) * k.xxx + k.yyy); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. -/// -/// @param [in] value The value to convert to gamma space from linear. -/// @param [in] power The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxGammaFromLinear(FfxFloat32 value, FfxFloat32 power) -{ - return pow(value, FfxFloat32(power)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. -/// -/// @param [in] value The value to convert to gamma space from linear. -/// @param [in] power The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxGammaFromLinear(FfxFloat32x2 value, FfxFloat32 power) -{ - return pow(value, ffxBroadcast2(power)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGamma. -/// -/// @param [in] value The value to convert to gamma space from linear. -/// @param [in] power The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxGammaFromLinear(FfxFloat32x3 value, FfxFloat32 power) -{ - return pow(value, ffxBroadcast3(power)); -} - -/// Compute a PQ value from a linear value. -/// -/// @param [in] value The value to convert to PQ from linear. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxPQToLinear(FfxFloat32 value) -{ - FfxFloat32 p = pow(value, FfxFloat32(0.159302)); - return pow((FfxFloat32(0.835938) + FfxFloat32(18.8516) * p) / (FfxFloat32(1.0) + FfxFloat32(18.6875) * p), FfxFloat32(78.8438)); -} - -/// Compute a PQ value from a linear value. -/// -/// @param [in] value The value to convert to PQ from linear. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxPQToLinear(FfxFloat32x2 value) -{ - FfxFloat32x2 p = pow(value, ffxBroadcast2(0.159302)); - return pow((ffxBroadcast2(0.835938) + ffxBroadcast2(18.8516) * p) / (ffxBroadcast2(1.0) + ffxBroadcast2(18.6875) * p), ffxBroadcast2(78.8438)); -} - -/// Compute a PQ value from a linear value. -/// -/// @param [in] value The value to convert to PQ from linear. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxPQToLinear(FfxFloat32x3 value) -{ - FfxFloat32x3 p = pow(value, ffxBroadcast3(0.159302)); - return pow((ffxBroadcast3(0.835938) + ffxBroadcast3(18.8516) * p) / (ffxBroadcast3(1.0) + ffxBroadcast3(18.6875) * p), ffxBroadcast3(78.8438)); -} - -/// Compute a linear value from a SRGB value. -/// -/// @param [in] value The value to convert to linear from SRGB. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxSrgbToLinear(FfxFloat32 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); - return clamp(j.x, value * j.y, pow(value, j.z) * k.x + k.y); -} - -/// Compute a linear value from a SRGB value. -/// -/// @param [in] value The value to convert to linear from SRGB. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxSrgbToLinear(FfxFloat32x2 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); - return clamp(j.xx, value * j.yy, pow(value, j.zz) * k.xx + k.yy); -} - -/// Compute a linear value from a SRGB value. -/// -/// @param [in] value The value to convert to linear from SRGB. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxSrgbToLinear(FfxFloat32x3 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.055, -0.055); - return clamp(j.xxx, value * j.yyy, pow(value, j.zzz) * k.xxx + k.yyy); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] color The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromRec709(FfxFloat32 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.x), color * j.y, pow(color * k.x + k.y, j.z)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] color The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromRec709(FfxFloat32x2 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.xx), color * j.yy, pow(color * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] color The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromRec709(FfxFloat32x3 color) -{ - FfxFloat32x3 j = FfxFloat32x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelect(ffxZeroOneIsSigned(color - j.xxx), color * j.yyy, pow(color * k.xxx + k.yyy, j.zzz)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] color The value to convert to linear in gamma space. -/// @param [in] power The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromGamma(FfxFloat32 color, FfxFloat32 power) -{ - return pow(color, FfxFloat32(power)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] color The value to convert to linear in gamma space. -/// @param [in] power The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromGamma(FfxFloat32x2 color, FfxFloat32 power) -{ - return pow(color, ffxBroadcast2(power)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] color The value to convert to linear in gamma space. -/// @param [in] power The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromGamma(FfxFloat32x3 color, FfxFloat32 power) -{ - return pow(color, ffxBroadcast3(power)); -} - -/// Compute a linear value from a value in a PQ space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in PQ space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromPQ(FfxFloat32 value) -{ - FfxFloat32 p = pow(value, FfxFloat32(0.0126833)); - return pow(ffxSaturate(p - FfxFloat32(0.835938)) / (FfxFloat32(18.8516) - FfxFloat32(18.6875) * p), FfxFloat32(6.27739)); -} - -/// Compute a linear value from a value in a PQ space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in PQ space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromPQ(FfxFloat32x2 value) -{ - FfxFloat32x2 p = pow(value, ffxBroadcast2(0.0126833)); - return pow(ffxSaturate(p - ffxBroadcast2(0.835938)) / (ffxBroadcast2(18.8516) - ffxBroadcast2(18.6875) * p), ffxBroadcast2(6.27739)); -} - -/// Compute a linear value from a value in a PQ space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in PQ space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromPQ(FfxFloat32x3 value) -{ - FfxFloat32x3 p = pow(value, ffxBroadcast3(0.0126833)); - return pow(ffxSaturate(p - ffxBroadcast3(0.835938)) / (ffxBroadcast3(18.8516) - ffxBroadcast3(18.6875) * p), ffxBroadcast3(6.27739)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32 ffxLinearFromSrgb(FfxFloat32 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.x), value * j.y, pow(value * k.x + k.y, j.z)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x2 ffxLinearFromSrgb(FfxFloat32x2 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.xx), value * j.yy, pow(value * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] value The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat32x3 ffxLinearFromSrgb(FfxFloat32x3 value) -{ - FfxFloat32x3 j = FfxFloat32x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat32x2 k = FfxFloat32x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelect(ffxZeroOneIsSigned(value - j.xxx), value * j.yyy, pow(value * k.xxx + k.yyy, j.zzz)); -} - -/// A remapping of 64x1 to 8x8 imposing rotated 2x2 pixel quads in quad linear. -/// -/// 543210 -/// ====== -/// ..xxx. -/// yy...y -/// -/// @param [in] a The input 1D coordinates to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxRemapForQuad(FfxUInt32 a) -{ - return FfxUInt32x2(bitfieldExtract(a, 1u, 3u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), a, 1u)); -} - -/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. -/// -/// The 64-wide lane indices to 8x8 remapping is performed as follows: -/// -/// 00 01 08 09 10 11 18 19 -/// 02 03 0a 0b 12 13 1a 1b -/// 04 05 0c 0d 14 15 1c 1d -/// 06 07 0e 0f 16 17 1e 1f -/// 20 21 28 29 30 31 38 39 -/// 22 23 2a 2b 32 33 3a 3b -/// 24 25 2c 2d 34 35 3c 3d -/// 26 27 2e 2f 36 37 3e 3f -/// -/// @param [in] a The input 1D coordinate to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxRemapForWaveReduction(FfxUInt32 a) -{ - return FfxUInt32x2(bitfieldInsertMask(bitfieldExtract(a, 2u, 3u), a, 1u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), bitfieldExtract(a, 1u, 2u), 2u)); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta deleted file mode 100644 index 14292a9..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 05b921699d1374a429e32afca13137e2 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h deleted file mode 100644 index 4c73daf..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h +++ /dev/null @@ -1,2979 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#if FFX_HALF -#if FFX_HLSL_SM >= 62 -/// A define value for 16bit positive infinity. -/// -/// @ingroup GPUCore -#define FFX_POSITIVE_INFINITY_HALF FFX_TO_FLOAT16((uint16_t)0x7c00u) - -/// A define value for 16bit negative infinity. -/// -/// @ingroup GPUCore -#define FFX_NEGATIVE_INFINITY_HALF FFX_TO_FLOAT16((uint16_t)0xfc00u) -#else -/// A define value for 16bit positive infinity. -/// -/// @ingroup GPUCore -#define FFX_POSITIVE_INFINITY_HALF FFX_TO_FLOAT16(0x7c00u) - -/// A define value for 16bit negative infinity. -/// -/// @ingroup GPUCore -#define FFX_NEGATIVE_INFINITY_HALF FFX_TO_FLOAT16(0xfc00u) -#endif // #if FFX_HLSL_SM>=62 - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16 ffxMin(FfxFloat16 x, FfxFloat16 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxMin(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxMin(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxMin(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16 ffxMin(FfxInt16 x, FfxInt16 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x2 ffxMin(FfxInt16x2 x, FfxInt16x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x3 ffxMin(FfxInt16x3 x, FfxInt16x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x4 ffxMin(FfxInt16x4 x, FfxInt16x4 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16 ffxMin(FfxUInt16 x, FfxUInt16 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxMin(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxMin(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return min(x, y); -} - -/// Compute the min of two values. -/// -/// @param [in] x The first value to compute the min of. -/// @param [in] y The second value to compute the min of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxMin(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return min(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16 ffxMax(FfxFloat16 x, FfxFloat16 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxMax(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxMax(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxMax(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16 ffxMax(FfxInt16 x, FfxInt16 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x2 ffxMax(FfxInt16x2 x, FfxInt16x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x3 ffxMax(FfxInt16x3 x, FfxInt16x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxInt16x4 ffxMax(FfxInt16x4 x, FfxInt16x4 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16 ffxMax(FfxUInt16 x, FfxUInt16 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxMax(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxMax(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return max(x, y); -} - -/// Compute the max of two values. -/// -/// @param [in] x The first value to compute the max of. -/// @param [in] y The second value to compute the max of. -/// -/// @returns -/// The the lowest of two values. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxMax(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return max(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16 ffxPow(FfxFloat16 x, FfxFloat16 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPow(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxPow(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return pow(x, y); -} - -/// Compute the value of the first parameter raised to the power of the second. -/// -/// @param [in] x The value to raise to the power y. -/// @param [in] y The power to which to raise x. -/// -/// @returns -/// The value of the first parameter raised to the power of the second. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxPow(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return pow(x, y); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSqrt(FfxFloat16 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSqrt(FfxFloat16x2 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSqrt(FfxFloat16x3 x) -{ - return sqrt(x); -} - -/// Compute the square root of a value. -/// -/// @param [in] x The first value to compute the min of. -/// -/// @returns -/// The the square root of x. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxSqrt(FfxFloat16x4 x) -{ - return sqrt(x); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16 ffxCopySignBitHalf(FfxFloat16 d, FfxFloat16 s) -{ - return FFX_TO_FLOAT16(FFX_TO_UINT16(d) | (FFX_TO_UINT16(s) & FFX_BROADCAST_UINT16(0x8000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxCopySignBitHalf(FfxFloat16x2 d, FfxFloat16x2 s) -{ - return FFX_TO_FLOAT16X2(FFX_TO_UINT16X2(d) | (FFX_TO_UINT16X2(s) & FFX_BROADCAST_UINT16X2(0x8000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxCopySignBitHalf(FfxFloat16x3 d, FfxFloat16x3 s) -{ - return FFX_TO_FLOAT16X3(FFX_TO_UINT16X3(d) | (FFX_TO_UINT16X3(s) & FFX_BROADCAST_UINT16X3(0x8000u))); -} - -/// Copy the sign bit from 's' to positive 'd'. -/// -/// @param [in] d The value to copy the sign bit into. -/// @param [in] s The value to copy the sign bit from. -/// -/// @returns -/// The value of d with the sign bit from s. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxCopySignBitHalf(FfxFloat16x4 d, FfxFloat16x4 s) -{ - return FFX_TO_FLOAT16X4(FFX_TO_UINT16X4(d) | (FFX_TO_UINT16X4(s) & FFX_BROADCAST_UINT16X4(0x8000u))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16 ffxIsSignedHalf(FfxFloat16 m) -{ - return FfxFloat16(ffxSaturate(m * FFX_BROADCAST_FLOAT16(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxIsSignedHalf(FfxFloat16x2 m) -{ - return FfxFloat16x2(ffxSaturate(m * FFX_BROADCAST_FLOAT16X2(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxIsSignedHalf(FfxFloat16x3 m) -{ - return FfxFloat16x3(ffxSaturate(m * FFX_BROADCAST_FLOAT16X3(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 0 -/// m >= 0 := 0 -/// m < 0 := 1 -/// -/// Uses the following useful floating point logic, -/// saturate(+a*(-INF)==-INF) := 0 -/// saturate( 0*(-INF)== NaN) := 0 -/// saturate(-a*(-INF)==+INF) := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against 0. -/// -/// @returns -/// 1.0 when the value is negative, or 0.0 when the value is 0 or position. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxIsSignedHalf(FfxFloat16x4 m) -{ - return FfxFloat16x4(ffxSaturate(m * FFX_BROADCAST_FLOAT16X4(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16 ffxIsGreaterThanZeroHalf(FfxFloat16 m) -{ - return FfxFloat16(ffxSaturate(m * FFX_BROADCAST_FLOAT16(FFX_POSITIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxIsGreaterThanZeroHalf(FfxFloat16x2 m) -{ - return FfxFloat16x2(ffxSaturate(m * FFX_BROADCAST_FLOAT16X2(FFX_POSITIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxIsGreaterThanZeroHalf(FfxFloat16x3 m) -{ - return FfxFloat16x3(ffxSaturate(m * FFX_BROADCAST_FLOAT16X3(FFX_POSITIVE_INFINITY_HALF))); -} - -/// A single operation to return the following: -/// m = NaN := 1 -/// m > 0 := 0 -/// m <= 0 := 1 -/// -/// This function is useful when creating masks for branch-free logic. -/// -/// @param [in] m The value to test against zero. -/// -/// @returns -/// 1.0 when the value is position, or 0.0 when the value is 0 or negative. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxIsGreaterThanZeroHalf(FfxFloat16x4 m) -{ - return FfxFloat16x4(ffxSaturate(m * FFX_BROADCAST_FLOAT16X4(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Convert a 16bit floating point value to sortable integer. -/// -/// - If sign bit=0, flip the sign bit (positives). -/// - If sign bit=1, flip all bits (negatives). -/// -/// The function has the side effects that: -/// - Larger integers are more positive values. -/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). -/// -/// @param [in] x The floating point value to make sortable. -/// -/// @returns -/// The sortable integer value. -/// -/// @ingroup GPUCore -FfxUInt16 ffxFloatToSortableIntegerHalf(FfxUInt16 x) -{ - return x ^ ((ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16(15))) | FFX_BROADCAST_UINT16(0x8000)); -} - -/// Convert a sortable integer to a 16bit floating point value. -/// -/// The function has the side effects that: -/// - If sign bit=1, flip the sign bit (positives). -/// - If sign bit=0, flip all bits (negatives). -/// -/// @param [in] x The sortable integer value to make floating point. -/// -/// @returns -/// The floating point value. -/// -/// @ingroup GPUCore -FfxUInt16 ffxSortableIntegerToFloatHalf(FfxUInt16 x) -{ - return x ^ ((~ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16(15))) | FFX_BROADCAST_UINT16(0x8000)); -} - -/// Convert a pair of 16bit floating point values to a pair of sortable integers. -/// -/// - If sign bit=0, flip the sign bit (positives). -/// - If sign bit=1, flip all bits (negatives). -/// -/// The function has the side effects that: -/// - Larger integers are more positive values. -/// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). -/// -/// @param [in] x The floating point values to make sortable. -/// -/// @returns -/// The sortable integer values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxFloatToSortableIntegerHalf(FfxUInt16x2 x) -{ - return x ^ ((ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16X2(15))) | FFX_BROADCAST_UINT16X2(0x8000)); -} - -/// Convert a pair of sortable integers to a pair of 16bit floating point values. -/// -/// The function has the side effects that: -/// - If sign bit=1, flip the sign bit (positives). -/// - If sign bit=0, flip all bits (negatives). -/// -/// @param [in] x The sortable integer values to make floating point. -/// -/// @returns -/// The floating point values. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxSortableIntegerToFloatHalf(FfxUInt16x2 x) -{ - return x ^ ((~ffxBitShiftRightHalf(x, FFX_BROADCAST_UINT16X2(15))) | FFX_BROADCAST_UINT16X2(0x8000)); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y0 [Zero] X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY0ZeroX0(FfxUInt32x2 i) -{ - return ((i.x) & 0xffu) | ((i.y << 16) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y1 [Zero] X1 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY1ZeroX1(FfxUInt32x2 i) -{ - return ((i.x >> 8) & 0xffu) | ((i.y << 8) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y2 [Zero] X2 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY2ZeroX2(FfxUInt32x2 i) -{ - return ((i.x >> 16) & 0xffu) | ((i.y) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// [Zero] Y3 [Zero] X3 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesZeroY3ZeroX3(FfxUInt32x2 i) -{ - return ((i.x >> 24) & 0xffu) | ((i.y >> 8) & 0xff0000u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 Y1 X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2Y1X0(FfxUInt32x2 i) -{ - return ((i.x) & 0x000000ffu) | (i.y & 0xffffff00u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 Y1 X2 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2Y1X2(FfxUInt32x2 i) -{ - return ((i.x >> 16) & 0x000000ffu) | (i.y & 0xffffff00u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 X0 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2X0Y0(FfxUInt32x2 i) -{ - return ((i.x << 8) & 0x0000ff00u) | (i.y & 0xffff00ffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 Y2 X2 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3Y2X2Y0(FfxUInt32x2 i) -{ - return ((i.x >> 8) & 0x0000ff00u) | (i.y & 0xffff00ffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 X0 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3X0Y1Y0(FfxUInt32x2 i) -{ - return ((i.x << 16) & 0x00ff0000u) | (i.y & 0xff00ffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y3 X2 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY3X2Y1Y0(FfxUInt32x2 i) -{ - return ((i.x) & 0x00ff0000u) | (i.y & 0xff00ffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// X0 Y2 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesX0Y2Y1Y0(FfxUInt32x2 i) -{ - return ((i.x << 24) & 0xff000000u) | (i.y & 0x00ffffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// X2 Y2 Y1 Y0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesX2Y2Y1Y0(FfxUInt32x2 i) -{ - return ((i.x << 8) & 0xff000000u) | (i.y & 0x00ffffffu); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y2 X2 Y0 X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY2X2Y0X0(FfxUInt32x2 i) -{ - return ((i.x) & 0x00ff00ffu) | ((i.y << 8) & 0xff00ff00u); -} - -/// Packs the bytes from the X and Y components of a FfxUInt32x2 into a single 32-bit integer. -/// -/// The resulting integer will contain bytes in the following order, from most to least significant: -/// Y2 Y0 X2 X0 -/// -/// @param [in] i The integer pair to pack. -/// -/// @returns -/// The packed integer value. -/// -/// @ingroup GPUCore -FfxUInt32 ffxPackBytesY2Y0X2X0(FfxUInt32x2 i) -{ - return (((i.x) & 0xffu) | ((i.x >> 8) & 0xff00u) | ((i.y << 16) & 0xff0000u) | ((i.y << 8) & 0xff000000u)); -} - -/// Takes two Float16x2 values x and y, normalizes them and builds a single Uint16x2 value in the format {{x0,y0},{x1,y1}}. -/// -/// @param [in] x The first float16x2 value to pack. -/// @param [in] y The second float16x2 value to pack. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxPackX0Y0X1Y1UnsignedToUint16x2(FfxFloat16x2 x, FfxFloat16x2 y) -{ - x *= FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0); - y *= FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0); - return FFX_UINT32_TO_UINT16X2(ffxPackBytesY2X2Y0X0(FfxUInt32x2(FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(x)), FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(y))))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], -/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// r=ffxPermuteUByte0Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], -/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// r=ffxPermuteUByte1Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], -/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. -/// -/// r=ffxPermuteUByte2Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], -/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. -/// -/// r=ffxPermuteUByte3Float16x2ToUint2(d,i) -/// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits -/// Where 'k1' is an SGPR with 0x???? -/// Where 'k2' is an SGPR with 0x???? -/// V_PK_FMA_F16 i,i,k0.x,0 -/// V_PERM_B32 r.x,i,i,k1 -/// V_PERM_B32 r.y,i,i,k2 -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteUByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0))); - return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte0Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte1Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte2Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteUByte3Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i))) * FFX_BROADCAST_FLOAT16X2(32768.0); -} - -/// Takes two Float16x2 values x and y, normalizes them and builds a single Uint16x2 value in the format {{x0,y0},{x1,y1}}. -/// -/// @param [in] x The first float16x2 value to pack. -/// @param [in] y The second float16x2 value to pack. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxPackX0Y0X1Y1SignedToUint16x2(FfxFloat16x2 x, FfxFloat16x2 y) -{ - x = x * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0); - y = y * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0); - return FFX_UINT32_TO_UINT16X2(ffxPackBytesY2X2Y0X0(FfxUInt32x2(FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(x)), FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(y))))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], -/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], -/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], -/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], -/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteSByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))); - return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[0:7], -/// d.y[0:7] into r.y[0:7], i.x[8:15] into r.x[8:15], r.y[8:15] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte0Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesY3Y2Y1X0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2Y1X2(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[8:15], -/// d.y[0:7] into r.y[8:15], i.x[0:7] into r.x[0:7], r.y[0:7] and i.y[0:15] into r.x[16:31], r.y[16:31] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte1Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesY3Y2X0Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3Y2X2Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[16:23], -/// d.y[0:7] into r.y[16:23], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[8:15] into r.x[24:31], r.y[24:31] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte2Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesY3X0Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesY3X2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value d, Float16x2 value i and a resulting FfxUInt32x2 value r, this function packs d.x[0:7] into r.x[24:31], -/// d.y[0:7] into r.y[24:31], i.x[0:15] into r.x[0:15], r.y[0:15] and i.y[0:7] into r.x[16:23], r.y[16:23] using 3 ops. -/// -/// Zero-based flips the MSB bit of the byte (making 128 "exact zero" actually zero). -/// This is useful if there is a desire for cleared values to decode as zero. -/// -/// Handles signed byte values. -/// -/// @param [in] d The FfxUInt32x2 value to be packed. -/// @param [in] i The FfxFloat16x2 value to be packed. -/// -/// @returns -/// The packed FfxUInt32x2 value. -/// -/// @ingroup GPUCore -FfxUInt32x2 ffxPermuteZeroBasedSByte3Float16x2ToUint2(FfxUInt32x2 d, FfxFloat16x2 i) -{ - FfxUInt32 b = FFX_UINT16X2_TO_UINT32(FFX_TO_UINT16X2(i * FFX_BROADCAST_FLOAT16X2(1.0 / 32768.0) + FFX_BROADCAST_FLOAT16X2(0.25 / 32768.0))) ^ 0x00800080u; - return FfxUInt32x2(ffxPackBytesX0Y2Y1Y0(FfxUInt32x2(d.x, b)), ffxPackBytesX2Y2Y1Y0(FfxUInt32x2(d.y, b))); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte0Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte1Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte2Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteSByte3Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i))) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[0:7] into r.x[0:7] and i.y[0:7] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte0Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY0ZeroX0(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[8:15] into r.x[0:7] and i.y[8:15] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte1Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY1ZeroX1(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[16:23] into r.x[0:7] and i.y[16:23] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte2Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY2ZeroX2(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Given a FfxUInt32x2 value i and a resulting Float16x2 value r, this function packs i.x[24:31] into r.x[0:7] and i.y[24:31] into r.y[0:7] using 2 ops. -/// -/// Handles signed byte values. -/// -/// @param [in] i The FfxUInt32x2 value to be unpacked. -/// -/// @returns -/// The unpacked FfxFloat16x2. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxPermuteZeroBasedSByte3Uint2ToFloat16x2(FfxUInt32x2 i) -{ - return FFX_TO_FLOAT16X2(FFX_UINT32_TO_UINT16X2(ffxPackBytesZeroY3ZeroX3(i) ^ 0x00800080u)) * FFX_BROADCAST_FLOAT16X2(32768.0) - FFX_BROADCAST_FLOAT16X2(0.25); -} - -/// Calculate a half-precision low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateSqrtHalf(FfxFloat16 a) -{ - return FFX_TO_FLOAT16((FFX_TO_UINT16(a) >> FFX_BROADCAST_UINT16(1)) + FFX_BROADCAST_UINT16(0x1de2)); -} - -/// Calculate a half-precision low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateSqrtHalf(FfxFloat16x2 a) -{ - return FFX_TO_FLOAT16X2((FFX_TO_UINT16X2(a) >> FFX_BROADCAST_UINT16X2(1)) + FFX_BROADCAST_UINT16X2(0x1de2)); -} - -/// Calculate a half-precision low-quality approximation for the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the square root for. -/// -/// @returns -/// An approximation of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateSqrtHalf(FfxFloat16x3 a) -{ - return FFX_TO_FLOAT16X3((FFX_TO_UINT16X3(a) >> FFX_BROADCAST_UINT16X3(1)) + FFX_BROADCAST_UINT16X3(0x1de2)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateReciprocalHalf(FfxFloat16 a) -{ - return FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x7784) - FFX_TO_UINT16(a)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateReciprocalHalf(FfxFloat16x2 a) -{ - return FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x7784) - FFX_TO_UINT16X2(a)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateReciprocalHalf(FfxFloat16x3 a) -{ - return FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x7784) - FFX_TO_UINT16X3(a)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxApproximateReciprocalHalf(FfxFloat16x4 a) -{ - return FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x7784) - FFX_TO_UINT16X4(a)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateReciprocalMediumHalf(FfxFloat16 a) -{ - FfxFloat16 b = FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x778d) - FFX_TO_UINT16(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16(2.0)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateReciprocalMediumHalf(FfxFloat16x2 a) -{ - FfxFloat16x2 b = FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x778d) - FFX_TO_UINT16X2(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16X2(2.0)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateReciprocalMediumHalf(FfxFloat16x3 a) -{ - FfxFloat16x3 b = FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x778d) - FFX_TO_UINT16X3(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16X3(2.0)); -} - -/// Calculate a half-precision medium-quality approximation for the reciprocal of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal for. -/// -/// @returns -/// An approximation of the reciprocal, estimated to medium quality. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxApproximateReciprocalMediumHalf(FfxFloat16x4 a) -{ - FfxFloat16x4 b = FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x778d) - FFX_TO_UINT16X4(a)); - return b * (-b * a + FFX_BROADCAST_FLOAT16X4(2.0)); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16 ffxApproximateReciprocalSquareRootHalf(FfxFloat16 a) -{ - return FFX_TO_FLOAT16(FFX_BROADCAST_UINT16(0x59a3) - (FFX_TO_UINT16(a) >> FFX_BROADCAST_UINT16(1))); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x2 a) -{ - return FFX_TO_FLOAT16X2(FFX_BROADCAST_UINT16X2(0x59a3) - (FFX_TO_UINT16X2(a) >> FFX_BROADCAST_UINT16X2(1))); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x3 a) -{ - return FFX_TO_FLOAT16X3(FFX_BROADCAST_UINT16X3(0x59a3) - (FFX_TO_UINT16X3(a) >> FFX_BROADCAST_UINT16X3(1))); -} - -/// Calculate a half-precision low-quality approximation for the reciprocal of the square root of a value. -/// -/// For additional information on the approximation family of functions, you can refer to Michal Drobot's excellent -/// presentation materials: -/// -/// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf -/// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h -/// -/// @param [in] a The value to calculate an approximate to the reciprocal of the square root for. -/// -/// @returns -/// An approximation of the reciprocal of the square root, estimated to low quality. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxApproximateReciprocalSquareRootHalf(FfxFloat16x4 a) -{ - return FFX_TO_FLOAT16X4(FFX_BROADCAST_UINT16X4(0x59a3) - (FFX_TO_UINT16X4(a) >> FFX_BROADCAST_UINT16X4(1))); -} - -/// An approximation of sine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate sine for. -/// -/// @returns -/// The approximate sine of value. -FfxFloat16 ffxParabolicSinHalf(FfxFloat16 x) -{ - return x * abs(x) - x; -} - -/// An approximation of sine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate sine for. -/// -/// @returns -/// The approximate sine of value. -FfxFloat16x2 ffxParabolicSinHalf(FfxFloat16x2 x) -{ - return x * abs(x) - x; -} - -/// An approximation of cosine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate cosine for. -/// -/// @returns -/// The approximate cosine of value. -FfxFloat16 ffxParabolicCosHalf(FfxFloat16 x) -{ - x = ffxFract(x * FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16(0.75)); - x = x * FFX_BROADCAST_FLOAT16(2.0) - FFX_BROADCAST_FLOAT16(1.0); - return ffxParabolicSinHalf(x); -} - -/// An approximation of cosine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate cosine for. -/// -/// @returns -/// The approximate cosine of value. -FfxFloat16x2 ffxParabolicCosHalf(FfxFloat16x2 x) -{ - x = ffxFract(x * FFX_BROADCAST_FLOAT16X2(0.5) + FFX_BROADCAST_FLOAT16X2(0.75)); - x = x * FFX_BROADCAST_FLOAT16X2(2.0) - FFX_BROADCAST_FLOAT16X2(1.0); - return ffxParabolicSinHalf(x); -} - -/// An approximation of both sine and cosine. -/// -/// Valid input range is {-1 to 1} representing {0 to 2 pi}, and the output range -/// is {-1/4 to 1/4} representing {-1 to 1}. -/// -/// @param [in] x The value to calculate approximate cosine for. -/// -/// @returns -/// A FfxFloat32x2 containing approximations of both sine and cosine of value. -FfxFloat16x2 ffxParabolicSinCosHalf(FfxFloat16 x) -{ - FfxFloat16 y = ffxFract(x * FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16(0.75)); - y = y * FFX_BROADCAST_FLOAT16(2.0) - FFX_BROADCAST_FLOAT16(1.0); - return ffxParabolicSinHalf(FfxFloat16x2(x, y)); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneAndHalf(FfxUInt16 x, FfxUInt16 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneAndHalf(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneAndHalf(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneAndHalf(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return min(x, y); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneNotHalf(FfxUInt16 x) -{ - return x ^ FFX_BROADCAST_UINT16(1); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneNotHalf(FfxUInt16x2 x) -{ - return x ^ FFX_BROADCAST_UINT16X2(1); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneNotHalf(FfxUInt16x3 x) -{ - return x ^ FFX_BROADCAST_UINT16X3(1); -} - -/// Conditional free logic NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the NOT operator. -/// @param [in] y The second value to be fed into the NOT operator. -/// -/// @returns -/// Result of the NOT operation. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneNotHalf(FfxUInt16x4 x) -{ - return x ^ FFX_BROADCAST_UINT16X4(1); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneOrHalf(FfxUInt16 x, FfxUInt16 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneOrHalf(FfxUInt16x2 x, FfxUInt16x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneOrHalf(FfxUInt16x3 x, FfxUInt16x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneOrHalf(FfxUInt16x4 x, FfxUInt16x4 y) -{ - return max(x, y); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16 ffxZeroOneFloat16ToUint16(FfxFloat16 x) -{ - return FFX_TO_UINT16(x * FFX_TO_FLOAT16(FFX_TO_UINT16(1))); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxZeroOneFloat16x2ToUint16x2(FfxFloat16x2 x) -{ - return FFX_TO_UINT16X2(x * FFX_TO_FLOAT16X2(FfxUInt16x2(1, 1))); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16x3 ffxZeroOneFloat16x3ToUint16x3(FfxFloat16x3 x) -{ - return FFX_TO_UINT16X3(x * FFX_TO_FLOAT16X3(FfxUInt16x3(1, 1, 1))); -} - -/// Convert a half-precision FfxFloat32 value between 0.0f and 1.0f to a half-precision Uint. -/// -/// @param [in] x The value to converted to a Uint. -/// -/// @returns -/// The converted Uint value. -/// -/// @ingroup GPUCore -FfxUInt16x4 ffxZeroOneFloat16x4ToUint16x4(FfxFloat16x4 x) -{ - return FFX_TO_UINT16X4(x * FFX_TO_FLOAT16X4(FfxUInt16x4(1, 1, 1, 1))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneUint16ToFloat16(FfxUInt16 x) -{ - return FFX_TO_FLOAT16(x * FFX_TO_UINT16(FFX_TO_FLOAT16(1.0))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneUint16x2ToFloat16x2(FfxUInt16x2 x) -{ - return FFX_TO_FLOAT16X2(x * FFX_TO_UINT16X2(FfxUInt16x2(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneUint16x3ToFloat16x3(FfxUInt16x3 x) -{ - return FFX_TO_FLOAT16X3(x * FFX_TO_UINT16X3(FfxUInt16x3(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); -} - -/// Convert a half-precision FfxUInt32 value between 0 and 1 to a half-precision FfxFloat32. -/// -/// @param [in] x The value to converted to a half-precision FfxFloat32. -/// -/// @returns -/// The converted half-precision FfxFloat32 value. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneUint16x4ToFloat16x4(FfxUInt16x4 x) -{ - return FFX_TO_FLOAT16X4(x * FFX_TO_UINT16X4(FfxUInt16x4(FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0), FFX_TO_FLOAT16(1.0)))); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneAndHalf(FfxFloat16 x, FfxFloat16 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneAndHalf(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneAndHalf(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return min(x, y); -} - -/// Conditional free logic AND operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// -/// @returns -/// Result of the AND operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneAndHalf(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return min(x, y); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSignedZeroOneAndOrHalf(FfxFloat16 x, FfxFloat16 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16(1.0); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSignedZeroOneAndOrHalf(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16X2(1.0); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSignedZeroOneAndOrHalf(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16X3(1.0); -} - -/// Conditional free logic AND NOT operation using two half-precision values. -/// -/// @param [in] x The first value to be fed into the AND NOT operator. -/// @param [in] y The second value to be fed into the AND NOT operator. -/// -/// @returns -/// Result of the AND NOT operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxSignedZeroOneAndOrHalf(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return (-x) * y + FFX_BROADCAST_FLOAT16X4(1.0); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneAndOrHalf(FfxFloat16 x, FfxFloat16 y, FfxFloat16 z) -{ - return FfxFloat16(ffxSaturate(x * y + z)); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneAndOrHalf(FfxFloat16x2 x, FfxFloat16x2 y, FfxFloat16x2 z) -{ - return FfxFloat16x2(ffxSaturate(x * y + z)); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneAndOrHalf(FfxFloat16x3 x, FfxFloat16x3 y, FfxFloat16x3 z) -{ - return FfxFloat16x3(ffxSaturate(x * y + z)); -} - -/// Conditional free logic AND operation using two half-precision values followed by -/// a NOT operation using the resulting value and a third half-precision value. -/// -/// @param [in] x The first value to be fed into the AND operator. -/// @param [in] y The second value to be fed into the AND operator. -/// @param [in] z The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneAndOrHalf(FfxFloat16x4 x, FfxFloat16x4 y, FfxFloat16x4 z) -{ - return FfxFloat16x4(ffxSaturate(x * y + z)); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16 x) -{ - return FfxFloat16(ffxSaturate(x * FFX_BROADCAST_FLOAT16(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x2 x) -{ - return FfxFloat16x2(ffxSaturate(x * FFX_BROADCAST_FLOAT16X2(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x3 x) -{ - return FfxFloat16x3(ffxSaturate(x * FFX_BROADCAST_FLOAT16X3(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if greater than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the greater than zero comparison. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneIsGreaterThanZeroHalf(FfxFloat16x4 x) -{ - return FfxFloat16x4(ffxSaturate(x * FFX_BROADCAST_FLOAT16X4(FFX_POSITIVE_INFINITY_HALF))); -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneNotHalf(FfxFloat16 x) -{ - return FFX_BROADCAST_FLOAT16(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneNotHalf(FfxFloat16x2 x) -{ - return FFX_BROADCAST_FLOAT16X2(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneNotHalf(FfxFloat16x3 x) -{ - return FFX_BROADCAST_FLOAT16X3(1.0) - x; -} - -/// Conditional free logic signed NOT operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the AND OR operator. -/// -/// @returns -/// Result of the AND OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneNotHalf(FfxFloat16x4 x) -{ - return FFX_BROADCAST_FLOAT16X4(1.0) - x; -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneOrHalf(FfxFloat16 x, FfxFloat16 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneOrHalf(FfxFloat16x2 x, FfxFloat16x2 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneOrHalf(FfxFloat16x3 x, FfxFloat16x3 y) -{ - return max(x, y); -} - -/// Conditional free logic OR operation using two half-precision FfxFloat32 values. -/// -/// @param [in] x The first value to be fed into the OR operator. -/// @param [in] y The second value to be fed into the OR operator. -/// -/// @returns -/// Result of the OR operation. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneOrHalf(FfxFloat16x4 x, FfxFloat16x4 y) -{ - return max(x, y); -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneSelectHalf(FfxFloat16 x, FfxFloat16 y, FfxFloat16 z) -{ - FfxFloat16 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneSelectHalf(FfxFloat16x2 x, FfxFloat16x2 y, FfxFloat16x2 z) -{ - FfxFloat16x2 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneSelectHalf(FfxFloat16x3 x, FfxFloat16x3 y, FfxFloat16x3 z) -{ - FfxFloat16x3 r = (-x) * z + z; - return x * y + r; -} - -/// Choose between two half-precision FfxFloat32 values if the first paramter is greater than zero. -/// -/// @param [in] x The value to compare against zero. -/// @param [in] y The value to return if the comparision is greater than zero. -/// @param [in] z The value to return if the comparision is less than or equal to zero. -/// -/// @returns -/// The selected value. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneSelectHalf(FfxFloat16x4 x, FfxFloat16x4 y, FfxFloat16x4 z) -{ - FfxFloat16x4 r = (-x) * z + z; - return x * y + r; -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxZeroOneIsSignedHalf(FfxFloat16 x) -{ - return FfxFloat16(ffxSaturate(x * FFX_BROADCAST_FLOAT16(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxZeroOneIsSignedHalf(FfxFloat16x2 x) -{ - return FfxFloat16x2(ffxSaturate(x * FFX_BROADCAST_FLOAT16X2(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxZeroOneIsSignedHalf(FfxFloat16x3 x) -{ - return FfxFloat16x3(ffxSaturate(x * FFX_BROADCAST_FLOAT16X3(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Given a half-precision value, returns 1.0 if less than zero and 0.0 if not. -/// -/// @param [in] x The value to be compared. -/// -/// @returns -/// Result of the sign value. -/// -/// @ingroup GPUCore -FfxFloat16x4 ffxZeroOneIsSignedHalf(FfxFloat16x4 x) -{ - return FfxFloat16x4(ffxSaturate(x * FFX_BROADCAST_FLOAT16X4(FFX_NEGATIVE_INFINITY_HALF))); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] c The color to convert to Rec. 709. -/// -/// @returns -/// The color in Rec.709 space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxRec709FromLinearHalf(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); - return clamp(j.x, c * j.y, pow(c, j.z) * k.x + k.y); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] c The color to convert to Rec. 709. -/// -/// @returns -/// The color in Rec.709 space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxRec709FromLinearHalf(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); - return clamp(j.xx, c * j.yy, pow(c, j.zz) * k.xx + k.yy); -} - -/// Compute a Rec.709 color space. -/// -/// Rec.709 is used for some HDTVs. -/// -/// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. -/// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). -/// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). -/// -/// @param [in] c The color to convert to Rec. 709. -/// -/// @returns -/// The color in Rec.709 space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxRec709FromLinearHalf(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.018 * 4.5, 4.5, 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.099, -0.099); - return clamp(j.xxx, c * j.yyy, pow(c, j.zzz) * k.xxx + k.yyy); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. -/// -/// @param [in] c The value to convert to gamma space from linear. -/// @param [in] rcpX The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxGammaFromLinearHalf(FfxFloat16 c, FfxFloat16 rcpX) -{ - return pow(c, FFX_BROADCAST_FLOAT16(rcpX)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. -/// -/// @param [in] c The value to convert to gamma space from linear. -/// @param [in] rcpX The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxGammaFromLinearHalf(FfxFloat16x2 c, FfxFloat16 rcpX) -{ - return pow(c, FFX_BROADCAST_FLOAT16X2(rcpX)); -} - -/// Compute a gamma value from a linear value. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// Note: 'rcpX' is '1/x', where the 'x' is what would be used in ffxLinearFromGammaHalf. -/// -/// @param [in] c The value to convert to gamma space from linear. -/// @param [in] rcpX The reciprocal of power value used for the gamma curve. -/// -/// @returns -/// A value in gamma space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxGammaFromLinearHalf(FfxFloat16x3 c, FfxFloat16 rcpX) -{ - return pow(c, FFX_BROADCAST_FLOAT16X3(rcpX)); -} - -/// Compute an SRGB value from a linear value. -/// -/// @param [in] c The value to convert to SRGB from linear. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSrgbFromLinearHalf(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); - return clamp(j.x, c * j.y, pow(c, j.z) * k.x + k.y); -} - -/// Compute an SRGB value from a linear value. -/// -/// @param [in] c The value to convert to SRGB from linear. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSrgbFromLinearHalf(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); - return clamp(j.xx, c * j.yy, pow(c, j.zz) * k.xx + k.yy); -} - -/// Compute an SRGB value from a linear value. -/// -/// @param [in] c The value to convert to SRGB from linear. -/// -/// @returns -/// A value in SRGB space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSrgbFromLinearHalf(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.055, -0.055); - return clamp(j.xxx, c * j.yyy, pow(c, j.zzz) * k.xxx + k.yyy); -} - -/// Compute the square root of a value. -/// -/// @param [in] c The value to compute the square root for. -/// -/// @returns -/// A square root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxSquareRootHalf(FfxFloat16 c) -{ - return sqrt(c); -} - -/// Compute the square root of a value. -/// -/// @param [in] c The value to compute the square root for. -/// -/// @returns -/// A square root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxSquareRootHalf(FfxFloat16x2 c) -{ - return sqrt(c); -} - -/// Compute the square root of a value. -/// -/// @param [in] c The value to compute the square root for. -/// -/// @returns -/// A square root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxSquareRootHalf(FfxFloat16x3 c) -{ - return sqrt(c); -} - -/// Compute the cube root of a value. -/// -/// @param [in] c The value to compute the cube root for. -/// -/// @returns -/// A cube root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16 ffxCubeRootHalf(FfxFloat16 c) -{ - return pow(c, FFX_BROADCAST_FLOAT16(1.0 / 3.0)); -} - -/// Compute the cube root of a value. -/// -/// @param [in] c The value to compute the cube root for. -/// -/// @returns -/// A cube root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxCubeRootHalf(FfxFloat16x2 c) -{ - return pow(c, FFX_BROADCAST_FLOAT16X2(1.0 / 3.0)); -} - -/// Compute the cube root of a value. -/// -/// @param [in] c The value to compute the cube root for. -/// -/// @returns -/// A cube root of the input value. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxCubeRootHalf(FfxFloat16x3 c) -{ - return pow(c, FFX_BROADCAST_FLOAT16X3(1.0 / 3.0)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] c The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxLinearFromRec709Half(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.x), c * j.y, pow(c * k.x + k.y, j.z)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] c The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxLinearFromRec709Half(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xx), c * j.yy, pow(c * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a REC.709 value. -/// -/// @param [in] c The value to convert to linear from REC.709. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxLinearFromRec709Half(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.081 / 4.5, 1.0 / 4.5, 1.0 / 0.45); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.099, 0.099 / 1.099); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xxx), c * j.yyy, pow(c * k.xxx + k.yyy, j.zzz)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in gamma space. -/// @param [in] x The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxLinearFromGammaHalf(FfxFloat16 c, FfxFloat16 x) -{ - return pow(c, FFX_BROADCAST_FLOAT16(x)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in gamma space. -/// @param [in] x The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxLinearFromGammaHalf(FfxFloat16x2 c, FfxFloat16 x) -{ - return pow(c, FFX_BROADCAST_FLOAT16X2(x)); -} - -/// Compute a linear value from a value in a gamma space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in gamma space. -/// @param [in] x The power value used for the gamma curve. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxLinearFromGammaHalf(FfxFloat16x3 c, FfxFloat16 x) -{ - return pow(c, FFX_BROADCAST_FLOAT16X3(x)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16 ffxLinearFromSrgbHalf(FfxFloat16 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.x), c * j.y, pow(c * k.x + k.y, j.z)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x2 ffxLinearFromSrgbHalf(FfxFloat16x2 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xx), c * j.yy, pow(c * k.xx + k.yy, j.zz)); -} - -/// Compute a linear value from a value in a SRGB space. -/// -/// Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native. -/// -/// @param [in] c The value to convert to linear in SRGB space. -/// -/// @returns -/// A value in linear space. -/// -/// @ingroup GPUCore -FfxFloat16x3 ffxLinearFromSrgbHalf(FfxFloat16x3 c) -{ - FfxFloat16x3 j = FfxFloat16x3(0.04045 / 12.92, 1.0 / 12.92, 2.4); - FfxFloat16x2 k = FfxFloat16x2(1.0 / 1.055, 0.055 / 1.055); - return ffxZeroOneSelectHalf(ffxZeroOneIsSignedHalf(c - j.xxx), c * j.yyy, pow(c * k.xxx + k.yyy, j.zzz)); -} - -/// A remapping of 64x1 to 8x8 imposing rotated 2x2 pixel quads in quad linear. -/// -/// 543210 -/// ====== -/// ..xxx. -/// yy...y -/// -/// @param [in] a The input 1D coordinates to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxRemapForQuadHalf(FfxUInt32 a) -{ - return FfxUInt16x2(bitfieldExtract(a, 1u, 3u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), a, 1u)); -} - -/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. -/// -/// The 64-wide lane indices to 8x8 remapping is performed as follows: -/// -/// 00 01 08 09 10 11 18 19 -/// 02 03 0a 0b 12 13 1a 1b -/// 04 05 0c 0d 14 15 1c 1d -/// 06 07 0e 0f 16 17 1e 1f -/// 20 21 28 29 30 31 38 39 -/// 22 23 2a 2b 32 33 3a 3b -/// 24 25 2c 2d 34 35 3c 3d -/// 26 27 2e 2f 36 37 3e 3f -/// -/// @param [in] a The input 1D coordinate to remap. -/// -/// @returns -/// The remapped 2D coordinates. -/// -/// @ingroup GPUCore -FfxUInt16x2 ffxRemapForWaveReductionHalf(FfxUInt32 a) -{ - return FfxUInt16x2(bitfieldInsertMask(bitfieldExtract(a, 2u, 3u), a, 1u), bitfieldInsertMask(bitfieldExtract(a, 3u, 3u), bitfieldExtract(a, 1u, 2u), 2u)); -} - -#endif // FFX_HALF diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta deleted file mode 100644 index 234aa90..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_gpu_common_half.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 1bdb323791a91a5438ee8e1e63187840 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h deleted file mode 100644 index 337eb06..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h +++ /dev/null @@ -1,1651 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup HLSLCore HLSL Core -/// HLSL core defines and functions -/// -/// @ingroup FfxHLSL - -#define DECLARE_SRV_REGISTER(regIndex) t##regIndex -#define DECLARE_UAV_REGISTER(regIndex) u##regIndex -#define DECLARE_CB_REGISTER(regIndex) b##regIndex -#define FFX_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) -#define FFX_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) -#define FFX_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) - -/// A define for abstracting shared memory between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_GROUPSHARED groupshared - -/// A define for abstracting compute memory barriers between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_GROUP_MEMORY_BARRIER GroupMemoryBarrierWithGroupSync - -/// A define for abstracting compute atomic additions between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_ATOMIC_ADD(x, y) InterlockedAdd(x, y) - -/// A define added to accept static markup on functions to aid CPU/GPU portability of code. -/// -/// @ingroup HLSLCore -#define FFX_STATIC static - -/// A define for abstracting loop unrolling between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_UNROLL [unroll] - -/// A define for abstracting a 'greater than' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_GREATER_THAN(x, y) x > y - -/// A define for abstracting a 'greater than or equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_GREATER_THAN_EQUAL(x, y) x >= y - -/// A define for abstracting a 'less than' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_LESS_THAN(x, y) x < y - -/// A define for abstracting a 'less than or equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_LESS_THAN_EQUAL(x, y) x <= y - -/// A define for abstracting an 'equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_EQUAL(x, y) x == y - -/// A define for abstracting a 'not equal' comparison operator between two types. -/// -/// @ingroup HLSLCore -#define FFX_NOT_EQUAL(x, y) x != y - -/// A define for abstracting matrix multiply operations between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_MATRIX_MULTIPLY(a, b) mul(a, b) - -/// A define for abstracting vector transformations between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_TRANSFORM_VECTOR(a, b) mul(a, b) - -/// A define for abstracting modulo operations between shading languages. -/// -/// @ingroup HLSLCore -#define FFX_MODULO(a, b) (fmod(a, b)) - -/// Broadcast a scalar value to a 1-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 2-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32X2(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 3-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32X3(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 4-dimensional floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_FLOAT32X4(x) FfxFloat32(x) - -/// Broadcast a scalar value to a 1-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 2-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32X2(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32X3(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_UINT32X4(x) FfxUInt32(x) - -/// Broadcast a scalar value to a 1-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32(x) FfxInt32(x) - -/// Broadcast a scalar value to a 2-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32X2(x) FfxInt32(x) - -/// Broadcast a scalar value to a 3-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32X3(x) FfxInt32(x) - -/// Broadcast a scalar value to a 4-dimensional signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_INT32X4(x) FfxInt32(x) - -/// Broadcast a scalar value to a 1-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 2-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16X2(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 3-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16X3(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 4-dimensional half-precision floating point vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_FLOAT16X4(a) FFX_MIN16_F(a) - -/// Broadcast a scalar value to a 1-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 2-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16X2(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 3-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16X3(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 4-dimensional half-precision unsigned integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_UINT16X4(a) FFX_MIN16_U(a) - -/// Broadcast a scalar value to a 1-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16(a) FFX_MIN16_I(a) - -/// Broadcast a scalar value to a 2-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16X2(a) FFX_MIN16_I(a) - -/// Broadcast a scalar value to a 3-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16X3(a) FFX_MIN16_I(a) - -/// Broadcast a scalar value to a 4-dimensional half-precision signed integer vector. -/// -/// @ingroup HLSLCore -#define FFX_BROADCAST_MIN_INT16X4(a) FFX_MIN16_I(a) - -/// Pack 2x32-bit floating point values in a single 32bit value. -/// -/// This function first converts each component of value into their nearest 16-bit floating -/// point representation, and then stores the X and Y components in the lower and upper 16 bits of the -/// 32bit unsigned integer respectively. -/// -/// @param [in] value A 2-dimensional floating point value to convert and pack. -/// -/// @returns -/// A packed 32bit value containing 2 16bit floating point values. -/// -/// @ingroup HLSLCore -FfxUInt32 packHalf2x16(FfxFloat32x2 value) -{ - return f32tof16(value.x) | (f32tof16(value.y) << 16); -} - -/// Broadcast a scalar value to a 2-dimensional floating point vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 2-dimensional floating point vector with value in each component. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxBroadcast2(FfxFloat32 value) -{ - return FfxFloat32x2(value, value); -} - -/// Broadcast a scalar value to a 3-dimensional floating point vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 3-dimensional floating point vector with value in each component. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxBroadcast3(FfxFloat32 value) -{ - return FfxFloat32x3(value, value, value); -} - -/// Broadcast a scalar value to a 4-dimensional floating point vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 4-dimensional floating point vector with value in each component. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxBroadcast4(FfxFloat32 value) -{ - return FfxFloat32x4(value, value, value, value); -} - -/// Broadcast a scalar value to a 2-dimensional signed integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 2-dimensional signed integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxInt32x2 ffxBroadcast2(FfxInt32 value) -{ - return FfxInt32x2(value, value); -} - -/// Broadcast a scalar value to a 3-dimensional signed integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 3-dimensional signed integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxBroadcast3(FfxInt32 value) -{ - return FfxUInt32x3(value, value, value); -} - -/// Broadcast a scalar value to a 4-dimensional signed integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 4-dimensional signed integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxInt32x4 ffxBroadcast4(FfxInt32 value) -{ - return FfxInt32x4(value, value, value, value); -} - -/// Broadcast a scalar value to a 2-dimensional unsigned integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 2-dimensional unsigned integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxBroadcast2(FfxUInt32 value) -{ - return FfxUInt32x2(value, value); -} - -/// Broadcast a scalar value to a 3-dimensional unsigned integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 3-dimensional unsigned integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxBroadcast3(FfxUInt32 value) -{ - return FfxUInt32x3(value, value, value); -} - -/// Broadcast a scalar value to a 4-dimensional unsigned integer vector. -/// -/// @param [in] value The value to to broadcast. -/// -/// @returns -/// A 4-dimensional unsigned integer vector with value in each component. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxBroadcast4(FfxUInt32 value) -{ - return FfxUInt32x4(value, value, value, value); -} - -FfxUInt32 bitfieldExtract(FfxUInt32 src, FfxUInt32 off, FfxUInt32 bits) -{ - FfxUInt32 mask = (1u << bits) - 1; - return (src >> off) & mask; -} - -FfxUInt32 bitfieldInsert(FfxUInt32 src, FfxUInt32 ins, FfxUInt32 mask) -{ - return (ins & mask) | (src & (~mask)); -} - -FfxUInt32 bitfieldInsertMask(FfxUInt32 src, FfxUInt32 ins, FfxUInt32 bits) -{ - FfxUInt32 mask = (1u << bits) - 1; - return (ins & mask) | (src & (~mask)); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32 ffxAsUInt32(FfxFloat32 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxAsUInt32(FfxFloat32x2 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxAsUInt32(FfxFloat32x3 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as an unsigned integer. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as an unsigned integer. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxAsUInt32(FfxFloat32x4 x) -{ - return asuint(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxAsFloat(FfxUInt32 x) -{ - return asfloat(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxAsFloat(FfxUInt32x2 x) -{ - return asfloat(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxAsFloat(FfxUInt32x3 x) -{ - return asfloat(x); -} - -/// Interprets the bit pattern of x as a floating-point number. -/// -/// @param [in] x The input value. -/// -/// @returns -/// The input interpreted as a floating-point number. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxAsFloat(FfxUInt32x4 x) -{ - return asfloat(x); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxLerp(FfxFloat32 x, FfxFloat32 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxLerp(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxLerp(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxLerp(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxLerp(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxLerp(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32 t) -{ - return lerp(x, y, t); -} - -/// Compute the linear interopation between two values. -/// -/// Implemented by calling the HLSL mix instrinsic function. Implements the -/// following math: -/// -/// (1 - t) * x + t * y -/// -/// @param [in] x The first value to lerp between. -/// @param [in] y The second value to lerp between. -/// @param [in] t The value to determine how much of x and how much of y. -/// -/// @returns -/// A linearly interpolated value between x and y according to t. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxLerp(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 t) -{ - return lerp(x, y, t); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxSaturate(FfxFloat32 x) -{ - return saturate(x); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxSaturate(FfxFloat32x2 x) -{ - return saturate(x); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxSaturate(FfxFloat32x3 x) -{ - return saturate(x); -} - -/// Clamp a value to a [0..1] range. -/// -/// @param [in] x The value to clamp to [0..1] range. -/// -/// @returns -/// The clamped version of x. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxSaturate(FfxFloat32x4 x) -{ - return saturate(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxFract(FfxFloat32 x) -{ - return x - floor(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxFract(FfxFloat32x2 x) -{ - return x - floor(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxFract(FfxFloat32x3 x) -{ - return x - floor(x); -} - -/// Compute the factional part of a decimal value. -/// -/// This function calculates x - floor(x). Where floor is the intrinsic HLSL function. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. It is -/// worth further noting that this function is intentionally distinct from the HLSL frac intrinsic -/// function. -/// -/// @param [in] x The value to compute the fractional part from. -/// -/// @returns -/// The fractional part of x. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxFract(FfxFloat32x4 x) -{ - return x - floor(x); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxMax3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxMax3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxMax3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxMax3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32 ffxMax3(FfxUInt32 x, FfxUInt32 y, FfxUInt32 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxMax3(FfxUInt32x2 x, FfxUInt32x2 y, FfxUInt32x2 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxMax3(FfxUInt32x3 x, FfxUInt32x3 y, FfxUInt32x3 z) -{ - return max(x, max(y, z)); -} - -/// Compute the maximum of three values. -/// -/// NOTE: This function should compile down to a single V_MAX3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the max calculation. -/// @param [in] y The second value to include in the max calcuation. -/// @param [in] z The third value to include in the max calcuation. -/// -/// @returns -/// The maximum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxMax3(FfxUInt32x4 x, FfxUInt32x4 y, FfxUInt32x4 z) -{ - return max(x, max(y, z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxMed3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxMed3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxMed3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxMed3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32 ffxMed3(FfxInt32 x, FfxInt32 y, FfxInt32 z) -{ - return max(min(x, y), min(max(x, y), z)); - // return min(max(min(y, z), x), max(y, z)); - // return max(max(x, y), z) == x ? max(y, z) : (max(max(x, y), z) == y ? max(x, z) : max(x, y)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32x2 ffxMed3(FfxInt32x2 x, FfxInt32x2 y, FfxInt32x2 z) -{ - return max(min(x, y), min(max(x, y), z)); - // return min(max(min(y, z), x), max(y, z)); - // return max(max(x, y), z) == x ? max(y, z) : (max(max(x, y), z) == y ? max(x, z) : max(x, y)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32x3 ffxMed3(FfxInt32x3 x, FfxInt32x3 y, FfxInt32x3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the median of three values. -/// -/// NOTE: This function should compile down to a single V_MED3_I32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the median calculation. -/// @param [in] y The second value to include in the median calcuation. -/// @param [in] z The third value to include in the median calcuation. -/// -/// @returns -/// The median value of x, y, and z. -/// -/// @ingroup HLSL -FfxInt32x4 ffxMed3(FfxInt32x4 x, FfxInt32x4 y, FfxInt32x4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32 ffxMin3(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x2 ffxMin3(FfxFloat32x2 x, FfxFloat32x2 y, FfxFloat32x2 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x3 ffxMin3(FfxFloat32x3 x, FfxFloat32x3 y, FfxFloat32x3 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxFloat32x4 ffxMin3(FfxFloat32x4 x, FfxFloat32x4 y, FfxFloat32x4 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32 ffxMin3(FfxUInt32 x, FfxUInt32 y, FfxUInt32 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x2 ffxMin3(FfxUInt32x2 x, FfxUInt32x2 y, FfxUInt32x2 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calculation. -/// @param [in] z The third value to include in the min calculation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x3 ffxMin3(FfxUInt32x3 x, FfxUInt32x3 y, FfxUInt32x3 z) -{ - return min(x, min(y, z)); -} - -/// Compute the minimum of three values. -/// -/// NOTE: This function should compile down to a single V_MIN3_F32 operation on GCN/RDNA hardware. -/// -/// @param [in] x The first value to include in the min calculation. -/// @param [in] y The second value to include in the min calcuation. -/// @param [in] z The third value to include in the min calcuation. -/// -/// @returns -/// The minimum value of x, y, and z. -/// -/// @ingroup HLSLCore -FfxUInt32x4 ffxMin3(FfxUInt32x4 x, FfxUInt32x4 y, FfxUInt32x4 z) -{ - return min(x, min(y, z)); -} - - -FfxUInt32 AShrSU1(FfxUInt32 a, FfxUInt32 b) -{ - return FfxUInt32(FfxInt32(a) >> FfxInt32(b)); -} - -FfxUInt32 ffxPackF32(FfxFloat32x2 v){ - FfxUInt32x2 p = FfxUInt32x2(f32tof16(FfxFloat32x2(v).x), f32tof16(FfxFloat32x2(v).y)); - return p.x | (p.y << 16); -} - -FfxFloat32x2 ffxUnpackF32(FfxUInt32 a){ - return f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16)); -} - -//============================================================================================================================== -// HLSL HALF -//============================================================================================================================== -//============================================================================================================================== -// Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). -// Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ -FFX_MIN16_F2 ffxUint32ToFloat16x2(FfxUInt32 x) -{ - FfxFloat32x2 t = f16tof32(FfxUInt32x2(x & 0xFFFF, x >> 16)); - return FFX_MIN16_F2(t); -} -FFX_MIN16_F4 ffxUint32x2ToFloat16x4(FfxUInt32x2 x) -{ - return FFX_MIN16_F4(ffxUint32ToFloat16x2(x.x), ffxUint32ToFloat16x2(x.y)); -} -FFX_MIN16_U2 ffxUint32ToUint16x2(FfxUInt32 x) -{ - FfxUInt32x2 t = FfxUInt32x2(x & 0xFFFF, x >> 16); - return FFX_MIN16_U2(t); -} -FFX_MIN16_U4 ffxUint32x2ToUint16x4(FfxUInt32x2 x) -{ - return FFX_MIN16_U4(ffxUint32ToUint16x2(x.x), ffxUint32ToUint16x2(x.y)); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32 ffxInvertSafe(FfxFloat32 v){ - FfxFloat32 s = sign(v); - FfxFloat32 s2 = s*s; - return s2/(v + s2 - 1.0); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32x2 ffxInvertSafe(FfxFloat32x2 v){ - FfxFloat32x2 s = sign(v); - FfxFloat32x2 s2 = s*s; - return s2/(v + s2 - FfxFloat32x2(1.0, 1.0)); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32x3 ffxInvertSafe(FfxFloat32x3 v){ - FfxFloat32x3 s = sign(v); - FfxFloat32x3 s2 = s*s; - return s2/(v + s2 - FfxFloat32x3(1.0, 1.0, 1.0)); -} - -/// @brief Inverts the value while avoiding division by zero. If the value is zero, zero is returned. -/// @param v Value to invert. -/// @return If v = 0 returns 0. If v != 0 returns 1/v. -FfxFloat32x4 ffxInvertSafe(FfxFloat32x4 v){ - FfxFloat32x4 s = sign(v); - FfxFloat32x4 s2 = s*s; - return s2/(v + s2 - FfxFloat32x4(1.0, 1.0, 1.0, 1.0)); -} - -#define FFX_UINT32_TO_FLOAT16X2(x) ffxUint32ToFloat16x2(FfxUInt32(x)) -#if FFX_HALF - -#define FFX_UINT32X2_TO_FLOAT16X4(x) ffxUint32x2ToFloat16x4(FfxUInt32x2(x)) -#define FFX_UINT32_TO_UINT16X2(x) ffxUint32ToUint16x2(FfxUInt32(x)) -#define FFX_UINT32X2_TO_UINT16X4(x) ffxUint32x2ToUint16x4(FfxUInt32x2(x)) - -FfxUInt32 ffxPackF16(FfxFloat16x2 v){ - FfxUInt32x2 p = FfxUInt32x2(f32tof16(FfxFloat32x2(v).x), f32tof16(FfxFloat32x2(v).y)); - return p.x | (p.y << 16); -} - -FfxFloat16x2 ffxUnpackF16(FfxUInt32 a){ - return FfxFloat16x2(f16tof32(FfxUInt32x2(a & 0xFFFF, a >> 16))); -} - -//------------------------------------------------------------------------------------------------------------------------------ -FfxUInt32 FFX_MIN16_F2ToUint32(FFX_MIN16_F2 x) -{ - return f32tof16(x.x) + (f32tof16(x.y) << 16); -} -FfxUInt32x2 FFX_MIN16_F4ToUint32x2(FFX_MIN16_F4 x) -{ - return FfxUInt32x2(FFX_MIN16_F2ToUint32(x.xy), FFX_MIN16_F2ToUint32(x.zw)); -} -FfxUInt32 FFX_MIN16_U2ToUint32(FFX_MIN16_U2 x) -{ - return FfxUInt32(x.x) + (FfxUInt32(x.y) << 16); -} -FfxUInt32x2 FFX_MIN16_U4ToUint32x2(FFX_MIN16_U4 x) -{ - return FfxUInt32x2(FFX_MIN16_U2ToUint32(x.xy), FFX_MIN16_U2ToUint32(x.zw)); -} -#define FFX_FLOAT16X2_TO_UINT32(x) FFX_MIN16_F2ToUint32(FFX_MIN16_F2(x)) -#define FFX_FLOAT16X4_TO_UINT32X2(x) FFX_MIN16_F4ToUint32x2(FFX_MIN16_F4(x)) -#define FFX_UINT16X2_TO_UINT32(x) FFX_MIN16_U2ToUint32(FFX_MIN16_U2(x)) -#define FFX_UINT16X4_TO_UINT32X2(x) FFX_MIN16_U4ToUint32x2(FFX_MIN16_U4(x)) - -#if (FFX_HLSL_SM >= 62) && !defined(FFX_NO_16_BIT_CAST) -#define FFX_TO_UINT16(x) asuint16(x) -#define FFX_TO_UINT16X2(x) asuint16(x) -#define FFX_TO_UINT16X3(x) asuint16(x) -#define FFX_TO_UINT16X4(x) asuint16(x) -#else -#define FFX_TO_UINT16(a) FFX_MIN16_U(f32tof16(FfxFloat32(a))) -#define FFX_TO_UINT16X2(a) FFX_MIN16_U2(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y)) -#define FFX_TO_UINT16X3(a) FFX_MIN16_U3(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y), FFX_TO_UINT16((a).z)) -#define FFX_TO_UINT16X4(a) FFX_MIN16_U4(FFX_TO_UINT16((a).x), FFX_TO_UINT16((a).y), FFX_TO_UINT16((a).z), FFX_TO_UINT16((a).w)) -#endif // #if (FFX_HLSL_SM>=62) && !defined(FFX_NO_16_BIT_CAST) - -#if (FFX_HLSL_SM >= 62) && !defined(FFX_NO_16_BIT_CAST) -#define FFX_TO_FLOAT16(x) asfloat16(x) -#define FFX_TO_FLOAT16X2(x) asfloat16(x) -#define FFX_TO_FLOAT16X3(x) asfloat16(x) -#define FFX_TO_FLOAT16X4(x) asfloat16(x) -#else -#define FFX_TO_FLOAT16(a) FFX_MIN16_F(f16tof32(FfxUInt32(a))) -#define FFX_TO_FLOAT16X2(a) FFX_MIN16_F2(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y)) -#define FFX_TO_FLOAT16X3(a) FFX_MIN16_F3(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y), FFX_TO_FLOAT16((a).z)) -#define FFX_TO_FLOAT16X4(a) FFX_MIN16_F4(FFX_TO_FLOAT16((a).x), FFX_TO_FLOAT16((a).y), FFX_TO_FLOAT16((a).z), FFX_TO_FLOAT16((a).w)) -#endif // #if (FFX_HLSL_SM>=62) && !defined(FFX_NO_16_BIT_CAST) - -//============================================================================================================================== -#define FFX_BROADCAST_FLOAT16(a) FFX_MIN16_F(a) -#define FFX_BROADCAST_FLOAT16X2(a) FFX_MIN16_F(a) -#define FFX_BROADCAST_FLOAT16X3(a) FFX_MIN16_F(a) -#define FFX_BROADCAST_FLOAT16X4(a) FFX_MIN16_F(a) - -//------------------------------------------------------------------------------------------------------------------------------ -#define FFX_BROADCAST_INT16(a) FFX_MIN16_I(a) -#define FFX_BROADCAST_INT16X2(a) FFX_MIN16_I(a) -#define FFX_BROADCAST_INT16X3(a) FFX_MIN16_I(a) -#define FFX_BROADCAST_INT16X4(a) FFX_MIN16_I(a) - -//------------------------------------------------------------------------------------------------------------------------------ -#define FFX_BROADCAST_UINT16(a) FFX_MIN16_U(a) -#define FFX_BROADCAST_UINT16X2(a) FFX_MIN16_U(a) -#define FFX_BROADCAST_UINT16X3(a) FFX_MIN16_U(a) -#define FFX_BROADCAST_UINT16X4(a) FFX_MIN16_U(a) - -//============================================================================================================================== -FFX_MIN16_U ffxAbsHalf(FFX_MIN16_U a) -{ - return FFX_MIN16_U(abs(FFX_MIN16_I(a))); -} -FFX_MIN16_U2 ffxAbsHalf(FFX_MIN16_U2 a) -{ - return FFX_MIN16_U2(abs(FFX_MIN16_I2(a))); -} -FFX_MIN16_U3 ffxAbsHalf(FFX_MIN16_U3 a) -{ - return FFX_MIN16_U3(abs(FFX_MIN16_I3(a))); -} -FFX_MIN16_U4 ffxAbsHalf(FFX_MIN16_U4 a) -{ - return FFX_MIN16_U4(abs(FFX_MIN16_I4(a))); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxClampHalf(FFX_MIN16_F x, FFX_MIN16_F n, FFX_MIN16_F m) -{ - return max(n, min(x, m)); -} -FFX_MIN16_F2 ffxClampHalf(FFX_MIN16_F2 x, FFX_MIN16_F2 n, FFX_MIN16_F2 m) -{ - return max(n, min(x, m)); -} -FFX_MIN16_F3 ffxClampHalf(FFX_MIN16_F3 x, FFX_MIN16_F3 n, FFX_MIN16_F3 m) -{ - return max(n, min(x, m)); -} -FFX_MIN16_F4 ffxClampHalf(FFX_MIN16_F4 x, FFX_MIN16_F4 n, FFX_MIN16_F4 m) -{ - return max(n, min(x, m)); -} -//------------------------------------------------------------------------------------------------------------------------------ -// V_FRACT_F16 (note DX frac() is different). -FFX_MIN16_F ffxFract(FFX_MIN16_F x) -{ - return x - floor(x); -} -FFX_MIN16_F2 ffxFract(FFX_MIN16_F2 x) -{ - return x - floor(x); -} -FFX_MIN16_F3 ffxFract(FFX_MIN16_F3 x) -{ - return x - floor(x); -} -FFX_MIN16_F4 ffxFract(FFX_MIN16_F4 x) -{ - return x - floor(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxLerp(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F2 ffxLerp(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F2 ffxLerp(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F3 ffxLerp(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F3 ffxLerp(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F4 ffxLerp(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F a) -{ - return lerp(x, y, a); -} -FFX_MIN16_F4 ffxLerp(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 a) -{ - return lerp(x, y, a); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxMax3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) -{ - return max(x, max(y, z)); -} -FFX_MIN16_F2 ffxMax3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) -{ - return max(x, max(y, z)); -} -FFX_MIN16_F3 ffxMax3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) -{ - return max(x, max(y, z)); -} -FFX_MIN16_F4 ffxMax3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) -{ - return max(x, max(y, z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxMin3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) -{ - return min(x, min(y, z)); -} -FFX_MIN16_F2 ffxMin3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) -{ - return min(x, min(y, z)); -} -FFX_MIN16_F3 ffxMin3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) -{ - return min(x, min(y, z)); -} -FFX_MIN16_F4 ffxMin3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) -{ - return min(x, min(y, z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxMed3Half(FFX_MIN16_F x, FFX_MIN16_F y, FFX_MIN16_F z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_F2 ffxMed3Half(FFX_MIN16_F2 x, FFX_MIN16_F2 y, FFX_MIN16_F2 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_F3 ffxMed3Half(FFX_MIN16_F3 x, FFX_MIN16_F3 y, FFX_MIN16_F3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_F4 ffxMed3Half(FFX_MIN16_F4 x, FFX_MIN16_F4 y, FFX_MIN16_F4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_I ffxMed3Half(FFX_MIN16_I x, FFX_MIN16_I y, FFX_MIN16_I z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_I2 ffxMed3Half(FFX_MIN16_I2 x, FFX_MIN16_I2 y, FFX_MIN16_I2 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_I3 ffxMed3Half(FFX_MIN16_I3 x, FFX_MIN16_I3 y, FFX_MIN16_I3 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -FFX_MIN16_I4 ffxMed3Half(FFX_MIN16_I4 x, FFX_MIN16_I4 y, FFX_MIN16_I4 z) -{ - return max(min(x, y), min(max(x, y), z)); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxReciprocalHalf(FFX_MIN16_F x) -{ - return rcp(x); -} -FFX_MIN16_F2 ffxReciprocalHalf(FFX_MIN16_F2 x) -{ - return rcp(x); -} -FFX_MIN16_F3 ffxReciprocalHalf(FFX_MIN16_F3 x) -{ - return rcp(x); -} -FFX_MIN16_F4 ffxReciprocalHalf(FFX_MIN16_F4 x) -{ - return rcp(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxReciprocalSquareRootHalf(FFX_MIN16_F x) -{ - return rsqrt(x); -} -FFX_MIN16_F2 ffxReciprocalSquareRootHalf(FFX_MIN16_F2 x) -{ - return rsqrt(x); -} -FFX_MIN16_F3 ffxReciprocalSquareRootHalf(FFX_MIN16_F3 x) -{ - return rsqrt(x); -} -FFX_MIN16_F4 ffxReciprocalSquareRootHalf(FFX_MIN16_F4 x) -{ - return rsqrt(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_F ffxSaturate(FFX_MIN16_F x) -{ - return saturate(x); -} -FFX_MIN16_F2 ffxSaturate(FFX_MIN16_F2 x) -{ - return saturate(x); -} -FFX_MIN16_F3 ffxSaturate(FFX_MIN16_F3 x) -{ - return saturate(x); -} -FFX_MIN16_F4 ffxSaturate(FFX_MIN16_F4 x) -{ - return saturate(x); -} -//------------------------------------------------------------------------------------------------------------------------------ -FFX_MIN16_U ffxBitShiftRightHalf(FFX_MIN16_U a, FFX_MIN16_U b) -{ - return FFX_MIN16_U(FFX_MIN16_I(a) >> FFX_MIN16_I(b)); -} -FFX_MIN16_U2 ffxBitShiftRightHalf(FFX_MIN16_U2 a, FFX_MIN16_U2 b) -{ - return FFX_MIN16_U2(FFX_MIN16_I2(a) >> FFX_MIN16_I2(b)); -} -FFX_MIN16_U3 ffxBitShiftRightHalf(FFX_MIN16_U3 a, FFX_MIN16_U3 b) -{ - return FFX_MIN16_U3(FFX_MIN16_I3(a) >> FFX_MIN16_I3(b)); -} -FFX_MIN16_U4 ffxBitShiftRightHalf(FFX_MIN16_U4 a, FFX_MIN16_U4 b) -{ - return FFX_MIN16_U4(FFX_MIN16_I4(a) >> FFX_MIN16_I4(b)); -} -#endif // FFX_HALF - -//============================================================================================================================== -// HLSL WAVE -//============================================================================================================================== -#if defined(FFX_WAVE) -// Where 'x' must be a compile time literal. -FfxFloat32 AWaveXorF1(FfxFloat32 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxFloat32x2 AWaveXorF2(FfxFloat32x2 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxFloat32x3 AWaveXorF3(FfxFloat32x3 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxFloat32x4 AWaveXorF4(FfxFloat32x4 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32 AWaveXorU1(FfxUInt32 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32x2 AWaveXorU1(FfxUInt32x2 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32x3 AWaveXorU1(FfxUInt32x3 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxUInt32x4 AWaveXorU1(FfxUInt32x4 v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, WaveGetLaneIndex() ^ x); -} -FfxBoolean AWaveIsFirstLane() -{ - return WaveIsFirstLane(); -} -FfxUInt32 AWaveLaneIndex() -{ - return WaveGetLaneIndex(); -} -FfxBoolean AWaveReadAtLaneIndexB1(FfxBoolean v, FfxUInt32 x) -{ - return WaveReadLaneAt(v, x); -} -FfxUInt32 AWavePrefixCountBits(FfxBoolean v) -{ - return WavePrefixCountBits(v); -} -FfxUInt32 AWaveActiveCountBits(FfxBoolean v) -{ - return WaveActiveCountBits(v); -} -FfxUInt32 AWaveReadLaneFirstU1(FfxUInt32 v) -{ - return WaveReadLaneFirst(v); -} -FfxUInt32 WaveOr(FfxUInt32 a) -{ - return WaveActiveBitOr(a); -} -FfxFloat32 WaveMin(FfxFloat32 a) -{ - return WaveActiveMin(a); -} -FfxFloat32 WaveMax(FfxFloat32 a) -{ - return WaveActiveMax(a); -} -FfxUInt32 WaveLaneCount() -{ - return WaveGetLaneCount(); -} -FfxBoolean WaveAllTrue(FfxBoolean v) -{ - return WaveActiveAllTrue(v); -} -FfxFloat32 QuadReadX(FfxFloat32 v) -{ - return QuadReadAcrossX(v); -} -FfxFloat32x2 QuadReadX(FfxFloat32x2 v) -{ - return QuadReadAcrossX(v); -} -FfxFloat32 QuadReadY(FfxFloat32 v) -{ - return QuadReadAcrossY(v); -} -FfxFloat32x2 QuadReadY(FfxFloat32x2 v) -{ - return QuadReadAcrossY(v); -} - -#if FFX_HALF -FfxFloat16x2 ffxWaveXorFloat16x2(FfxFloat16x2 v, FfxUInt32 x) -{ - return FFX_UINT32_TO_FLOAT16X2(WaveReadLaneAt(FFX_FLOAT16X2_TO_UINT32(v), WaveGetLaneIndex() ^ x)); -} -FfxFloat16x4 ffxWaveXorFloat16x4(FfxFloat16x4 v, FfxUInt32 x) -{ - return FFX_UINT32X2_TO_FLOAT16X4(WaveReadLaneAt(FFX_FLOAT16X4_TO_UINT32X2(v), WaveGetLaneIndex() ^ x)); -} -FfxUInt16x2 ffxWaveXorUint16x2(FfxUInt16x2 v, FfxUInt32 x) -{ - return FFX_UINT32_TO_UINT16X2(WaveReadLaneAt(FFX_UINT16X2_TO_UINT32(v), WaveGetLaneIndex() ^ x)); -} -FfxUInt16x4 ffxWaveXorUint16x4(FfxUInt16x4 v, FfxUInt32 x) -{ - return FFX_UINT32X2_TO_UINT16X4(WaveReadLaneAt(FFX_UINT16X4_TO_UINT32X2(v), WaveGetLaneIndex() ^ x)); -} -#endif // FFX_HALF -#endif // #if defined(FFX_WAVE) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta deleted file mode 100644 index 2aa0691..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_hlsl.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 299f67e8b7e1d1a48a577bf8b328ac92 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h deleted file mode 100644 index 84a62d6..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h +++ /dev/null @@ -1,51 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -FfxFloat32x3 opAAddOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d = a + ffxBroadcast3(b); - return d; -} - -FfxFloat32x3 opACpyF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d = a; - return d; -} - -FfxFloat32x3 opAMulF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32x3 b) -{ - d = a * b; - return d; -} - -FfxFloat32x3 opAMulOneF3(FfxFloat32x3 d, FfxFloat32x3 a, FfxFloat32 b) -{ - d = a * ffxBroadcast3(b); - return d; -} - -FfxFloat32x3 opARcpF3(FfxFloat32x3 d, FfxFloat32x3 a) -{ - d = rcp(a); - return d; -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta deleted file mode 100644 index b333bf0..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_core_portability.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 8d2ace0bd52e0e1438e08ddaccd3ba24 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h deleted file mode 100644 index c425de7..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h +++ /dev/null @@ -1,288 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_ACCUMULATE_H -#define FFX_FSR3UPSCALER_ACCUMULATE_H - -FfxFloat32 GetPxHrVelocity(FfxFloat32x2 fMotionVector) -{ - return length(fMotionVector * DisplaySize()); -} -#if FFX_HALF -FFX_MIN16_F GetPxHrVelocity(FFX_MIN16_F2 fMotionVector) -{ - return length(fMotionVector * FFX_MIN16_F2(DisplaySize())); -} -#endif - -void Accumulate(const AccumulationPassCommonParams params, FFX_PARAMETER_INOUT FfxFloat32x3 fHistoryColor, FfxFloat32x3 fAccumulation, FFX_PARAMETER_IN FfxFloat32x4 fUpsampledColorAndWeight) -{ - // Aviod invalid values when accumulation and upsampled weight is 0 - fAccumulation = ffxMax(FSR3UPSCALER_EPSILON.xxx, fAccumulation + fUpsampledColorAndWeight.www); - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - //YCoCg -> RGB -> Tonemap -> YCoCg (Use RGB tonemapper to avoid color desaturation) - fUpsampledColorAndWeight.xyz = RGBToYCoCg(Tonemap(YCoCgToRGB(fUpsampledColorAndWeight.xyz))); - fHistoryColor = RGBToYCoCg(Tonemap(YCoCgToRGB(fHistoryColor))); -#endif - - const FfxFloat32x3 fAlpha = fUpsampledColorAndWeight.www / fAccumulation; - fHistoryColor = ffxLerp(fHistoryColor, fUpsampledColorAndWeight.xyz, fAlpha); - - fHistoryColor = YCoCgToRGB(fHistoryColor); - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - fHistoryColor = InverseTonemap(fHistoryColor); -#endif -} - -void RectifyHistory( - const AccumulationPassCommonParams params, - RectificationBox clippingBox, - FFX_PARAMETER_INOUT FfxFloat32x3 fHistoryColor, - FFX_PARAMETER_INOUT FfxFloat32x3 fAccumulation, - FfxFloat32 fLockContributionThisFrame, - FfxFloat32 fTemporalReactiveFactor, - FfxFloat32 fLumaInstabilityFactor) -{ - const FfxFloat32 fVecolityFactor = ffxSaturate(params.fHrVelocity / 20.0f); - const FfxFloat32 fBoxScaleT = ffxMax(params.fDepthClipFactor, ffxMax(params.fAccumulationMask, fVecolityFactor)); - const FfxFloat32 fBoxScale = ffxLerp(3.0f, 1.0f, fBoxScaleT); - - const FfxFloat32x3 fScaledBoxVec = clippingBox.boxVec * fBoxScale; - const FfxFloat32x3 boxMin = clippingBox.boxCenter - fScaledBoxVec; - const FfxFloat32x3 boxMax = clippingBox.boxCenter + fScaledBoxVec; - - if (any(FFX_GREATER_THAN(boxMin, fHistoryColor)) || any(FFX_GREATER_THAN(fHistoryColor, boxMax))) { - - const FfxFloat32x3 fClampedHistoryColor = clamp(fHistoryColor, boxMin, boxMax); - - FfxFloat32x3 fHistoryContribution = ffxMax(fLumaInstabilityFactor, fLockContributionThisFrame).xxx; - - const FfxFloat32 fReactiveFactor = params.fDilatedReactiveFactor; - const FfxFloat32 fReactiveContribution = 1.0f - ffxPow(fReactiveFactor, 1.0f / 2.0f); - fHistoryContribution *= fReactiveContribution; - - // Scale history color using rectification info, also using accumulation mask to avoid potential invalid color protection - fHistoryColor = ffxLerp(fClampedHistoryColor, fHistoryColor, ffxSaturate(fHistoryContribution)); - - // Scale accumulation using rectification info - const FfxFloat32x3 fAccumulationMin = ffxMin(fAccumulation, FFX_BROADCAST_FLOAT32X3(0.1f)); - fAccumulation = ffxLerp(fAccumulationMin, fAccumulation, ffxSaturate(fHistoryContribution)); - } -} - -void WriteUpscaledOutput(FfxInt32x2 iPxHrPos, FfxFloat32x3 fUpscaledColor) -{ - StoreUpscaledOutput(iPxHrPos, fUpscaledColor); -} - -void FinalizeLockStatus(const AccumulationPassCommonParams params, FfxFloat32x2 fLockStatus, FfxFloat32 fUpsampledWeight) -{ - // we expect similar motion for next frame - // kill lock if that location is outside screen, avoid locks to be clamped to screen borders - FfxFloat32x2 fEstimatedUvNextFrame = params.fHrUv - params.fMotionVector; - if (IsUvInside(fEstimatedUvNextFrame) == false) { - KillLock(fLockStatus); - } - else { - // Decrease lock lifetime - const FfxFloat32 fLifetimeDecreaseLanczosMax = FfxFloat32(JitterSequenceLength()) * FfxFloat32(fAverageLanczosWeightPerFrame); - const FfxFloat32 fLifetimeDecrease = FfxFloat32(fUpsampledWeight / fLifetimeDecreaseLanczosMax); - fLockStatus[LOCK_LIFETIME_REMAINING] = ffxMax(FfxFloat32(0), fLockStatus[LOCK_LIFETIME_REMAINING] - fLifetimeDecrease); - } - - StoreLockStatus(params.iPxHrPos, fLockStatus); -} - - -FfxFloat32x3 ComputeBaseAccumulationWeight(const AccumulationPassCommonParams params, FfxFloat32 fThisFrameReactiveFactor, FfxBoolean bInMotionLastFrame, FfxFloat32 fUpsampledWeight, LockState lockState) -{ - // Always assume max accumulation was reached - FfxFloat32 fBaseAccumulation = fMaxAccumulationLanczosWeight * FfxFloat32(params.bIsExistingSample) * (1.0f - fThisFrameReactiveFactor) * (1.0f - params.fDepthClipFactor); - - fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, fUpsampledWeight * 10.0f, ffxMax(FfxFloat32(bInMotionLastFrame), ffxSaturate(params.fHrVelocity * FfxFloat32(10))))); - - fBaseAccumulation = ffxMin(fBaseAccumulation, ffxLerp(fBaseAccumulation, fUpsampledWeight, ffxSaturate(params.fHrVelocity / FfxFloat32(20)))); - - return fBaseAccumulation.xxx; -} - -FfxFloat32 ComputeLumaInstabilityFactor(const AccumulationPassCommonParams params, RectificationBox clippingBox, FfxFloat32 fThisFrameReactiveFactor, FfxFloat32 fLuminanceDiff) -{ - const FfxFloat32 fUnormThreshold = 1.0f / 255.0f; - const FfxInt32 N_MINUS_1 = 0; - const FfxInt32 N_MINUS_2 = 1; - const FfxInt32 N_MINUS_3 = 2; - const FfxInt32 N_MINUS_4 = 3; - - FfxFloat32 fCurrentFrameLuma = clippingBox.boxCenter.x; - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - fCurrentFrameLuma = fCurrentFrameLuma / (1.0f + ffxMax(0.0f, fCurrentFrameLuma)); -#endif - - fCurrentFrameLuma = round(fCurrentFrameLuma * 255.0f) / 255.0f; - - const FfxBoolean bSampleLumaHistory = (ffxMax(ffxMax(params.fDepthClipFactor, params.fAccumulationMask), fLuminanceDiff) < 0.1f) && (params.bIsNewSample == false); - FfxFloat32x4 fCurrentFrameLumaHistory = bSampleLumaHistory ? SampleLumaHistory(params.fReprojectedHrUv) : FFX_BROADCAST_FLOAT32X4(0.0f); - - FfxFloat32 fLumaInstability = 0.0f; - FfxFloat32 fDiffs0 = (fCurrentFrameLuma - fCurrentFrameLumaHistory[N_MINUS_1]); - - FfxFloat32 fMin = abs(fDiffs0); - - if (fMin >= fUnormThreshold) { - for (int i = N_MINUS_2; i <= N_MINUS_4; i++) { - FfxFloat32 fDiffs1 = (fCurrentFrameLuma - fCurrentFrameLumaHistory[i]); - - if (sign(fDiffs0) == sign(fDiffs1)) { - - // Scale difference to protect historically similar values - const FfxFloat32 fMinBias = 1.0f; - fMin = ffxMin(fMin, abs(fDiffs1) * fMinBias); - } - } - - const FfxFloat32 fBoxSize = clippingBox.boxVec.x; - const FfxFloat32 fBoxSizeFactor = ffxPow(ffxSaturate(fBoxSize / 0.1f), 6.0f); - - fLumaInstability = FfxFloat32(fMin != abs(fDiffs0)) * fBoxSizeFactor; - fLumaInstability = FfxFloat32(fLumaInstability > fUnormThreshold); - - fLumaInstability *= 1.0f - ffxMax(params.fAccumulationMask, ffxPow(fThisFrameReactiveFactor, 1.0f / 6.0f)); - } - - //shift history - fCurrentFrameLumaHistory[N_MINUS_4] = fCurrentFrameLumaHistory[N_MINUS_3]; - fCurrentFrameLumaHistory[N_MINUS_3] = fCurrentFrameLumaHistory[N_MINUS_2]; - fCurrentFrameLumaHistory[N_MINUS_2] = fCurrentFrameLumaHistory[N_MINUS_1]; - fCurrentFrameLumaHistory[N_MINUS_1] = fCurrentFrameLuma; - - StoreLumaHistory(params.iPxHrPos, fCurrentFrameLumaHistory); - - return fLumaInstability * FfxFloat32(fCurrentFrameLumaHistory[N_MINUS_4] != 0); -} - -FfxFloat32 ComputeTemporalReactiveFactor(const AccumulationPassCommonParams params, FfxFloat32 fTemporalReactiveFactor) -{ - FfxFloat32 fNewFactor = ffxMin(0.99f, fTemporalReactiveFactor); - - fNewFactor = ffxMax(fNewFactor, ffxLerp(fNewFactor, 0.4f, ffxSaturate(params.fHrVelocity))); - - fNewFactor = ffxMax(fNewFactor * fNewFactor, ffxMax(params.fDepthClipFactor * 0.1f, params.fDilatedReactiveFactor)); - - // Force reactive factor for new samples - fNewFactor = params.bIsNewSample ? 1.0f : fNewFactor; - - if (ffxSaturate(params.fHrVelocity * 10.0f) >= 1.0f) { - fNewFactor = ffxMax(FSR3UPSCALER_EPSILON, fNewFactor) * -1.0f; - } - - return fNewFactor; -} - -AccumulationPassCommonParams InitParams(FfxInt32x2 iPxHrPos) -{ - AccumulationPassCommonParams params; - - params.iPxHrPos = iPxHrPos; - const FfxFloat32x2 fHrUv = (iPxHrPos + 0.5f) / DisplaySize(); - params.fHrUv = fHrUv; - - const FfxFloat32x2 fLrUvJittered = fHrUv + Jitter() / RenderSize(); - params.fLrUv_HwSampler = ClampUv(fLrUvJittered, RenderSize(), MaxRenderSize()); - - params.fMotionVector = GetMotionVector(iPxHrPos, fHrUv); - params.fHrVelocity = GetPxHrVelocity(params.fMotionVector); - - ComputeReprojectedUVs(params, params.fReprojectedHrUv, params.bIsExistingSample); - - params.fDepthClipFactor = ffxSaturate(SampleDepthClip(params.fLrUv_HwSampler)); - - const FfxFloat32x2 fDilatedReactiveMasks = SampleDilatedReactiveMasks(params.fLrUv_HwSampler); - params.fDilatedReactiveFactor = fDilatedReactiveMasks.x; - params.fAccumulationMask = fDilatedReactiveMasks.y; - params.bIsResetFrame = (0 == FrameIndex()); - - params.bIsNewSample = (params.bIsExistingSample == false || params.bIsResetFrame); - - return params; -} - -void Accumulate(FfxInt32x2 iPxHrPos) -{ - const AccumulationPassCommonParams params = InitParams(iPxHrPos); - - FfxFloat32x3 fHistoryColor = FfxFloat32x3(0, 0, 0); - FfxFloat32x2 fLockStatus; - InitializeNewLockSample(fLockStatus); - - FfxFloat32 fTemporalReactiveFactor = 0.0f; - FfxBoolean bInMotionLastFrame = FFX_FALSE; - LockState lockState = { FFX_FALSE , FFX_FALSE }; - if (params.bIsExistingSample && !params.bIsResetFrame) { - ReprojectHistoryColor(params, fHistoryColor, fTemporalReactiveFactor, bInMotionLastFrame); - lockState = ReprojectHistoryLockStatus(params, fLockStatus); - } - - FfxFloat32 fThisFrameReactiveFactor = ffxMax(params.fDilatedReactiveFactor, fTemporalReactiveFactor); - - FfxFloat32 fLuminanceDiff = 0.0f; - FfxFloat32 fLockContributionThisFrame = 0.0f; - UpdateLockStatus(params, fThisFrameReactiveFactor, lockState, fLockStatus, fLockContributionThisFrame, fLuminanceDiff); - - // Load upsampled input color - RectificationBox clippingBox; - FfxFloat32x4 fUpsampledColorAndWeight = ComputeUpsampledColorAndWeight(params, clippingBox, fThisFrameReactiveFactor); - - const FfxFloat32 fLumaInstabilityFactor = ComputeLumaInstabilityFactor(params, clippingBox, fThisFrameReactiveFactor, fLuminanceDiff); - - - FfxFloat32x3 fAccumulation = ComputeBaseAccumulationWeight(params, fThisFrameReactiveFactor, bInMotionLastFrame, fUpsampledColorAndWeight.w, lockState); - - if (params.bIsNewSample) { - fHistoryColor = YCoCgToRGB(fUpsampledColorAndWeight.xyz); - } - else { - RectifyHistory(params, clippingBox, fHistoryColor, fAccumulation, fLockContributionThisFrame, fThisFrameReactiveFactor, fLumaInstabilityFactor); - - Accumulate(params, fHistoryColor, fAccumulation, fUpsampledColorAndWeight); - } - - fHistoryColor = UnprepareRgb(fHistoryColor, Exposure()); - - FinalizeLockStatus(params, fLockStatus, fUpsampledColorAndWeight.w); - - // Get new temporal reactive factor - fTemporalReactiveFactor = ComputeTemporalReactiveFactor(params, fThisFrameReactiveFactor); - - StoreInternalColorAndWeight(iPxHrPos, FfxFloat32x4(fHistoryColor, fTemporalReactiveFactor)); - - // Output final color when RCAS is disabled -#if FFX_FSR3UPSCALER_OPTION_APPLY_SHARPENING == 0 - WriteUpscaledOutput(iPxHrPos, fHistoryColor); -#endif - StoreNewLocks(iPxHrPos, 0); -} - -#endif // FFX_FSR3UPSCALER_ACCUMULATE_H diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta deleted file mode 100644 index 79fa7a3..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_accumulate.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 3fc2f7a2c8c31324a949e1761bf599cc -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h deleted file mode 100644 index 13b317a..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h +++ /dev/null @@ -1,928 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#include "ffx_fsr3upscaler_resources.h" - -#if defined(FFX_GPU) -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic push -#pragma dxc diagnostic ignored "-Wambig-lit-shift" -#endif //__hlsl_dx_compiler -#include "ffx_core.h" -#ifdef __hlsl_dx_compiler -#pragma dxc diagnostic pop -#endif //__hlsl_dx_compiler -#endif // #if defined(FFX_GPU) - -#if defined(FFX_GPU) -#ifndef FFX_PREFER_WAVE64 -#define FFX_PREFER_WAVE64 -#endif // FFX_PREFER_WAVE64 - -#if defined(FFX_GPU) -#pragma warning(disable: 3205) // conversion from larger type to smaller -#endif // #if defined(FFX_GPU) - -#define DECLARE_SRV_REGISTER(regIndex) t##regIndex -#define DECLARE_UAV_REGISTER(regIndex) u##regIndex -#define DECLARE_CB_REGISTER(regIndex) b##regIndex -#define FFX_FSR3UPSCALER_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex)) -#define FFX_FSR3UPSCALER_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex)) -#define FFX_FSR3UPSCALER_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex)) - -#if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) - cbuffer cbFSR3Upscaler : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) - { - FfxInt32x2 iRenderSize; - FfxInt32x2 iMaxRenderSize; - FfxInt32x2 iDisplaySize; - FfxInt32x2 iInputColorResourceDimensions; - FfxInt32x2 iLumaMipDimensions; - FfxInt32 iLumaMipLevelToUse; - FfxInt32 iFrameIndex; - - FfxFloat32x4 fDeviceToViewDepth; - FfxFloat32x2 fJitter; - FfxFloat32x2 fMotionVectorScale; - FfxFloat32x2 fDownscaleFactor; - FfxFloat32x2 fMotionVectorJitterCancellation; - FfxFloat32 fPreExposure; - FfxFloat32 fPreviousFramePreExposure; - FfxFloat32 fTanHalfFOV; - FfxFloat32 fJitterSequenceLength; - FfxFloat32 fDeltaTime; - FfxFloat32 fDynamicResChangeFactor; - FfxFloat32 fViewSpaceToMetersFactor; - - FfxInt32 iDummy; - }; - -#define FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE (sizeof(cbFSR3Upscaler) / 4) // Number of 32-bit values. This must be kept in sync with the cbFSR3Upscaler size. - -/* Define getter functions in the order they are defined in the CB! */ -FfxInt32x2 RenderSize() -{ - return iRenderSize; -} - -FfxInt32x2 MaxRenderSize() -{ - return iMaxRenderSize; -} - -FfxInt32x2 DisplaySize() -{ - return iDisplaySize; -} - -FfxInt32x2 InputColorResourceDimensions() -{ - return iInputColorResourceDimensions; -} - -FfxInt32x2 LumaMipDimensions() -{ - return iLumaMipDimensions; -} - -FfxInt32 LumaMipLevelToUse() -{ - return iLumaMipLevelToUse; -} - -FfxInt32 FrameIndex() -{ - return iFrameIndex; -} - -FfxFloat32x2 Jitter() -{ - return fJitter; -} - -FfxFloat32x4 DeviceToViewSpaceTransformFactors() -{ - return fDeviceToViewDepth; -} - -FfxFloat32x2 MotionVectorScale() -{ - return fMotionVectorScale; -} - -FfxFloat32x2 DownscaleFactor() -{ - return fDownscaleFactor; -} - -FfxFloat32x2 MotionVectorJitterCancellation() -{ - return fMotionVectorJitterCancellation; -} - -FfxFloat32 PreExposure() -{ - return fPreExposure; -} - -FfxFloat32 PreviousFramePreExposure() -{ - return fPreviousFramePreExposure; -} - -FfxFloat32 TanHalfFoV() -{ - return fTanHalfFOV; -} - -FfxFloat32 JitterSequenceLength() -{ - return fJitterSequenceLength; -} - -FfxFloat32 DeltaTime() -{ - return fDeltaTime; -} - -FfxFloat32 DynamicResChangeFactor() -{ - return fDynamicResChangeFactor; -} - -FfxFloat32 ViewSpaceToMetersFactor() -{ - return fViewSpaceToMetersFactor; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_FSR3UPSCALER) - -#define FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(p) FFX_FSR3UPSCALER_ROOTSIG_STR(p) -#define FFX_FSR3UPSCALER_ROOTSIG_STR(p) #p -#define FFX_FSR3UPSCALER_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "RootConstants(num32BitConstants=" FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE) ", b0), " \ - "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ - "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] - -#define FFX_FSR3UPSCALER_CONSTANT_BUFFER_2_SIZE 6 // Number of 32-bit values. This must be kept in sync with max( cbRCAS , cbSPD) size. - -#define FFX_FSR3UPSCALER_CB2_ROOTSIG [RootSignature( "DescriptorTable(UAV(u0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "DescriptorTable(SRV(t0, numDescriptors = " FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT) ")), " \ - "RootConstants(num32BitConstants=" FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_CONSTANT_BUFFER_1_SIZE) ", b0), " \ - "RootConstants(num32BitConstants=" FFX_FSR3UPSCALER_ROOTSIG_STRINGIFY(FFX_FSR3UPSCALER_CONSTANT_BUFFER_2_SIZE) ", b1), " \ - "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK), " \ - "StaticSampler(s1, filter = FILTER_MIN_MAG_MIP_LINEAR, " \ - "addressU = TEXTURE_ADDRESS_CLAMP, " \ - "addressV = TEXTURE_ADDRESS_CLAMP, " \ - "addressW = TEXTURE_ADDRESS_CLAMP, " \ - "comparisonFunc = COMPARISON_NEVER, " \ - "borderColor = STATIC_BORDER_COLOR_TRANSPARENT_BLACK)" )] -#if defined(FFX_FSR3UPSCALER_EMBED_ROOTSIG) -#define FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT FFX_FSR3UPSCALER_ROOTSIG -#define FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT FFX_FSR3UPSCALER_CB2_ROOTSIG -#else -#define FFX_FSR3UPSCALER_EMBED_ROOTSIG_CONTENT -#define FFX_FSR3UPSCALER_EMBED_CB2_ROOTSIG_CONTENT -#endif // #if FFX_FSR3UPSCALER_EMBED_ROOTSIG - -#if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) -cbuffer cbGenerateReactive : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) -{ - FfxFloat32 fTcThreshold; // 0.1 is a good starting value, lower will result in more TC pixels - FfxFloat32 fTcScale; - FfxFloat32 fReactiveScale; - FfxFloat32 fReactiveMax; -}; - -FfxFloat32 TcThreshold() -{ - return fTcThreshold; -} - -FfxFloat32 TcScale() -{ - return fTcScale; -} - -FfxFloat32 ReactiveScale() -{ - return fReactiveScale; -} - -FfxFloat32 ReactiveMax() -{ - return fReactiveMax; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_AUTOREACTIVE) - -#if defined(FSR3UPSCALER_BIND_CB_RCAS) -cbuffer cbRCAS : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_RCAS) -{ - FfxUInt32x4 rcasConfig; -}; - -FfxUInt32x4 RCASConfig() -{ - return rcasConfig; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_RCAS) - - -#if defined(FSR3UPSCALER_BIND_CB_REACTIVE) -cbuffer cbGenerateReactive : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_REACTIVE) -{ - FfxFloat32 gen_reactive_scale; - FfxFloat32 gen_reactive_threshold; - FfxFloat32 gen_reactive_binaryValue; - FfxUInt32 gen_reactive_flags; -}; - -FfxFloat32 GenReactiveScale() -{ - return gen_reactive_scale; -} - -FfxFloat32 GenReactiveThreshold() -{ - return gen_reactive_threshold; -} - -FfxFloat32 GenReactiveBinaryValue() -{ - return gen_reactive_binaryValue; -} - -FfxUInt32 GenReactiveFlags() -{ - return gen_reactive_flags; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_REACTIVE) - -#if defined(FSR3UPSCALER_BIND_CB_SPD) -cbuffer cbSPD : FFX_FSR3UPSCALER_DECLARE_CB(FSR3UPSCALER_BIND_CB_SPD) { - - FfxUInt32 mips; - FfxUInt32 numWorkGroups; - FfxUInt32x2 workGroupOffset; - FfxUInt32x2 renderSize; -}; - -FfxUInt32 MipCount() -{ - return mips; -} - -FfxUInt32 NumWorkGroups() -{ - return numWorkGroups; -} - -FfxUInt32x2 WorkGroupOffset() -{ - return workGroupOffset; -} - -FfxUInt32x2 SPD_RenderSize() -{ - return renderSize; -} -#endif // #if defined(FSR3UPSCALER_BIND_CB_SPD) - -// Declare and sample camera buffers as regular textures, unless overridden -#if !defined(UNITY_FSR3_TEX2D) -#define UNITY_FSR3_TEX2D(type) Texture2D -#endif -#if !defined(UNITY_FSR3_RWTEX2D) -#define UNITY_FSR3_RWTEX2D(type) RWTexture2D -#endif -#if !defined(UNITY_FSR3_POS) -#define UNITY_FSR3_POS(pxPos) (pxPos) -#endif -#if !defined(UNITY_FSR3_UV) -#define UNITY_FSR3_UV(uv) (uv) -#endif - -SamplerState s_PointClamp : register(s0); -SamplerState s_LinearClamp : register(s1); - - // SRVs - #if defined FSR3UPSCALER_BIND_SRV_INPUT_COLOR - UNITY_FSR3_TEX2D(FfxFloat32x4) r_input_color_jittered : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY - UNITY_FSR3_TEX2D(FfxFloat32x4) r_input_opaque_only : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS - UNITY_FSR3_TEX2D(FfxFloat32x4) r_input_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_DEPTH - UNITY_FSR3_TEX2D(FfxFloat32) r_input_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE - Texture2D r_input_exposure : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE - Texture2D r_auto_exposure : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_SRV_REACTIVE_MASK - UNITY_FSR3_TEX2D(FfxFloat32) r_reactive_mask : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK); - #endif - #if defined FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK - UNITY_FSR3_TEX2D(FfxFloat32) r_transparency_and_composition_mask : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK); - #endif - #if defined FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH - Texture2D r_reconstructed_previous_nearest_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS - Texture2D r_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS - Texture2D r_previous_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_DILATED_DEPTH - Texture2D r_dilated_depth : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED - Texture2D r_internal_upscaled_color : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LOCK_STATUS - Texture2D r_lock_status : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LOCK_STATUS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA - Texture2D r_lock_input_luma : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA); - #endif - #if defined FSR3UPSCALER_BIND_SRV_NEW_LOCKS - Texture2D r_new_locks : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_NEW_LOCKS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR - Texture2D r_prepared_input_color : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LUMA_HISTORY - Texture2D r_luma_history : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY); - #endif - #if defined FSR3UPSCALER_BIND_SRV_RCAS_INPUT - Texture2D r_rcas_input : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_RCAS_INPUT); - #endif - #if defined FSR3UPSCALER_BIND_SRV_LANCZOS_LUT - Texture2D r_lanczos_lut : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT); - #endif - #if defined FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS - Texture2D r_imgMips : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS); - #endif - #if defined FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT - Texture2D r_upsample_maximum_bias_lut : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT); - #endif - #if defined FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS - Texture2D r_dilated_reactive_masks : FFX_FSR3UPSCALER_DECLARE_SRV(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS); - #endif - - #if defined FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR - Texture2D r_input_prev_color_pre_alpha : FFX_FSR3UPSCALER_DECLARE_SRV(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR - Texture2D r_input_prev_color_post_alpha : FFX_FSR3UPSCALER_DECLARE_SRV(FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR); - #endif - - // UAV declarations - #if defined FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH - RWTexture2D rw_reconstructed_previous_nearest_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS - RWTexture2D rw_dilated_motion_vectors : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_DILATED_DEPTH - RWTexture2D rw_dilated_depth : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH); - #endif - #if defined FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED - RWTexture2D rw_internal_upscaled_color : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED); - #endif - #if defined FSR3UPSCALER_BIND_UAV_LOCK_STATUS - RWTexture2D rw_lock_status : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LOCK_STATUS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA - RWTexture2D rw_lock_input_luma : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA); - #endif - #if defined FSR3UPSCALER_BIND_UAV_NEW_LOCKS - RWTexture2D rw_new_locks : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_NEW_LOCKS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR - RWTexture2D rw_prepared_input_color : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_UAV_LUMA_HISTORY - RWTexture2D rw_luma_history : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY); - #endif - #if defined FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT - UNITY_FSR3_RWTEX2D(FfxFloat32x4) rw_upscaled_output : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT); - #endif - #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE - globallycoherent RWTexture2D rw_img_mip_shading_change : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - globallycoherent RWTexture2D rw_img_mip_5 : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5); - #endif - #if defined FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS - RWTexture2D rw_dilated_reactive_masks : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS); - #endif - #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE - RWTexture2D rw_exposure : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE - RWTexture2D rw_auto_exposure : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC - globallycoherent RWTexture2D rw_spd_global_atomic : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC); - #endif - - #if defined FSR3UPSCALER_BIND_UAV_AUTOREACTIVE - RWTexture2D rw_output_autoreactive : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE); - #endif - #if defined FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION - RWTexture2D rw_output_autocomposition : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION); - #endif - #if defined FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR - RWTexture2D rw_output_prev_color_pre_alpha : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR); - #endif - #if defined FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR - RWTexture2D rw_output_prev_color_post_alpha : FFX_FSR3UPSCALER_DECLARE_UAV(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR); - #endif - -#if defined(FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS) -FfxFloat32 LoadMipLuma(FfxUInt32x2 iPxPos, FfxUInt32 mipLevel) -{ - return r_imgMips.mips[mipLevel][iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_SCENE_LUMINANCE_MIPS) -FfxFloat32 SampleMipLuma(FfxFloat32x2 fUV, FfxUInt32 mipLevel) -{ - return r_imgMips.SampleLevel(s_LinearClamp, fUV, mipLevel); -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH) -FfxFloat32 LoadInputDepth(FfxUInt32x2 iPxPos) -{ - return r_input_depth[UNITY_FSR3_POS(iPxPos)]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_DEPTH) -FfxFloat32 SampleInputDepth(FfxFloat32x2 fUV) -{ - return r_input_depth.SampleLevel(s_LinearClamp, UNITY_FSR3_UV(fUV), 0).x; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_REACTIVE_MASK) -FfxFloat32 LoadReactiveMask(FfxUInt32x2 iPxPos) -{ - return r_reactive_mask[UNITY_FSR3_POS(iPxPos)]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK) -FfxFloat32 LoadTransparencyAndCompositionMask(FfxUInt32x2 iPxPos) -{ - return r_transparency_and_composition_mask[UNITY_FSR3_POS(iPxPos)]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_COLOR) -FfxFloat32x3 LoadInputColor(FfxUInt32x2 iPxPos) -{ - return r_input_color_jittered[UNITY_FSR3_POS(iPxPos)].rgb; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_COLOR) -FfxFloat32x3 SampleInputColor(FfxFloat32x2 fUV) -{ - return r_input_color_jittered.SampleLevel(s_LinearClamp, UNITY_FSR3_UV(fUV), 0).rgb; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR) -FfxFloat32x3 LoadPreparedInputColor(FfxUInt32x2 iPxPos) -{ - return r_prepared_input_color[iPxPos].xyz; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_MOTION_VECTORS) -FfxFloat32x2 LoadInputMotionVector(FfxUInt32x2 iPxDilatedMotionVectorPos) -{ - FfxFloat32x2 fSrcMotionVector = r_input_motion_vectors[UNITY_FSR3_POS(iPxDilatedMotionVectorPos)].xy; - - FfxFloat32x2 fUvMotionVector = fSrcMotionVector * MotionVectorScale(); - -#if FFX_FSR3UPSCALER_OPTION_JITTERED_MOTION_VECTORS - fUvMotionVector -= MotionVectorJitterCancellation(); -#endif - - return fUvMotionVector; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INTERNAL_UPSCALED) -FfxFloat32x4 LoadHistory(FfxUInt32x2 iPxHistory) -{ - return r_internal_upscaled_color[iPxHistory]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_LUMA_HISTORY) -void StoreLumaHistory(FfxUInt32x2 iPxPos, FfxFloat32x4 fLumaHistory) -{ - rw_luma_history[iPxPos] = fLumaHistory; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_LUMA_HISTORY) -FfxFloat32x4 SampleLumaHistory(FfxFloat32x2 fUV) -{ - return r_luma_history.SampleLevel(s_LinearClamp, fUV, 0); -} -#endif - -FfxFloat32x4 LoadRCAS_Input(FfxInt32x2 iPxPos) -{ -#if defined(FSR3UPSCALER_BIND_SRV_RCAS_INPUT) - return r_rcas_input[iPxPos]; -#else - return 0.0; -#endif -} - -#if defined(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED) -void StoreReprojectedHistory(FfxUInt32x2 iPxHistory, FfxFloat32x4 fHistory) -{ - rw_internal_upscaled_color[iPxHistory] = fHistory; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_INTERNAL_UPSCALED) -void StoreInternalColorAndWeight(FfxUInt32x2 iPxPos, FfxFloat32x4 fColorAndWeight) -{ - rw_internal_upscaled_color[iPxPos] = fColorAndWeight; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_UPSCALED_OUTPUT) -void StoreUpscaledOutput(FfxUInt32x2 iPxPos, FfxFloat32x3 fColor) -{ - rw_upscaled_output[UNITY_FSR3_POS(iPxPos)] = FfxFloat32x4(fColor, 1.f); -} -#endif - -//LOCK_LIFETIME_REMAINING == 0 -//Should make LockInitialLifetime() return a const 1.0f later -#if defined(FSR3UPSCALER_BIND_SRV_LOCK_STATUS) -FfxFloat32x2 LoadLockStatus(FfxUInt32x2 iPxPos) -{ - return r_lock_status[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_LOCK_STATUS) -void StoreLockStatus(FfxUInt32x2 iPxPos, FfxFloat32x2 fLockStatus) -{ - rw_lock_status[iPxPos] = fLockStatus; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_LOCK_INPUT_LUMA) -FfxFloat32 LoadLockInputLuma(FfxUInt32x2 iPxPos) -{ - return r_lock_input_luma[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_LOCK_INPUT_LUMA) -void StoreLockInputLuma(FfxUInt32x2 iPxPos, FfxFloat32 fLuma) -{ - rw_lock_input_luma[iPxPos] = fLuma; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_NEW_LOCKS) -FfxFloat32 LoadNewLocks(FfxUInt32x2 iPxPos) -{ - return r_new_locks[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_NEW_LOCKS) -FfxFloat32 LoadRwNewLocks(FfxUInt32x2 iPxPos) -{ - return rw_new_locks[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_NEW_LOCKS) -void StoreNewLocks(FfxUInt32x2 iPxPos, FfxFloat32 newLock) -{ - rw_new_locks[iPxPos] = newLock; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_PREPARED_INPUT_COLOR) -void StorePreparedInputColor(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x4 fTonemapped) -{ - rw_prepared_input_color[iPxPos] = fTonemapped; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREPARED_INPUT_COLOR) -FfxFloat32 SampleDepthClip(FfxFloat32x2 fUV) -{ - return r_prepared_input_color.SampleLevel(s_LinearClamp, fUV, 0).w; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_LOCK_STATUS) -FfxFloat32x2 SampleLockStatus(FfxFloat32x2 fUV) -{ - FfxFloat32x2 fLockStatus = r_lock_status.SampleLevel(s_LinearClamp, fUV, 0); - return fLockStatus; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_RECONSTRUCTED_PREV_NEAREST_DEPTH) -FfxFloat32 LoadReconstructedPrevDepth(FfxUInt32x2 iPxPos) -{ - return asfloat(r_reconstructed_previous_nearest_depth[iPxPos]); -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) -void StoreReconstructedDepth(FfxUInt32x2 iPxSample, FfxFloat32 fDepth) -{ - FfxUInt32 uDepth = asuint(fDepth); - - #if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - InterlockedMax(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); - #else - InterlockedMin(rw_reconstructed_previous_nearest_depth[iPxSample], uDepth); // min for standard, max for inverted depth - #endif -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_RECONSTRUCTED_PREV_NEAREST_DEPTH) -void SetReconstructedDepth(FfxUInt32x2 iPxSample, const FfxUInt32 uValue) -{ - rw_reconstructed_previous_nearest_depth[iPxSample] = uValue; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_DILATED_DEPTH) -void StoreDilatedDepth(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32 fDepth) -{ - rw_dilated_depth[iPxPos] = fDepth; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_DILATED_MOTION_VECTORS) -void StoreDilatedMotionVector(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fMotionVector) -{ - rw_dilated_motion_vectors[iPxPos] = fMotionVector; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_MOTION_VECTORS) -FfxFloat32x2 LoadDilatedMotionVector(FfxUInt32x2 iPxInput) -{ - return r_dilated_motion_vectors[iPxInput].xy; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS) -FfxFloat32x2 LoadPreviousDilatedMotionVector(FfxUInt32x2 iPxInput) -{ - return r_previous_dilated_motion_vectors[iPxInput].xy; -} - -FfxFloat32x2 SamplePreviousDilatedMotionVector(FfxFloat32x2 uv) -{ - return r_previous_dilated_motion_vectors.SampleLevel(s_LinearClamp, uv, 0).xy; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_DEPTH) -FfxFloat32 LoadDilatedDepth(FfxUInt32x2 iPxInput) -{ - return r_dilated_depth[iPxInput]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_EXPOSURE) -FfxFloat32 Exposure() -{ - FfxFloat32 exposure = r_input_exposure[FfxUInt32x2(0, 0)].x; - - if (exposure == 0.0f) { - exposure = 1.0f; - } - - return exposure; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_AUTO_EXPOSURE) -FfxFloat32 AutoExposure() -{ - FfxFloat32 exposure = r_auto_exposure[FfxUInt32x2(0, 0)].x; - - if (exposure == 0.0f) { - exposure = 1.0f; - } - - return exposure; -} -#endif - -FfxFloat32 SampleLanczos2Weight(FfxFloat32 x) -{ -#if defined(FSR3UPSCALER_BIND_SRV_LANCZOS_LUT) - return r_lanczos_lut.SampleLevel(s_LinearClamp, FfxFloat32x2(x / 2, 0.5f), 0); -#else - return 0.f; -#endif -} - -#if defined(FSR3UPSCALER_BIND_SRV_UPSCALE_MAXIMUM_BIAS_LUT) -FfxFloat32 SampleUpsampleMaximumBias(FfxFloat32x2 uv) -{ - // Stored as a SNORM, so make sure to multiply by 2 to retrieve the actual expected range. - return FfxFloat32(2.0) * r_upsample_maximum_bias_lut.SampleLevel(s_LinearClamp, abs(uv) * 2.0, 0); -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS) -FfxFloat32x2 SampleDilatedReactiveMasks(FfxFloat32x2 fUV) -{ - return r_dilated_reactive_masks.SampleLevel(s_LinearClamp, fUV, 0); -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_DILATED_REACTIVE_MASKS) -FfxFloat32x2 LoadDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos) -{ - return r_dilated_reactive_masks[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_DILATED_REACTIVE_MASKS) -void StoreDilatedReactiveMasks(FFX_PARAMETER_IN FfxUInt32x2 iPxPos, FFX_PARAMETER_IN FfxFloat32x2 fDilatedReactiveMasks) -{ - rw_dilated_reactive_masks[iPxPos] = fDilatedReactiveMasks; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_INPUT_OPAQUE_ONLY) -FfxFloat32x3 LoadOpaqueOnly(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) -{ - return r_input_opaque_only[UNITY_FSR3_POS(iPxPos)].xyz; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREV_PRE_ALPHA_COLOR) -FfxFloat32x3 LoadPrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) -{ - return r_input_prev_color_pre_alpha[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_SRV_PREV_POST_ALPHA_COLOR) -FfxFloat32x3 LoadPrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos) -{ - return r_input_prev_color_post_alpha[iPxPos]; -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_AUTOREACTIVE) -#if defined(FSR3UPSCALER_BIND_UAV_AUTOCOMPOSITION) -void StoreAutoReactive(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F2 fReactive) -{ - rw_output_autoreactive[iPxPos] = fReactive.x; - - rw_output_autocomposition[iPxPos] = fReactive.y; -} -#endif -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_PREV_PRE_ALPHA_COLOR) -void StorePrevPreAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) -{ - rw_output_prev_color_pre_alpha[iPxPos] = color; - -} -#endif - -#if defined(FSR3UPSCALER_BIND_UAV_PREV_POST_ALPHA_COLOR) -void StorePrevPostAlpha(FFX_PARAMETER_IN FFX_MIN16_I2 iPxPos, FFX_PARAMETER_IN FFX_MIN16_F3 color) -{ - rw_output_prev_color_post_alpha[iPxPos] = color; -} -#endif - -FfxFloat32x2 SPD_LoadExposureBuffer() -{ -#if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE - return rw_auto_exposure[FfxInt32x2(0, 0)]; -#else - return FfxFloat32x2(0.f, 0.f); -#endif // #if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE -} - -void SPD_SetExposureBuffer(FfxFloat32x2 value) -{ -#if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE - rw_auto_exposure[FfxInt32x2(0, 0)] = value; -#endif // #if defined FSR3UPSCALER_BIND_UAV_AUTO_EXPOSURE -} - -FfxFloat32x4 SPD_LoadMipmap5(FfxInt32x2 iPxPos) -{ -#if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - return FfxFloat32x4(rw_img_mip_5[iPxPos], 0, 0, 0); -#else - return FfxFloat32x4(0.f, 0.f, 0.f, 0.f); -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 -} - -void SPD_SetMipmap(FfxInt32x2 iPxPos, FfxUInt32 slice, FfxFloat32 value) -{ - switch (slice) - { - case FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL: -#if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE - rw_img_mip_shading_change[iPxPos] = value; -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE - break; - case 5: -#if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - rw_img_mip_5[iPxPos] = value; -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - break; - default: - - // avoid flattened side effect -#if defined(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_LUMA_CHANGE) - rw_img_mip_shading_change[iPxPos] = rw_img_mip_shading_change[iPxPos]; -#elif defined(FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5) - rw_img_mip_5[iPxPos] = rw_img_mip_5[iPxPos]; -#endif // #if defined FSR3UPSCALER_BIND_UAV_EXPOSURE_MIP_5 - break; - } -} - -void SPD_IncreaseAtomicCounter(inout FfxUInt32 spdCounter) -{ -#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC - InterlockedAdd(rw_spd_global_atomic[FfxInt32x2(0, 0)], 1, spdCounter); -#endif // #if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC -} - -void SPD_ResetAtomicCounter() -{ -#if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC - rw_spd_global_atomic[FfxInt32x2(0, 0)] = 0; -#endif // #if defined FSR3UPSCALER_BIND_UAV_SPD_GLOBAL_ATOMIC -} - -#endif // #if defined(FFX_GPU) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta deleted file mode 100644 index e78e6a1..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_callbacks_hlsl.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: ba849fdeb042e7f458c81408414db834 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h deleted file mode 100644 index 1f78a29..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h +++ /dev/null @@ -1,566 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#if !defined(FFX_FSR3UPSCALER_COMMON_H) -#define FFX_FSR3UPSCALER_COMMON_H - -#if defined(FFX_CPU) || defined(FFX_GPU) -//Locks -#define LOCK_LIFETIME_REMAINING 0 -#define LOCK_TEMPORAL_LUMA 1 -#endif // #if defined(FFX_CPU) || defined(FFX_GPU) - -#if defined(FFX_GPU) -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MIN = 6.10e-05f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FP16_MAX = 65504.0f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_EPSILON = 1e-03f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_TONEMAP_EPSILON = 1.0f / FSR3UPSCALER_FP16_MAX; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FLT_MAX = 3.402823466e+38f; -FFX_STATIC const FfxFloat32 FSR3UPSCALER_FLT_MIN = 1.175494351e-38f; - -// treat vector truncation warnings as errors -#pragma warning(error: 3206) - -// suppress warnings -#pragma warning(disable: 3205) // conversion from larger type to smaller -#pragma warning(disable: 3571) // in ffxPow(f, e), f could be negative - -// Reconstructed depth usage -FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = 0.01f; - -// Accumulation -FFX_STATIC const FfxFloat32 fUpsampleLanczosWeightScale = 1.0f / 12.0f; -FFX_STATIC const FfxFloat32 fMaxAccumulationLanczosWeight = 1.0f; -FFX_STATIC const FfxFloat32 fAverageLanczosWeightPerFrame = 0.74f * fUpsampleLanczosWeightScale; // Average lanczos weight for jitter accumulated samples -FFX_STATIC const FfxFloat32 fAccumulationMaxOnMotion = 3.0f * fUpsampleLanczosWeightScale; - -// Auto exposure -FFX_STATIC const FfxFloat32 resetAutoExposureAverageSmoothing = 1e8f; - -struct AccumulationPassCommonParams -{ - FfxInt32x2 iPxHrPos; - FfxFloat32x2 fHrUv; - FfxFloat32x2 fLrUv_HwSampler; - FfxFloat32x2 fMotionVector; - FfxFloat32x2 fReprojectedHrUv; - FfxFloat32 fHrVelocity; - FfxFloat32 fDepthClipFactor; - FfxFloat32 fDilatedReactiveFactor; - FfxFloat32 fAccumulationMask; - - FfxBoolean bIsResetFrame; - FfxBoolean bIsExistingSample; - FfxBoolean bIsNewSample; -}; - -struct LockState -{ - FfxBoolean NewLock; //Set for both unique new and re-locked new - FfxBoolean WasLockedPrevFrame; //Set to identify if the pixel was already locked (relock) -}; - -void InitializeNewLockSample(FFX_PARAMETER_OUT FfxFloat32x2 fLockStatus) -{ - fLockStatus = FfxFloat32x2(0, 0); -} - -#if FFX_HALF -void InitializeNewLockSample(FFX_PARAMETER_OUT FFX_MIN16_F2 fLockStatus) -{ - fLockStatus = FFX_MIN16_F2(0, 0); -} -#endif - - -void KillLock(FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus) -{ - fLockStatus[LOCK_LIFETIME_REMAINING] = 0; -} - -#if FFX_HALF -void KillLock(FFX_PARAMETER_INOUT FFX_MIN16_F2 fLockStatus) -{ - fLockStatus[LOCK_LIFETIME_REMAINING] = FFX_MIN16_F(0); -} -#endif - -struct RectificationBox -{ - FfxFloat32x3 boxCenter; - FfxFloat32x3 boxVec; - FfxFloat32x3 aabbMin; - FfxFloat32x3 aabbMax; - FfxFloat32 fBoxCenterWeight; -}; -#if FFX_HALF -struct RectificationBoxMin16 -{ - FFX_MIN16_F3 boxCenter; - FFX_MIN16_F3 boxVec; - FFX_MIN16_F3 aabbMin; - FFX_MIN16_F3 aabbMax; - FFX_MIN16_F fBoxCenterWeight; -}; -#endif - -void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBox rectificationBox) -{ - rectificationBox.fBoxCenterWeight = FfxFloat32(0); - - rectificationBox.boxCenter = FfxFloat32x3(0, 0, 0); - rectificationBox.boxVec = FfxFloat32x3(0, 0, 0); - rectificationBox.aabbMin = FfxFloat32x3(FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX); - rectificationBox.aabbMax = -FfxFloat32x3(FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX, FSR3UPSCALER_FLT_MAX); -} -#if FFX_HALF -void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) -{ - rectificationBox.fBoxCenterWeight = FFX_MIN16_F(0); - - rectificationBox.boxCenter = FFX_MIN16_F3(0, 0, 0); - rectificationBox.boxVec = FFX_MIN16_F3(0, 0, 0); - rectificationBox.aabbMin = FFX_MIN16_F3(FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX); - rectificationBox.aabbMax = -FFX_MIN16_F3(FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX, FSR3UPSCALER_FP16_MAX); -} -#endif - -void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) -{ - rectificationBox.aabbMin = colorSample; - rectificationBox.aabbMax = colorSample; - - FfxFloat32x3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter = weightedSample; - rectificationBox.boxVec = colorSample * weightedSample; - rectificationBox.fBoxCenterWeight = fSampleWeight; -} - -void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) -{ - if (bInitialSample) { - RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); - } else { - rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); - rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); - - FfxFloat32x3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter += weightedSample; - rectificationBox.boxVec += colorSample * weightedSample; - rectificationBox.fBoxCenterWeight += fSampleWeight; - } -} -#if FFX_HALF -void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) -{ - rectificationBox.aabbMin = colorSample; - rectificationBox.aabbMax = colorSample; - - FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter = weightedSample; - rectificationBox.boxVec = colorSample * weightedSample; - rectificationBox.fBoxCenterWeight = fSampleWeight; -} - -void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) -{ - if (bInitialSample) { - RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); - } else { - rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); - rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); - - FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; - rectificationBox.boxCenter += weightedSample; - rectificationBox.boxVec += colorSample * weightedSample; - rectificationBox.fBoxCenterWeight += fSampleWeight; - } -} -#endif - -void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBox rectificationBox) -{ - rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FfxFloat32(FSR3UPSCALER_EPSILON) ? rectificationBox.fBoxCenterWeight : FfxFloat32(1.f)); - rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; - rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; - FfxFloat32x3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); - rectificationBox.boxVec = stdDev; -} -#if FFX_HALF -void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) -{ - rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FFX_MIN16_F(FSR3UPSCALER_EPSILON) ? rectificationBox.fBoxCenterWeight : FFX_MIN16_F(1.f)); - rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; - rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; - FFX_MIN16_F3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); - rectificationBox.boxVec = stdDev; -} -#endif - -FfxFloat32x3 SafeRcp3(FfxFloat32x3 v) -{ - return (all(FFX_NOT_EQUAL(v, FfxFloat32x3(0, 0, 0)))) ? (FfxFloat32x3(1, 1, 1) / v) : FfxFloat32x3(0, 0, 0); -} -#if FFX_HALF -FFX_MIN16_F3 SafeRcp3(FFX_MIN16_F3 v) -{ - return (all(FFX_NOT_EQUAL(v, FFX_MIN16_F3(0, 0, 0)))) ? (FFX_MIN16_F3(1, 1, 1) / v) : FFX_MIN16_F3(0, 0, 0); -} -#endif - -FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1) -{ - const FfxFloat32 m = ffxMax(v0, v1); - return m != 0 ? ffxMin(v0, v1) / m : 0; -} - -#if FFX_HALF -FFX_MIN16_F MinDividedByMax(const FFX_MIN16_F v0, const FFX_MIN16_F v1) -{ - const FFX_MIN16_F m = ffxMax(v0, v1); - return m != FFX_MIN16_F(0) ? ffxMin(v0, v1) / m : FFX_MIN16_F(0); -} -#endif - -FfxFloat32x3 YCoCgToRGB(FfxFloat32x3 fYCoCg) -{ - FfxFloat32x3 fRgb; - - fRgb = FfxFloat32x3( - fYCoCg.x + fYCoCg.y - fYCoCg.z, - fYCoCg.x + fYCoCg.z, - fYCoCg.x - fYCoCg.y - fYCoCg.z); - - return fRgb; -} -#if FFX_HALF -FFX_MIN16_F3 YCoCgToRGB(FFX_MIN16_F3 fYCoCg) -{ - FFX_MIN16_F3 fRgb; - - fRgb = FFX_MIN16_F3( - fYCoCg.x + fYCoCg.y - fYCoCg.z, - fYCoCg.x + fYCoCg.z, - fYCoCg.x - fYCoCg.y - fYCoCg.z); - - return fRgb; -} -#endif - -FfxFloat32x3 RGBToYCoCg(FfxFloat32x3 fRgb) -{ - FfxFloat32x3 fYCoCg; - - fYCoCg = FfxFloat32x3( - 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b, - 0.5f * fRgb.r - 0.5f * fRgb.b, - -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b); - - return fYCoCg; -} -#if FFX_HALF -FFX_MIN16_F3 RGBToYCoCg(FFX_MIN16_F3 fRgb) -{ - FFX_MIN16_F3 fYCoCg; - - fYCoCg = FFX_MIN16_F3( - 0.25 * fRgb.r + 0.5 * fRgb.g + 0.25 * fRgb.b, - 0.5 * fRgb.r - 0.5 * fRgb.b, - -0.25 * fRgb.r + 0.5 * fRgb.g - 0.25 * fRgb.b); - - return fYCoCg; -} -#endif - -FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb) -{ - return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f)); -} -#if FFX_HALF -FFX_MIN16_F RGBToLuma(FFX_MIN16_F3 fLinearRgb) -{ - return dot(fLinearRgb, FFX_MIN16_F3(0.2126f, 0.7152f, 0.0722f)); -} -#endif - -FfxFloat32 RGBToPerceivedLuma(FfxFloat32x3 fLinearRgb) -{ - FfxFloat32 fLuminance = RGBToLuma(fLinearRgb); - - FfxFloat32 fPercievedLuminance = 0; - if (fLuminance <= 216.0f / 24389.0f) { - fPercievedLuminance = fLuminance * (24389.0f / 27.0f); - } - else { - fPercievedLuminance = ffxPow(fLuminance, 1.0f / 3.0f) * 116.0f - 16.0f; - } - - return fPercievedLuminance * 0.01f; -} -#if FFX_HALF -FFX_MIN16_F RGBToPerceivedLuma(FFX_MIN16_F3 fLinearRgb) -{ - FFX_MIN16_F fLuminance = RGBToLuma(fLinearRgb); - - FFX_MIN16_F fPercievedLuminance = FFX_MIN16_F(0); - if (fLuminance <= FFX_MIN16_F(216.0f / 24389.0f)) { - fPercievedLuminance = fLuminance * FFX_MIN16_F(24389.0f / 27.0f); - } - else { - fPercievedLuminance = ffxPow(fLuminance, FFX_MIN16_F(1.0f / 3.0f)) * FFX_MIN16_F(116.0f) - FFX_MIN16_F(16.0f); - } - - return fPercievedLuminance * FFX_MIN16_F(0.01f); -} -#endif - -FfxFloat32x3 Tonemap(FfxFloat32x3 fRgb) -{ - return fRgb / (ffxMax(ffxMax(0.f, fRgb.r), ffxMax(fRgb.g, fRgb.b)) + 1.f).xxx; -} - -FfxFloat32x3 InverseTonemap(FfxFloat32x3 fRgb) -{ - return fRgb / ffxMax(FSR3UPSCALER_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; -} - -#if FFX_HALF -FFX_MIN16_F3 Tonemap(FFX_MIN16_F3 fRgb) -{ - return fRgb / (ffxMax(ffxMax(FFX_MIN16_F(0.f), fRgb.r), ffxMax(fRgb.g, fRgb.b)) + FFX_MIN16_F(1.f)).xxx; -} - -FFX_MIN16_F3 InverseTonemap(FFX_MIN16_F3 fRgb) -{ - return fRgb / ffxMax(FFX_MIN16_F(FSR3UPSCALER_TONEMAP_EPSILON), FFX_MIN16_F(1.f) - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; -} -#endif - -FfxInt32x2 ClampLoad(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) -{ - FfxInt32x2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; - result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; - result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; - result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; - return result; - - // return ffxMed3(iPxSample + iPxOffset, FfxInt32x2(0, 0), iTextureSize - FfxInt32x2(1, 1)); -} -#if FFX_HALF -FFX_MIN16_I2 ClampLoad(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) -{ - FFX_MIN16_I2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < 0) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; - result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; - result.y = (iPxOffset.y < 0) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; - result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; - return result; - - // return ffxMed3Half(iPxSample + iPxOffset, FFX_MIN16_I2(0, 0), iTextureSize - FFX_MIN16_I2(1, 1)); -} -#endif - -FfxFloat32x2 ClampUv(FfxFloat32x2 fUv, FfxInt32x2 iTextureSize, FfxInt32x2 iResourceSize) -{ - const FfxFloat32x2 fSampleLocation = fUv * iTextureSize; - const FfxFloat32x2 fClampedLocation = ffxMax(FfxFloat32x2(0.5f, 0.5f), ffxMin(fSampleLocation, FfxFloat32x2(iTextureSize) - FfxFloat32x2(0.5f, 0.5f))); - const FfxFloat32x2 fClampedUv = fClampedLocation / FfxFloat32x2(iResourceSize); - - return fClampedUv; -} - -FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size) -{ - return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size))); -} -#if FFX_HALF -FfxBoolean IsOnScreen(FFX_MIN16_I2 pos, FFX_MIN16_I2 size) -{ - return all(FFX_LESS_THAN(FFX_MIN16_U2(pos), FFX_MIN16_U2(size))); -} -#endif - -FfxFloat32 ComputeAutoExposureFromLavg(FfxFloat32 Lavg) -{ - Lavg = exp(Lavg); - - const FfxFloat32 S = 100.0f; //ISO arithmetic speed - const FfxFloat32 K = 12.5f; - FfxFloat32 ExposureISO100 = log2((Lavg * S) / K); - - const FfxFloat32 q = 0.65f; - FfxFloat32 Lmax = (78.0f / (q * S)) * ffxPow(2.0f, ExposureISO100); - - return 1 / Lmax; -} -#if FFX_HALF -FFX_MIN16_F ComputeAutoExposureFromLavg(FFX_MIN16_F Lavg) -{ - Lavg = exp(Lavg); - - const FFX_MIN16_F S = FFX_MIN16_F(100.0f); //ISO arithmetic speed - const FFX_MIN16_F K = FFX_MIN16_F(12.5f); - const FFX_MIN16_F ExposureISO100 = log2((Lavg * S) / K); - - const FFX_MIN16_F q = FFX_MIN16_F(0.65f); - const FFX_MIN16_F Lmax = (FFX_MIN16_F(78.0f) / (q * S)) * ffxPow(FFX_MIN16_F(2.0f), ExposureISO100); - - return FFX_MIN16_F(1) / Lmax; -} -#endif - -FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos) -{ - FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter(); - FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * DisplaySize(); - FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr)); - return iPxHrPos; -} -#if FFX_HALF -FFX_MIN16_I2 ComputeHrPosFromLrPos(FFX_MIN16_I2 iPxLrPos) -{ - FFX_MIN16_F2 fSrcJitteredPos = FFX_MIN16_F2(iPxLrPos) + FFX_MIN16_F(0.5f) - FFX_MIN16_F2(Jitter()); - FFX_MIN16_F2 fLrPosInHr = (fSrcJitteredPos / FFX_MIN16_F2(RenderSize())) * FFX_MIN16_F2(DisplaySize()); - FFX_MIN16_I2 iPxHrPos = FFX_MIN16_I2(floor(fLrPosInHr)); - return iPxHrPos; -} -#endif - -FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize) -{ - return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f); -} - -FfxFloat32 GetViewSpaceDepth(FfxFloat32 fDeviceDepth) -{ - const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); - - // fDeviceToViewDepth details found in ffx_fsr3upscaler.cpp - return (fDeviceToViewDepth[1] / (fDeviceDepth - fDeviceToViewDepth[0])); -} - -FfxFloat32 GetViewSpaceDepthInMeters(FfxFloat32 fDeviceDepth) -{ - return GetViewSpaceDepth(fDeviceDepth) * ViewSpaceToMetersFactor(); -} - -FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) -{ - const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); - - const FfxFloat32 Z = GetViewSpaceDepth(fDeviceDepth); - - const FfxFloat32x2 fNdcPos = ComputeNdc(iViewportPos, iViewportSize); - const FfxFloat32 X = fDeviceToViewDepth[2] * fNdcPos.x * Z; - const FfxFloat32 Y = fDeviceToViewDepth[3] * fNdcPos.y * Z; - - return FfxFloat32x3(X, Y, Z); -} - -FfxFloat32x3 GetViewSpacePositionInMeters(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) -{ - return GetViewSpacePosition(iViewportPos, iViewportSize, fDeviceDepth) * ViewSpaceToMetersFactor(); -} - -FfxFloat32 GetMaxDistanceInMeters() -{ -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - return GetViewSpaceDepth(0.0f) * ViewSpaceToMetersFactor(); -#else - return GetViewSpaceDepth(1.0f) * ViewSpaceToMetersFactor(); -#endif -} - -FfxFloat32x3 PrepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure, FfxFloat32 fPreExposure) -{ - fRgb /= fPreExposure; - fRgb *= fExposure; - - fRgb = clamp(fRgb, 0.0f, FSR3UPSCALER_FP16_MAX); - - return fRgb; -} - -FfxFloat32x3 UnprepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure) -{ - fRgb /= fExposure; - fRgb *= PreExposure(); - - return fRgb; -} - - -struct BilinearSamplingData -{ - FfxInt32x2 iOffsets[4]; - FfxFloat32 fWeights[4]; - FfxInt32x2 iBasePos; -}; - -BilinearSamplingData GetBilinearSamplingData(FfxFloat32x2 fUv, FfxInt32x2 iSize) -{ - BilinearSamplingData data; - - FfxFloat32x2 fPxSample = (fUv * iSize) - FfxFloat32x2(0.5f, 0.5f); - data.iBasePos = FfxInt32x2(floor(fPxSample)); - FfxFloat32x2 fPxFrac = ffxFract(fPxSample); - - data.iOffsets[0] = FfxInt32x2(0, 0); - data.iOffsets[1] = FfxInt32x2(1, 0); - data.iOffsets[2] = FfxInt32x2(0, 1); - data.iOffsets[3] = FfxInt32x2(1, 1); - - data.fWeights[0] = (1 - fPxFrac.x) * (1 - fPxFrac.y); - data.fWeights[1] = (fPxFrac.x) * (1 - fPxFrac.y); - data.fWeights[2] = (1 - fPxFrac.x) * (fPxFrac.y); - data.fWeights[3] = (fPxFrac.x) * (fPxFrac.y); - - return data; -} - -struct PlaneData -{ - FfxFloat32x3 fNormal; - FfxFloat32 fDistanceFromOrigin; -}; - -PlaneData GetPlaneFromPoints(FfxFloat32x3 fP0, FfxFloat32x3 fP1, FfxFloat32x3 fP2) -{ - PlaneData plane; - - FfxFloat32x3 v0 = fP0 - fP1; - FfxFloat32x3 v1 = fP0 - fP2; - plane.fNormal = normalize(cross(v0, v1)); - plane.fDistanceFromOrigin = -dot(fP0, plane.fNormal); - - return plane; -} - -FfxFloat32 PointToPlaneDistance(PlaneData plane, FfxFloat32x3 fPoint) -{ - return abs(dot(plane.fNormal, fPoint) + plane.fDistanceFromOrigin); -} - -#endif // #if defined(FFX_GPU) - -#endif //!defined(FFX_FSR3UPSCALER_COMMON_H) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta deleted file mode 100644 index 08b2046..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_common.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 6a638bec681caac4fa8e2ca198726694 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h deleted file mode 100644 index d26cf23..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h +++ /dev/null @@ -1,176 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -FFX_GROUPSHARED FfxUInt32 spdCounter; - -void SpdIncreaseAtomicCounter(FfxUInt32 slice) -{ - SPD_IncreaseAtomicCounter(spdCounter); -} - -FfxUInt32 SpdGetAtomicCounter() -{ - return spdCounter; -} - -void SpdResetAtomicCounter(FfxUInt32 slice) -{ - SPD_ResetAtomicCounter(); -} - -#ifndef SPD_PACKED_ONLY -FFX_GROUPSHARED FfxFloat32 spdIntermediateR[16][16]; -FFX_GROUPSHARED FfxFloat32 spdIntermediateG[16][16]; -FFX_GROUPSHARED FfxFloat32 spdIntermediateB[16][16]; -FFX_GROUPSHARED FfxFloat32 spdIntermediateA[16][16]; - -FfxFloat32x4 SpdLoadSourceImage(FfxFloat32x2 tex, FfxUInt32 slice) -{ - FfxFloat32x2 fUv = (tex + 0.5f + Jitter()) / RenderSize(); - fUv = ClampUv(fUv, RenderSize(), InputColorResourceDimensions()); - FfxFloat32x3 fRgb = SampleInputColor(fUv); - - fRgb /= PreExposure(); - - //compute log luma - const FfxFloat32 fLogLuma = log(ffxMax(FSR3UPSCALER_EPSILON, RGBToLuma(fRgb))); - - // Make sure out of screen pixels contribute no value to the end result - const FfxFloat32 result = all(FFX_LESS_THAN(tex, RenderSize())) ? fLogLuma : 0.0f; - - return FfxFloat32x4(result, 0, 0, 0); -} - -FfxFloat32x4 SpdLoad(FfxInt32x2 tex, FfxUInt32 slice) -{ - return SPD_LoadMipmap5(tex); -} - -void SpdStore(FfxInt32x2 pix, FfxFloat32x4 outValue, FfxUInt32 index, FfxUInt32 slice) -{ - if (index == LumaMipLevelToUse() || index == 5) - { - SPD_SetMipmap(pix, index, outValue.r); - } - - if (index == MipCount() - 1) { //accumulate on 1x1 level - - if (all(FFX_EQUAL(pix, FfxInt32x2(0, 0)))) - { - FfxFloat32 prev = SPD_LoadExposureBuffer().y; - FfxFloat32 result = outValue.r; - - if (prev < resetAutoExposureAverageSmoothing) // Compare Lavg, so small or negative values - { - FfxFloat32 rate = 1.0f; - result = prev + (result - prev) * (1 - exp(-DeltaTime() * rate)); - } - FfxFloat32x2 spdOutput = FfxFloat32x2(ComputeAutoExposureFromLavg(result), result); - SPD_SetExposureBuffer(spdOutput); - } - } -} - -FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) -{ - return FfxFloat32x4( - spdIntermediateR[x][y], - spdIntermediateG[x][y], - spdIntermediateB[x][y], - spdIntermediateA[x][y]); -} -void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) -{ - spdIntermediateR[x][y] = value.x; - spdIntermediateG[x][y] = value.y; - spdIntermediateB[x][y] = value.z; - spdIntermediateA[x][y] = value.w; -} -FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) -{ - return (v0 + v1 + v2 + v3) * 0.25f; -} -#endif - -// define fetch and store functions Packed -#if FFX_HALF - -FFX_GROUPSHARED FfxFloat16x2 spdIntermediateRG[16][16]; -FFX_GROUPSHARED FfxFloat16x2 spdIntermediateBA[16][16]; - -FfxFloat16x4 SpdLoadSourceImageH(FfxFloat32x2 tex, FfxUInt32 slice) -{ - return FfxFloat16x4(0, 0, 0, 0); -} - -FfxFloat16x4 SpdLoadH(FfxInt32x2 p, FfxUInt32 slice) -{ - return FfxFloat16x4(0, 0, 0, 0); -} - -void SpdStoreH(FfxInt32x2 p, FfxFloat16x4 value, FfxUInt32 mip, FfxUInt32 slice) -{ -} - -FfxFloat16x4 SpdLoadIntermediateH(FfxUInt32 x, FfxUInt32 y) -{ - return FfxFloat16x4( - spdIntermediateRG[x][y].x, - spdIntermediateRG[x][y].y, - spdIntermediateBA[x][y].x, - spdIntermediateBA[x][y].y); -} - -void SpdStoreIntermediateH(FfxUInt32 x, FfxUInt32 y, FfxFloat16x4 value) -{ - spdIntermediateRG[x][y] = value.xy; - spdIntermediateBA[x][y] = value.zw; -} - -FfxFloat16x4 SpdReduce4H(FfxFloat16x4 v0, FfxFloat16x4 v1, FfxFloat16x4 v2, FfxFloat16x4 v3) -{ - return (v0 + v1 + v2 + v3) * FfxFloat16(0.25); -} -#endif - -#include "spd/ffx_spd.h" - -void ComputeAutoExposure(FfxUInt32x3 WorkGroupId, FfxUInt32 LocalThreadIndex) -{ -#if FFX_HALF - SpdDownsampleH( - FfxUInt32x2(WorkGroupId.xy), - FfxUInt32(LocalThreadIndex), - FfxUInt32(MipCount()), - FfxUInt32(NumWorkGroups()), - FfxUInt32(WorkGroupId.z), - FfxUInt32x2(WorkGroupOffset())); -#else - SpdDownsample( - FfxUInt32x2(WorkGroupId.xy), - FfxUInt32(LocalThreadIndex), - FfxUInt32(MipCount()), - FfxUInt32(NumWorkGroups()), - FfxUInt32(WorkGroupId.z), - FfxUInt32x2(WorkGroupOffset())); -#endif -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta deleted file mode 100644 index 9fb7653..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_compute_luminance_pyramid.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 61bd10363d44ee2478461c9e9efbcb67 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h deleted file mode 100644 index 53763c8..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h +++ /dev/null @@ -1,259 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_DEPTH_CLIP_H -#define FFX_FSR3UPSCALER_DEPTH_CLIP_H - -FFX_STATIC const FfxFloat32 DepthClipBaseScale = 4.0f; - -FfxFloat32 ComputeDepthClip(FfxFloat32x2 fUvSample, FfxFloat32 fCurrentDepthSample) -{ - FfxFloat32 fCurrentDepthViewSpace = GetViewSpaceDepth(fCurrentDepthSample); - BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fUvSample, RenderSize()); - - FfxFloat32 fDilatedSum = 0.0f; - FfxFloat32 fDepth = 0.0f; - FfxFloat32 fWeightSum = 0.0f; - for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { - - const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; - const FfxInt32x2 iSamplePos = bilinearInfo.iBasePos + iOffset; - - if (IsOnScreen(iSamplePos, RenderSize())) { - const FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; - if (fWeight > fReconstructedDepthBilinearWeightThreshold) { - - const FfxFloat32 fPrevDepthSample = LoadReconstructedPrevDepth(iSamplePos); - const FfxFloat32 fPrevNearestDepthViewSpace = GetViewSpaceDepth(fPrevDepthSample); - - const FfxFloat32 fDepthDiff = fCurrentDepthViewSpace - fPrevNearestDepthViewSpace; - - if (fDepthDiff > 0.0f) { - -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - const FfxFloat32 fPlaneDepth = ffxMin(fPrevDepthSample, fCurrentDepthSample); -#else - const FfxFloat32 fPlaneDepth = ffxMax(fPrevDepthSample, fCurrentDepthSample); -#endif - - const FfxFloat32x3 fCenter = GetViewSpacePosition(FfxInt32x2(RenderSize() * 0.5f), RenderSize(), fPlaneDepth); - const FfxFloat32x3 fCorner = GetViewSpacePosition(FfxInt32x2(0, 0), RenderSize(), fPlaneDepth); - - const FfxFloat32 fHalfViewportWidth = length(FfxFloat32x2(RenderSize())); - const FfxFloat32 fDepthThreshold = ffxMax(fCurrentDepthViewSpace, fPrevNearestDepthViewSpace); - - const FfxFloat32 Ksep = 1.37e-05f; - const FfxFloat32 Kfov = length(fCorner) / length(fCenter); - const FfxFloat32 fRequiredDepthSeparation = Ksep * Kfov * fHalfViewportWidth * fDepthThreshold; - - const FfxFloat32 fResolutionFactor = ffxSaturate(length(FfxFloat32x2(RenderSize())) / length(FfxFloat32x2(1920.0f, 1080.0f))); - const FfxFloat32 fPower = ffxLerp(1.0f, 3.0f, fResolutionFactor); - fDepth += ffxPow(ffxSaturate(FfxFloat32(fRequiredDepthSeparation / fDepthDiff)), fPower) * fWeight; - fWeightSum += fWeight; - } - } - } - } - - return (fWeightSum > 0) ? ffxSaturate(1.0f - fDepth / fWeightSum) : 0.0f; -} - -FfxFloat32 ComputeMotionDivergence(FfxInt32x2 iPxPos, FfxInt32x2 iPxInputMotionVectorSize) -{ - FfxFloat32 minconvergence = 1.0f; - - FfxFloat32x2 fMotionVectorNucleus = LoadInputMotionVector(iPxPos); - FfxFloat32 fNucleusVelocityLr = length(fMotionVectorNucleus * RenderSize()); - FfxFloat32 fMaxVelocityUv = length(fMotionVectorNucleus); - - const FfxFloat32 MotionVectorVelocityEpsilon = 1e-02f; - - if (fNucleusVelocityLr > MotionVectorVelocityEpsilon) { - for (FfxInt32 y = -1; y <= 1; ++y) { - for (FfxInt32 x = -1; x <= 1; ++x) { - - FfxInt32x2 sp = ClampLoad(iPxPos, FfxInt32x2(x, y), iPxInputMotionVectorSize); - - FfxFloat32x2 fMotionVector = LoadInputMotionVector(sp); - FfxFloat32 fVelocityUv = length(fMotionVector); - - fMaxVelocityUv = ffxMax(fVelocityUv, fMaxVelocityUv); - fVelocityUv = ffxMax(fVelocityUv, fMaxVelocityUv); - minconvergence = ffxMin(minconvergence, dot(fMotionVector / fVelocityUv, fMotionVectorNucleus / fVelocityUv)); - } - } - } - - return ffxSaturate(1.0f - minconvergence) * ffxSaturate(fMaxVelocityUv / 0.01f); -} - -FfxFloat32 ComputeDepthDivergence(FfxInt32x2 iPxPos) -{ - const FfxFloat32 fMaxDistInMeters = GetMaxDistanceInMeters(); - FfxFloat32 fDepthMax = 0.0f; - FfxFloat32 fDepthMin = fMaxDistInMeters; - - FfxInt32 iMaxDistFound = 0; - - for (FfxInt32 y = -1; y < 2; y++) { - for (FfxInt32 x = -1; x < 2; x++) { - - const FfxInt32x2 iOffset = FfxInt32x2(x, y); - const FfxInt32x2 iSamplePos = iPxPos + iOffset; - - const FfxFloat32 fOnScreenFactor = IsOnScreen(iSamplePos, RenderSize()) ? 1.0f : 0.0f; - FfxFloat32 fDepth = GetViewSpaceDepthInMeters(LoadDilatedDepth(iSamplePos)) * fOnScreenFactor; - - iMaxDistFound |= FfxInt32(fMaxDistInMeters == fDepth); - - fDepthMin = ffxMin(fDepthMin, fDepth); - fDepthMax = ffxMax(fDepthMax, fDepth); - } - } - - return (1.0f - fDepthMin / fDepthMax) * (FfxBoolean(iMaxDistFound) ? 0.0f : 1.0f); -} - -FfxFloat32 ComputeTemporalMotionDivergence(FfxInt32x2 iPxPos) -{ - const FfxFloat32x2 fUv = FfxFloat32x2(iPxPos + 0.5f) / RenderSize(); - - FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); - FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; - fReprojectedUv = ClampUv(fReprojectedUv, RenderSize(), MaxRenderSize()); - FfxFloat32x2 fPrevMotionVector = SamplePreviousDilatedMotionVector(fReprojectedUv); - - float fPxDistance = length(fMotionVector * DisplaySize()); - return fPxDistance > 1.0f ? ffxLerp(0.0f, 1.0f - ffxSaturate(length(fPrevMotionVector) / length(fMotionVector)), ffxSaturate(ffxPow(fPxDistance / 20.0f, 3.0f))) : 0; -} - -void PreProcessReactiveMasks(FfxInt32x2 iPxLrPos, FfxFloat32 fMotionDivergence) -{ - // Compensate for bilinear sampling in accumulation pass - - FfxFloat32x3 fReferenceColor = LoadInputColor(iPxLrPos).xyz; - FfxFloat32x2 fReactiveFactor = FfxFloat32x2(0.0f, fMotionDivergence); - - float fMasksSum = 0.0f; - - FfxFloat32x3 fColorSamples[9]; - FfxFloat32 fReactiveSamples[9]; - FfxFloat32 fTransparencyAndCompositionSamples[9]; - - FFX_UNROLL - for (FfxInt32 y = -1; y < 2; y++) { - FFX_UNROLL - for (FfxInt32 x = -1; x < 2; x++) { - - const FfxInt32x2 sampleCoord = ClampLoad(iPxLrPos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); - - FfxInt32 sampleIdx = (y + 1) * 3 + x + 1; - - FfxFloat32x3 fColorSample = LoadInputColor(sampleCoord).xyz; - FfxFloat32 fReactiveSample = LoadReactiveMask(sampleCoord); - FfxFloat32 fTransparencyAndCompositionSample = LoadTransparencyAndCompositionMask(sampleCoord); - - fColorSamples[sampleIdx] = fColorSample; - fReactiveSamples[sampleIdx] = fReactiveSample; - fTransparencyAndCompositionSamples[sampleIdx] = fTransparencyAndCompositionSample; - - fMasksSum += (fReactiveSample + fTransparencyAndCompositionSample); - } - } - - if (fMasksSum > 0) - { - for (FfxInt32 sampleIdx = 0; sampleIdx < 9; sampleIdx++) - { - FfxFloat32x3 fColorSample = fColorSamples[sampleIdx]; - FfxFloat32 fReactiveSample = fReactiveSamples[sampleIdx]; - FfxFloat32 fTransparencyAndCompositionSample = fTransparencyAndCompositionSamples[sampleIdx]; - - const FfxFloat32 fMaxLenSq = ffxMax(dot(fReferenceColor, fReferenceColor), dot(fColorSample, fColorSample)); - const FfxFloat32 fSimilarity = dot(fReferenceColor, fColorSample) / fMaxLenSq; - - // Increase power for non-similar samples - const FfxFloat32 fPowerBiasMax = 6.0f; - const FfxFloat32 fSimilarityPower = 1.0f + (fPowerBiasMax - fSimilarity * fPowerBiasMax); - const FfxFloat32 fWeightedReactiveSample = ffxPow(fReactiveSample, fSimilarityPower); - const FfxFloat32 fWeightedTransparencyAndCompositionSample = ffxPow(fTransparencyAndCompositionSample, fSimilarityPower); - - fReactiveFactor = ffxMax(fReactiveFactor, FfxFloat32x2(fWeightedReactiveSample, fWeightedTransparencyAndCompositionSample)); - } - } - - StoreDilatedReactiveMasks(iPxLrPos, fReactiveFactor); -} - -FfxFloat32x3 ComputePreparedInputColor(FfxInt32x2 iPxLrPos) -{ - //We assume linear data. if non-linear input (sRGB, ...), - //then we should convert to linear first and back to sRGB on output. - FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxLrPos)); - - fRgb = PrepareRgb(fRgb, Exposure(), PreExposure()); - - const FfxFloat32x3 fPreparedYCoCg = RGBToYCoCg(fRgb); - - return fPreparedYCoCg; -} - -FfxFloat32 EvaluateSurface(FfxInt32x2 iPxPos, FfxFloat32x2 fMotionVector) -{ - FfxFloat32 d0 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, -1))); - FfxFloat32 d1 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, 0))); - FfxFloat32 d2 = GetViewSpaceDepth(LoadReconstructedPrevDepth(iPxPos + FfxInt32x2(0, 1))); - - return 1.0f - FfxFloat32(((d0 - d1) > (d1 * 0.01f)) && ((d1 - d2) > (d2 * 0.01f))); -} - -void DepthClip(FfxInt32x2 iPxPos) -{ - FfxFloat32x2 fDepthUv = (iPxPos + 0.5f) / RenderSize(); - FfxFloat32x2 fMotionVector = LoadDilatedMotionVector(iPxPos); - - // Discard tiny mvs - fMotionVector *= FfxFloat32(length(fMotionVector * DisplaySize()) > 0.01f); - - const FfxFloat32x2 fDilatedUv = fDepthUv + fMotionVector; - const FfxFloat32 fDilatedDepth = LoadDilatedDepth(iPxPos); - const FfxFloat32 fCurrentDepthViewSpace = GetViewSpaceDepth(LoadInputDepth(iPxPos)); - - // Compute prepared input color and depth clip - FfxFloat32 fDepthClip = ComputeDepthClip(fDilatedUv, fDilatedDepth) * EvaluateSurface(iPxPos, fMotionVector); - FfxFloat32x3 fPreparedYCoCg = ComputePreparedInputColor(iPxPos); - StorePreparedInputColor(iPxPos, FfxFloat32x4(fPreparedYCoCg, fDepthClip)); - - // Compute dilated reactive mask -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS - FfxInt32x2 iSamplePos = iPxPos; -#else - FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxPos); -#endif - - FfxFloat32 fMotionDivergence = ComputeMotionDivergence(iSamplePos, RenderSize()); - FfxFloat32 fTemporalMotionDifference = ffxSaturate(ComputeTemporalMotionDivergence(iPxPos) - ComputeDepthDivergence(iPxPos)); - - PreProcessReactiveMasks(iPxPos, ffxMax(fTemporalMotionDifference, fMotionDivergence)); -} - -#endif //!defined( FFX_FSR3UPSCALER_DEPTH_CLIPH ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta deleted file mode 100644 index 891d3d1..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_depth_clip.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 7c662249d70c4434da4f2da00e432c38 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h deleted file mode 100644 index e1a0d06..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h +++ /dev/null @@ -1,116 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_LOCK_H -#define FFX_FSR3UPSCALER_LOCK_H - -void ClearResourcesForNextFrame(in FfxInt32x2 iPxHrPos) -{ - if (all(FFX_LESS_THAN(iPxHrPos, FfxInt32x2(RenderSize())))) - { -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - const FfxUInt32 farZ = 0x0; -#else - const FfxUInt32 farZ = 0x3f800000; -#endif - SetReconstructedDepth(iPxHrPos, farZ); - } -} - -FfxBoolean ComputeThinFeatureConfidence(FfxInt32x2 pos) -{ - const FfxInt32 RADIUS = 1; - - FfxFloat32 fNucleus = LoadLockInputLuma(pos); - - FfxFloat32 similar_threshold = 1.05f; - FfxFloat32 dissimilarLumaMin = FSR3UPSCALER_FLT_MAX; - FfxFloat32 dissimilarLumaMax = 0; - - /* - 0 1 2 - 3 4 5 - 6 7 8 - */ - - #define SETBIT(x) (1U << x) - - FfxUInt32 mask = SETBIT(4); //flag fNucleus as similar - - const FfxUInt32 uNumRejectionMasks = 4; - const FfxUInt32 uRejectionMasks[uNumRejectionMasks] = { - SETBIT(0) | SETBIT(1) | SETBIT(3) | SETBIT(4), //Upper left - SETBIT(1) | SETBIT(2) | SETBIT(4) | SETBIT(5), //Upper right - SETBIT(3) | SETBIT(4) | SETBIT(6) | SETBIT(7), //Lower left - SETBIT(4) | SETBIT(5) | SETBIT(7) | SETBIT(8), //Lower right - }; - - FfxInt32 idx = 0; - FFX_UNROLL - for (FfxInt32 y = -RADIUS; y <= RADIUS; y++) { - FFX_UNROLL - for (FfxInt32 x = -RADIUS; x <= RADIUS; x++, idx++) { - if (x == 0 && y == 0) continue; - - FfxInt32x2 samplePos = ClampLoad(pos, FfxInt32x2(x, y), FfxInt32x2(RenderSize())); - - FfxFloat32 sampleLuma = LoadLockInputLuma(samplePos); - FfxFloat32 difference = ffxMax(sampleLuma, fNucleus) / ffxMin(sampleLuma, fNucleus); - - if (difference > 0 && (difference < similar_threshold)) { - mask |= SETBIT(idx); - } else { - dissimilarLumaMin = ffxMin(dissimilarLumaMin, sampleLuma); - dissimilarLumaMax = ffxMax(dissimilarLumaMax, sampleLuma); - } - } - } - - FfxBoolean isRidge = fNucleus > dissimilarLumaMax || fNucleus < dissimilarLumaMin; - - if (FFX_FALSE == isRidge) { - - return false; - } - - FFX_UNROLL - for (FfxInt32 i = 0; i < 4; i++) { - - if ((mask & uRejectionMasks[i]) == uRejectionMasks[i]) { - return false; - } - } - - return true; -} - -void ComputeLock(FfxInt32x2 iPxLrPos) -{ - if (ComputeThinFeatureConfidence(iPxLrPos)) - { - StoreNewLocks(ComputeHrPosFromLrPos(iPxLrPos), 1.f); - } - - // ClearResourcesForNextFrame(iPxLrPos); -} - -#endif // FFX_FSR3UPSCALER_LOCK_H diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta deleted file mode 100644 index 4013169..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_lock.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: c7e9f53dd040b2645af5ccd936a94b0e -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h deleted file mode 100644 index 3709113..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h +++ /dev/null @@ -1,107 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_POSTPROCESS_LOCK_STATUS_H -#define FFX_FSR3UPSCALER_POSTPROCESS_LOCK_STATUS_H - -FfxFloat32x4 WrapShadingChangeLuma(FfxInt32x2 iPxSample) -{ - return FfxFloat32x4(LoadMipLuma(iPxSample, LumaMipLevelToUse()), 0, 0, 0); -} - -#if FFX_HALF -FFX_MIN16_F4 WrapShadingChangeLuma(FFX_MIN16_I2 iPxSample) -{ - return FFX_MIN16_F4(LoadMipLuma(iPxSample, LumaMipLevelToUse()), 0, 0, 0); -} -#endif - -#if FFX_FSR3UPSCALER_OPTION_POSTPROCESSLOCKSTATUS_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBilinearSamplesMin16(FetchShadingChangeLumaSamples, WrapShadingChangeLuma) -#else -DeclareCustomFetchBicubicSamples(FetchShadingChangeLumaSamples, WrapShadingChangeLuma) -#endif -DeclareCustomTextureSample(ShadingChangeLumaSample, Lanczos2, FetchShadingChangeLumaSamples) - -FfxFloat32 GetShadingChangeLuma(FfxInt32x2 iPxHrPos, FfxFloat32x2 fUvCoord) -{ - FfxFloat32 fShadingChangeLuma = 0; - -#if 0 - fShadingChangeLuma = Exposure() * exp(ShadingChangeLumaSample(fUvCoord, LumaMipDimensions()).x); -#else - - const FfxFloat32 fDiv = FfxFloat32(2u << LumaMipLevelToUse()); - FfxInt32x2 iMipRenderSize = FfxInt32x2(RenderSize() / fDiv); - - fUvCoord = ClampUv(fUvCoord, iMipRenderSize, LumaMipDimensions()); - fShadingChangeLuma = Exposure() * exp(FfxFloat32(SampleMipLuma(fUvCoord, LumaMipLevelToUse()))); -#endif - - fShadingChangeLuma = ffxPow(fShadingChangeLuma, 1.0f / 6.0f); - - return fShadingChangeLuma; -} - -void UpdateLockStatus(AccumulationPassCommonParams params, - FFX_PARAMETER_INOUT FfxFloat32 fReactiveFactor, LockState state, - FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus, - FFX_PARAMETER_OUT FfxFloat32 fLockContributionThisFrame, - FFX_PARAMETER_OUT FfxFloat32 fLuminanceDiff) { - - const FfxFloat32 fShadingChangeLuma = GetShadingChangeLuma(params.iPxHrPos, params.fHrUv); - - //init temporal shading change factor, init to -1 or so in reproject to know if "true new"? - fLockStatus[LOCK_TEMPORAL_LUMA] = (fLockStatus[LOCK_TEMPORAL_LUMA] == FfxFloat32(0.0f)) ? fShadingChangeLuma : fLockStatus[LOCK_TEMPORAL_LUMA]; - - FfxFloat32 fPreviousShadingChangeLuma = fLockStatus[LOCK_TEMPORAL_LUMA]; - - fLuminanceDiff = 1.0f - MinDividedByMax(fPreviousShadingChangeLuma, fShadingChangeLuma); - - if (state.NewLock) { - fLockStatus[LOCK_TEMPORAL_LUMA] = fShadingChangeLuma; - - fLockStatus[LOCK_LIFETIME_REMAINING] = (fLockStatus[LOCK_LIFETIME_REMAINING] != 0.0f) ? 2.0f : 1.0f; - } - else if(fLockStatus[LOCK_LIFETIME_REMAINING] <= 1.0f) { - fLockStatus[LOCK_TEMPORAL_LUMA] = ffxLerp(fLockStatus[LOCK_TEMPORAL_LUMA], FfxFloat32(fShadingChangeLuma), 0.5f); - } - else { - if (fLuminanceDiff > 0.1f) { - KillLock(fLockStatus); - } - } - - fReactiveFactor = ffxMax(fReactiveFactor, ffxSaturate((fLuminanceDiff - 0.1f) * 10.0f)); - fLockStatus[LOCK_LIFETIME_REMAINING] *= (1.0f - fReactiveFactor); - - fLockStatus[LOCK_LIFETIME_REMAINING] *= ffxSaturate(1.0f - params.fAccumulationMask); - fLockStatus[LOCK_LIFETIME_REMAINING] *= FfxFloat32(params.fDepthClipFactor < 0.1f); - - // Compute this frame lock contribution - const FfxFloat32 fLifetimeContribution = ffxSaturate(fLockStatus[LOCK_LIFETIME_REMAINING] - 1.0f); - const FfxFloat32 fShadingChangeContribution = ffxSaturate(MinDividedByMax(fLockStatus[LOCK_TEMPORAL_LUMA], fShadingChangeLuma)); - - fLockContributionThisFrame = ffxSaturate(ffxSaturate(fLifetimeContribution * 4.0f) * fShadingChangeContribution); -} - -#endif //!defined( FFX_FSR3UPSCALER_POSTPROCESS_LOCK_STATUS_H ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta deleted file mode 100644 index 8c8bf49..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_postprocess_lock_status.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 67a8b72ceb93d634f883b086fdccb348 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h deleted file mode 100644 index 77619a5..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#define GROUP_SIZE 8 -#define FSR_RCAS_DENOISE 1 - -#include "ffx_core.h" - -void WriteUpscaledOutput(FFX_MIN16_U2 iPxHrPos, FfxFloat32x3 fUpscaledColor) -{ - StoreUpscaledOutput(FFX_MIN16_I2(iPxHrPos), fUpscaledColor); -} - -#define FSR_RCAS_F 1 -FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p) -{ - FfxFloat32x4 fColor = LoadRCAS_Input(p); - - fColor.rgb = PrepareRgb(fColor.rgb, Exposure(), PreExposure()); - - return fColor; -} -void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {} - -#include "fsr1/ffx_fsr1.h" - -void CurrFilter(FFX_MIN16_U2 pos) -{ - FfxFloat32x3 c; - FsrRcasF(c.r, c.g, c.b, pos, RCASConfig()); - - c = UnprepareRgb(c, Exposure()); - - WriteUpscaledOutput(pos, c); -} - -void RCAS(FfxUInt32x3 LocalThreadId, FfxUInt32x3 WorkGroupId, FfxUInt32x3 Dtid) -{ - // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. - FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u); - CurrFilter(FFX_MIN16_U2(gxy)); - gxy.x += 8u; - CurrFilter(FFX_MIN16_U2(gxy)); - gxy.y += 8u; - CurrFilter(FFX_MIN16_U2(gxy)); - gxy.x -= 8u; - CurrFilter(FFX_MIN16_U2(gxy)); -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta deleted file mode 100644 index a315002..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_rcas.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 692efb7cec0df67408a583a7ff34146a -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h deleted file mode 100644 index a822dfc..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h +++ /dev/null @@ -1,146 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H -#define FFX_FSR3UPSCALER_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H - -void ReconstructPrevDepth(FfxInt32x2 iPxPos, FfxFloat32 fDepth, FfxFloat32x2 fMotionVector, FfxInt32x2 iPxDepthSize) -{ - fMotionVector *= FfxFloat32(length(fMotionVector * DisplaySize()) > 0.1f); - - FfxFloat32x2 fUv = (iPxPos + FfxFloat32(0.5)) / iPxDepthSize; - FfxFloat32x2 fReprojectedUv = fUv + fMotionVector; - - BilinearSamplingData bilinearInfo = GetBilinearSamplingData(fReprojectedUv, RenderSize()); - - // Project current depth into previous frame locations. - // Push to all pixels having some contribution if reprojection is using bilinear logic. - for (FfxInt32 iSampleIndex = 0; iSampleIndex < 4; iSampleIndex++) { - - const FfxInt32x2 iOffset = bilinearInfo.iOffsets[iSampleIndex]; - FfxFloat32 fWeight = bilinearInfo.fWeights[iSampleIndex]; - - if (fWeight > fReconstructedDepthBilinearWeightThreshold) { - - FfxInt32x2 iStorePos = bilinearInfo.iBasePos + iOffset; - if (IsOnScreen(iStorePos, iPxDepthSize)) { - StoreReconstructedDepth(iStorePos, fDepth); - } - } - } -} - -void FindNearestDepth(FFX_PARAMETER_IN FfxInt32x2 iPxPos, FFX_PARAMETER_IN FfxInt32x2 iPxSize, FFX_PARAMETER_OUT FfxFloat32 fNearestDepth, FFX_PARAMETER_OUT FfxInt32x2 fNearestDepthCoord) -{ - const FfxInt32 iSampleCount = 9; - const FfxInt32x2 iSampleOffsets[iSampleCount] = { - FfxInt32x2(+0, +0), - FfxInt32x2(+1, +0), - FfxInt32x2(+0, +1), - FfxInt32x2(+0, -1), - FfxInt32x2(-1, +0), - FfxInt32x2(-1, +1), - FfxInt32x2(+1, +1), - FfxInt32x2(-1, -1), - FfxInt32x2(+1, -1), - }; - - // pull out the depth loads to allow SC to batch them - FfxFloat32 depth[9]; - FfxInt32 iSampleIndex = 0; - FFX_UNROLL - for (iSampleIndex = 0; iSampleIndex < iSampleCount; ++iSampleIndex) { - - FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; - depth[iSampleIndex] = LoadInputDepth(iPos); - } - - // find closest depth - fNearestDepthCoord = iPxPos; - fNearestDepth = depth[0]; - FFX_UNROLL - for (iSampleIndex = 1; iSampleIndex < iSampleCount; ++iSampleIndex) { - - FfxInt32x2 iPos = iPxPos + iSampleOffsets[iSampleIndex]; - if (IsOnScreen(iPos, iPxSize)) { - - FfxFloat32 fNdDepth = depth[iSampleIndex]; -#if FFX_FSR3UPSCALER_OPTION_INVERTED_DEPTH - if (fNdDepth > fNearestDepth) { -#else - if (fNdDepth < fNearestDepth) { -#endif - fNearestDepthCoord = iPos; - fNearestDepth = fNdDepth; - } - } - } -} - -FfxFloat32 ComputeLockInputLuma(FfxInt32x2 iPxLrPos) -{ - //We assume linear data. if non-linear input (sRGB, ...), - //then we should convert to linear first and back to sRGB on output. - FfxFloat32x3 fRgb = ffxMax(FfxFloat32x3(0, 0, 0), LoadInputColor(iPxLrPos)); - - // Use internal auto exposure for locking logic - fRgb /= PreExposure(); - fRgb *= Exposure(); - -#if FFX_FSR3UPSCALER_OPTION_HDR_COLOR_INPUT - fRgb = Tonemap(fRgb); -#endif - - //compute luma used to lock pixels, if used elsewhere the ffxPow must be moved! - const FfxFloat32 fLockInputLuma = ffxPow(RGBToPerceivedLuma(fRgb), FfxFloat32(1.0 / 6.0)); - - return fLockInputLuma; -} - -void ReconstructAndDilate(FfxInt32x2 iPxLrPos) -{ - FfxFloat32 fDilatedDepth; - FfxInt32x2 iNearestDepthCoord; - - FindNearestDepth(iPxLrPos, RenderSize(), fDilatedDepth, iNearestDepthCoord); - -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS - FfxInt32x2 iSamplePos = iPxLrPos; - FfxInt32x2 iMotionVectorPos = iNearestDepthCoord; -#else - FfxInt32x2 iSamplePos = ComputeHrPosFromLrPos(iPxLrPos); - FfxInt32x2 iMotionVectorPos = ComputeHrPosFromLrPos(iNearestDepthCoord); -#endif - - FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iMotionVectorPos); - - StoreDilatedDepth(iPxLrPos, fDilatedDepth); - StoreDilatedMotionVector(iPxLrPos, fDilatedMotionVector); - - ReconstructPrevDepth(iPxLrPos, fDilatedDepth, fDilatedMotionVector, RenderSize()); - - FfxFloat32 fLockInputLuma = ComputeLockInputLuma(iPxLrPos); - StoreLockInputLuma(iPxLrPos, fLockInputLuma); -} - - -#endif //!defined( FFX_FSR3UPSCALER_RECONSTRUCT_DILATED_VELOCITY_AND_PREVIOUS_DEPTH_H ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta deleted file mode 100644 index a1fd018..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reconstruct_dilated_velocity_and_previous_depth.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: c8b3854bad30a8b40babc5a9805f294e -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h deleted file mode 100644 index 29b7584..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h +++ /dev/null @@ -1,137 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_REPROJECT_H -#define FFX_FSR3UPSCALER_REPROJECT_H - -#ifndef FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE -#define FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE 0 // Reference -#endif - -FfxFloat32x4 WrapHistory(FfxInt32x2 iPxSample) -{ - return LoadHistory(iPxSample); -} - -#if FFX_HALF -FFX_MIN16_F4 WrapHistory(FFX_MIN16_I2 iPxSample) -{ - return FFX_MIN16_F4(LoadHistory(iPxSample)); -} -#endif - - -#if FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBicubicSamplesMin16(FetchHistorySamples, WrapHistory) -DeclareCustomTextureSampleMin16(HistorySample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) -#else -DeclareCustomFetchBicubicSamples(FetchHistorySamples, WrapHistory) -DeclareCustomTextureSample(HistorySample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchHistorySamples) -#endif - -FfxFloat32x4 WrapLockStatus(FfxInt32x2 iPxSample) -{ - FfxFloat32x4 fSample = FfxFloat32x4(LoadLockStatus(iPxSample), 0.0f, 0.0f); - return fSample; -} - -#if FFX_HALF -FFX_MIN16_F4 WrapLockStatus(FFX_MIN16_I2 iPxSample) -{ - FFX_MIN16_F4 fSample = FFX_MIN16_F4(LoadLockStatus(iPxSample), 0.0, 0.0); - - return fSample; -} -#endif - -#if 1 -#if FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBilinearSamplesMin16(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSampleMin16(LockStatusSample, Bilinear, FetchLockStatusSamples) -#else -DeclareCustomFetchBilinearSamples(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSample(LockStatusSample, Bilinear, FetchLockStatusSamples) -#endif -#else -#if FFX_FSR3UPSCALER_OPTION_REPROJECT_SAMPLERS_USE_DATA_HALF && FFX_HALF -DeclareCustomFetchBicubicSamplesMin16(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSampleMin16(LockStatusSample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchLockStatusSamples) -#else -DeclareCustomFetchBicubicSamples(FetchLockStatusSamples, WrapLockStatus) -DeclareCustomTextureSample(LockStatusSample, FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(FFX_FSR3UPSCALER_OPTION_REPROJECT_USE_LANCZOS_TYPE), FetchLockStatusSamples) -#endif -#endif - -FfxFloat32x2 GetMotionVector(FfxInt32x2 iPxHrPos, FfxFloat32x2 fHrUv) -{ -#if FFX_FSR3UPSCALER_OPTION_LOW_RESOLUTION_MOTION_VECTORS - FfxFloat32x2 fDilatedMotionVector = LoadDilatedMotionVector(FFX_MIN16_I2(fHrUv * RenderSize())); -#else - FfxFloat32x2 fDilatedMotionVector = LoadInputMotionVector(iPxHrPos); -#endif - - return fDilatedMotionVector; -} - -FfxBoolean IsUvInside(FfxFloat32x2 fUv) -{ - return (fUv.x >= 0.0f && fUv.x <= 1.0f) && (fUv.y >= 0.0f && fUv.y <= 1.0f); -} - -void ComputeReprojectedUVs(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedHrUv, FFX_PARAMETER_OUT FfxBoolean bIsExistingSample) -{ - fReprojectedHrUv = params.fHrUv + params.fMotionVector; - - bIsExistingSample = IsUvInside(fReprojectedHrUv); -} - -void ReprojectHistoryColor(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x3 fHistoryColor, FFX_PARAMETER_OUT FfxFloat32 fTemporalReactiveFactor, FFX_PARAMETER_OUT FfxBoolean bInMotionLastFrame) -{ - FfxFloat32x4 fHistory = HistorySample(params.fReprojectedHrUv, DisplaySize()); - - fHistoryColor = PrepareRgb(fHistory.rgb, Exposure(), PreviousFramePreExposure()); - - fHistoryColor = RGBToYCoCg(fHistoryColor); - - //Compute temporal reactivity info - fTemporalReactiveFactor = ffxSaturate(abs(fHistory.w)); - bInMotionLastFrame = (fHistory.w < 0.0f); -} - -LockState ReprojectHistoryLockStatus(const AccumulationPassCommonParams params, FFX_PARAMETER_OUT FfxFloat32x2 fReprojectedLockStatus) -{ - LockState state = { FFX_FALSE, FFX_FALSE }; - const FfxFloat32 fNewLockIntensity = LoadRwNewLocks(params.iPxHrPos); - state.NewLock = fNewLockIntensity > (127.0f / 255.0f); - - FfxFloat32 fInPlaceLockLifetime = state.NewLock ? fNewLockIntensity : 0; - - fReprojectedLockStatus = SampleLockStatus(params.fReprojectedHrUv); - - if (fReprojectedLockStatus[LOCK_LIFETIME_REMAINING] != FfxFloat32(0.0f)) { - state.WasLockedPrevFrame = true; - } - - return state; -} - -#endif //!defined( FFX_FSR3UPSCALER_REPROJECT_H ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta deleted file mode 100644 index 3fa39db..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_reproject.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 9d893016eebb2564f9a66b80afb0849f -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h deleted file mode 100644 index d98cfcc..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h +++ /dev/null @@ -1,104 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_RESOURCES_H -#define FFX_FSR3UPSCALER_RESOURCES_H - -#if defined(FFX_CPU) || defined(FFX_GPU) -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NULL 0 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_OPAQUE_ONLY 1 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_COLOR 2 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 3 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_DEPTH 4 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_EXPOSURE 5 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_REACTIVE_MASK 6 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INPUT_TRANSPARENCY_AND_COMPOSITION_MASK 7 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RECONSTRUCTED_PREVIOUS_NEAREST_DEPTH 8 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_MOTION_VECTORS 9 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_DEPTH 10 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR 11 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_STATUS 12 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_NEW_LOCKS 13 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREPARED_INPUT_COLOR 14 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY 15 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DEBUG_OUTPUT 16 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LANCZOS_LUT 17 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SPD_ATOMIC_COUNT 18 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_UPSCALED_OUTPUT 19 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_RCAS_INPUT 20 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_STATUS_1 21 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_STATUS_2 22 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_1 23 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_2 24 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_REACTIVITY 25 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_TRANSPARENCY_AND_COMPOSITION 26 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTITIER_UPSAMPLE_MAXIMUM_BIAS_LUT 27 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_DILATED_REACTIVE_MASKS 28 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE 29 // same as FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 29 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_1 30 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_2 31 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_3 32 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 33 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_5 34 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_6 35 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_7 36 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_8 37 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_9 38 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_10 39 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_11 40 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12 41 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_EXPOSURE 42 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTO_EXPOSURE 43 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOREACTIVE 44 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_AUTOCOMPOSITION_DEPRECATED 45 - -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR 46 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR 47 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_1 48 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_1 49 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_2 50 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_2 51 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_PREVIOUS_DILATED_MOTION_VECTORS 52 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_1 53 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LUMA_HISTORY_2 54 -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_LOCK_INPUT_LUMA 55 - -// Shading change detection mip level setting, value must be in the range [FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0, FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12] -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 -#define FFX_FSR3UPSCALER_SHADING_CHANGE_MIP_LEVEL (FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE - FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_SCENE_LUMINANCE) - -#define FFX_FSR3UPSCALER_RESOURCE_IDENTIFIER_COUNT 56 - -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_FSR3UPSCALER 0 -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_SPD 1 -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_RCAS 2 -#define FFX_FSR3UPSCALER_CONSTANTBUFFER_IDENTIFIER_GENREACTIVE 3 - -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_TONEMAP 1 -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP 2 -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_APPLY_THRESHOLD 4 -#define FFX_FSR3UPSCALER_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX 8 - -#endif // #if defined(FFX_CPU) || defined(FFX_GPU) - -#endif //!defined( FFX_FSR3UPSCALER_RESOURCES_H ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta deleted file mode 100644 index 8e862df..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_resources.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: b5a95a38dcfaf3946a5095bbbc42939a -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h deleted file mode 100644 index d33f70c..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h +++ /dev/null @@ -1,606 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_SAMPLE_H -#define FFX_FSR3UPSCALER_SAMPLE_H - -// suppress warnings -#ifdef FFX_HLSL -#pragma warning(disable: 4008) // potentially divide by zero -#endif //FFX_HLSL - -struct FetchedBilinearSamples { - - FfxFloat32x4 fColor00; - FfxFloat32x4 fColor10; - - FfxFloat32x4 fColor01; - FfxFloat32x4 fColor11; -}; - -struct FetchedBicubicSamples { - - FfxFloat32x4 fColor00; - FfxFloat32x4 fColor10; - FfxFloat32x4 fColor20; - FfxFloat32x4 fColor30; - - FfxFloat32x4 fColor01; - FfxFloat32x4 fColor11; - FfxFloat32x4 fColor21; - FfxFloat32x4 fColor31; - - FfxFloat32x4 fColor02; - FfxFloat32x4 fColor12; - FfxFloat32x4 fColor22; - FfxFloat32x4 fColor32; - - FfxFloat32x4 fColor03; - FfxFloat32x4 fColor13; - FfxFloat32x4 fColor23; - FfxFloat32x4 fColor33; -}; - -#if FFX_HALF -struct FetchedBilinearSamplesMin16 { - - FFX_MIN16_F4 fColor00; - FFX_MIN16_F4 fColor10; - - FFX_MIN16_F4 fColor01; - FFX_MIN16_F4 fColor11; -}; - -struct FetchedBicubicSamplesMin16 { - - FFX_MIN16_F4 fColor00; - FFX_MIN16_F4 fColor10; - FFX_MIN16_F4 fColor20; - FFX_MIN16_F4 fColor30; - - FFX_MIN16_F4 fColor01; - FFX_MIN16_F4 fColor11; - FFX_MIN16_F4 fColor21; - FFX_MIN16_F4 fColor31; - - FFX_MIN16_F4 fColor02; - FFX_MIN16_F4 fColor12; - FFX_MIN16_F4 fColor22; - FFX_MIN16_F4 fColor32; - - FFX_MIN16_F4 fColor03; - FFX_MIN16_F4 fColor13; - FFX_MIN16_F4 fColor23; - FFX_MIN16_F4 fColor33; -}; -#else //FFX_HALF -#define FetchedBicubicSamplesMin16 FetchedBicubicSamples -#define FetchedBilinearSamplesMin16 FetchedBilinearSamples -#endif //FFX_HALF - -FfxFloat32x4 Linear(FfxFloat32x4 A, FfxFloat32x4 B, FfxFloat32 t) -{ - return A + (B - A) * t; -} - -FfxFloat32x4 Bilinear(FetchedBilinearSamples BilinearSamples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); - FfxFloat32x4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); - FfxFloat32x4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Linear(FFX_MIN16_F4 A, FFX_MIN16_F4 B, FFX_MIN16_F t) -{ - return A + (B - A) * t; -} - -FFX_MIN16_F4 Bilinear(FetchedBilinearSamplesMin16 BilinearSamples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Linear(BilinearSamples.fColor00, BilinearSamples.fColor10, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Linear(BilinearSamples.fColor01, BilinearSamples.fColor11, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Linear(fColorX0, fColorX1, fPxFrac.y); - return fColorXY; -} -#endif - -FfxFloat32 Lanczos2NoClamp(FfxFloat32 x) -{ - const FfxFloat32 PI = 3.141592653589793f; // TODO: share SDK constants - return abs(x) < FSR3UPSCALER_EPSILON ? 1.f : (sin(PI * x) / (PI * x)) * (sin(0.5f * PI * x) / (0.5f * PI * x)); -} - -FfxFloat32 Lanczos2(FfxFloat32 x) -{ - x = ffxMin(abs(x), 2.0f); - return Lanczos2NoClamp(x); -} - -#if FFX_HALF - -#if 0 -FFX_MIN16_F Lanczos2NoClamp(FFX_MIN16_F x) -{ - const FFX_MIN16_F PI = FFX_MIN16_F(3.141592653589793f); // TODO: share SDK constants - return abs(x) < FFX_MIN16_F(FSR3UPSCALER_EPSILON) ? FFX_MIN16_F(1.f) : (sin(PI * x) / (PI * x)) * (sin(FFX_MIN16_F(0.5f) * PI * x) / (FFX_MIN16_F(0.5f) * PI * x)); -} -#endif - -FFX_MIN16_F Lanczos2(FFX_MIN16_F x) -{ - x = ffxMin(abs(x), FFX_MIN16_F(2.0f)); - return FFX_MIN16_F(Lanczos2NoClamp(x)); -} -#endif //FFX_HALF - -// FSR1 lanczos approximation. Input is x*x and must be <= 4. -FfxFloat32 Lanczos2ApproxSqNoClamp(FfxFloat32 x2) -{ - FfxFloat32 a = (2.0f / 5.0f) * x2 - 1; - FfxFloat32 b = (1.0f / 4.0f) * x2 - 1; - return ((25.0f / 16.0f) * a * a - (25.0f / 16.0f - 1)) * (b * b); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2ApproxSqNoClamp(FFX_MIN16_F x2) -{ - FFX_MIN16_F a = FFX_MIN16_F(2.0f / 5.0f) * x2 - FFX_MIN16_F(1); - FFX_MIN16_F b = FFX_MIN16_F(1.0f / 4.0f) * x2 - FFX_MIN16_F(1); - return (FFX_MIN16_F(25.0f / 16.0f) * a * a - FFX_MIN16_F(25.0f / 16.0f - 1)) * (b * b); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2ApproxSq(FfxFloat32 x2) -{ - x2 = ffxMin(x2, 4.0f); - return Lanczos2ApproxSqNoClamp(x2); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2ApproxSq(FFX_MIN16_F x2) -{ - x2 = ffxMin(x2, FFX_MIN16_F(4.0f)); - return Lanczos2ApproxSqNoClamp(x2); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2ApproxNoClamp(FfxFloat32 x) -{ - return Lanczos2ApproxSqNoClamp(x * x); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2ApproxNoClamp(FFX_MIN16_F x) -{ - return Lanczos2ApproxSqNoClamp(x * x); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2Approx(FfxFloat32 x) -{ - return Lanczos2ApproxSq(x * x); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2Approx(FFX_MIN16_F x) -{ - return Lanczos2ApproxSq(x * x); -} -#endif //FFX_HALF - -FfxFloat32 Lanczos2_UseLUT(FfxFloat32 x) -{ - return SampleLanczos2Weight(abs(x)); -} - -#if FFX_HALF -FFX_MIN16_F Lanczos2_UseLUT(FFX_MIN16_F x) -{ - return FFX_MIN16_F(SampleLanczos2Weight(abs(x))); -} -#endif //FFX_HALF - -FfxFloat32x4 Lanczos2_UseLUT(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) -{ - FfxFloat32 fWeight0 = Lanczos2_UseLUT(-1.f - t); - FfxFloat32 fWeight1 = Lanczos2_UseLUT(-0.f - t); - FfxFloat32 fWeight2 = Lanczos2_UseLUT(+1.f - t); - FfxFloat32 fWeight3 = Lanczos2_UseLUT(+2.f - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} -#if FFX_HALF -FFX_MIN16_F4 Lanczos2_UseLUT(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) -{ - FFX_MIN16_F fWeight0 = Lanczos2_UseLUT(FFX_MIN16_F(-1.f) - t); - FFX_MIN16_F fWeight1 = Lanczos2_UseLUT(FFX_MIN16_F(-0.f) - t); - FFX_MIN16_F fWeight2 = Lanczos2_UseLUT(FFX_MIN16_F(+1.f) - t); - FFX_MIN16_F fWeight3 = Lanczos2_UseLUT(FFX_MIN16_F(+2.f) - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} -#endif - -FfxFloat32x4 Lanczos2(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) -{ - FfxFloat32 fWeight0 = Lanczos2(-1.f - t); - FfxFloat32 fWeight1 = Lanczos2(-0.f - t); - FfxFloat32 fWeight2 = Lanczos2(+1.f - t); - FfxFloat32 fWeight3 = Lanczos2(+2.f - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} - -FfxFloat32x4 Lanczos2(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FfxFloat32x4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FfxFloat32x4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FfxFloat32x4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FfxFloat32x4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FfxFloat32x4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; - FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { - - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) -{ - FFX_MIN16_F fWeight0 = Lanczos2(FFX_MIN16_F(-1.f) - t); - FFX_MIN16_F fWeight1 = Lanczos2(FFX_MIN16_F(-0.f) - t); - FFX_MIN16_F fWeight2 = Lanczos2(FFX_MIN16_F(+1.f) - t); - FFX_MIN16_F fWeight3 = Lanczos2(FFX_MIN16_F(+2.f) - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} - -FFX_MIN16_F4 Lanczos2(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Lanczos2(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Lanczos2(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FFX_MIN16_F4 fColorX2 = Lanczos2(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FFX_MIN16_F4 fColorX3 = Lanczos2(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Lanczos2(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FFX_MIN16_F4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; - FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} -#endif //FFX_HALF - - -FfxFloat32x4 Lanczos2LUT(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FfxFloat32x4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FfxFloat32x4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FfxFloat32x4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FfxFloat32x4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FfxFloat32x4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; - FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) { - - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2LUT(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Lanczos2_UseLUT(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Lanczos2_UseLUT(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FFX_MIN16_F4 fColorX2 = Lanczos2_UseLUT(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FFX_MIN16_F4 fColorX3 = Lanczos2_UseLUT(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Lanczos2_UseLUT(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FFX_MIN16_F4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; - FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} -#endif //FFX_HALF - - - -FfxFloat32x4 Lanczos2Approx(FfxFloat32x4 fColor0, FfxFloat32x4 fColor1, FfxFloat32x4 fColor2, FfxFloat32x4 fColor3, FfxFloat32 t) -{ - FfxFloat32 fWeight0 = Lanczos2ApproxNoClamp(-1.f - t); - FfxFloat32 fWeight1 = Lanczos2ApproxNoClamp(-0.f - t); - FfxFloat32 fWeight2 = Lanczos2ApproxNoClamp(+1.f - t); - FfxFloat32 fWeight3 = Lanczos2ApproxNoClamp(+2.f - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2Approx(FFX_MIN16_F4 fColor0, FFX_MIN16_F4 fColor1, FFX_MIN16_F4 fColor2, FFX_MIN16_F4 fColor3, FFX_MIN16_F t) -{ - FFX_MIN16_F fWeight0 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-1.f) - t); - FFX_MIN16_F fWeight1 = Lanczos2ApproxNoClamp(FFX_MIN16_F(-0.f) - t); - FFX_MIN16_F fWeight2 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+1.f) - t); - FFX_MIN16_F fWeight3 = Lanczos2ApproxNoClamp(FFX_MIN16_F(+2.f) - t); - return (fWeight0 * fColor0 + fWeight1 * fColor1 + fWeight2 * fColor2 + fWeight3 * fColor3) / (fWeight0 + fWeight1 + fWeight2 + fWeight3); -} -#endif //FFX_HALF - -FfxFloat32x4 Lanczos2Approx(FetchedBicubicSamples Samples, FfxFloat32x2 fPxFrac) -{ - FfxFloat32x4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FfxFloat32x4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FfxFloat32x4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FfxFloat32x4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FfxFloat32x4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FfxFloat32x4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FfxFloat32x4 fDeringingMin = fDeringingSamples[0]; - FfxFloat32x4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} - -#if FFX_HALF -FFX_MIN16_F4 Lanczos2Approx(FetchedBicubicSamplesMin16 Samples, FFX_MIN16_F2 fPxFrac) -{ - FFX_MIN16_F4 fColorX0 = Lanczos2Approx(Samples.fColor00, Samples.fColor10, Samples.fColor20, Samples.fColor30, fPxFrac.x); - FFX_MIN16_F4 fColorX1 = Lanczos2Approx(Samples.fColor01, Samples.fColor11, Samples.fColor21, Samples.fColor31, fPxFrac.x); - FFX_MIN16_F4 fColorX2 = Lanczos2Approx(Samples.fColor02, Samples.fColor12, Samples.fColor22, Samples.fColor32, fPxFrac.x); - FFX_MIN16_F4 fColorX3 = Lanczos2Approx(Samples.fColor03, Samples.fColor13, Samples.fColor23, Samples.fColor33, fPxFrac.x); - FFX_MIN16_F4 fColorXY = Lanczos2Approx(fColorX0, fColorX1, fColorX2, fColorX3, fPxFrac.y); - - // Deringing - - // TODO: only use 4 by checking jitter - const FfxInt32 iDeringingSampleCount = 4; - const FFX_MIN16_F4 fDeringingSamples[4] = { - Samples.fColor11, - Samples.fColor21, - Samples.fColor12, - Samples.fColor22, - }; - - FFX_MIN16_F4 fDeringingMin = fDeringingSamples[0]; - FFX_MIN16_F4 fDeringingMax = fDeringingSamples[0]; - - FFX_UNROLL - for (FfxInt32 iSampleIndex = 1; iSampleIndex < iDeringingSampleCount; ++iSampleIndex) - { - fDeringingMin = ffxMin(fDeringingMin, fDeringingSamples[iSampleIndex]); - fDeringingMax = ffxMax(fDeringingMax, fDeringingSamples[iSampleIndex]); - } - - fColorXY = clamp(fColorXY, fDeringingMin, fDeringingMax); - - return fColorXY; -} -#endif - -// Clamp by offset direction. Assuming iPxSample is already in range and iPxOffset is compile time constant. -FfxInt32x2 ClampCoord(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) -{ - FfxInt32x2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; - result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; - result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; - result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; - return result; -} -#if FFX_HALF -FFX_MIN16_I2 ClampCoord(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) -{ - FFX_MIN16_I2 result = iPxSample + iPxOffset; - result.x = (iPxOffset.x < FFX_MIN16_I(0)) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; - result.x = (iPxOffset.x > FFX_MIN16_I(0)) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; - result.y = (iPxOffset.y < FFX_MIN16_I(0)) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; - result.y = (iPxOffset.y > FFX_MIN16_I(0)) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; - return result; -} -#endif //FFX_HALF - - -#define DeclareCustomFetchBicubicSamplesWithType(SampleType, TextureType, AddrType, Name, LoadTexture) \ - SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ - { \ - SampleType Samples; \ - \ - Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, -1), iTextureSize))); \ - Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, -1), iTextureSize))); \ - Samples.fColor20 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, -1), iTextureSize))); \ - Samples.fColor30 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, -1), iTextureSize))); \ - \ - Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +0), iTextureSize))); \ - Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ - Samples.fColor21 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ - Samples.fColor31 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +0), iTextureSize))); \ - \ - Samples.fColor02 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +1), iTextureSize))); \ - Samples.fColor12 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ - Samples.fColor22 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ - Samples.fColor32 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +1), iTextureSize))); \ - \ - Samples.fColor03 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(-1, +2), iTextureSize))); \ - Samples.fColor13 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +2), iTextureSize))); \ - Samples.fColor23 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +2), iTextureSize))); \ - Samples.fColor33 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+2, +2), iTextureSize))); \ - \ - return Samples; \ - } - -#define DeclareCustomFetchBicubicSamples(Name, LoadTexture) \ - DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) - -#define DeclareCustomFetchBicubicSamplesMin16(Name, LoadTexture) \ - DeclareCustomFetchBicubicSamplesWithType(FetchedBicubicSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) - -#define DeclareCustomFetchBilinearSamplesWithType(SampleType, TextureType,AddrType, Name, LoadTexture) \ - SampleType Name(AddrType iPxSample, AddrType iTextureSize) \ - { \ - SampleType Samples; \ - Samples.fColor00 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +0), iTextureSize))); \ - Samples.fColor10 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +0), iTextureSize))); \ - Samples.fColor01 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+0, +1), iTextureSize))); \ - Samples.fColor11 = TextureType(LoadTexture(ClampCoord(iPxSample, AddrType(+1, +1), iTextureSize))); \ - return Samples; \ - } - -#define DeclareCustomFetchBilinearSamples(Name, LoadTexture) \ - DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamples, FfxFloat32x4, FfxInt32x2, Name, LoadTexture) - -#define DeclareCustomFetchBilinearSamplesMin16(Name, LoadTexture) \ - DeclareCustomFetchBilinearSamplesWithType(FetchedBilinearSamplesMin16, FFX_MIN16_F4, FfxInt32x2, Name, LoadTexture) - -// BE CAREFUL: there is some precision issues and (3253, 125) leading to (3252.9989778, 125.001102) -// is common, so iPxSample can "jitter" -#define DeclareCustomTextureSample(Name, InterpolateSamples, FetchSamples) \ - FfxFloat32x4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ - { \ - FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ - /* Clamp base coords */ \ - fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ - fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ - /* */ \ - FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ - FfxFloat32x2 fPxFrac = ffxFract(fPxSample); \ - FfxFloat32x4 fColorXY = FfxFloat32x4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ - return fColorXY; \ - } - -#define DeclareCustomTextureSampleMin16(Name, InterpolateSamples, FetchSamples) \ - FFX_MIN16_F4 Name(FfxFloat32x2 fUvSample, FfxInt32x2 iTextureSize) \ - { \ - FfxFloat32x2 fPxSample = (fUvSample * FfxFloat32x2(iTextureSize)) - FfxFloat32x2(0.5f, 0.5f); \ - /* Clamp base coords */ \ - fPxSample.x = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.x), fPxSample.x)); \ - fPxSample.y = ffxMax(0.0f, ffxMin(FfxFloat32(iTextureSize.y), fPxSample.y)); \ - /* */ \ - FfxInt32x2 iPxSample = FfxInt32x2(floor(fPxSample)); \ - FFX_MIN16_F2 fPxFrac = FFX_MIN16_F2(ffxFract(fPxSample)); \ - FFX_MIN16_F4 fColorXY = FFX_MIN16_F4(InterpolateSamples(FetchSamples(iPxSample, iTextureSize), fPxFrac)); \ - return fColorXY; \ - } - -#define FFX_FSR3UPSCALER_CONCAT_ID(x, y) x ## y -#define FFX_FSR3UPSCALER_CONCAT(x, y) FFX_FSR3UPSCALER_CONCAT_ID(x, y) -#define FFX_FSR3UPSCALER_SAMPLER_1D_0 Lanczos2 -#define FFX_FSR3UPSCALER_SAMPLER_1D_1 Lanczos2LUT -#define FFX_FSR3UPSCALER_SAMPLER_1D_2 Lanczos2Approx - -#define FFX_FSR3UPSCALER_GET_LANCZOS_SAMPLER1D(x) FFX_FSR3UPSCALER_CONCAT(FFX_FSR3UPSCALER_SAMPLER_1D_, x) - -#endif //!defined( FFX_FSR3UPSCALER_SAMPLE_H ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta deleted file mode 100644 index 8e3cf72..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_sample.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: dcb900c9deecd06419a8a4c10c305890 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h deleted file mode 100644 index 2d446bb..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h +++ /dev/null @@ -1,250 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - -#define USE_YCOCG 1 - -#define fAutogenEpsilon 0.01f - -// EXPERIMENTAL - -FFX_MIN16_F ComputeAutoTC_01(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) -{ - FfxFloat32x3 colorPreAlpha = LoadOpaqueOnly(uDispatchThreadId); - FfxFloat32x3 colorPostAlpha = LoadInputColor(uDispatchThreadId); - FfxFloat32x3 colorPrevPreAlpha = LoadPrevPreAlpha(iPrevIdx); - FfxFloat32x3 colorPrevPostAlpha = LoadPrevPostAlpha(iPrevIdx); - -#if USE_YCOCG - colorPreAlpha = RGBToYCoCg(colorPreAlpha); - colorPostAlpha = RGBToYCoCg(colorPostAlpha); - colorPrevPreAlpha = RGBToYCoCg(colorPrevPreAlpha); - colorPrevPostAlpha = RGBToYCoCg(colorPrevPostAlpha); -#endif - - FfxFloat32x3 colorDeltaCurr = colorPostAlpha - colorPreAlpha; - FfxFloat32x3 colorDeltaPrev = colorPrevPostAlpha - colorPrevPreAlpha; - bool hasAlpha = any(FFX_GREATER_THAN(abs(colorDeltaCurr), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); - bool hadAlpha = any(FFX_GREATER_THAN(abs(colorDeltaPrev), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); - - FfxFloat32x3 X = colorPreAlpha; - FfxFloat32x3 Y = colorPostAlpha; - FfxFloat32x3 Z = colorPrevPreAlpha; - FfxFloat32x3 W = colorPrevPostAlpha; - - FFX_MIN16_F retVal = FFX_MIN16_F(ffxSaturate(dot(abs(abs(Y - X) - abs(W - Z)), FfxFloat32x3(1, 1, 1)))); - - // cleanup very small values - retVal = (retVal < TcThreshold()) ? FFX_MIN16_F(0.0f) : FFX_MIN16_F(1.f); - - return retVal; -} - -// works ok: thin edges -FFX_MIN16_F ComputeAutoTC_02(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) -{ - FfxFloat32x3 colorPreAlpha = LoadOpaqueOnly(uDispatchThreadId); - FfxFloat32x3 colorPostAlpha = LoadInputColor(uDispatchThreadId); - FfxFloat32x3 colorPrevPreAlpha = LoadPrevPreAlpha(iPrevIdx); - FfxFloat32x3 colorPrevPostAlpha = LoadPrevPostAlpha(iPrevIdx); - -#if USE_YCOCG - colorPreAlpha = RGBToYCoCg(colorPreAlpha); - colorPostAlpha = RGBToYCoCg(colorPostAlpha); - colorPrevPreAlpha = RGBToYCoCg(colorPrevPreAlpha); - colorPrevPostAlpha = RGBToYCoCg(colorPrevPostAlpha); -#endif - - FfxFloat32x3 colorDelta = colorPostAlpha - colorPreAlpha; - FfxFloat32x3 colorPrevDelta = colorPrevPostAlpha - colorPrevPreAlpha; - bool hasAlpha = any(FFX_GREATER_THAN(abs(colorDelta), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); - bool hadAlpha = any(FFX_GREATER_THAN(abs(colorPrevDelta), FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon))); - - FfxFloat32x3 delta = colorPostAlpha - colorPreAlpha; //prev+1*d = post => d = color, alpha = - FfxFloat32x3 deltaPrev = colorPrevPostAlpha - colorPrevPreAlpha; - - FfxFloat32x3 X = colorPrevPreAlpha; - FfxFloat32x3 N = colorPreAlpha - colorPrevPreAlpha; - FfxFloat32x3 YAminusXA = colorPrevPostAlpha - colorPrevPreAlpha; - FfxFloat32x3 NminusNA = colorPostAlpha - colorPrevPostAlpha; - - FfxFloat32x3 A = (hasAlpha || hadAlpha) ? NminusNA / max(FfxFloat32x3(fAutogenEpsilon, fAutogenEpsilon, fAutogenEpsilon), N) : FfxFloat32x3(0, 0, 0); - - FFX_MIN16_F retVal = FFX_MIN16_F( max(max(A.x, A.y), A.z) ); - - // only pixels that have significantly changed in color shuold be considered - retVal = ffxSaturate(retVal * FFX_MIN16_F(length(colorPostAlpha - colorPrevPostAlpha)) ); - - return retVal; -} - -// This function computes the TransparencyAndComposition mask: -// This mask indicates pixels that should discard locks and apply color clamping. -// -// Typically this is the case for translucent pixels (that don't write depth values) or pixels where the correctness of -// the MVs can not be guaranteed (e.g. procedutal movement or vegetation that does not have MVs to reduce the cost during rasterization) -// Also, large changes in color due to changed lighting should be marked to remove locks on pixels with "old" lighting. -// -// This function takes a opaque only and a final texture and uses internal copies of those textures from the last frame. -// The function tries to determine where the color changes between opaque only and final image to determine the pixels that use transparency. -// Also it uses the previous frames and detects where the use of transparency changed to mark those pixels. -// Additionally it marks pixels where the color changed significantly in the opaque only image, e.g. due to lighting or texture animation. -// -// In the final step it stores the current textures in internal textures for the next frame - -FFX_MIN16_F ComputeTransparencyAndComposition(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) -{ - FFX_MIN16_F retVal = ComputeAutoTC_02(uDispatchThreadId, iPrevIdx); - - // [branch] - if (retVal > FFX_MIN16_F(0.01f)) - { - retVal = ComputeAutoTC_01(uDispatchThreadId, iPrevIdx); - } - return retVal; -} - -float computeSolidEdge(FFX_MIN16_I2 curPos, FFX_MIN16_I2 prevPos) -{ - float lum[9]; - int i = 0; - for (int y = -1; y < 2; ++y) - { - for (int x = -1; x < 2; ++x) - { - FfxFloat32x3 curCol = LoadOpaqueOnly(curPos + FFX_MIN16_I2(x, y)).rgb; - FfxFloat32x3 prevCol = LoadPrevPreAlpha(prevPos + FFX_MIN16_I2(x, y)).rgb; - lum[i++] = length(curCol - prevCol); - } - } - - //float gradX = abs(lum[3] - lum[4]) + abs(lum[5] - lum[4]); - //float gradY = abs(lum[1] - lum[4]) + abs(lum[7] - lum[4]); - - //return sqrt(gradX * gradX + gradY * gradY); - - float gradX = abs(lum[3] - lum[4]) * abs(lum[5] - lum[4]); - float gradY = abs(lum[1] - lum[4]) * abs(lum[7] - lum[4]); - - return sqrt(sqrt(gradX * gradY)); -} - -float computeAlphaEdge(FFX_MIN16_I2 curPos, FFX_MIN16_I2 prevPos) -{ - float lum[9]; - int i = 0; - for (int y = -1; y < 2; ++y) - { - for (int x = -1; x < 2; ++x) - { - FfxFloat32x3 curCol = abs(LoadInputColor(curPos + FFX_MIN16_I2(x, y)).rgb - LoadOpaqueOnly(curPos + FFX_MIN16_I2(x, y)).rgb); - FfxFloat32x3 prevCol = abs(LoadPrevPostAlpha(prevPos + FFX_MIN16_I2(x, y)).rgb - LoadPrevPreAlpha(prevPos + FFX_MIN16_I2(x, y)).rgb); - lum[i++] = length(curCol - prevCol); - } - } - - //float gradX = abs(lum[3] - lum[4]) + abs(lum[5] - lum[4]); - //float gradY = abs(lum[1] - lum[4]) + abs(lum[7] - lum[4]); - - //return sqrt(gradX * gradX + gradY * gradY); - - float gradX = abs(lum[3] - lum[4]) * abs(lum[5] - lum[4]); - float gradY = abs(lum[1] - lum[4]) * abs(lum[7] - lum[4]); - - return sqrt(sqrt(gradX * gradY)); -} - -FFX_MIN16_F ComputeAabbOverlap(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) -{ - FFX_MIN16_F retVal = FFX_MIN16_F(0.f); - - FfxFloat32x2 fMotionVector = LoadInputMotionVector(uDispatchThreadId); - FfxFloat32x3 colorPreAlpha = LoadOpaqueOnly(uDispatchThreadId); - FfxFloat32x3 colorPostAlpha = LoadInputColor(uDispatchThreadId); - FfxFloat32x3 colorPrevPreAlpha = LoadPrevPreAlpha(iPrevIdx); - FfxFloat32x3 colorPrevPostAlpha = LoadPrevPostAlpha(iPrevIdx); - -#if USE_YCOCG - colorPreAlpha = RGBToYCoCg(colorPreAlpha); - colorPostAlpha = RGBToYCoCg(colorPostAlpha); - colorPrevPreAlpha = RGBToYCoCg(colorPrevPreAlpha); - colorPrevPostAlpha = RGBToYCoCg(colorPrevPostAlpha); -#endif - FfxFloat32x3 minPrev = FFX_MIN16_F3(+1000.f, +1000.f, +1000.f); - FfxFloat32x3 maxPrev = FFX_MIN16_F3(-1000.f, -1000.f, -1000.f); - for (int y = -1; y < 2; ++y) - { - for (int x = -1; x < 2; ++x) - { - FfxFloat32x3 W = LoadPrevPostAlpha(iPrevIdx + FFX_MIN16_I2(x, y)); - -#if USE_YCOCG - W = RGBToYCoCg(W); -#endif - minPrev = min(minPrev, W); - maxPrev = max(maxPrev, W); - } - } - // instead of computing the overlap: simply count how many samples are outside - // set reactive based on that - FFX_MIN16_F count = FFX_MIN16_F(0.f); - for (int y = -1; y < 2; ++y) - { - for (int x = -1; x < 2; ++x) - { - FfxFloat32x3 Y = LoadInputColor(uDispatchThreadId + FFX_MIN16_I2(x, y)); - -#if USE_YCOCG - Y = RGBToYCoCg(Y); -#endif - count += ((Y.x < minPrev.x) || (Y.x > maxPrev.x)) ? FFX_MIN16_F(1.f) : FFX_MIN16_F(0.f); - count += ((Y.y < minPrev.y) || (Y.y > maxPrev.y)) ? FFX_MIN16_F(1.f) : FFX_MIN16_F(0.f); - count += ((Y.z < minPrev.z) || (Y.z > maxPrev.z)) ? FFX_MIN16_F(1.f) : FFX_MIN16_F(0.f); - } - } - retVal = count / FFX_MIN16_F(27.f); - - return retVal; -} - - -// This function computes the Reactive mask: -// We want pixels marked where the alpha portion of the frame changes a lot between neighbours -// Those pixels are expected to change quickly between frames, too. (e.g. small particles, reflections on curved surfaces...) -// As a result history would not be trustworthy. -// On the other hand we don't want pixels marked where pre-alpha has a large differnce, since those would profit from accumulation -// For mirrors we may assume the pre-alpha is pretty uniform color. -// -// This works well generally, but also marks edge pixels -FFX_MIN16_F ComputeReactive(FFX_MIN16_I2 uDispatchThreadId, FFX_MIN16_I2 iPrevIdx) -{ - // we only get here if alpha has a significant contribution and has changed since last frame. - FFX_MIN16_F retVal = FFX_MIN16_F(0.f); - - // mark pixels with huge variance in alpha as reactive - FFX_MIN16_F alphaEdge = FFX_MIN16_F(computeAlphaEdge(uDispatchThreadId, iPrevIdx)); - FFX_MIN16_F opaqueEdge = FFX_MIN16_F(computeSolidEdge(uDispatchThreadId, iPrevIdx)); - retVal = ffxSaturate(alphaEdge - opaqueEdge); - - // the above also marks edge pixels due to jitter, so we need to cancel those out - - - return retVal; -} diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta deleted file mode 100644 index bdeb970..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_tcr_autogen.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: f01d5a8fbd1f34a4ea8d971755a21b6c -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h deleted file mode 100644 index 47e7ccf..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h +++ /dev/null @@ -1,195 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -#ifndef FFX_FSR3UPSCALER_UPSAMPLE_H -#define FFX_FSR3UPSCALER_UPSAMPLE_H - -FFX_STATIC const FfxUInt32 iLanczos2SampleCount = 16; - -void Deringing(RectificationBox clippingBox, FFX_PARAMETER_INOUT FfxFloat32x3 fColor) -{ - fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); -} -#if FFX_HALF -void Deringing(RectificationBoxMin16 clippingBox, FFX_PARAMETER_INOUT FFX_MIN16_F3 fColor) -{ - fColor = clamp(fColor, clippingBox.aabbMin, clippingBox.aabbMax); -} -#endif - -#ifndef FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE -#define FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE 2 // Approximate -#endif - -FfxFloat32 GetUpsampleLanczosWeight(FfxFloat32x2 fSrcSampleOffset, FfxFloat32 fKernelWeight) -{ - FfxFloat32x2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; -#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE - FfxFloat32 fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT - FfxFloat32 fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE - FfxFloat32 fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); -#else -#error "Invalid Lanczos type" -#endif - return fSampleWeight; -} - -#if FFX_HALF -FFX_MIN16_F GetUpsampleLanczosWeight(FFX_MIN16_F2 fSrcSampleOffset, FFX_MIN16_F fKernelWeight) -{ - FFX_MIN16_F2 fSrcSampleOffsetBiased = fSrcSampleOffset * fKernelWeight.xx; -#if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 0 // LANCZOS_TYPE_REFERENCE - FFX_MIN16_F fSampleWeight = Lanczos2(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 1 // LANCZOS_TYPE_LUT - FFX_MIN16_F fSampleWeight = Lanczos2_UseLUT(length(fSrcSampleOffsetBiased)); -#elif FFX_FSR3UPSCALER_OPTION_UPSAMPLE_USE_LANCZOS_TYPE == 2 // LANCZOS_TYPE_APPROXIMATE - FFX_MIN16_F fSampleWeight = Lanczos2ApproxSq(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); - - // To Test: Save reciproqual sqrt compute - // FfxFloat32 fSampleWeight = Lanczos2Sq_UseLUT(dot(fSrcSampleOffsetBiased, fSrcSampleOffsetBiased)); -#else -#error "Invalid Lanczos type" -#endif - return fSampleWeight; -} -#endif - -FfxFloat32 ComputeMaxKernelWeight() { - const FfxFloat32 fKernelSizeBias = 1.0f; - - FfxFloat32 fKernelWeight = FfxFloat32(1) + (FfxFloat32(1.0f) / FfxFloat32x2(DownscaleFactor()) - FfxFloat32(1)).x * FfxFloat32(fKernelSizeBias); - - return ffxMin(FfxFloat32(1.99f), fKernelWeight); -} - -FfxFloat32x4 ComputeUpsampledColorAndWeight(const AccumulationPassCommonParams params, - FFX_PARAMETER_INOUT RectificationBox clippingBox, FfxFloat32 fReactiveFactor) -{ - #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF - #include "ffx_fsr3upscaler_force16_begin.h" - #endif - // We compute a sliced lanczos filter with 2 lobes (other slices are accumulated temporaly) - FfxFloat32x2 fDstOutputPos = FfxFloat32x2(params.iPxHrPos) + FFX_BROADCAST_FLOAT32X2(0.5f); // Destination resolution output pixel center position - FfxFloat32x2 fSrcOutputPos = fDstOutputPos * DownscaleFactor(); // Source resolution output pixel center position - FfxInt32x2 iSrcInputPos = FfxInt32x2(floor(fSrcOutputPos)); // TODO: what about weird upscale factors... - - #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF - #include "ffx_fsr3upscaler_force16_end.h" - #endif - - FfxFloat32x3 fSamples[iLanczos2SampleCount]; - - FfxFloat32x2 fSrcUnjitteredPos = (FfxFloat32x2(iSrcInputPos) + FfxFloat32x2(0.5f, 0.5f)) - Jitter(); // This is the un-jittered position of the sample at offset 0,0 - - FfxInt32x2 offsetTL; - offsetTL.x = (fSrcUnjitteredPos.x > fSrcOutputPos.x) ? FfxInt32(-2) : FfxInt32(-1); - offsetTL.y = (fSrcUnjitteredPos.y > fSrcOutputPos.y) ? FfxInt32(-2) : FfxInt32(-1); - - //Load samples - // If fSrcUnjitteredPos.y > fSrcOutputPos.y, indicates offsetTL.y = -2, sample offset Y will be [-2, 1], clipbox will be rows [1, 3]. - // Flip row# for sampling offset in this case, so first 0~2 rows in the sampled array can always be used for computing the clipbox. - // This reduces branch or cmove on sampled colors, but moving this overhead to sample position / weight calculation time which apply to less values. - const FfxBoolean bFlipRow = fSrcUnjitteredPos.y > fSrcOutputPos.y; - const FfxBoolean bFlipCol = fSrcUnjitteredPos.x > fSrcOutputPos.x; - - FfxFloat32x2 fOffsetTL = FfxFloat32x2(offsetTL); - - FFX_UNROLL - for (FfxInt32 row = 0; row < 3; row++) { - - FFX_UNROLL - for (FfxInt32 col = 0; col < 3; col++) { - FfxInt32 iSampleIndex = col + (row << 2); - - FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); - FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + offsetTL + sampleColRow; - - const FfxInt32x2 sampleCoord = ClampLoad(iSrcSamplePos, FfxInt32x2(0, 0), FfxInt32x2(RenderSize())); - - fSamples[iSampleIndex] = LoadPreparedInputColor(FfxInt32x2(sampleCoord)); - } - } - - FfxFloat32x4 fColorAndWeight = FfxFloat32x4(0.0f, 0.0f, 0.0f, 0.0f); - - FfxFloat32x2 fBaseSampleOffset = FfxFloat32x2(fSrcUnjitteredPos - fSrcOutputPos); - - // Identify how much of each upsampled color to be used for this frame - const FfxFloat32 fKernelReactiveFactor = ffxMax(fReactiveFactor, FfxFloat32(params.bIsNewSample)); - const FfxFloat32 fKernelBiasMax = ComputeMaxKernelWeight() * (1.0f - fKernelReactiveFactor); - - const FfxFloat32 fKernelBiasMin = ffxMax(1.0f, ((1.0f + fKernelBiasMax) * 0.3f)); - const FfxFloat32 fKernelBiasFactor = ffxMax(0.0f, ffxMax(0.25f * params.fDepthClipFactor, fKernelReactiveFactor)); - const FfxFloat32 fKernelBias = ffxLerp(fKernelBiasMax, fKernelBiasMin, fKernelBiasFactor); - - const FfxFloat32 fRectificationCurveBias = ffxLerp(-2.0f, -3.0f, ffxSaturate(params.fHrVelocity / 50.0f)); - - FFX_UNROLL - for (FfxInt32 row = 0; row < 3; row++) { - FFX_UNROLL - for (FfxInt32 col = 0; col < 3; col++) { - FfxInt32 iSampleIndex = col + (row << 2); - - const FfxInt32x2 sampleColRow = FfxInt32x2(bFlipCol ? (3 - col) : col, bFlipRow ? (3 - row) : row); - const FfxFloat32x2 fOffset = fOffsetTL + FfxFloat32x2(sampleColRow); - FfxFloat32x2 fSrcSampleOffset = fBaseSampleOffset + fOffset; - - FfxInt32x2 iSrcSamplePos = FfxInt32x2(iSrcInputPos) + FfxInt32x2(offsetTL) + sampleColRow; - - const FfxFloat32 fOnScreenFactor = FfxFloat32(IsOnScreen(FfxInt32x2(iSrcSamplePos), FfxInt32x2(RenderSize()))); - FfxFloat32 fSampleWeight = fOnScreenFactor * FfxFloat32(GetUpsampleLanczosWeight(fSrcSampleOffset, fKernelBias)); - - fColorAndWeight += FfxFloat32x4(fSamples[iSampleIndex] * fSampleWeight, fSampleWeight); - - // Update rectification box - { - const FfxFloat32 fSrcSampleOffsetSq = dot(fSrcSampleOffset, fSrcSampleOffset); - const FfxFloat32 fBoxSampleWeight = exp(fRectificationCurveBias * fSrcSampleOffsetSq); - - const FfxBoolean bInitialSample = (row == 0) && (col == 0); - RectificationBoxAddSample(bInitialSample, clippingBox, fSamples[iSampleIndex], fBoxSampleWeight); - } - } - } - - RectificationBoxComputeVarianceBoxData(clippingBox); - - fColorAndWeight.w *= FfxFloat32(fColorAndWeight.w > FSR3UPSCALER_EPSILON); - - if (fColorAndWeight.w > FSR3UPSCALER_EPSILON) { - // Normalize for deringing (we need to compare colors) - fColorAndWeight.xyz = fColorAndWeight.xyz / fColorAndWeight.w; - fColorAndWeight.w *= fUpsampleLanczosWeightScale; - - Deringing(clippingBox, fColorAndWeight.xyz); - } - - #if FFX_FSR3UPSCALER_OPTION_UPSAMPLE_SAMPLERS_USE_DATA_HALF && FFX_HALF - #include "ffx_fsr3upscaler_force16_end.h" - #endif - - return fColorAndWeight; -} - -#endif //!defined( FFX_FSR3UPSCALER_UPSAMPLE_H ) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta deleted file mode 100644 index dc0a729..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/ffx_fsr3upscaler_upsample.h.meta +++ /dev/null @@ -1,65 +0,0 @@ -fileFormatVersion: 2 -guid: 3e7832c4a9154414f9eaa125acfe6cd5 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - DefaultValueInitialized: true - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta deleted file mode 100644 index 731c94f..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 09438bc445e66204f970dc99ca8dae5a -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h deleted file mode 100644 index e780995..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h +++ /dev/null @@ -1,1252 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup FfxGPUFsr1 FidelityFX FSR1 -/// FidelityFX Super Resolution 1 GPU documentation -/// -/// @ingroup FfxGPUEffects - -/// Setup required constant values for EASU (works on CPU or GPU). -/// -/// @param [out] con0 -/// @param [out] con1 -/// @param [out] con2 -/// @param [out] con3 -/// @param [in] inputViewportInPixelsX The rendered image resolution being upscaled in X dimension. -/// @param [in] inputViewportInPixelsY The rendered image resolution being upscaled in Y dimension. -/// @param [in] inputSizeInPixelsX The resolution of the resource containing the input image (useful for dynamic resolution) in X dimension. -/// @param [in] inputSizeInPixelsY The resolution of the resource containing the input image (useful for dynamic resolution) in Y dimension. -/// @param [in] outputSizeInPixelsX The display resolution which the input image gets upscaled to in X dimension. -/// @param [in] outputSizeInPixelsY The display resolution which the input image gets upscaled to in Y dimension. -/// -/// @ingroup FfxGPUFsr1 -FFX_STATIC void ffxFsrPopulateEasuConstants( - FFX_PARAMETER_INOUT FfxUInt32x4 con0, - FFX_PARAMETER_INOUT FfxUInt32x4 con1, - FFX_PARAMETER_INOUT FfxUInt32x4 con2, - FFX_PARAMETER_INOUT FfxUInt32x4 con3, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsY, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsY, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsY) -{ - // Output integer position to a pixel position in viewport. - con0[0] = ffxAsUInt32(inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX)); - con0[1] = ffxAsUInt32(inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY)); - con0[2] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX) - FfxFloat32(0.5)); - con0[3] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY) - FfxFloat32(0.5)); - - // Viewport pixel position to normalized image space. - // This is used to get upper-left of 'F' tap. - con1[0] = ffxAsUInt32(ffxReciprocal(inputSizeInPixelsX)); - con1[1] = ffxAsUInt32(ffxReciprocal(inputSizeInPixelsY)); - - // Centers of gather4, first offset from upper-left of 'F'. - // +---+---+ - // | | | - // +--(0)--+ - // | b | c | - // +---F---+---+---+ - // | e | f | g | h | - // +--(1)--+--(2)--+ - // | i | j | k | l | - // +---+---+---+---+ - // | n | o | - // +--(3)--+ - // | | | - // +---+---+ - con1[2] = ffxAsUInt32(FfxFloat32(1.0) * ffxReciprocal(inputSizeInPixelsX)); - con1[3] = ffxAsUInt32(FfxFloat32(-1.0) * ffxReciprocal(inputSizeInPixelsY)); - - // These are from (0) instead of 'F'. - con2[0] = ffxAsUInt32(FfxFloat32(-1.0) * ffxReciprocal(inputSizeInPixelsX)); - con2[1] = ffxAsUInt32(FfxFloat32(2.0) * ffxReciprocal(inputSizeInPixelsY)); - con2[2] = ffxAsUInt32(FfxFloat32(1.0) * ffxReciprocal(inputSizeInPixelsX)); - con2[3] = ffxAsUInt32(FfxFloat32(2.0) * ffxReciprocal(inputSizeInPixelsY)); - con3[0] = ffxAsUInt32(FfxFloat32(0.0) * ffxReciprocal(inputSizeInPixelsX)); - con3[1] = ffxAsUInt32(FfxFloat32(4.0) * ffxReciprocal(inputSizeInPixelsY)); - con3[2] = con3[3] = 0; -} - -/// Setup required constant values for EASU (works on CPU or GPU). -/// -/// @param [out] con0 -/// @param [out] con1 -/// @param [out] con2 -/// @param [out] con3 -/// @param [in] inputViewportInPixelsX The resolution of the input in the X dimension. -/// @param [in] inputViewportInPixelsY The resolution of the input in the Y dimension. -/// @param [in] inputSizeInPixelsX The input size in pixels in the X dimension. -/// @param [in] inputSizeInPixelsY The input size in pixels in the Y dimension. -/// @param [in] outputSizeInPixelsX The output size in pixels in the X dimension. -/// @param [in] outputSizeInPixelsY The output size in pixels in the Y dimension. -/// @param [in] inputOffsetInPixelsX The input image offset in the X dimension into the resource containing it (useful for dynamic resolution). -/// @param [in] inputOffsetInPixelsY The input image offset in the Y dimension into the resource containing it (useful for dynamic resolution). -/// -/// @ingroup FfxGPUFsr1 -FFX_STATIC void ffxFsrPopulateEasuConstantsOffset( - FFX_PARAMETER_INOUT FfxUInt32x4 con0, - FFX_PARAMETER_INOUT FfxUInt32x4 con1, - FFX_PARAMETER_INOUT FfxUInt32x4 con2, - FFX_PARAMETER_INOUT FfxUInt32x4 con3, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputViewportInPixelsY, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputSizeInPixelsY, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsX, - FFX_PARAMETER_IN FfxFloat32 outputSizeInPixelsY, - FFX_PARAMETER_IN FfxFloat32 inputOffsetInPixelsX, - FFX_PARAMETER_IN FfxFloat32 inputOffsetInPixelsY) -{ - ffxFsrPopulateEasuConstants( - con0, - con1, - con2, - con3, - inputViewportInPixelsX, - inputViewportInPixelsY, - inputSizeInPixelsX, - inputSizeInPixelsY, - outputSizeInPixelsX, - outputSizeInPixelsY); - - // override - con0[2] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsX * ffxReciprocal(outputSizeInPixelsX) - FfxFloat32(0.5) + inputOffsetInPixelsX); - con0[3] = ffxAsUInt32(FfxFloat32(0.5) * inputViewportInPixelsY * ffxReciprocal(outputSizeInPixelsY) - FfxFloat32(0.5) + inputOffsetInPixelsY); -} - -#if defined(FFX_GPU) && defined(FFX_FSR_EASU_FLOAT) -// Input callback prototypes, need to be implemented by calling shader -FfxFloat32x4 FsrEasuRF(FfxFloat32x2 p); -FfxFloat32x4 FsrEasuGF(FfxFloat32x2 p); -FfxFloat32x4 FsrEasuBF(FfxFloat32x2 p); - -// Filtering for a given tap for the scalar. -void fsrEasuTapFloat( - FFX_PARAMETER_INOUT FfxFloat32x3 accumulatedColor, // Accumulated color, with negative lobe. - FFX_PARAMETER_INOUT FfxFloat32 accumulatedWeight, // Accumulated weight. - FFX_PARAMETER_IN FfxFloat32x2 pixelOffset, // Pixel offset from resolve position to tap. - FFX_PARAMETER_IN FfxFloat32x2 gradientDirection, // Gradient direction. - FFX_PARAMETER_IN FfxFloat32x2 length, // Length. - FFX_PARAMETER_IN FfxFloat32 negativeLobeStrength, // Negative lobe strength. - FFX_PARAMETER_IN FfxFloat32 clippingPoint, // Clipping point. - FFX_PARAMETER_IN FfxFloat32x3 color) // Tap color. -{ - // Rotate offset by direction. - FfxFloat32x2 rotatedOffset; - rotatedOffset.x = (pixelOffset.x * (gradientDirection.x)) + (pixelOffset.y * gradientDirection.y); - rotatedOffset.y = (pixelOffset.x * (-gradientDirection.y)) + (pixelOffset.y * gradientDirection.x); - - // Anisotropy. - rotatedOffset *= length; - - // Compute distance^2. - FfxFloat32 distanceSquared = rotatedOffset.x * rotatedOffset.x + rotatedOffset.y * rotatedOffset.y; - - // Limit to the window as at corner, 2 taps can easily be outside. - distanceSquared = ffxMin(distanceSquared, clippingPoint); - - // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. - // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 - // |_______________________________________| |_______________| - // base window - // The general form of the 'base' is, - // (a*(b*x^2-1)^2-(a-1)) - // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. - FfxFloat32 weightB = FfxFloat32(2.0 / 5.0) * distanceSquared + FfxFloat32(-1.0); - FfxFloat32 weightA = negativeLobeStrength * distanceSquared + FfxFloat32(-1.0); - weightB *= weightB; - weightA *= weightA; - weightB = FfxFloat32(25.0 / 16.0) * weightB + FfxFloat32(-(25.0 / 16.0 - 1.0)); - FfxFloat32 weight = weightB * weightA; - - // Do weighted average. - accumulatedColor += color * weight; - accumulatedWeight += weight; -} - -// Accumulate direction and length. -void fsrEasuSetFloat( - FFX_PARAMETER_INOUT FfxFloat32x2 direction, - FFX_PARAMETER_INOUT FfxFloat32 length, - FFX_PARAMETER_IN FfxFloat32x2 pp, - FFX_PARAMETER_IN FfxBoolean biS, - FFX_PARAMETER_IN FfxBoolean biT, - FFX_PARAMETER_IN FfxBoolean biU, - FFX_PARAMETER_IN FfxBoolean biV, - FFX_PARAMETER_IN FfxFloat32 lA, - FFX_PARAMETER_IN FfxFloat32 lB, - FFX_PARAMETER_IN FfxFloat32 lC, - FFX_PARAMETER_IN FfxFloat32 lD, - FFX_PARAMETER_IN FfxFloat32 lE) -{ - // Compute bilinear weight, branches factor out as predicates are compiler time immediates. - // s t - // u v - FfxFloat32 weight = FfxFloat32(0.0); - if (biS) - weight = (FfxFloat32(1.0) - pp.x) * (FfxFloat32(1.0) - pp.y); - if (biT) - weight = pp.x * (FfxFloat32(1.0) - pp.y); - if (biU) - weight = (FfxFloat32(1.0) - pp.x) * pp.y; - if (biV) - weight = pp.x * pp.y; - - // Direction is the '+' diff. - // a - // b c d - // e - // Then takes magnitude from abs average of both sides of 'c'. - // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. - FfxFloat32 dc = lD - lC; - FfxFloat32 cb = lC - lB; - FfxFloat32 lengthX = max(abs(dc), abs(cb)); - lengthX = ffxApproximateReciprocal(lengthX); - FfxFloat32 directionX = lD - lB; - direction.x += directionX * weight; - lengthX = ffxSaturate(abs(directionX) * lengthX); - lengthX *= lengthX; - length += lengthX * weight; - - // Repeat for the y axis. - FfxFloat32 ec = lE - lC; - FfxFloat32 ca = lC - lA; - FfxFloat32 lengthY = max(abs(ec), abs(ca)); - lengthY = ffxApproximateReciprocal(lengthY); - FfxFloat32 directionY = lE - lA; - direction.y += directionY * weight; - lengthY = ffxSaturate(abs(directionY) * lengthY); - lengthY *= lengthY; - length += lengthY * weight; -} - -/// Apply edge-aware spatial upsampling using 32bit floating point precision calculations. -/// -/// @param [out] outPixel The computed color of a pixel. -/// @param [in] integerPosition Integer pixel position within the output. -/// @param [in] con0 The first constant value generated by ffxFsrPopulateEasuConstants. -/// @param [in] con1 The second constant value generated by ffxFsrPopulateEasuConstants. -/// @param [in] con2 The third constant value generated by ffxFsrPopulateEasuConstants. -/// @param [in] con3 The fourth constant value generated by ffxFsrPopulateEasuConstants. -/// -/// @ingroup FSR -void ffxFsrEasuFloat( - FFX_PARAMETER_OUT FfxFloat32x3 pix, - FFX_PARAMETER_IN FfxUInt32x2 ip, - FFX_PARAMETER_IN FfxUInt32x4 con0, - FFX_PARAMETER_IN FfxUInt32x4 con1, - FFX_PARAMETER_IN FfxUInt32x4 con2, - FFX_PARAMETER_IN FfxUInt32x4 con3) -{ - // Get position of 'f'. - FfxFloat32x2 pp = FfxFloat32x2(ip) * ffxAsFloat(con0.xy) + ffxAsFloat(con0.zw); - FfxFloat32x2 fp = floor(pp); - pp -= fp; - - // 12-tap kernel. - // b c - // e f g h - // i j k l - // n o - // Gather 4 ordering. - // a b - // r g - // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, - // a b <- unused (z) - // r g - // a b a b - // r g r g - // a b - // r g <- unused (z) - // Allowing dead-code removal to remove the 'z's. - FfxFloat32x2 p0 = fp * ffxAsFloat(con1.xy) + ffxAsFloat(con1.zw); - - // These are from p0 to avoid pulling two constants on pre-Navi hardware. - FfxFloat32x2 p1 = p0 + ffxAsFloat(con2.xy); - FfxFloat32x2 p2 = p0 + ffxAsFloat(con2.zw); - FfxFloat32x2 p3 = p0 + ffxAsFloat(con3.xy); - FfxFloat32x4 bczzR = FsrEasuRF(p0); - FfxFloat32x4 bczzG = FsrEasuGF(p0); - FfxFloat32x4 bczzB = FsrEasuBF(p0); - FfxFloat32x4 ijfeR = FsrEasuRF(p1); - FfxFloat32x4 ijfeG = FsrEasuGF(p1); - FfxFloat32x4 ijfeB = FsrEasuBF(p1); - FfxFloat32x4 klhgR = FsrEasuRF(p2); - FfxFloat32x4 klhgG = FsrEasuGF(p2); - FfxFloat32x4 klhgB = FsrEasuBF(p2); - FfxFloat32x4 zzonR = FsrEasuRF(p3); - FfxFloat32x4 zzonG = FsrEasuGF(p3); - FfxFloat32x4 zzonB = FsrEasuBF(p3); - - // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). - FfxFloat32x4 bczzL = bczzB * ffxBroadcast4(0.5) + (bczzR * ffxBroadcast4(0.5) + bczzG); - FfxFloat32x4 ijfeL = ijfeB * ffxBroadcast4(0.5) + (ijfeR * ffxBroadcast4(0.5) + ijfeG); - FfxFloat32x4 klhgL = klhgB * ffxBroadcast4(0.5) + (klhgR * ffxBroadcast4(0.5) + klhgG); - FfxFloat32x4 zzonL = zzonB * ffxBroadcast4(0.5) + (zzonR * ffxBroadcast4(0.5) + zzonG); - - // Rename. - FfxFloat32 bL = bczzL.x; - FfxFloat32 cL = bczzL.y; - FfxFloat32 iL = ijfeL.x; - FfxFloat32 jL = ijfeL.y; - FfxFloat32 fL = ijfeL.z; - FfxFloat32 eL = ijfeL.w; - FfxFloat32 kL = klhgL.x; - FfxFloat32 lL = klhgL.y; - FfxFloat32 hL = klhgL.z; - FfxFloat32 gL = klhgL.w; - FfxFloat32 oL = zzonL.z; - FfxFloat32 nL = zzonL.w; - - // Accumulate for bilinear interpolation. - FfxFloat32x2 dir = ffxBroadcast2(0.0); - FfxFloat32 len = FfxFloat32(0.0); - fsrEasuSetFloat(dir, len, pp, FFX_TRUE, FFX_FALSE, FFX_FALSE, FFX_FALSE, bL, eL, fL, gL, jL); - fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_TRUE, FFX_FALSE, FFX_FALSE, cL, fL, gL, hL, kL); - fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_FALSE, FFX_TRUE, FFX_FALSE, fL, iL, jL, kL, nL); - fsrEasuSetFloat(dir, len, pp, FFX_FALSE, FFX_FALSE, FFX_FALSE, FFX_TRUE, gL, jL, kL, lL, oL); - - // Normalize with approximation, and cleanup close to zero. - FfxFloat32x2 dir2 = dir * dir; - FfxFloat32 dirR = dir2.x + dir2.y; - FfxBoolean zro = dirR < FfxFloat32(1.0 / 32768.0); - dirR = ffxApproximateReciprocalSquareRoot(dirR); - dirR = zro ? FfxFloat32(1.0) : dirR; - dir.x = zro ? FfxFloat32(1.0) : dir.x; - dir *= ffxBroadcast2(dirR); - - // Transform from {0 to 2} to {0 to 1} range, and shape with square. - len = len * FfxFloat32(0.5); - len *= len; - - // Stretch kernel {1.0 vert|horz, to sqrt(2.0) on diagonal}. - FfxFloat32 stretch = (dir.x * dir.x + dir.y * dir.y) * ffxApproximateReciprocal(max(abs(dir.x), abs(dir.y))); - - // Anisotropic length after rotation, - // x := 1.0 lerp to 'stretch' on edges - // y := 1.0 lerp to 2x on edges - FfxFloat32x2 len2 = FfxFloat32x2(FfxFloat32(1.0) + (stretch - FfxFloat32(1.0)) * len, FfxFloat32(1.0) + FfxFloat32(-0.5) * len); - - // Based on the amount of 'edge', - // the window shifts from +/-{sqrt(2.0) to slightly beyond 2.0}. - FfxFloat32 lob = FfxFloat32(0.5) + FfxFloat32((1.0 / 4.0 - 0.04) - 0.5) * len; - - // Set distance^2 clipping point to the end of the adjustable window. - FfxFloat32 clp = ffxApproximateReciprocal(lob); - - // Accumulation mixed with min/max of 4 nearest. - // b c - // e f g h - // i j k l - // n o - FfxFloat32x3 min4 = - ffxMin(ffxMin3(FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z), FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w), FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)), - FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); - FfxFloat32x3 max4 = - max(ffxMax3(FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z), FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w), FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)), FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); - - // Accumulation. - FfxFloat32x3 aC = ffxBroadcast3(0.0); - FfxFloat32 aW = FfxFloat32(0.0); - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, -1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(bczzR.x, bczzG.x, bczzB.x)); // b - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, -1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(bczzR.y, bczzG.y, bczzB.y)); // c - fsrEasuTapFloat(aC, aW, FfxFloat32x2(-1.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.x, ijfeG.x, ijfeB.x)); // i - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.y, ijfeG.y, ijfeB.y)); // j - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.z, ijfeG.z, ijfeB.z)); // f - fsrEasuTapFloat(aC, aW, FfxFloat32x2(-1.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(ijfeR.w, ijfeG.w, ijfeB.w)); // e - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.x, klhgG.x, klhgB.x)); // k - fsrEasuTapFloat(aC, aW, FfxFloat32x2(2.0, 1.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.y, klhgG.y, klhgB.y)); // l - fsrEasuTapFloat(aC, aW, FfxFloat32x2(2.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.z, klhgG.z, klhgB.z)); // h - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 0.0) - pp, dir, len2, lob, clp, FfxFloat32x3(klhgR.w, klhgG.w, klhgB.w)); // g - fsrEasuTapFloat(aC, aW, FfxFloat32x2(1.0, 2.0) - pp, dir, len2, lob, clp, FfxFloat32x3(zzonR.z, zzonG.z, zzonB.z)); // o - fsrEasuTapFloat(aC, aW, FfxFloat32x2(0.0, 2.0) - pp, dir, len2, lob, clp, FfxFloat32x3(zzonR.w, zzonG.w, zzonB.w)); // n - - // Normalize and dering. - pix = ffxMin(max4, max(min4, aC * ffxBroadcast3(rcp(aW)))); -} -#endif // #if defined(FFX_GPU) && defined(FFX_FSR_EASU_FLOAT) - -#if defined(FFX_GPU) && FFX_HALF == 1 && defined(FFX_FSR_EASU_HALF) -// Input callback prototypes, need to be implemented by calling shader -FfxFloat16x4 FsrEasuRH(FfxFloat32x2 p); -FfxFloat16x4 FsrEasuGH(FfxFloat32x2 p); -FfxFloat16x4 FsrEasuBH(FfxFloat32x2 p); - -// This runs 2 taps in parallel. -void FsrEasuTapH( - FFX_PARAMETER_INOUT FfxFloat16x2 aCR, - FFX_PARAMETER_INOUT FfxFloat16x2 aCG, - FFX_PARAMETER_INOUT FfxFloat16x2 aCB, - FFX_PARAMETER_INOUT FfxFloat16x2 aW, - FFX_PARAMETER_IN FfxFloat16x2 offX, - FFX_PARAMETER_IN FfxFloat16x2 offY, - FFX_PARAMETER_IN FfxFloat16x2 dir, - FFX_PARAMETER_IN FfxFloat16x2 len, - FFX_PARAMETER_IN FfxFloat16 lob, - FFX_PARAMETER_IN FfxFloat16 clp, - FFX_PARAMETER_IN FfxFloat16x2 cR, - FFX_PARAMETER_IN FfxFloat16x2 cG, - FFX_PARAMETER_IN FfxFloat16x2 cB) -{ - FfxFloat16x2 vX, vY; - vX = offX * dir.xx + offY * dir.yy; - vY = offX * (-dir.yy) + offY * dir.xx; - vX *= len.x; - vY *= len.y; - FfxFloat16x2 d2 = vX * vX + vY * vY; - d2 = min(d2, FFX_BROADCAST_FLOAT16X2(clp)); - FfxFloat16x2 wB = FFX_BROADCAST_FLOAT16X2(2.0 / 5.0) * d2 + FFX_BROADCAST_FLOAT16X2(-1.0); - FfxFloat16x2 wA = FFX_BROADCAST_FLOAT16X2(lob) * d2 + FFX_BROADCAST_FLOAT16X2(-1.0); - wB *= wB; - wA *= wA; - wB = FFX_BROADCAST_FLOAT16X2(25.0 / 16.0) * wB + FFX_BROADCAST_FLOAT16X2(-(25.0 / 16.0 - 1.0)); - FfxFloat16x2 w = wB * wA; - aCR += cR * w; - aCG += cG * w; - aCB += cB * w; - aW += w; -} - -// This runs 2 taps in parallel. -void FsrEasuSetH( - FFX_PARAMETER_INOUT FfxFloat16x2 dirPX, - FFX_PARAMETER_INOUT FfxFloat16x2 dirPY, - FFX_PARAMETER_INOUT FfxFloat16x2 lenP, - FFX_PARAMETER_IN FfxFloat16x2 pp, - FFX_PARAMETER_IN FfxBoolean biST, - FFX_PARAMETER_IN FfxBoolean biUV, - FFX_PARAMETER_IN FfxFloat16x2 lA, - FFX_PARAMETER_IN FfxFloat16x2 lB, - FFX_PARAMETER_IN FfxFloat16x2 lC, - FFX_PARAMETER_IN FfxFloat16x2 lD, - FFX_PARAMETER_IN FfxFloat16x2 lE) -{ - FfxFloat16x2 w = FFX_BROADCAST_FLOAT16X2(0.0); - - if (biST) - w = (FfxFloat16x2(1.0, 0.0) + FfxFloat16x2(-pp.x, pp.x)) * FFX_BROADCAST_FLOAT16X2(FFX_BROADCAST_FLOAT16(1.0) - pp.y); - - if (biUV) - w = (FfxFloat16x2(1.0, 0.0) + FfxFloat16x2(-pp.x, pp.x)) * FFX_BROADCAST_FLOAT16X2(pp.y); - - // ABS is not free in the packed FP16 path. - FfxFloat16x2 dc = lD - lC; - FfxFloat16x2 cb = lC - lB; - FfxFloat16x2 lenX = max(abs(dc), abs(cb)); - lenX = ffxReciprocalHalf(lenX); - - FfxFloat16x2 dirX = lD - lB; - dirPX += dirX * w; - lenX = FfxFloat16x2(ffxSaturate(abs(dirX) * lenX)); - lenX *= lenX; - lenP += lenX * w; - FfxFloat16x2 ec = lE - lC; - FfxFloat16x2 ca = lC - lA; - FfxFloat16x2 lenY = max(abs(ec), abs(ca)); - lenY = ffxReciprocalHalf(lenY); - FfxFloat16x2 dirY = lE - lA; - dirPY += dirY * w; - lenY = FfxFloat16x2(ffxSaturate(abs(dirY) * lenY)); - lenY *= lenY; - lenP += lenY * w; -} - -void FsrEasuH( - FFX_PARAMETER_OUT FfxFloat16x3 pix, - FFX_PARAMETER_IN FfxUInt32x2 ip, - FFX_PARAMETER_IN FfxUInt32x4 con0, - FFX_PARAMETER_IN FfxUInt32x4 con1, - FFX_PARAMETER_IN FfxUInt32x4 con2, - FFX_PARAMETER_IN FfxUInt32x4 con3) -{ - FfxFloat32x2 pp = FfxFloat32x2(ip) * ffxAsFloat(con0.xy) + ffxAsFloat(con0.zw); - FfxFloat32x2 fp = floor(pp); - pp -= fp; - FfxFloat16x2 ppp = FfxFloat16x2(pp); - - FfxFloat32x2 p0 = fp * ffxAsFloat(con1.xy) + ffxAsFloat(con1.zw); - FfxFloat32x2 p1 = p0 + ffxAsFloat(con2.xy); - FfxFloat32x2 p2 = p0 + ffxAsFloat(con2.zw); - FfxFloat32x2 p3 = p0 + ffxAsFloat(con3.xy); - FfxFloat16x4 bczzR = FsrEasuRH(p0); - FfxFloat16x4 bczzG = FsrEasuGH(p0); - FfxFloat16x4 bczzB = FsrEasuBH(p0); - FfxFloat16x4 ijfeR = FsrEasuRH(p1); - FfxFloat16x4 ijfeG = FsrEasuGH(p1); - FfxFloat16x4 ijfeB = FsrEasuBH(p1); - FfxFloat16x4 klhgR = FsrEasuRH(p2); - FfxFloat16x4 klhgG = FsrEasuGH(p2); - FfxFloat16x4 klhgB = FsrEasuBH(p2); - FfxFloat16x4 zzonR = FsrEasuRH(p3); - FfxFloat16x4 zzonG = FsrEasuGH(p3); - FfxFloat16x4 zzonB = FsrEasuBH(p3); - - FfxFloat16x4 bczzL = bczzB * FFX_BROADCAST_FLOAT16X4(0.5) + (bczzR * FFX_BROADCAST_FLOAT16X4(0.5) + bczzG); - FfxFloat16x4 ijfeL = ijfeB * FFX_BROADCAST_FLOAT16X4(0.5) + (ijfeR * FFX_BROADCAST_FLOAT16X4(0.5) + ijfeG); - FfxFloat16x4 klhgL = klhgB * FFX_BROADCAST_FLOAT16X4(0.5) + (klhgR * FFX_BROADCAST_FLOAT16X4(0.5) + klhgG); - FfxFloat16x4 zzonL = zzonB * FFX_BROADCAST_FLOAT16X4(0.5) + (zzonR * FFX_BROADCAST_FLOAT16X4(0.5) + zzonG); - FfxFloat16 bL = bczzL.x; - FfxFloat16 cL = bczzL.y; - FfxFloat16 iL = ijfeL.x; - FfxFloat16 jL = ijfeL.y; - FfxFloat16 fL = ijfeL.z; - FfxFloat16 eL = ijfeL.w; - FfxFloat16 kL = klhgL.x; - FfxFloat16 lL = klhgL.y; - FfxFloat16 hL = klhgL.z; - FfxFloat16 gL = klhgL.w; - FfxFloat16 oL = zzonL.z; - FfxFloat16 nL = zzonL.w; - - // This part is different, accumulating 2 taps in parallel. - FfxFloat16x2 dirPX = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 dirPY = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 lenP = FFX_BROADCAST_FLOAT16X2(0.0); - FsrEasuSetH(dirPX, - dirPY, - lenP, - ppp, - FfxBoolean(true), - FfxBoolean(false), - FfxFloat16x2(bL, cL), - FfxFloat16x2(eL, fL), - FfxFloat16x2(fL, gL), - FfxFloat16x2(gL, hL), - FfxFloat16x2(jL, kL)); - FsrEasuSetH(dirPX, - dirPY, - lenP, - ppp, - FfxBoolean(false), - FfxBoolean(true), - FfxFloat16x2(fL, gL), - FfxFloat16x2(iL, jL), - FfxFloat16x2(jL, kL), - FfxFloat16x2(kL, lL), - FfxFloat16x2(nL, oL)); - FfxFloat16x2 dir = FfxFloat16x2(dirPX.r + dirPX.g, dirPY.r + dirPY.g); - FfxFloat16 len = lenP.r + lenP.g; - - FfxFloat16x2 dir2 = dir * dir; - FfxFloat16 dirR = dir2.x + dir2.y; - FfxUInt32 zro = FfxUInt32(dirR < FFX_BROADCAST_FLOAT16(1.0 / 32768.0)); - dirR = ffxApproximateReciprocalSquareRootHalf(dirR); - dirR = (zro > 0) ? FFX_BROADCAST_FLOAT16(1.0) : dirR; - dir.x = (zro > 0) ? FFX_BROADCAST_FLOAT16(1.0) : dir.x; - dir *= FFX_BROADCAST_FLOAT16X2(dirR); - len = len * FFX_BROADCAST_FLOAT16(0.5); - len *= len; - FfxFloat16 stretch = (dir.x * dir.x + dir.y * dir.y) * ffxApproximateReciprocalHalf(max(abs(dir.x), abs(dir.y))); - FfxFloat16x2 len2 = - FfxFloat16x2(FFX_BROADCAST_FLOAT16(1.0) + (stretch - FFX_BROADCAST_FLOAT16(1.0)) * len, FFX_BROADCAST_FLOAT16(1.0) + FFX_BROADCAST_FLOAT16(-0.5) * len); - FfxFloat16 lob = FFX_BROADCAST_FLOAT16(0.5) + FFX_BROADCAST_FLOAT16((1.0 / 4.0 - 0.04) - 0.5) * len; - FfxFloat16 clp = ffxApproximateReciprocalHalf(lob); - - // FP16 is different, using packed trick to do min and max in same operation. - FfxFloat16x2 bothR = - max(max(FfxFloat16x2(-ijfeR.z, ijfeR.z), FfxFloat16x2(-klhgR.w, klhgR.w)), max(FfxFloat16x2(-ijfeR.y, ijfeR.y), FfxFloat16x2(-klhgR.x, klhgR.x))); - FfxFloat16x2 bothG = - max(max(FfxFloat16x2(-ijfeG.z, ijfeG.z), FfxFloat16x2(-klhgG.w, klhgG.w)), max(FfxFloat16x2(-ijfeG.y, ijfeG.y), FfxFloat16x2(-klhgG.x, klhgG.x))); - FfxFloat16x2 bothB = - max(max(FfxFloat16x2(-ijfeB.z, ijfeB.z), FfxFloat16x2(-klhgB.w, klhgB.w)), max(FfxFloat16x2(-ijfeB.y, ijfeB.y), FfxFloat16x2(-klhgB.x, klhgB.x))); - - // This part is different for FP16, working pairs of taps at a time. - FfxFloat16x2 pR = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 pG = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 pB = FFX_BROADCAST_FLOAT16X2(0.0); - FfxFloat16x2 pW = FFX_BROADCAST_FLOAT16X2(0.0); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(0.0, 1.0) - ppp.xx, FfxFloat16x2(-1.0, -1.0) - ppp.yy, dir, len2, lob, clp, bczzR.xy, bczzG.xy, bczzB.xy); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(-1.0, 0.0) - ppp.xx, FfxFloat16x2(1.0, 1.0) - ppp.yy, dir, len2, lob, clp, ijfeR.xy, ijfeG.xy, ijfeB.xy); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(0.0, -1.0) - ppp.xx, FfxFloat16x2(0.0, 0.0) - ppp.yy, dir, len2, lob, clp, ijfeR.zw, ijfeG.zw, ijfeB.zw); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(1.0, 2.0) - ppp.xx, FfxFloat16x2(1.0, 1.0) - ppp.yy, dir, len2, lob, clp, klhgR.xy, klhgG.xy, klhgB.xy); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(2.0, 1.0) - ppp.xx, FfxFloat16x2(0.0, 0.0) - ppp.yy, dir, len2, lob, clp, klhgR.zw, klhgG.zw, klhgB.zw); - FsrEasuTapH(pR, pG, pB, pW, FfxFloat16x2(1.0, 0.0) - ppp.xx, FfxFloat16x2(2.0, 2.0) - ppp.yy, dir, len2, lob, clp, zzonR.zw, zzonG.zw, zzonB.zw); - FfxFloat16x3 aC = FfxFloat16x3(pR.x + pR.y, pG.x + pG.y, pB.x + pB.y); - FfxFloat16 aW = pW.x + pW.y; - - // Slightly different for FP16 version due to combined min and max. - pix = min(FfxFloat16x3(bothR.y, bothG.y, bothB.y), max(-FfxFloat16x3(bothR.x, bothG.x, bothB.x), aC * FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(aW)))); -} -#endif // #if defined(FFX_GPU) && defined(FFX_HALF) && defined(FFX_FSR_EASU_HALF) - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING -// -//------------------------------------------------------------------------------------------------------------------------------ -// CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness. -// RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. -// RCAS also has a built in process to limit sharpening of what it detects as possible noise. -// RCAS sharper does not support scaling, as it should be applied after EASU scaling. -// Pass EASU output straight into RCAS, no color conversions necessary. -//------------------------------------------------------------------------------------------------------------------------------ -// RCAS is based on the following logic. -// RCAS uses a 5 tap filter in a cross pattern (same as CAS), -// w n -// w 1 w for taps w m e -// w s -// Where 'w' is the negative lobe weight. -// output = (w*(n+e+w+s)+m)/(4*w+1) -// RCAS solves for 'w' by seeing where the signal might clip out of the {0 to 1} input range, -// 0 == (w*(n+e+w+s)+m)/(4*w+1) -> w = -m/(n+e+w+s) -// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) -// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. -// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. -// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. -// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. -// This stabilizes RCAS. -// RCAS does a simple highpass which is normalized against the local contrast then shaped, -// 0.25 -// 0.25 -1 0.25 -// 0.25 -// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. -// -// GLSL example for the required callbacks : -// -// FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p){return FfxFloat16x4(imageLoad(imgSrc,FfxInt32x2(p)));} -// void FsrRcasInputH(inout FfxFloat16 r,inout FfxFloat16 g,inout FfxFloat16 b) -// { -// //do any simple input color conversions here or leave empty if none needed -// } -// -// FsrRcasCon need to be called from the CPU or GPU to set up constants. -// Including a GPU example here, the 'con' value would be stored out to a constant buffer. -// -// FfxUInt32x4 con; -// FsrRcasCon(con, -// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. -// --------------- -// RCAS sharpening supports a CAS-like pass-through alpha via, -// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 -// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. -// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, -// #define FSR_RCAS_DENOISE 1 -//============================================================================================================================== -// This is set at the limit of providing unnatural results for sharpening. -#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// CONSTANT SETUP -//============================================================================================================================== -// Call to setup required constant values (works on CPU or GPU). - FFX_STATIC void FsrRcasCon(FfxUInt32x4 con, - // The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. - FfxFloat32 sharpness) - { - // Transform from stops to linear value. - sharpness = exp2(-sharpness); - FfxFloat32x2 hSharp = {sharpness, sharpness}; - con[0] = ffxAsUInt32(sharpness); - con[1] = packHalf2x16(hSharp); - con[2] = 0; - con[3] = 0; - } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// NON-PACKED 32-BIT VERSION -//============================================================================================================================== -#if defined(FFX_GPU)&&defined(FSR_RCAS_F) - // Input callback prototypes that need to be implemented by calling shader - FfxFloat32x4 FsrRcasLoadF(FfxInt32x2 p); - void FsrRcasInputF(inout FfxFloat32 r,inout FfxFloat32 g,inout FfxFloat32 b); -//------------------------------------------------------------------------------------------------------------------------------ - void FsrRcasF(out FfxFloat32 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. - out FfxFloat32 pixG, - out FfxFloat32 pixB, -#ifdef FSR_RCAS_PASSTHROUGH_ALPHA - out FfxFloat32 pixA, -#endif - FfxUInt32x2 ip, // Integer pixel position in output. - FfxUInt32x4 con) - { // Constant generated by RcasSetup(). - // Algorithm uses minimal 3x3 pixel neighborhood. - // b - // d e f - // h - FfxInt32x2 sp = FfxInt32x2(ip); - FfxFloat32x3 b = FsrRcasLoadF(sp + FfxInt32x2(0, -1)).rgb; - FfxFloat32x3 d = FsrRcasLoadF(sp + FfxInt32x2(-1, 0)).rgb; -#ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat32x4 ee = FsrRcasLoadF(sp); - FfxFloat32x3 e = ee.rgb; - pixA = ee.a; -#else - FfxFloat32x3 e = FsrRcasLoadF(sp).rgb; -#endif - FfxFloat32x3 f = FsrRcasLoadF(sp + FfxInt32x2(1, 0)).rgb; - FfxFloat32x3 h = FsrRcasLoadF(sp + FfxInt32x2(0, 1)).rgb; - // Rename (32-bit) or regroup (16-bit). - FfxFloat32 bR = b.r; - FfxFloat32 bG = b.g; - FfxFloat32 bB = b.b; - FfxFloat32 dR = d.r; - FfxFloat32 dG = d.g; - FfxFloat32 dB = d.b; - FfxFloat32 eR = e.r; - FfxFloat32 eG = e.g; - FfxFloat32 eB = e.b; - FfxFloat32 fR = f.r; - FfxFloat32 fG = f.g; - FfxFloat32 fB = f.b; - FfxFloat32 hR = h.r; - FfxFloat32 hG = h.g; - FfxFloat32 hB = h.b; - // Run optional input transform. - FsrRcasInputF(bR, bG, bB); - FsrRcasInputF(dR, dG, dB); - FsrRcasInputF(eR, eG, eB); - FsrRcasInputF(fR, fG, fB); - FsrRcasInputF(hR, hG, hB); - // Luma times 2. - FfxFloat32 bL = bB * FfxFloat32(0.5) + (bR * FfxFloat32(0.5) + bG); - FfxFloat32 dL = dB * FfxFloat32(0.5) + (dR * FfxFloat32(0.5) + dG); - FfxFloat32 eL = eB * FfxFloat32(0.5) + (eR * FfxFloat32(0.5) + eG); - FfxFloat32 fL = fB * FfxFloat32(0.5) + (fR * FfxFloat32(0.5) + fG); - FfxFloat32 hL = hB * FfxFloat32(0.5) + (hR * FfxFloat32(0.5) + hG); - // Noise detection. - FfxFloat32 nz = FfxFloat32(0.25) * bL + FfxFloat32(0.25) * dL + FfxFloat32(0.25) * fL + FfxFloat32(0.25) * hL - eL; - nz = ffxSaturate(abs(nz) * ffxApproximateReciprocalMedium(ffxMax3(ffxMax3(bL, dL, eL), fL, hL) - ffxMin3(ffxMin3(bL, dL, eL), fL, hL))); - nz = FfxFloat32(-0.5) * nz + FfxFloat32(1.0); - // Min and max of ring. - FfxFloat32 mn4R = ffxMin(ffxMin3(bR, dR, fR), hR); - FfxFloat32 mn4G = ffxMin(ffxMin3(bG, dG, fG), hG); - FfxFloat32 mn4B = ffxMin(ffxMin3(bB, dB, fB), hB); - FfxFloat32 mx4R = max(ffxMax3(bR, dR, fR), hR); - FfxFloat32 mx4G = max(ffxMax3(bG, dG, fG), hG); - FfxFloat32 mx4B = max(ffxMax3(bB, dB, fB), hB); - // Immediate constants for peak range. - FfxFloat32x2 peakC = FfxFloat32x2(1.0, -1.0 * 4.0); - // Limiters, these need to be high precision RCPs. - FfxFloat32 hitMinR = mn4R * rcp(FfxFloat32(4.0) * mx4R); - FfxFloat32 hitMinG = mn4G * rcp(FfxFloat32(4.0) * mx4G); - FfxFloat32 hitMinB = mn4B * rcp(FfxFloat32(4.0) * mx4B); - FfxFloat32 hitMaxR = (peakC.x - mx4R) * rcp(FfxFloat32(4.0) * mn4R + peakC.y); - FfxFloat32 hitMaxG = (peakC.x - mx4G) * rcp(FfxFloat32(4.0) * mn4G + peakC.y); - FfxFloat32 hitMaxB = (peakC.x - mx4B) * rcp(FfxFloat32(4.0) * mn4B + peakC.y); - FfxFloat32 lobeR = max(-hitMinR, hitMaxR); - FfxFloat32 lobeG = max(-hitMinG, hitMaxG); - FfxFloat32 lobeB = max(-hitMinB, hitMaxB); - FfxFloat32 lobe = max(FfxFloat32(-FSR_RCAS_LIMIT), ffxMin(ffxMax3(lobeR, lobeG, lobeB), FfxFloat32(0.0))) * ffxAsFloat - (con.x); - // Apply noise removal. -#ifdef FSR_RCAS_DENOISE - lobe *= nz; -#endif - // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. - FfxFloat32 rcpL = ffxApproximateReciprocalMedium(FfxFloat32(4.0) * lobe + FfxFloat32(1.0)); - pixR = (lobe * bR + lobe * dR + lobe * hR + lobe * fR + eR) * rcpL; - pixG = (lobe * bG + lobe * dG + lobe * hG + lobe * fG + eG) * rcpL; - pixB = (lobe * bB + lobe * dB + lobe * hB + lobe * fB + eB) * rcpL; - return; - } -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// NON-PACKED 16-BIT VERSION -//============================================================================================================================== -#if defined(FFX_GPU) && FFX_HALF == 1 && defined(FSR_RCAS_H) - // Input callback prototypes that need to be implemented by calling shader - FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p); - void FsrRcasInputH(inout FfxFloat16 r,inout FfxFloat16 g,inout FfxFloat16 b); -//------------------------------------------------------------------------------------------------------------------------------ - void FsrRcasH( - out FfxFloat16 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. - out FfxFloat16 pixG, - out FfxFloat16 pixB, - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - out FfxFloat16 pixA, - #endif - FfxUInt32x2 ip, // Integer pixel position in output. - FfxUInt32x4 con){ // Constant generated by RcasSetup(). - // Sharpening algorithm uses minimal 3x3 pixel neighborhood. - // b - // d e f - // h - FfxInt16x2 sp=FfxInt16x2(ip); - FfxFloat16x3 b=FsrRcasLoadH(sp+FfxInt16x2( 0,-1)).rgb; - FfxFloat16x3 d=FsrRcasLoadH(sp+FfxInt16x2(-1, 0)).rgb; - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat16x4 ee=FsrRcasLoadH(sp); - FfxFloat16x3 e=ee.rgb;pixA=ee.a; - #else - FfxFloat16x3 e=FsrRcasLoadH(sp).rgb; - #endif - FfxFloat16x3 f=FsrRcasLoadH(sp+FfxInt16x2( 1, 0)).rgb; - FfxFloat16x3 h=FsrRcasLoadH(sp+FfxInt16x2( 0, 1)).rgb; - // Rename (32-bit) or regroup (16-bit). - FfxFloat16 bR=b.r; - FfxFloat16 bG=b.g; - FfxFloat16 bB=b.b; - FfxFloat16 dR=d.r; - FfxFloat16 dG=d.g; - FfxFloat16 dB=d.b; - FfxFloat16 eR=e.r; - FfxFloat16 eG=e.g; - FfxFloat16 eB=e.b; - FfxFloat16 fR=f.r; - FfxFloat16 fG=f.g; - FfxFloat16 fB=f.b; - FfxFloat16 hR=h.r; - FfxFloat16 hG=h.g; - FfxFloat16 hB=h.b; - // Run optional input transform. - FsrRcasInputH(bR,bG,bB); - FsrRcasInputH(dR,dG,dB); - FsrRcasInputH(eR,eG,eB); - FsrRcasInputH(fR,fG,fB); - FsrRcasInputH(hR,hG,hB); - // Luma times 2. - FfxFloat16 bL=bB*FFX_BROADCAST_FLOAT16(0.5)+(bR*FFX_BROADCAST_FLOAT16(0.5)+bG); - FfxFloat16 dL=dB*FFX_BROADCAST_FLOAT16(0.5)+(dR*FFX_BROADCAST_FLOAT16(0.5)+dG); - FfxFloat16 eL=eB*FFX_BROADCAST_FLOAT16(0.5)+(eR*FFX_BROADCAST_FLOAT16(0.5)+eG); - FfxFloat16 fL=fB*FFX_BROADCAST_FLOAT16(0.5)+(fR*FFX_BROADCAST_FLOAT16(0.5)+fG); - FfxFloat16 hL=hB*FFX_BROADCAST_FLOAT16(0.5)+(hR*FFX_BROADCAST_FLOAT16(0.5)+hG); - // Noise detection. - FfxFloat16 nz=FFX_BROADCAST_FLOAT16(0.25)*bL+FFX_BROADCAST_FLOAT16(0.25)*dL+FFX_BROADCAST_FLOAT16(0.25)*fL+FFX_BROADCAST_FLOAT16(0.25)*hL-eL; - nz=FfxFloat16(ffxSaturate(abs(nz)*ffxApproximateReciprocalMediumHalf(ffxMax3Half(ffxMax3Half(bL,dL,eL),fL,hL)-ffxMin3Half(ffxMin3Half(bL,dL,eL),fL,hL)))); - nz=FFX_BROADCAST_FLOAT16(-0.5)*nz+FFX_BROADCAST_FLOAT16(1.0); - // Min and max of ring. - FfxFloat16 mn4R=min(ffxMin3Half(bR,dR,fR),hR); - FfxFloat16 mn4G=min(ffxMin3Half(bG,dG,fG),hG); - FfxFloat16 mn4B=min(ffxMin3Half(bB,dB,fB),hB); - FfxFloat16 mx4R=max(ffxMax3Half(bR,dR,fR),hR); - FfxFloat16 mx4G=max(ffxMax3Half(bG,dG,fG),hG); - FfxFloat16 mx4B=max(ffxMax3Half(bB,dB,fB),hB); - // Immediate constants for peak range. - FfxFloat16x2 peakC=FfxFloat16x2(1.0,-1.0*4.0); - // Limiters, these need to be high precision RCPs. - FfxFloat16 hitMinR=mn4R*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4R); - FfxFloat16 hitMinG=mn4G*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4G); - FfxFloat16 hitMinB=mn4B*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mx4B); - FfxFloat16 hitMaxR=(peakC.x-mx4R)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4R+peakC.y); - FfxFloat16 hitMaxG=(peakC.x-mx4G)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4G+peakC.y); - FfxFloat16 hitMaxB=(peakC.x-mx4B)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16(4.0)*mn4B+peakC.y); - FfxFloat16 lobeR=max(-hitMinR,hitMaxR); - FfxFloat16 lobeG=max(-hitMinG,hitMaxG); - FfxFloat16 lobeB=max(-hitMinB,hitMaxB); - FfxFloat16 lobe=max(FFX_BROADCAST_FLOAT16(-FSR_RCAS_LIMIT),min(ffxMax3Half(lobeR,lobeG,lobeB),FFX_BROADCAST_FLOAT16(0.0)))*FFX_UINT32_TO_FLOAT16X2(con.y).x; - // Apply noise removal. - #ifdef FSR_RCAS_DENOISE - lobe*=nz; - #endif - // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. - FfxFloat16 rcpL=ffxApproximateReciprocalMediumHalf(FFX_BROADCAST_FLOAT16(4.0)*lobe+FFX_BROADCAST_FLOAT16(1.0)); - pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; - pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; - pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; -} -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// PACKED 16-BIT VERSION -//============================================================================================================================== -#if defined(FFX_GPU)&& FFX_HALF == 1 && defined(FSR_RCAS_HX2) - // Input callback prototypes that need to be implemented by the calling shader - FfxFloat16x4 FsrRcasLoadHx2(FfxInt16x2 p); - void FsrRcasInputHx2(inout FfxFloat16x2 r,inout FfxFloat16x2 g,inout FfxFloat16x2 b); -//------------------------------------------------------------------------------------------------------------------------------ - // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. - void FsrRcasDepackHx2(out FfxFloat16x4 pix0,out FfxFloat16x4 pix1,FfxFloat16x2 pixR,FfxFloat16x2 pixG,FfxFloat16x2 pixB){ - #ifdef FFX_HLSL - // Invoke a slower path for DX only, since it won't allow uninitialized values. - pix0.a=pix1.a=0.0; - #endif - pix0.rgb=FfxFloat16x3(pixR.x,pixG.x,pixB.x); - pix1.rgb=FfxFloat16x3(pixR.y,pixG.y,pixB.y);} -//------------------------------------------------------------------------------------------------------------------------------ - void FsrRcasHx2( - // Output values are for 2 8x8 tiles in a 16x8 region. - // pix.x = left 8x8 tile - // pix.y = right 8x8 tile - // This enables later processing to easily be packed as well. - out FfxFloat16x2 pixR, - out FfxFloat16x2 pixG, - out FfxFloat16x2 pixB, - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - out FfxFloat16x2 pixA, - #endif - FfxUInt32x2 ip, // Integer pixel position in output. - FfxUInt32x4 con){ // Constant generated by RcasSetup(). - // No scaling algorithm uses minimal 3x3 pixel neighborhood. - FfxInt16x2 sp0=FfxInt16x2(ip); - FfxFloat16x3 b0=FsrRcasLoadHx2(sp0+FfxInt16x2( 0,-1)).rgb; - FfxFloat16x3 d0=FsrRcasLoadHx2(sp0+FfxInt16x2(-1, 0)).rgb; - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat16x4 ee0=FsrRcasLoadHx2(sp0); - FfxFloat16x3 e0=ee0.rgb;pixA.r=ee0.a; - #else - FfxFloat16x3 e0=FsrRcasLoadHx2(sp0).rgb; - #endif - FfxFloat16x3 f0=FsrRcasLoadHx2(sp0+FfxInt16x2( 1, 0)).rgb; - FfxFloat16x3 h0=FsrRcasLoadHx2(sp0+FfxInt16x2( 0, 1)).rgb; - FfxInt16x2 sp1=sp0+FfxInt16x2(8,0); - FfxFloat16x3 b1=FsrRcasLoadHx2(sp1+FfxInt16x2( 0,-1)).rgb; - FfxFloat16x3 d1=FsrRcasLoadHx2(sp1+FfxInt16x2(-1, 0)).rgb; - #ifdef FSR_RCAS_PASSTHROUGH_ALPHA - FfxFloat16x4 ee1=FsrRcasLoadHx2(sp1); - FfxFloat16x3 e1=ee1.rgb;pixA.g=ee1.a; - #else - FfxFloat16x3 e1=FsrRcasLoadHx2(sp1).rgb; - #endif - FfxFloat16x3 f1=FsrRcasLoadHx2(sp1+FfxInt16x2( 1, 0)).rgb; - FfxFloat16x3 h1=FsrRcasLoadHx2(sp1+FfxInt16x2( 0, 1)).rgb; - // Arrays of Structures to Structures of Arrays conversion. - FfxFloat16x2 bR=FfxFloat16x2(b0.r,b1.r); - FfxFloat16x2 bG=FfxFloat16x2(b0.g,b1.g); - FfxFloat16x2 bB=FfxFloat16x2(b0.b,b1.b); - FfxFloat16x2 dR=FfxFloat16x2(d0.r,d1.r); - FfxFloat16x2 dG=FfxFloat16x2(d0.g,d1.g); - FfxFloat16x2 dB=FfxFloat16x2(d0.b,d1.b); - FfxFloat16x2 eR=FfxFloat16x2(e0.r,e1.r); - FfxFloat16x2 eG=FfxFloat16x2(e0.g,e1.g); - FfxFloat16x2 eB=FfxFloat16x2(e0.b,e1.b); - FfxFloat16x2 fR=FfxFloat16x2(f0.r,f1.r); - FfxFloat16x2 fG=FfxFloat16x2(f0.g,f1.g); - FfxFloat16x2 fB=FfxFloat16x2(f0.b,f1.b); - FfxFloat16x2 hR=FfxFloat16x2(h0.r,h1.r); - FfxFloat16x2 hG=FfxFloat16x2(h0.g,h1.g); - FfxFloat16x2 hB=FfxFloat16x2(h0.b,h1.b); - // Run optional input transform. - FsrRcasInputHx2(bR,bG,bB); - FsrRcasInputHx2(dR,dG,dB); - FsrRcasInputHx2(eR,eG,eB); - FsrRcasInputHx2(fR,fG,fB); - FsrRcasInputHx2(hR,hG,hB); - // Luma times 2. - FfxFloat16x2 bL=bB*FFX_BROADCAST_FLOAT16X2(0.5)+(bR*FFX_BROADCAST_FLOAT16X2(0.5)+bG); - FfxFloat16x2 dL=dB*FFX_BROADCAST_FLOAT16X2(0.5)+(dR*FFX_BROADCAST_FLOAT16X2(0.5)+dG); - FfxFloat16x2 eL=eB*FFX_BROADCAST_FLOAT16X2(0.5)+(eR*FFX_BROADCAST_FLOAT16X2(0.5)+eG); - FfxFloat16x2 fL=fB*FFX_BROADCAST_FLOAT16X2(0.5)+(fR*FFX_BROADCAST_FLOAT16X2(0.5)+fG); - FfxFloat16x2 hL=hB*FFX_BROADCAST_FLOAT16X2(0.5)+(hR*FFX_BROADCAST_FLOAT16X2(0.5)+hG); - // Noise detection. - FfxFloat16x2 nz=FFX_BROADCAST_FLOAT16X2(0.25)*bL+FFX_BROADCAST_FLOAT16X2(0.25)*dL+FFX_BROADCAST_FLOAT16X2(0.25)*fL+FFX_BROADCAST_FLOAT16X2(0.25)*hL-eL; - nz=ffxSaturate(abs(nz)*ffxApproximateReciprocalMediumHalf(ffxMax3Half(ffxMax3Half(bL,dL,eL),fL,hL)-ffxMin3Half(ffxMin3Half(bL,dL,eL),fL,hL))); - nz=FFX_BROADCAST_FLOAT16X2(-0.5)*nz+FFX_BROADCAST_FLOAT16X2(1.0); - // Min and max of ring. - FfxFloat16x2 mn4R=min(ffxMin3Half(bR,dR,fR),hR); - FfxFloat16x2 mn4G=min(ffxMin3Half(bG,dG,fG),hG); - FfxFloat16x2 mn4B=min(ffxMin3Half(bB,dB,fB),hB); - FfxFloat16x2 mx4R=max(ffxMax3Half(bR,dR,fR),hR); - FfxFloat16x2 mx4G=max(ffxMax3Half(bG,dG,fG),hG); - FfxFloat16x2 mx4B=max(ffxMax3Half(bB,dB,fB),hB); - // Immediate constants for peak range. - FfxFloat16x2 peakC=FfxFloat16x2(1.0,-1.0*4.0); - // Limiters, these need to be high precision RCPs. - FfxFloat16x2 hitMinR=mn4R*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4R); - FfxFloat16x2 hitMinG=mn4G*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4G); - FfxFloat16x2 hitMinB=mn4B*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mx4B); - FfxFloat16x2 hitMaxR=(peakC.x-mx4R)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4R+peakC.y); - FfxFloat16x2 hitMaxG=(peakC.x-mx4G)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4G+peakC.y); - FfxFloat16x2 hitMaxB=(peakC.x-mx4B)*ffxReciprocalHalf(FFX_BROADCAST_FLOAT16X2(4.0)*mn4B+peakC.y); - FfxFloat16x2 lobeR=max(-hitMinR,hitMaxR); - FfxFloat16x2 lobeG=max(-hitMinG,hitMaxG); - FfxFloat16x2 lobeB=max(-hitMinB,hitMaxB); - FfxFloat16x2 lobe=max(FFX_BROADCAST_FLOAT16X2(-FSR_RCAS_LIMIT),min(ffxMax3Half(lobeR,lobeG,lobeB),FFX_BROADCAST_FLOAT16X2(0.0)))*FFX_BROADCAST_FLOAT16X2(FFX_UINT32_TO_FLOAT16X2(con.y).x); - // Apply noise removal. - #ifdef FSR_RCAS_DENOISE - lobe*=nz; - #endif - // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. - FfxFloat16x2 rcpL=ffxApproximateReciprocalMediumHalf(FFX_BROADCAST_FLOAT16X2(4.0)*lobe+FFX_BROADCAST_FLOAT16X2(1.0)); - pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; - pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; - pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR -// -//------------------------------------------------------------------------------------------------------------------------------ -// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. -// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. -// The 'Lfga*()' functions provide a convenient way to introduce grain. -// These functions limit grain based on distance to signal limits. -// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. -// Grain application should be done in a linear colorspace. -// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). -//------------------------------------------------------------------------------------------------------------------------------ -// Usage, -// FsrLfga*( -// color, // In/out linear colorspace color {0 to 1} ranged. -// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. -// amount); // Amount of grain (0 to 1} ranged. -//------------------------------------------------------------------------------------------------------------------------------ -// Example if grain texture is monochrome: 'FsrLfgaF(color,ffxBroadcast3(grain),amount)' -//============================================================================================================================== -#if defined(FFX_GPU) - // Maximum grain is the minimum distance to the signal limit. - void FsrLfgaF(inout FfxFloat32x3 c, FfxFloat32x3 t, FfxFloat32 a) - { - c += (t * ffxBroadcast3(a)) * ffxMin(ffxBroadcast3(1.0) - c, c); - } -#endif -//============================================================================================================================== -#if defined(FFX_GPU)&& FFX_HALF == 1 - // Half precision version (slower). - void FsrLfgaH(inout FfxFloat16x3 c, FfxFloat16x3 t, FfxFloat16 a) - { - c += (t * FFX_BROADCAST_FLOAT16X3(a)) * min(FFX_BROADCAST_FLOAT16X3(1.0) - c, c); - } - //------------------------------------------------------------------------------------------------------------------------------ - // Packed half precision version (faster). - void FsrLfgaHx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB,FfxFloat16x2 tR,FfxFloat16x2 tG,FfxFloat16x2 tB,FfxFloat16 a){ - cR+=(tR*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cR,cR);cG+=(tG*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cG,cG);cB+=(tB*FFX_BROADCAST_FLOAT16X2(a))*min(FFX_BROADCAST_FLOAT16X2(1.0)-cB,cB);} -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER -// -//------------------------------------------------------------------------------------------------------------------------------ -// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. -// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. -//------------------------------------------------------------------------------------------------------------------------------ -// Reversible tonemapper usage, -// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. -// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. -//============================================================================================================================== -#if defined(FFX_GPU) - void FsrSrtmF(inout FfxFloat32x3 c) - { - c *= ffxBroadcast3(rcp(ffxMax3(c.r, c.g, c.b) + FfxFloat32(1.0))); - } - // The extra max solves the c=1.0 case (which is a /0). - void FsrSrtmInvF(inout FfxFloat32x3 c){c*=ffxBroadcast3(rcp(max(FfxFloat32(1.0/32768.0),FfxFloat32(1.0)-ffxMax3(c.r,c.g,c.b))));} -#endif -//============================================================================================================================== -#if defined(FFX_GPU )&& FFX_HALF == 1 - void FsrSrtmH(inout FfxFloat16x3 c) - { - c *= FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(ffxMax3Half(c.r, c.g, c.b) + FFX_BROADCAST_FLOAT16(1.0))); - } - void FsrSrtmInvH(inout FfxFloat16x3 c) - { - c *= FFX_BROADCAST_FLOAT16X3(ffxReciprocalHalf(max(FFX_BROADCAST_FLOAT16(1.0 / 32768.0), FFX_BROADCAST_FLOAT16(1.0) - ffxMax3Half(c.r, c.g, c.b)))); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrSrtmHx2(inout FfxFloat16x2 cR, inout FfxFloat16x2 cG, inout FfxFloat16x2 cB) - { - FfxFloat16x2 rcp = ffxReciprocalHalf(ffxMax3Half(cR, cG, cB) + FFX_BROADCAST_FLOAT16X2(1.0)); - cR *= rcp; - cG *= rcp; - cB *= rcp; - } - void FsrSrtmInvHx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB) - { - FfxFloat16x2 rcp=ffxReciprocalHalf(max(FFX_BROADCAST_FLOAT16X2(1.0/32768.0),FFX_BROADCAST_FLOAT16X2(1.0)-ffxMax3Half(cR,cG,cB))); - cR*=rcp; - cG*=rcp; - cB*=rcp; - } -#endif -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//_____________________________________________________________/\_______________________________________________________________ -//============================================================================================================================== -// -// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER -// -//------------------------------------------------------------------------------------------------------------------------------ -// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. -// Gamma 2.0 is used so that the conversion back to linear is just to square the color. -// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. -// Given good non-biased temporal blue noise as dither input, -// the output dither will temporally conserve energy. -// This is done by choosing the linear nearest step point instead of perceptual nearest. -// See code below for details. -//------------------------------------------------------------------------------------------------------------------------------ -// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION -// =============================================== -// - Output is 'FfxUInt32(floor(saturate(n)*255.0+0.5))'. -// - Thus rounding is to nearest. -// - NaN gets converted to zero. -// - INF is clamped to {0.0 to 1.0}. -//============================================================================================================================== -#if defined(FFX_GPU) - // Hand tuned integer position to dither value, with more values than simple checkerboard. - // Only 32-bit has enough precision for this compddation. - // Output is {0 to <1}. - FfxFloat32 FsrTepdDitF(FfxUInt32x2 p, FfxUInt32 f) - { - FfxFloat32 x = FfxFloat32(p.x + f); - FfxFloat32 y = FfxFloat32(p.y); - // The 1.61803 golden ratio. - FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); - // Number designed to provide a good visual pattern. - FfxFloat32 b = FfxFloat32(1.0 / 3.69); - x = x * a + (y * b); - return ffxFract(x); - } - //------------------------------------------------------------------------------------------------------------------------------ - // This version is 8-bit gamma 2.0. - // The 'c' input is {0 to 1}. - // Output is {0 to 1} ready for image store. - void FsrTepdC8F(inout FfxFloat32x3 c, FfxFloat32 dit) - { - FfxFloat32x3 n = ffxSqrt(c); - n = floor(n * ffxBroadcast3(255.0)) * ffxBroadcast3(1.0 / 255.0); - FfxFloat32x3 a = n * n; - FfxFloat32x3 b = n + ffxBroadcast3(1.0 / 255.0); - b = b * b; - // Ratio of 'a' to 'b' required to produce 'c'. - // ffxApproximateReciprocal() won't work here (at least for very high dynamic ranges). - // ffxApproximateReciprocalMedium() is an IADD,FMA,MUL. - FfxFloat32x3 r = (c - b) * ffxApproximateReciprocalMedium(a - b); - // Use the ratio as a cutoff to choose 'a' or 'b'. - // ffxIsGreaterThanZero() is a MUL. - c = ffxSaturate(n + ffxIsGreaterThanZero(ffxBroadcast3(dit) - r) * ffxBroadcast3(1.0 / 255.0)); - } - //------------------------------------------------------------------------------------------------------------------------------ - // This version is 10-bit gamma 2.0. - // The 'c' input is {0 to 1}. - // Output is {0 to 1} ready for image store. - void FsrTepdC10F(inout FfxFloat32x3 c, FfxFloat32 dit) - { - FfxFloat32x3 n = ffxSqrt(c); - n = floor(n * ffxBroadcast3(1023.0)) * ffxBroadcast3(1.0 / 1023.0); - FfxFloat32x3 a = n * n; - FfxFloat32x3 b = n + ffxBroadcast3(1.0 / 1023.0); - b = b * b; - FfxFloat32x3 r = (c - b) * ffxApproximateReciprocalMedium(a - b); - c = ffxSaturate(n + ffxIsGreaterThanZero(ffxBroadcast3(dit) - r) * ffxBroadcast3(1.0 / 1023.0)); - } -#endif -//============================================================================================================================== -#if defined(FFX_GPU)&& FFX_HALF == 1 - FfxFloat16 FsrTepdDitH(FfxUInt32x2 p, FfxUInt32 f) - { - FfxFloat32 x = FfxFloat32(p.x + f); - FfxFloat32 y = FfxFloat32(p.y); - FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); - FfxFloat32 b = FfxFloat32(1.0 / 3.69); - x = x * a + (y * b); - return FfxFloat16(ffxFract(x)); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC8H(inout FfxFloat16x3 c, FfxFloat16 dit) - { - FfxFloat16x3 n = sqrt(c); - n = floor(n * FFX_BROADCAST_FLOAT16X3(255.0)) * FFX_BROADCAST_FLOAT16X3(1.0 / 255.0); - FfxFloat16x3 a = n * n; - FfxFloat16x3 b = n + FFX_BROADCAST_FLOAT16X3(1.0 / 255.0); - b = b * b; - FfxFloat16x3 r = (c - b) * ffxApproximateReciprocalMediumHalf(a - b); - c = FfxFloat16x3(ffxSaturate(n + ffxIsGreaterThanZeroHalf(FFX_BROADCAST_FLOAT16X3(dit) - r) * FFX_BROADCAST_FLOAT16X3(1.0 / 255.0))); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC10H(inout FfxFloat16x3 c, FfxFloat16 dit) - { - FfxFloat16x3 n = sqrt(c); - n = floor(n * FFX_BROADCAST_FLOAT16X3(1023.0)) * FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0); - FfxFloat16x3 a = n * n; - FfxFloat16x3 b = n + FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0); - b = b * b; - FfxFloat16x3 r = (c - b) * ffxApproximateReciprocalMediumHalf(a - b); - c = FfxFloat16x3(ffxSaturate(n + ffxIsGreaterThanZeroHalf(FFX_BROADCAST_FLOAT16X3(dit) - r) * FFX_BROADCAST_FLOAT16X3(1.0 / 1023.0))); - } - //============================================================================================================================== - // This computes dither for positions 'p' and 'p+{8,0}'. - FfxFloat16x2 FsrTepdDitHx2(FfxUInt32x2 p, FfxUInt32 f) - { - FfxFloat32x2 x; - x.x = FfxFloat32(p.x + f); - x.y = x.x + FfxFloat32(8.0); - FfxFloat32 y = FfxFloat32(p.y); - FfxFloat32 a = FfxFloat32((1.0 + ffxSqrt(5.0f)) / 2.0); - FfxFloat32 b = FfxFloat32(1.0 / 3.69); - x = x * ffxBroadcast2(a) + ffxBroadcast2(y * b); - return FfxFloat16x2(ffxFract(x)); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC8Hx2(inout FfxFloat16x2 cR, inout FfxFloat16x2 cG, inout FfxFloat16x2 cB, FfxFloat16x2 dit) - { - FfxFloat16x2 nR = sqrt(cR); - FfxFloat16x2 nG = sqrt(cG); - FfxFloat16x2 nB = sqrt(cB); - nR = floor(nR * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - nG = floor(nG * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - nB = floor(nB * FFX_BROADCAST_FLOAT16X2(255.0)) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - FfxFloat16x2 aR = nR * nR; - FfxFloat16x2 aG = nG * nG; - FfxFloat16x2 aB = nB * nB; - FfxFloat16x2 bR = nR + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - bR = bR * bR; - FfxFloat16x2 bG = nG + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - bG = bG * bG; - FfxFloat16x2 bB = nB + FFX_BROADCAST_FLOAT16X2(1.0 / 255.0); - bB = bB * bB; - FfxFloat16x2 rR = (cR - bR) * ffxApproximateReciprocalMediumHalf(aR - bR); - FfxFloat16x2 rG = (cG - bG) * ffxApproximateReciprocalMediumHalf(aG - bG); - FfxFloat16x2 rB = (cB - bB) * ffxApproximateReciprocalMediumHalf(aB - bB); - cR = FfxFloat16x2(ffxSaturate(nR + ffxIsGreaterThanZeroHalf(dit - rR) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0))); - cG = FfxFloat16x2(ffxSaturate(nG + ffxIsGreaterThanZeroHalf(dit - rG) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0))); - cB = FfxFloat16x2(ffxSaturate(nB + ffxIsGreaterThanZeroHalf(dit - rB) * FFX_BROADCAST_FLOAT16X2(1.0 / 255.0))); - } - //------------------------------------------------------------------------------------------------------------------------------ - void FsrTepdC10Hx2(inout FfxFloat16x2 cR,inout FfxFloat16x2 cG,inout FfxFloat16x2 cB,FfxFloat16x2 dit){ - FfxFloat16x2 nR=sqrt(cR); - FfxFloat16x2 nG=sqrt(cG); - FfxFloat16x2 nB=sqrt(cB); - nR=floor(nR*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); - nG=floor(nG*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); - nB=floor(nB*FFX_BROADCAST_FLOAT16X2(1023.0))*FFX_BROADCAST_FLOAT16X2(1.0/1023.0); - FfxFloat16x2 aR=nR*nR; - FfxFloat16x2 aG=nG*nG; - FfxFloat16x2 aB=nB*nB; - FfxFloat16x2 bR=nR+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bR=bR*bR; - FfxFloat16x2 bG=nG+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bG=bG*bG; - FfxFloat16x2 bB=nB+FFX_BROADCAST_FLOAT16X2(1.0/1023.0);bB=bB*bB; - FfxFloat16x2 rR=(cR-bR)*ffxApproximateReciprocalMediumHalf(aR-bR); - FfxFloat16x2 rG=(cG-bG)*ffxApproximateReciprocalMediumHalf(aG-bG); - FfxFloat16x2 rB=(cB-bB)*ffxApproximateReciprocalMediumHalf(aB-bB); - cR=FfxFloat16x2(ffxSaturate(nR+ffxIsGreaterThanZeroHalf(dit-rR)*FFX_BROADCAST_FLOAT16X2(1.0/1023.0))); - cG=FfxFloat16x2(ffxSaturate(nG+ffxIsGreaterThanZeroHalf(dit-rG)*FFX_BROADCAST_FLOAT16X2(1.0/1023.0))); - cB=FfxFloat16x2(ffxSaturate(nB + ffxIsGreaterThanZeroHalf(dit - rB) * FFX_BROADCAST_FLOAT16X2(1.0 / 1023.0))); -} -#endif diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta deleted file mode 100644 index 3c97f69..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/fsr1/ffx_fsr1.h.meta +++ /dev/null @@ -1,67 +0,0 @@ -fileFormatVersion: 2 -guid: 628e23510f46ef44bbf0035ce9a63be0 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd.meta deleted file mode 100644 index 0b775af..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 0f03de1579ac3294595ae4f40106b7a2 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h deleted file mode 100644 index 6441419..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h +++ /dev/null @@ -1,1009 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. - - -/// @defgroup FfxGPUSpd FidelityFX SPD -/// FidelityFX Single Pass Downsampler 2.0 GPU documentation -/// -/// @ingroup FfxGPUEffects - -/// Setup required constant values for SPD (CPU). -/// -/// @param [out] dispatchThreadGroupCountXY CPU side: dispatch thread group count xy. z is number of slices of the input texture -/// @param [out] workGroupOffset GPU side: pass in as constant -/// @param [out] numWorkGroupsAndMips GPU side: pass in as constant -/// @param [in] rectInfo left, top, width, height -/// @param [in] mips optional: if -1, calculate based on rect width and height -/// -/// @ingroup FfxGPUSpd -#if defined(FFX_CPU) -FFX_STATIC void ffxSpdSetup(FfxUInt32x2 dispatchThreadGroupCountXY, - FfxUInt32x2 workGroupOffset, - FfxUInt32x2 numWorkGroupsAndMips, - FfxUInt32x4 rectInfo, - FfxInt32 mips) -{ - // determines the offset of the first tile to downsample based on - // left (rectInfo[0]) and top (rectInfo[1]) of the subregion. - workGroupOffset[0] = rectInfo[0] / 64; - workGroupOffset[1] = rectInfo[1] / 64; - - FfxUInt32 endIndexX = (rectInfo[0] + rectInfo[2] - 1) / 64; // rectInfo[0] = left, rectInfo[2] = width - FfxUInt32 endIndexY = (rectInfo[1] + rectInfo[3] - 1) / 64; // rectInfo[1] = top, rectInfo[3] = height - - // we only need to dispatch as many thread groups as tiles we need to downsample - // number of tiles per slice depends on the subregion to downsample - dispatchThreadGroupCountXY[0] = endIndexX + 1 - workGroupOffset[0]; - dispatchThreadGroupCountXY[1] = endIndexY + 1 - workGroupOffset[1]; - - // number of thread groups per slice - numWorkGroupsAndMips[0] = (dispatchThreadGroupCountXY[0]) * (dispatchThreadGroupCountXY[1]); - - if (mips >= 0) - { - numWorkGroupsAndMips[1] = FfxUInt32(mips); - } - else - { - // calculate based on rect width and height - FfxUInt32 resolution = ffxMax(rectInfo[2], rectInfo[3]); - numWorkGroupsAndMips[1] = FfxUInt32((ffxMin(floor(log2(FfxFloat32(resolution))), FfxFloat32(12)))); - } -} - -/// Setup required constant values for SPD (CPU). -/// -/// @param [out] dispatchThreadGroupCountXY CPU side: dispatch thread group count xy. z is number of slices of the input texture -/// @param [out] workGroupOffset GPU side: pass in as constant -/// @param [out] numWorkGroupsAndMips GPU side: pass in as constant -/// @param [in] rectInfo left, top, width, height -/// -/// @ingroup FfxGPUSpd -FFX_STATIC void ffxSpdSetup(FfxUInt32x2 dispatchThreadGroupCountXY, - FfxUInt32x2 workGroupOffset, - FfxUInt32x2 numWorkGroupsAndMips, - FfxUInt32x4 rectInfo) -{ - ffxSpdSetup(dispatchThreadGroupCountXY, workGroupOffset, numWorkGroupsAndMips, rectInfo, -1); -} -#endif // #if defined(FFX_CPU) - - -//============================================================================================================================== -// NON-PACKED VERSION -//============================================================================================================================== -#if defined(FFX_GPU) -#if defined(FFX_SPD_PACKED_ONLY) -// Avoid compiler errors by including default implementations of these callbacks. -FfxFloat32x4 SpdLoadSourceImage(FfxInt32x2 p, FfxUInt32 slice) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} - -FfxFloat32x4 SpdLoad(FfxInt32x2 p, FfxUInt32 slice) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} -void SpdStore(FfxInt32x2 p, FfxFloat32x4 value, FfxUInt32 mip, FfxUInt32 slice) -{ -} -FfxFloat32x4 SpdLoadIntermediate(FfxUInt32 x, FfxUInt32 y) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} -void SpdStoreIntermediate(FfxUInt32 x, FfxUInt32 y, FfxFloat32x4 value) -{ -} -FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3) -{ - return FfxFloat32x4(0.0, 0.0, 0.0, 0.0); -} -#endif // #if FFX_SPD_PACKED_ONLY - -//_____________________________________________________________/\_______________________________________________________________ - -void ffxSpdWorkgroupShuffleBarrier() -{ - FFX_GROUP_MEMORY_BARRIER(); -} - -// Only last active workgroup should proceed -bool SpdExitWorkgroup(FfxUInt32 numWorkGroups, FfxUInt32 localInvocationIndex, FfxUInt32 slice) -{ - // global atomic counter - if (localInvocationIndex == 0) - { - SpdIncreaseAtomicCounter(slice); - } - - ffxSpdWorkgroupShuffleBarrier(); - return (SpdGetAtomicCounter() != (numWorkGroups - 1)); -} - -// User defined: FfxFloat32x4 SpdReduce4(FfxFloat32x4 v0, FfxFloat32x4 v1, FfxFloat32x4 v2, FfxFloat32x4 v3); -FfxFloat32x4 SpdReduceQuad(FfxFloat32x4 v) -{ -#if defined(FFX_GLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - - FfxFloat32x4 v0 = v; - FfxFloat32x4 v1 = subgroupQuadSwapHorizontal(v); - FfxFloat32x4 v2 = subgroupQuadSwapVertical(v); - FfxFloat32x4 v3 = subgroupQuadSwapDiagonal(v); - return SpdReduce4(v0, v1, v2, v3); - -#elif defined(FFX_HLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - - // requires SM6.0 - FfxUInt32 quad = WaveGetLaneIndex() & (~0x3); - FfxFloat32x4 v0 = v; - FfxFloat32x4 v1 = WaveReadLaneAt(v, quad | 1); - FfxFloat32x4 v2 = WaveReadLaneAt(v, quad | 2); - FfxFloat32x4 v3 = WaveReadLaneAt(v, quad | 3); - return SpdReduce4(v0, v1, v2, v3); -/* - // if SM6.0 is not available, you can use the AMD shader intrinsics - // the AMD shader intrinsics are available in AMD GPU Services (AGS) library: - // https://gpuopen.com/amd-gpu-services-ags-library/ - // works for DX11 - FfxFloat32x4 v0 = v; - FfxFloat32x4 v1; - v1.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - FfxFloat32x4 v2; - v2.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - FfxFloat32x4 v3; - v3.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - return SpdReduce4(v0, v1, v2, v3); - */ -#endif - return v; -} - -FfxFloat32x4 SpdReduceIntermediate(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3) -{ - FfxFloat32x4 v0 = SpdLoadIntermediate(i0.x, i0.y); - FfxFloat32x4 v1 = SpdLoadIntermediate(i1.x, i1.y); - FfxFloat32x4 v2 = SpdLoadIntermediate(i2.x, i2.y); - FfxFloat32x4 v3 = SpdLoadIntermediate(i3.x, i3.y); - return SpdReduce4(v0, v1, v2, v3); -} - -FfxFloat32x4 SpdReduceLoad4(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat32x4 v0 = SpdLoad(FfxInt32x2(i0), slice); - FfxFloat32x4 v1 = SpdLoad(FfxInt32x2(i1), slice); - FfxFloat32x4 v2 = SpdLoad(FfxInt32x2(i2), slice); - FfxFloat32x4 v3 = SpdLoad(FfxInt32x2(i3), slice); - return SpdReduce4(v0, v1, v2, v3); -} - -FfxFloat32x4 SpdReduceLoad4(FfxUInt32x2 base, FfxUInt32 slice) -{ - return SpdReduceLoad4(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -} - -FfxFloat32x4 SpdReduceLoadSourceImage4(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat32x4 v0 = SpdLoadSourceImage(FfxInt32x2(i0), slice); - FfxFloat32x4 v1 = SpdLoadSourceImage(FfxInt32x2(i1), slice); - FfxFloat32x4 v2 = SpdLoadSourceImage(FfxInt32x2(i2), slice); - FfxFloat32x4 v3 = SpdLoadSourceImage(FfxInt32x2(i3), slice); - return SpdReduce4(v0, v1, v2, v3); -} - -FfxFloat32x4 SpdReduceLoadSourceImage(FfxUInt32x2 base, FfxUInt32 slice) -{ -#if defined(SPD_LINEAR_SAMPLER) - return SpdLoadSourceImage(FfxInt32x2(base), slice); -#else - return SpdReduceLoadSourceImage4(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -#endif -} - -void SpdDownsampleMips_0_1_Intrinsics(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ - FfxFloat32x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[3], 0, slice); - - if (mip <= 1) - return; - - v[0] = SpdReduceQuad(v[0]); - v[1] = SpdReduceQuad(v[1]); - v[2] = SpdReduceQuad(v[2]); - v[3] = SpdReduceQuad(v[3]); - - if ((localInvocationIndex % 4) == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2), v[0], 1, slice); - SpdStoreIntermediate(x / 2, y / 2, v[0]); - - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2), v[1], 1, slice); - SpdStoreIntermediate(x / 2 + 8, y / 2, v[1]); - - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2 + 8), v[2], 1, slice); - SpdStoreIntermediate(x / 2, y / 2 + 8, v[2]); - - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); - SpdStoreIntermediate(x / 2 + 8, y / 2 + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1_LDS(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ - FfxFloat32x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[3], 0, slice); - - if (mip <= 1) - return; - - for (FfxUInt32 i = 0; i < 4; i++) - { - SpdStoreIntermediate(x, y, v[i]); - ffxSpdWorkgroupShuffleBarrier(); - if (localInvocationIndex < 64) - { - v[i] = SpdReduceIntermediate(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStore(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); - } - ffxSpdWorkgroupShuffleBarrier(); - } - - if (localInvocationIndex < 64) - { - SpdStoreIntermediate(x + 0, y + 0, v[0]); - SpdStoreIntermediate(x + 8, y + 0, v[1]); - SpdStoreIntermediate(x + 0, y + 8, v[2]); - SpdStoreIntermediate(x + 8, y + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - SpdDownsampleMips_0_1_LDS(x, y, workGroupID, localInvocationIndex, mip, slice); -#else - SpdDownsampleMips_0_1_Intrinsics(x, y, workGroupID, localInvocationIndex, mip, slice); -#endif -} - - -void SpdDownsampleMip_2(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 64) - { - FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStore(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS, try to reduce bank conflicts - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // ... - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - SpdStoreIntermediate(x * 2 + y % 2, y * 2, v); - } -#else - FfxFloat32x4 v = SpdLoadIntermediate(x, y); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediate(x + (y / 2) % 2, y, v); - } -#endif -} - -void SpdDownsampleMip_3(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 16) - { - // x 0 x 0 - // 0 0 0 0 - // 0 x 0 x - // 0 0 0 0 - FfxFloat32x4 v = - SpdReduceIntermediate(FfxUInt32x2(x * 4 + 0 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 2 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 0 + 1, y * 4 + 2), FfxUInt32x2(x * 4 + 2 + 1, y * 4 + 2)); - SpdStore(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 - // ... - // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 - // ... - // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x - // ... - SpdStoreIntermediate(x * 4 + y, y * 4, v); - } -#else - if (localInvocationIndex < 64) - { - FfxFloat32x4 v = SpdLoadIntermediate(x * 2 + y % 2, y * 2); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediate(x * 2 + y / 2, y * 2, v); - } - } -#endif -} - -void SpdDownsampleMip_4(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 4) - { - // x 0 0 0 x 0 0 0 - // ... - // 0 x 0 0 0 x 0 0 - FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), - FfxUInt32x2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); - SpdStore(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x x x x 0 ... - // 0 ... - SpdStoreIntermediate(x + y * 2, 0, v); - } -#else - if (localInvocationIndex < 16) - { - FfxFloat32x4 v = SpdLoadIntermediate(x * 4 + y, y * 4); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediate(x / 2 + y, 0, v); - } - } -#endif -} - -void SpdDownsampleMip_5(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 1) - { - // x x x x 0 ... - // 0 ... - FfxFloat32x4 v = SpdReduceIntermediate(FfxUInt32x2(0, 0), FfxUInt32x2(1, 0), FfxUInt32x2(2, 0), FfxUInt32x2(3, 0)); - SpdStore(FfxInt32x2(workGroupID.xy), v, mip, slice); - } -#else - if (localInvocationIndex < 4) - { - FfxFloat32x4 v = SpdLoadIntermediate(localInvocationIndex, 0); - v = SpdReduceQuad(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStore(FfxInt32x2(workGroupID.xy), v, mip, slice); - } - } -#endif -} - -void SpdDownsampleMips_6_7(FfxUInt32 x, FfxUInt32 y, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxInt32x2 tex = FfxInt32x2(x * 4 + 0, y * 4 + 0); - FfxInt32x2 pix = FfxInt32x2(x * 2 + 0, y * 2 + 0); - FfxFloat32x4 v0 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v0, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 0); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 0); - FfxFloat32x4 v1 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v1, 6, slice); - - tex = FfxInt32x2(x * 4 + 0, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 0, y * 2 + 1); - FfxFloat32x4 v2 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v2, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 1); - FfxFloat32x4 v3 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v3, 6, slice); - - if (mips <= 7) - return; - // no barrier needed, working on values only from the same thread - - FfxFloat32x4 v = SpdReduce4(v0, v1, v2, v3); - SpdStore(FfxInt32x2(x, y), v, 7, slice); - SpdStoreIntermediate(x, y, v); -} - -void SpdDownsampleNextFour(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 baseMip, FfxUInt32 mips, FfxUInt32 slice) -{ - if (mips <= baseMip) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_2(x, y, workGroupID, localInvocationIndex, baseMip, slice); - - if (mips <= baseMip + 1) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_3(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); - - if (mips <= baseMip + 2) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_4(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); - - if (mips <= baseMip + 3) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_5(workGroupID, localInvocationIndex, baseMip + 3, slice); -} - -/// Downsamples a 64x64 tile based on the work group id. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// -/// @ingroup FfxGPUSpd -void SpdDownsample(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice) -{ - // compute MIP level 0 and 1 - FfxUInt32x2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); - FfxUInt32 x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); - FfxUInt32 y = sub_xy.y + 8 * ((localInvocationIndex >> 7)); - SpdDownsampleMips_0_1(x, y, workGroupID, localInvocationIndex, mips, slice); - - // compute MIP level 2, 3, 4, 5 - SpdDownsampleNextFour(x, y, workGroupID, localInvocationIndex, 2, mips, slice); - - if (mips <= 6) - return; - - // increase the global atomic counter for the given slice and check if it's the last remaining thread group: - // terminate if not, continue if yes. - if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) - return; - - // reset the global atomic counter back to 0 for the next spd dispatch - SpdResetAtomicCounter(slice); - - // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. - // compute MIP level 6 and 7 - SpdDownsampleMips_6_7(x, y, mips, slice); - - // compute MIP level 8, 9, 10, 11 - SpdDownsampleNextFour(x, y, FfxUInt32x2(0, 0), localInvocationIndex, 8, mips, slice); -} -/// Downsamples a 64x64 tile based on the work group id and work group offset. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. -/// -/// @ingroup FfxGPUSpd -void SpdDownsample(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice, FfxUInt32x2 workGroupOffset) -{ - SpdDownsample(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//============================================================================================================================== -// PACKED VERSION -//============================================================================================================================== - -#if FFX_HALF - -FfxFloat16x4 SpdReduceQuadH(FfxFloat16x4 v) -{ -#if defined(FFX_GLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - FfxFloat16x4 v0 = v; - FfxFloat16x4 v1 = subgroupQuadSwapHorizontal(v); - FfxFloat16x4 v2 = subgroupQuadSwapVertical(v); - FfxFloat16x4 v3 = subgroupQuadSwapDiagonal(v); - return SpdReduce4H(v0, v1, v2, v3); -#elif defined(FFX_HLSL) && !defined(FFX_SPD_NO_WAVE_OPERATIONS) - // requires SM6.0 - FfxUInt32 quad = WaveGetLaneIndex() & (~0x3); - FfxFloat16x4 v0 = v; - FfxFloat16x4 v1 = WaveReadLaneAt(v, quad | 1); - FfxFloat16x4 v2 = WaveReadLaneAt(v, quad | 2); - FfxFloat16x4 v3 = WaveReadLaneAt(v, quad | 3); - return SpdReduce4H(v0, v1, v2, v3); -/* - // if SM6.0 is not available, you can use the AMD shader intrinsics - // the AMD shader intrinsics are available in AMD GPU Services (AGS) library: - // https://gpuopen.com/amd-gpu-services-ags-library/ - // works for DX11 - FfxFloat16x4 v0 = v; - FfxFloat16x4 v1; - v1.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - v1.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX1); - FfxFloat16x4 v2; - v2.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - v2.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_SwapX2); - FfxFloat16x4 v3; - v3.x = AmdExtD3DShaderIntrinsics_SwizzleF(v.x, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.y = AmdExtD3DShaderIntrinsics_SwizzleF(v.y, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.z = AmdExtD3DShaderIntrinsics_SwizzleF(v.z, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - v3.w = AmdExtD3DShaderIntrinsics_SwizzleF(v.w, AmdExtD3DShaderIntrinsicsSwizzle_ReverseX4); - return SpdReduce4H(v0, v1, v2, v3); - */ -#endif - return FfxFloat16x4(0.0, 0.0, 0.0, 0.0); -} - -FfxFloat16x4 SpdReduceIntermediateH(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3) -{ - FfxFloat16x4 v0 = SpdLoadIntermediateH(i0.x, i0.y); - FfxFloat16x4 v1 = SpdLoadIntermediateH(i1.x, i1.y); - FfxFloat16x4 v2 = SpdLoadIntermediateH(i2.x, i2.y); - FfxFloat16x4 v3 = SpdLoadIntermediateH(i3.x, i3.y); - return SpdReduce4H(v0, v1, v2, v3); -} - -FfxFloat16x4 SpdReduceLoad4H(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat16x4 v0 = SpdLoadH(FfxInt32x2(i0), slice); - FfxFloat16x4 v1 = SpdLoadH(FfxInt32x2(i1), slice); - FfxFloat16x4 v2 = SpdLoadH(FfxInt32x2(i2), slice); - FfxFloat16x4 v3 = SpdLoadH(FfxInt32x2(i3), slice); - return SpdReduce4H(v0, v1, v2, v3); -} - -FfxFloat16x4 SpdReduceLoad4H(FfxUInt32x2 base, FfxUInt32 slice) -{ - return SpdReduceLoad4H(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -} - -FfxFloat16x4 SpdReduceLoadSourceImage4H(FfxUInt32x2 i0, FfxUInt32x2 i1, FfxUInt32x2 i2, FfxUInt32x2 i3, FfxUInt32 slice) -{ - FfxFloat16x4 v0 = SpdLoadSourceImageH(FfxInt32x2(i0), slice); - FfxFloat16x4 v1 = SpdLoadSourceImageH(FfxInt32x2(i1), slice); - FfxFloat16x4 v2 = SpdLoadSourceImageH(FfxInt32x2(i2), slice); - FfxFloat16x4 v3 = SpdLoadSourceImageH(FfxInt32x2(i3), slice); - return SpdReduce4H(v0, v1, v2, v3); -} - -FfxFloat16x4 SpdReduceLoadSourceImageH(FfxUInt32x2 base, FfxUInt32 slice) -{ -#if defined(SPD_LINEAR_SAMPLER) - return SpdLoadSourceImageH(FfxInt32x2(base), slice); -#else - return SpdReduceLoadSourceImage4H(FfxUInt32x2(base + FfxUInt32x2(0, 0)), FfxUInt32x2(base + FfxUInt32x2(0, 1)), FfxUInt32x2(base + FfxUInt32x2(1, 0)), FfxUInt32x2(base + FfxUInt32x2(1, 1)), slice); -#endif -} - -void SpdDownsampleMips_0_1_IntrinsicsH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxFloat16x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[3], 0, slice); - - if (mips <= 1) - return; - - v[0] = SpdReduceQuadH(v[0]); - v[1] = SpdReduceQuadH(v[1]); - v[2] = SpdReduceQuadH(v[2]); - v[3] = SpdReduceQuadH(v[3]); - - if ((localInvocationIndex % 4) == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2), v[0], 1, slice); - SpdStoreIntermediateH(x / 2, y / 2, v[0]); - - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2), v[1], 1, slice); - SpdStoreIntermediateH(x / 2 + 8, y / 2, v[1]); - - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2, y / 2 + 8), v[2], 1, slice); - SpdStoreIntermediateH(x / 2, y / 2 + 8, v[2]); - - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); - SpdStoreIntermediateH(x / 2 + 8, y / 2 + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1_LDSH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxFloat16x4 v[4]; - - FfxInt32x2 tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2); - FfxInt32x2 pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y); - v[0] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[0], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y); - v[1] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[1], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x, y + 16); - v[2] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[2], 0, slice); - - tex = FfxInt32x2(workGroupID.xy * 64) + FfxInt32x2(x * 2 + 32, y * 2 + 32); - pix = FfxInt32x2(workGroupID.xy * 32) + FfxInt32x2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImageH(tex, slice); - SpdStoreH(pix, v[3], 0, slice); - - if (mips <= 1) - return; - - for (FfxUInt32 i = 0; i < 4; i++) - { - SpdStoreIntermediateH(x, y, v[i]); - ffxSpdWorkgroupShuffleBarrier(); - if (localInvocationIndex < 64) - { - v[i] = SpdReduceIntermediateH(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 16) + FfxInt32x2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); - } - ffxSpdWorkgroupShuffleBarrier(); - } - - if (localInvocationIndex < 64) - { - SpdStoreIntermediateH(x + 0, y + 0, v[0]); - SpdStoreIntermediateH(x + 8, y + 0, v[1]); - SpdStoreIntermediateH(x + 0, y + 8, v[2]); - SpdStoreIntermediateH(x + 8, y + 8, v[3]); - } -} - -void SpdDownsampleMips_0_1H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - SpdDownsampleMips_0_1_LDSH(x, y, workGroupID, localInvocationIndex, mips, slice); -#else - SpdDownsampleMips_0_1_IntrinsicsH(x, y, workGroupID, localInvocationIndex, mips, slice); -#endif -} - - -void SpdDownsampleMip_2H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 64) - { - FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(x * 2 + 0, y * 2 + 0), FfxUInt32x2(x * 2 + 1, y * 2 + 0), FfxUInt32x2(x * 2 + 0, y * 2 + 1), FfxUInt32x2(x * 2 + 1, y * 2 + 1)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS, try to reduce bank conflicts - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - // ... - // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 - SpdStoreIntermediateH(x * 2 + y % 2, y * 2, v); - } -#else - FfxFloat16x4 v = SpdLoadIntermediateH(x, y); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 8) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediateH(x + (y / 2) % 2, y, v); - } -#endif -} - -void SpdDownsampleMip_3H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 16) - { - // x 0 x 0 - // 0 0 0 0 - // 0 x 0 x - // 0 0 0 0 - FfxFloat16x4 v = - SpdReduceIntermediateH(FfxUInt32x2(x * 4 + 0 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 2 + 0, y * 4 + 0), FfxUInt32x2(x * 4 + 0 + 1, y * 4 + 2), FfxUInt32x2(x * 4 + 2 + 1, y * 4 + 2)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 - // ... - // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 - // ... - // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x - // ... - SpdStoreIntermediateH(x * 4 + y, y * 4, v); - } -#else - if (localInvocationIndex < 64) - { - FfxFloat16x4 v = SpdLoadIntermediateH(x * 2 + y % 2, y * 2); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 4) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediateH(x * 2 + y / 2, y * 2, v); - } - } -#endif -} - -void SpdDownsampleMip_4H(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 4) - { - // x 0 0 0 x 0 0 0 - // ... - // 0 x 0 0 0 x 0 0 - FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), - FfxUInt32x2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), - FfxUInt32x2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); - SpdStoreH(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x, y), v, mip, slice); - // store to LDS - // x x x x 0 ... - // 0 ... - SpdStoreIntermediateH(x + y * 2, 0, v); - } -#else - if (localInvocationIndex < 16) - { - FfxFloat16x4 v = SpdLoadIntermediateH(x * 4 + y, y * 4); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy * 2) + FfxInt32x2(x / 2, y / 2), v, mip, slice); - SpdStoreIntermediateH(x / 2 + y, 0, v); - } - } -#endif -} - -void SpdDownsampleMip_5H(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mip, FfxUInt32 slice) -{ -#if defined(FFX_SPD_NO_WAVE_OPERATIONS) - if (localInvocationIndex < 1) - { - // x x x x 0 ... - // 0 ... - FfxFloat16x4 v = SpdReduceIntermediateH(FfxUInt32x2(0, 0), FfxUInt32x2(1, 0), FfxUInt32x2(2, 0), FfxUInt32x2(3, 0)); - SpdStoreH(FfxInt32x2(workGroupID.xy), v, mip, slice); - } -#else - if (localInvocationIndex < 4) - { - FfxFloat16x4 v = SpdLoadIntermediateH(localInvocationIndex, 0); - v = SpdReduceQuadH(v); - // quad index 0 stores result - if (localInvocationIndex % 4 == 0) - { - SpdStoreH(FfxInt32x2(workGroupID.xy), v, mip, slice); - } - } -#endif -} - -void SpdDownsampleMips_6_7H(FfxUInt32 x, FfxUInt32 y, FfxUInt32 mips, FfxUInt32 slice) -{ - FfxInt32x2 tex = FfxInt32x2(x * 4 + 0, y * 4 + 0); - FfxInt32x2 pix = FfxInt32x2(x * 2 + 0, y * 2 + 0); - FfxFloat16x4 v0 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v0, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 0); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 0); - FfxFloat16x4 v1 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v1, 6, slice); - - tex = FfxInt32x2(x * 4 + 0, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 0, y * 2 + 1); - FfxFloat16x4 v2 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v2, 6, slice); - - tex = FfxInt32x2(x * 4 + 2, y * 4 + 2); - pix = FfxInt32x2(x * 2 + 1, y * 2 + 1); - FfxFloat16x4 v3 = SpdReduceLoad4H(tex, slice); - SpdStoreH(pix, v3, 6, slice); - - if (mips < 8) - return; - // no barrier needed, working on values only from the same thread - - FfxFloat16x4 v = SpdReduce4H(v0, v1, v2, v3); - SpdStoreH(FfxInt32x2(x, y), v, 7, slice); - SpdStoreIntermediateH(x, y, v); -} - -void SpdDownsampleNextFourH(FfxUInt32 x, FfxUInt32 y, FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 baseMip, FfxUInt32 mips, FfxUInt32 slice) -{ - if (mips <= baseMip) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_2H(x, y, workGroupID, localInvocationIndex, baseMip, slice); - - if (mips <= baseMip + 1) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_3H(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); - - if (mips <= baseMip + 2) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_4H(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); - - if (mips <= baseMip + 3) - return; - ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_5H(workGroupID, localInvocationIndex, baseMip + 3, slice); -} - -/// Downsamples a 64x64 tile based on the work group id and work group offset. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// Uses half types. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// -/// @ingroup FfxGPUSpd -void SpdDownsampleH(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice) -{ - FfxUInt32x2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); - FfxUInt32 x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); - FfxUInt32 y = sub_xy.y + 8 * ((localInvocationIndex >> 7)); - - // compute MIP level 0 and 1 - SpdDownsampleMips_0_1H(x, y, workGroupID, localInvocationIndex, mips, slice); - - // compute MIP level 2, 3, 4, 5 - SpdDownsampleNextFourH(x, y, workGroupID, localInvocationIndex, 2, mips, slice); - - if (mips < 7) - return; - - // increase the global atomic counter for the given slice and check if it's the last remaining thread group: - // terminate if not, continue if yes. - if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) - return; - - // reset the global atomic counter back to 0 for the next spd dispatch - SpdResetAtomicCounter(slice); - - // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. - // compute MIP level 6 and 7 - SpdDownsampleMips_6_7H(x, y, mips, slice); - - // compute MIP level 8, 9, 10, 11 - SpdDownsampleNextFourH(x, y, FfxUInt32x2(0, 0), localInvocationIndex, 8, mips, slice); -} - -/// Downsamples a 64x64 tile based on the work group id and work group offset. -/// If after downsampling it's the last active thread group, computes the remaining MIP levels. -/// Uses half types. -/// -/// @param [in] workGroupID index of the work group / thread group -/// @param [in] localInvocationIndex index of the thread within the thread group in 1D -/// @param [in] mips the number of total MIP levels to compute for the input texture -/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice -/// @param [in] slice the slice of the input texture -/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. -/// -/// @ingroup FfxGPUSpd -void SpdDownsampleH(FfxUInt32x2 workGroupID, FfxUInt32 localInvocationIndex, FfxUInt32 mips, FfxUInt32 numWorkGroups, FfxUInt32 slice, FfxUInt32x2 workGroupOffset) -{ - SpdDownsampleH(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); -} - -#endif // #if FFX_HALF -#endif // #if defined(FFX_GPU) diff --git a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta b/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta deleted file mode 100644 index 2741ab1..0000000 --- a/com.unity.postprocessing/PostProcessing/Shaders/FSR3/shaders/fsr3upscaler/spd/ffx_spd.h.meta +++ /dev/null @@ -1,67 +0,0 @@ -fileFormatVersion: 2 -guid: face65176ee3b82498bd0b8fed0ddacd -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 0 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - : Any - second: - enabled: 0 - settings: - Exclude Editor: 1 - Exclude GameCoreScarlett: 1 - Exclude GameCoreXboxOne: 1 - Exclude Linux64: 1 - Exclude OSXUniversal: 1 - Exclude PS4: 1 - Exclude PS5: 1 - Exclude Win: 1 - Exclude Win64: 1 - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 0 - settings: - CPU: AnyCPU - DefaultValueInitialized: true - OS: AnyOS - - first: - Standalone: Linux64 - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: OSXUniversal - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win - second: - enabled: 0 - settings: - CPU: None - - first: - Standalone: Win64 - second: - enabled: 0 - settings: - CPU: None - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.postprocessing/package.json b/com.unity.postprocessing/package.json deleted file mode 100644 index 6d0688d..0000000 --- a/com.unity.postprocessing/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "com.unity.postprocessing", - "version": "3.4.0", - "displayName": "Post Processing", - "unity": "2019.4", - "unityRelease": "19f1", - "description": "The post-processing stack (v2) comes with a collection of effects and image filters you can apply to your cameras to improve the visuals of your games.", - "dependencies": { - "com.unity.modules.physics": "1.0.0" - } -} diff --git a/images/comparison-fsr3.1.png b/images/comparison-fsr3.1.png new file mode 100644 index 0000000000000000000000000000000000000000..c800c5770018c81e2f967ed921c17a682678b5c6 GIT binary patch literal 884986 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⩔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≻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%0D@*7KeLD9M%%5#P!=>togs^;Q=?O{(t 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 PNWg6db#@0l>IWXaKCf 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 ?wW`s&%9X+@mb_dQslaxaM36S4V7175UPx>T zEkp*`lP(|WO?8A(2)v4oXDxm(wDGXdDw*!QKo9vI9iA{eHAxn7(PTOqjSueb-MMpc z=kDQ~Z{5Cr{q}3Gz4iRd*S`DmTi<*6`g1SeT<>C@;f=i$d((zxn-pZ^lg$*spZZg& zsgE*Kn0b3aQD#$$paHgNMS7EdUEZ*Xdi$}1OXTFARc7t2a|7iM`*9dJsK*SzPzZ=7 zR%nQfsCU~Nzz)DAGRB3`;7~YT&M;U5%aAN1AzCXkS9UfYd+18D)ub67iCI*a2E&@Z z--j^~Z`&P)prUutWJ}t@RnHHU3i(>-UD#}2xk$LtlA%`WP@^TDkj8Mrb(* zK)=8P&l3h6qG58H&vdPeSvQH8R`ej6V$<2&55vVGho>rg{z_k~*5`Qk4^OV$IyO#h zG&$~s+@`#DIJD%{tzt=vqdT#hcaB-frLA>`NmnmalPN=H=`y-#;h^6M(}g1z8qqbaEf4ZjzW@6_V!Y)!W2t53y#z&ZmY(-bI)b3u_5V zIHBb<(nrNp;;`Ref-KF`s4?g9>!6m4VS_Ym{~kKJ?(Ap zY~8wbyIQLa`ojyCx8Y-K-zd9v_u$pH?rwD3U;XYIKmDnvf8}?-4b=pNknJu!fE?lJw^7J-jMhKz3YN{9b z9;Mi$P#PU12$1qDVkSwWmQ242F(q)6SSC&c*PpNHsq@lw6eVd43qj~JMOCY{MQ-)>^;`6O;o{EVbbyic z`~7;Y&U)aL*KgHoH6OLDmPf~f7hk_i<9fw=bH6{)vndiR*6X@3;gVPX0$5JYFt|}t$9vUDr=#?_MiX4`sUhjFd{N&w7OI!v&2RnmgCPBi(A+4 z9G)IQ+IFXP|NU3-D@@+N_m6v%XnN~l_~xyny%W8OeR;ck@1^yN7q`~d+Vy%Jn~dXW zG>&Rj?GwXd0ArJ{XPv#6V{e-~7f+AwnqC;=rXBvv$%uHggLx!Tf-JCDl!;$hF6NCo zqg(qfFel}UeiBX5w(l`-rOR{Z@8HOS#=c&E#!1R-{Y-TpXnzT+l zIXXhPjKFf`6ZI;d%q)$F}56wUK zwU~XRb6F503Wtj|UUt z3LNBGr*-+tPP^49u`-bp_KJE{o^IUc6vaZRyqwQ)AW(#NOU5Uw*`(y@-J;Wz+jxqw z+87Ud@pM}DifFLp)reFEgHfrFE0uH8UT>lA|CGP|+$;b73*VEElXk=^z~aY}6*v_` zJ6ZiFot;uEeZja@nRF}xKrC+0SZKsjawc4b4tDuL8&Kl3RY&A)55XyHa8*oBIy+0W zl612oyR@inXY{HCxP;IG0y+T1yrBXP+EC&Wzm(G#0Onpa@AA(zv@Qh9DH-Eh!HzXm z66rLuInog8jFVsU5Mk z=X6S;3E7CXZ}&ZM|Hb{oQ@osBm?xN95ajuIx9Mck?zXDc%6L3w)~$D1=&e$%1b(?z z2`^q)f9={$6tmu}zkc%oRYQPD%l^cb2Dve3g0c*WKtWtHkVLye1b>;o?_zsi$h{Gb zAzDpU3zzaQFr}4ysi2UYmP|E-=5tI-p@3utB(9zCRL}C~9`WjQ&>u~^7uwe*M~?;@ z=_LB>mtGi8=U@N9Ass&b;N`#hU;gE%pL*g)KK02*@40m6&aJ9f{K|8$N7Hzj_P_Iu zZ$I|r`}7zTPBQKlOF>xgSl};{W8qmS`HR^UgD${xnxK+ScZ(?jh53GP?>$#0e*ZfU+;f%T!3y%y<@>JObMJi*JovKof-cvp@amU-|dHDGBQj@t?T_=zDjvGuimN z(og>I2a+f{>5XuR_uhLM^Ggy|JX18NIJlS}4hF3Lf9)&pT;cfnpLjo^6>&1y@Sat@ z1Y5NIqL$;~us@nyzO)kr0Ub?7qv0T7VMc^88qWfs@Xs4g;*-;no_DJSkf=}GTev2p zlIhvnK-qVwb$NqPr8^V28?t28@T}DtyhgIYD4k{Ci4$Dfhu-(-cr=yXJiYFLvNYb9 z7?-OcsAA@`IAI$0y&@~n!{gJ_qoZ2I-`HGV+gRVZuzlsy&V`N6S|jvJ`Q!fhd#~Mk z<=X9|qZ8DR3DCUVYU`zsf|e7g3FK*-GP0}HYFMkM@zk6SLqr;05h15~b3hc@msmZo z%sPO@DC63*M?Rxzn#55U1gHjc@rq19KJltT_q^WI)>UNK@Pn{g)#hazx>+u*WbMP!uQ|$?)R@#gcWyY}7(ci;Qy<+WDr;&%6u`!C#calKXz@r;_Tna}*vTX#+*ToduR*lm1E zVdmpp2+HbAP!^ZSnOYJsO~?sUmFmOvnBg&dK>g0-Rl!eYNot-6*vct& zL&G4g*}LemLYL0Y#ff!kbpo>r#}ozNW->Hb_n)i^%|i=STyM8D)<3<35Dj}mh|Mk5}T}Pr*$1f!^45AkYZ@@SjA!f*0h#;^`Ix=+0 z*eF2mdvIW%383Z3Bgf?{pjMNZokjdIZzA3iA0*5ryL$$~h!855rZMm2WeBH+dnl)&sm*&-0POOmop{E~u{E0_id;MB| znb$TlKP9nsCcWtk&S1VzsfxpQ8_Satg(GR_6y%}EG?A*4y zcgWFXhO89rIpQH0Wx=duD}L<=1&zWgS>3a(aObsn?1HVFo-GjqmQkoE)4LvIQMClj zL4h-~C=|&C6mu}c&KH;F+U>hu4TENQI~vxG6P9tG`^M`C=VYkQ1ni%TuD|~3)ty$Y z*=(+FfAE7JXjOG(xKXS8?pI#e?luR9x31oIKiw&+_G%^HLv)IRWt*kdBK4~EiMB~GD{{f*uHZAGaq>0``-6trQTp_M>ngywms11-%(zPq?G*C3m^h1b%R*^AEvG9U^^@LZ+XU^730ZcBacd^nZ z-|Rs!3YW;+B9fxqv%Q9=3q&x~=bw1! zN}9~I_iNzW$Xm2p@B)FZmwc^RZ`Ny_Mw1u=y}os8|KR9!*ze;Q2$-w&R(EY}bG_Sb z!C$G}2%{u@^X;S8-`>A<=V&~hFpy+pK2au<1(p+*OZn+IuGDMQMuX)hLtp04h|o*3 zia21hQmM-*O4yZ0+*c0t$i_TP@VCC_SL^k#Qq}aJD0hap!5QIfW!|~MWHjvePEQUG z?%ck8?Txpt-?;hKwd>D+?+4#{?&TN0|Hkt#y>{cq^}C1AHtvn5J?*2<`b3X;UEFA0 z+F8TH;1(|LtnF?!8WrZb>Z1={zW37hR;Pa7eOE4Cym0Z-&I9)oek;Gd)xCK6(%Sl3 zyB1;wFT8dOk}Ns@lqpO;&K$LrV-W=zX~C3`o!gOQDil1;O`$Y9tCXt*-V9r1YUhJVFODm$MF3-f<5QNPITr9m6WA26M-tvO5Y7ed9l-| zfrK8hR?pt_qa92jGMGnliXQX@XDz&d)3@CiZ;N=B;q)wj&VD;I)XhBOI|LpsBVAGWk30v3GF*%msl-SjJL@r*Ci>XfG0w>H>3ioKWTDKR7w*>2-=?0XKAfe7do*uKO?ceX>fUNkam%DSoh6Vl7n(JXW-qx4K_@ z?zO8IHeR}R>*9(A)swJ-P+=iIoy^u6{^R$q=U4exqA~hq z`hydb5Xz&G0wrt#-XsgD7m*egvA)&~CjK#ja-=Vu=i}|p>Cs59{nyI7VJj?_zWC)A zZ|se3984Kk^-8c<#xneBaY|Z|^PV@vArXpMUkntbY`b z`wu+v4D&TnqpcSSMx#8U_jT1#7YY~iQs8?*6}1%$?%QL8B8^f|V^~0bi7zS8I3BTo6_mDoBm>CJq1pRQ(6AC0Tmk z2VS4<`sd#(-`>x3&-65AU^oLe00Dv&L1+m|q)2I(l0qVvv8&zHhNM^%)NZUKDq=$+ z1tl)Iv;w#UK^T!B+{|EHcTZ3IuCD4XZ{DkV_S*FK-u?Z*ysvwZRWI+!lgE7L^WVvn zC#i!Mi^E}DLoFAUfgR+!YXejL=5wvL)+>t@?2X=o0C%E4K8kQYmyae_LoAF7LbJlO z9?C|bonRK9SRU>tk(^5#yT0im+=&7w*vo+f_N z-`v@7ykPIno%;_@e0*f5;d_nUy(8(1a(Oh3r{k#GZH~v`%{vdj{ry{$IMH%Pv6aE6 zL)TVZ*(8D3BtWCE0GdE$zkHRnVSIWRw=Ek{F-o77EV7qp{Zml%GhhDPV~<^IHhnlG zoQ8Y5`^U$_Uaw0enkEqhIec)4Pv!bP4UI>`!Nk6$S#3mdM9DnS5=xxNm9>>#w}V~Y z=;NTcO}8-)c_Wk-Mw++_VsJGu3Wi}WAXiW zuDtr@mG`dPym{-vo!!IR_xB$hpG0v|sg;^dr`vM3*7|GRcDwEEY;@PxdS^D*o_O@^ z)@JAYna#7?>zB@LJa*~ag-bi@8>?r|?L7X(Bb!@0yM_tfvVWoC{3e0kLB`J zwD(=N*S~e^&YK_J7R?O1Qj>;u9sCw#DDZDLR8Zuy94jeOTCNUj$6^=Jy0|0VO))Dk z%F?bHDEyM5JN;Llb*aDNhU8g2sRi?6)R4;XXO&NjaumPh7Emc|Z7S!J@>YS8Y7N;e z=3hRopkI2?E#$XUUh8*W{_InDwUg;I2%I#UW?AaBdW5rSoceAXI^$o?XC*CtMBV5M zMf275Ro8ck4BKsYp6Aoy2to8ay_zF#)Y~UXj6EW3MivDibbBxXNlgf(+f@b zEBYuSOqN92l3$BM7v!TnAg7xpxh50l74#}$QW!?Bzjp(*a5@^3HA~Th@E{xrO~rix zIYkNQPjH|!cMNZ2Nph>*X7RNx?RY3vVHzI=s8ZZ7>p3D^Z%wMD0>3C#Gr`K$Kg-mQ zk<&nd1~Q`Na2Vcc-B?7_f(Q~>9fHFSQRQ@09L11ysn&`-CDei%kpH6UVACv?2Bd!i zGtYAc+4x49@TMaGv6Uhu3dhcnVy()+10*Q{X&x12axL<*YJqrtG@d7yhb zonY`zPEh@&D2&#(HoM){Gmo7ckH(v;ombzx`Sc^350A%&)Ftxi5j2uPryMeYMov4p zSN0o~wRXdA)IJQyfTdX>{%LG_fP&9~M9k88NxL9w&V#Dt=c(R%VjMV&B8{~Urqd|v zmFg)5$gen+%7fbnUw`9vl+GqND5dhh`}04x-U&XwcSy}ExA(vC?Kk~WxV^bXK=AZ) z&pmbN%>Da^S8v|E_we|w4{!Fo`OWKBpM2&;$eX62mfzd3sGG(^m|Ht6Rq&_lD1N9z zz=~HvNGh(d1r`#FaxG}K(fgWx5ZZIGN);WWFM0zw47r#kFb!j-JRXctEq_4|O=h!9 z>8hT*SSHq76Bw1MR<=sql&{TGxe^b}QduDRlbSgZXJ}SKb%k!=S3>|IY$1saFQ72T zZ~yB5ynFlF{oB{_>FB#(``uUH``B1SgHBr7K z_SpH=UT14(J7_iIaEi+_)!UndV(D*x+g2Id@xS@=UjcDYHOUf8tR4l-%j=sfb*K99 z;l3t>PYwvm?TM1Nx!%V@-_v&l=Y(sy%?rQ%{;dzM@7;SasFr4lzM~{po|XZUu@wn{ zDliT5WsZozNj#{8Zd!Svb2&~9OIf|sJmXolsChfxcb)o=f9W&6@5I{asO$#;vXI8p zhYt_*bcF; zvzz_4?~KOb^}7cj-q^ih<^Ezxww3SHJiD%O738bMwyb)th^VN5e3QcORaN zr%{sY72I~qIls|u`n5}ESGL#MPd$G2k&9c8UOMyWg)`^RZ=c;+J9}>P%$c3FwUv#H z_4SQ)pYix!r_=I0*Y~u36)c6xB8#Qh#=}t(hoiyd;ojcEy@&g|5ANN6@L+H6&fN!G zogAJV9}UnRUw`ery`wP|jWG@NhV#>uC}HHX1oRSR#L`A|T@<%Ip&_XyS;;USW@x5V zmhDkbm&@{2V0opAB#MlJ&jp0#aB#f@fEy}qa?Ou=)GhAFVl7)xeg2Ays-jG5-<%0G zz@PGU>$HHZNs*6;5!b!=?4vDxxsaQcEE=I+{8kU$f-1z!bIIWJs@<&A4BGQ7_L`oj zx6J`+-f-|RN>#^iwOS3oC6`IhR9timMx{Z3qMZgooQ`L+ zl#pn2a*P1Vk=4pV3?JbWDmQI74T9vwEF}br!sOd;ePoyyR`^F>hC>^d;m59YvJn=e z)WDRt#H&ge22_QERJGsLR(HrgTC%Wo+_IFkkY;HNq0(xG>mL;7q8s!h=(GL{CqtJU zx=j^XZs~EFET~xITklfSx};aZRpcry@JMjhY6hEc)R>EkpHi_X%wb(?T>1y~>?o!M zEKF5n)X_crZx@PbRe*NHHe?AriI-+6HJN09k*7Tsh+ipb2+^bjnROy&ttia3CZJN$ zH$91QaVr$+;%;a+0_(wEVAls61u!tE%|c2Hw<48GKECjoM}xql7tQR~8m*xD8^8bBH(vYT(X(r$t>3+OGMpyUSiJNTUwD3{*F4j2;|n|(OtO6b_SHM@zVqQu*J-t| z7%MM-{)I0*bMZI-;5!$$SHAo9$3X1YvOoOS|N4cOzL2D;*qlx>FrfVppk&<*h~J4V z^&7hUw3E5}ikjP(%J8Fgua&p@mq zzXS&{Ppb`2(w1j(C-wBiJVzLX7T|z+CQhuD07#CeqK(F4uCM;!1J$*3!@%6_$Nt{?maxZe&hD(e(PJGIvjuf-}$-z+RDmG zcXhQ3(}FAXDUfSvORc6b-}k!x-su zY20;c{NPgp7%jtX`jY}t(ijhxg4-3wkQBtG1T9egVD;0_L(4B2&b3_$l;+_TFs{Gx}CNU4PgM3IMPI$ z(=Z+lM<+)Elw!Nx>8-D9ZEWIk9R-5wR?DY1oxX3nb8D}^@IEO z4-WPZ9vt4har5JkZ{5Cm@A~!I@4x@iyB}PC|HB&}T)uJZ?t|mw;obZD#{(RgguZYJ z9H-vz1e>efjn(!_w?$tYYu$_IwjQ~-^TZ=(AG@@3?(D`Rk6k){{><46=Qp>u);6}f z-A>SKMcSrpDZ-;DPtr^jKZcj4qr;;U5>b3|5{|}WLTJ1ne4i{U;{f3h=GhFp)atBg z$jGMOgwSBl^PDT!?%dyJ_&TI7j9#3%G-}u!!|5R!Ao5G{igH$7`hoP4GetqE=;Giu zH^mWFk$=cp(V}o^^yMg6M)bAv9Lu|(mgl6b(>ZbLehCFRmgYExhenEGcEfHLg}AFc z*<&&&S3t|Xg5UVJOq9z{Up({NQ;%q#&v*4wUN%7qw0e5$IEms4v6yyInP+KQD$h{T zX_9nSR%eS^mTC7U6uWYI8}(-Y`1qKP9p59OccikMS(?nVtm-(D7j?%liQ&*R1kIu# z5H;H?+Kyv90zvXi>I6bFYP)U&m7JkQwPiykwlMoU=5UB!8kojtBUQku9~)aCeU>3Y zTOsABiXEi?Sb6SV+}S{Y@u+2Zq)&B#HHvBN0u&)FA3|0z%YfTjR97TvN|)$JWJxZH zre_f$+it;7jparIqOJ|^Mht@ozeEbTKurM?jA9R8?CwHF9ph^6fT%<8XS5V-);R?* zSa6ksKSm;$L6Fv4XmMqdYS!-)Y8TJ|uA&urBs78JVBUMB#4xDn$54>4KpXc15uBV*0W`YusyP0^^nYE8II>)fy|)F`9^3)&~GtTn)3Sg|hs zXcCO7jDA`Ct1>U+t~*Ky|mIVE>7y9>Jo5Aco#>oe8Q(&vfJ<9qu1hYYoR~y!O5Kjz?2KdG-BU zKml^V84zeVr4s7NX`rn%@yDgcP%34M^xys2CpvEZD9V0!dZ?1*;zkq0Qxi`#U1U+N zM+bE=4beU=%gE-7DxSSts{0sMy%q$u=XvSrM)O&(ll2>EX|XYDT={7C55IqJ|0E`A z`+xrL|H^1MIM^TL^Zal)`uZE!rU`taS8adz*>ivDXTH3#v(sAHNW$>dZ~W1>-gpZ; zaqaH@kALC0C!f6Z+$&$sQJK*c7gCUGbRx_wnP~Z)Lnv-@mBM@lu7>T?By6}oC?yw6 zz|vwv8#w?lf;61y`(7LjQ%^)SOHO&{L+g1SLo-`8SkLkd?-j!9NeV8kCXMu%b*^Xd zv!K&Q%((u|U-^5)&keU0G<%(X-`>^C2rzNtLaf>dyEZ*Jncx(hjI^i|)~8n(1NV1h zrL(z0H0Ax1-+k4{g}Wj~1Ft6hqKn2%$0l)0o?I->Y^?s|E6@18_nW`_#uuNzfb0ZK zclY7(dbfG+;n7o%ZEvoxOs3&q`M>>B)v)7V{SSW)Zkf-t-Y92mDe6yKsSd~Elamp0 zadY7Z-MfFLh~ zvFJ(w7Z+AQP)kbw_+=qrHo9eberbfX-7SjPrNj+k(^n7wuYcp;TH9FFw|dP*hJr+U z6uoovZf|v^)ow$gG@is!j-v@p@$+^Mj==b(vm299*y^pcdmShq$MYnN%=N5Jr&GE( zcW%dPX;V>5#lwdOheyX4*j0$tX|?6vRVsd~4e<3kY*xyXh#}CGEM)JNYq4QEl8aY98SjEB4Tz@MJ21Ayb#@T+T$zzZhPbTT|?w|lPZ z-M@7e_qV^cb$qB*AicGfPAiy(=-ljAf8+PxxO^LCfCPvN+6jc~QdC(Iiz~FNT^Y4> z=sxJ;9zUx5NpA56;ZBQdNLE1Qjr7ZVaxYXCZYRr>y|Pu7KlLasFWR?S)+*%-m9Ek{ z)U-87rhwG7$}79LD<7z6QkmpPi0p+gT83(5a>-paE4XB{NPP82Ui^t4dAU?aJ6GfB z$f=hgcWdozx!#zZ?90}dXKpi?#nYOj=v8gt>FP5*njiJ+4wBO9tcOvQ=2@%Vt(J0J z4a}b=D8f+hy%A&MbLhD>K`HppQ5s1K^LVK0np>X?4i4_?brrp&JzL;RH`@Uic(8kb z6{%J=%d#lz^IL!OzyD{N-8a?27!8eDdO@Nf5a2-wM)$`_X?HF66QrpD;X#nM|dBK|0mLf5f*OH_fl$xIr%`OLwTl697XYbaBAvs9~0Q6B373{7f$&`YE!?G3~8C1=*E#q+#ha|6F|`Q!b- zsT_}zpZU?x`+@Jd4#=rDDvxfjuXTg#_YPpwI}e9{`06{=e0=uoPQ!0rc=WLso`33i z|9CnXymj@?_uu=tnjW9*-#h>Ka}Y026DUonr*N}g)1F#d4?P$7SqvDcXh|}TaFw2%U}D4 z`w#9+hezYVags(!5*{Dz-M?}5_SMU-=S-vN!~Nr{H*SCP)i>XK>)kise*c}zAHDbC zwGTeJNr(*A@cAy^xFc6X5B;>BwA*xhEguV-Wa%HgdF@aCiI=b5v8CpP2pPMIzk$uj zr=-DZcPn4{{F9AF{k+ZvAAK&@#quaOc>_@S_iTcs?-Mzh&hlk_)yGQh+{VSa6nKK)p zG@8Z_9z3kn9pA0d{%{l`oN@8?ozwz9r#Gqilpu;N7pu1rBq%6TNaYG zAzOnrI`M0=gRTDZE6)YZfCx`L77v28F=!2~A4ds1in@SLseurI?A5iEwUzGPgZ=R| zzHsS+9)T0E>6x@%e5L7j$Hx!5_wexG==k`-ajWHNBe<3B=H|w^owYM-!Olu6aBJ^= zygM9EZ{FJ{cv$NP8*AN-)$WC@{)Ka!=XX}mZmn*uuQriY!O3ts;%B)3@F))Ba57D^ z8Sbp#^ca!nYro^p+J;Na?Yby2*VavY9(O_0Tp!`<^zgvZSiT>CBJJecsOhm$&`Q#z z)e1Vjp3s%g<2YVf?Owlr`^G(e90b}FZh_IU_*KdfFGX5p8kJPevZWTJH0;uT^y7b=U9GGe0iMePxBrME?J(XfeH;(z$3L(v|BW66{sz+bXkC@bn!#W zx;TZuXtemX^Ku}9)+FuH*{z+eb@+z@zK}mh_f{LNRMCFLHcU%#BeBIuv{QHM9^@eW zY@lh>lx^8DPxHF#bXV82IK(dF7lKW+5O64pHbX26R+bWjhz8CABBps__7vYYv@$i@ zFQANckA@*Z6DEVmLAyk`ZkD9q`u=6)mjM)`GHgho3UpdRP}!9(g`d;07Z@t=Q{Z2D zLUsu(Y?F%O1hmx6xPlu>0<#FBmGJT;C|OuR>!2WlQbke(vljV}-7NZ`EY=r$97nMp zLSW3*zHFqGG`GWaipr~o;KQ2Q>M0%YoNbjOI5|W#8yYR!MhJQgL{-RBFgK(EX8vnY zznKL)gsdfamaszx1BU~s+(sRkxP#`V52~pkKxx`vhnhB(C&G)K`HjpG0Tj_iWnDn?3W0SCKaj17MpgdtY_D80;ArWzLb3_MCQ&4utMheq`B z>ZQN_mwygzxVpJv^Ph?DYj>~p^_4u0(Tu2x2M-Ql*c-R^n$58VG zPLjtiuJ>10FiT(lEB}p3+wp(=r7v-gjNgR)|*-5DFN@)`mz#I}5@jW^G z$cDj4l1|fi$}ObgkLfiI72qG4MUKuz0h1#x`5_4>cy8mr_>cd5e{~i1uG;z@DyK=Dk+Clh25z63=m9S1A?1)WDd(hfj4ehLN(2aQ9+v3P)*qu zV0==ZOw13}v=qV$SmVl9Bi5oUQMvRN|MZt%c;Tr=qZ;@D7|61Sn#o*?+E&)q^;Nae zNnPP*y}ZC)%(c|BrY{WmzP7qalQ9u-(CXE_KrE_{495)@maELQz0<7f`t&MTDw@29 z35j9!G}dvlcoHXhtx=z))BSt5WaKL)ECret5>qlu=5^l<$HPS#_H(aXz4kx;>L19& zf?`9E*)9!JKf;6t%T3D>a+kTMM{!C1Cuy!olpi^}&1I}-Jo=P|o+cX~p|U9rT=a?- zB&+Z|K%fC2oPtd*tua86z30-_zo`!ntUit#`be0UGo`KcKB1P?lFh_)sXfW8CL}~K z^{iKFxhz;y!LSCcB_l>p{Ue`t&>H;LL1W4UNL_HNK!h>Dl5x4z1r~j_Nb=liXwy&i zfqSIYr;SErA`}z(fL9dIT;;NWsskc_)}!KkLCl#f zV`fz_I4QtC!<|8+#wUlFj|T(A*iP+0v$~_pAK!TPsY~lCtLjWopzD~phU;pdgeY5GTg4P=_%OQb-o1Nw zfB*Q`zxMV|fAQImZteZ}4?q7u{nM`}S?1Jh2BnelbWE+F^k=?&39=2VrLT{U@?@69 z`J!6#+FJesapI#{62|6jpq}wTd*Yt_Ql8Qu9wEqwaf4Qr~j|N`wKtux$nGv{c}&9|Lk*5Ub}VY&;Ou^=uz-IZdUzvRWs^{l2jJb!Vlfd=A(mqj@x7)j^8?Y zcyGSQ`|D@p>2N$c)~gU+P&8KlajtgOkV4ZR~8WCSf$5=%GNbr+t^B zY20bMP2YzuKl|7I2cx-;|My@2InQ+uj)vP?J%)kbws-#?>g$mwABW|4Z{L4-G9>aJ zj3Ox3ZPmj`eCF&1&hFdqT-`q$KXPgFweNo%M#=sCq1ULK#QEV=?_$6c;tF9&3Us8j z2AmYk^VxVZCSVkAh*!}<=zw%(ZL?e=pdwDyC+O^b!aAVhWlpEDA9y6_40ahDAIE8F zIG#{=ZKeI_qZeDPHcA7*%yAZ-z-u-2W^0b)oB@Hh>oE;1IjKm)1dfA|fOZe=?#7F} z)oPB%W4fDX`Q1AYP>DO+>$o_K&vkvo!Ex&Z#3f*^F2=*KtVf@k_^oLkMxXhqJ6@iQ zt2GY|Am5_y#1UMbq~Qb<;c5`5EV86r*CGS>d^{Rt;Y8b;HXO{7gjuS4hApHioSQlN zEzkeaul&aEy?UkaGZ=!@*eM;AwC5KE@{KqmhYm8$QTT*n{Fo~uGcV0}O7)@=?V|ha zVtH66YQajXCOJhlqnaW~T^blgx8xUKNImOVPNzxrS=!3TkD;V`vrCo!0eDE0!$8GJ zx?DC}+#*3crp%Q75C6i?Joeb5#6R6`dpMb>+4-!uzDe9dfuPxq;&D7a!DocoL2fpy zq~I%0S5`I}UK1x_Hj66ddZq3WliAD@sv^S_%+d(9a|3)_`iA3XdNR8xz;K+AQEctwZ5uqlk@6xEWf#uTv01BF&FE zCpuXT4t1iYCC{e2b-U0P@(j3XwPk?i7MN#&z{r16VZFYYBUpMvBu8y>7e^<}QVB}& zMwy+Js8$`lGpd8kLVaiog|X=@hzv&Sae0V8bVE=wqM4qYkg25u5VT4=RJVup`uv!c z)Ev5gBm-)cQNUx?uA~BeME4pFo!S4o77}t(m5Q;S0w5VE62gOeRD*U~IbclnL;-s_ zNER846%!{M0v-Ir1fMhsb{R_G)i(RBi|5aibLFEOn;Yw}^&||nSFXlbXQVhH1lg@1 z00oCfC!3pVNsLh$gLC2{$i=wm2+xDwlQ^Y)8n0KYk3D*Bz0>aEt=QoE(=X~;l|-MtuuEvqdNc!Q8xY@xh;QUb!` z8SOO=qq*SnsFdM%|so`S5r=nj{SNXp|T_O7C5}|65=C-p~Ko zORm1!F1N2yt37^hZL8y7y>|ev?(Ls^{k8YglfCWDRmX9*b}oMP$6p>E4)S<1oFrfW z-sQ(HZY7hW^)na2cZPud~mS;(e-Y{ac1RYGzf=>@yQX&1FtQMru%oUrPEQv z@y3J0G!Bz^IzBmZo6Tl_9iP-+->XdYu-)$T(O%rY< zh!U;wABL8f&k)L}b<~Ac{LD+S?6d_&@VMT|&m0HmjE)49-_)Bg-HyJ{zqfznlE!2jVw=wgOmM-hfuLJE6w1* zSt%Y*aYMJZ*Ecp-Hnuh$ZLqc=uy8%%R-)s&x$b&Xx>hT7dc9`5<2CgqdYlFnEWn}d zAg(5MZSL8&hbcEa+n&yGwR*bl5Q+~DkFsQ1L)&`wG}c-WA8~7TY=8X#=R&p!bC~C^ zfB*8G2io{c+T8GL#E})}DAN3rFYFf=rzT1a234$Qh;@hVE6f911`wW}~oQ>(1a&RcnIs1mIl-ir7l<=wr z@}MY<;MVZZCXI23Rh_0d^eT^L7^K|sB(T|fRX{Iy0HwA+qEYdUL_@zitQ(!wvnVP6 zIJjXc-d^q!UjBKF4C_z4@3@9O-gIxX+G+luNyNkQk z@|~TH9*`a#oS+MKcOR~AuAQ6=JWsl^?)ZAqE{j2Fdu?@fZ3Rap3?oWrsUkTo^qb8q z^(w={A3(?93J_R34TpiXJKgP_%|JUGE-qiczq!^s9*nQvIRqAgE>MecRh~Y-c3g?h z1$st2Smeaua4RYrOo0uWn&uTh}1LU^u$};NeGCuYK#)H{N{fowwh9|DE?fynOZAmFsuz z?Ct|{tL1%kb1%*^{zE;6gFWc91K5KRciYXa)s-IN=Cr@~((^z0<6rrSpZb$O`O}~M zu^)S>|M`_OFYjD<`P{RwJpTF5Kj(HzQTfoVIMajswT64vKeI6lT5+i{jw@-NMPp*g zESii*$3kkOzIS)`-FH7&$itJv4(Irt)mqT<`~7*#Puz;z?L|T5N^LM-X>EVMVgqyo$GOKoWOr#O}= zsjuQv0<~;W|J+9HhhKiybzN8$&5p1GeBEhe_!@Dfxd1JLh1=v#VALJoi(-8_fy+Q& zlh@K5|%uc?nwso0h`S#h(-s+0)2UYEQzDT14 zF>-x>>A7na-Ap=Uo@1YAInhUAwbLEoER7~2ExD6VsokR{CvaA+p_zH~wM<$x*1oZs zmYViAw+SA&-9H3*gG68-3#fgB2gGE6sfKS8hE!0PBc*D ztAypKj*9)D!d%|rpQx?d%Hps_mia1Vib(gRj8#SLTSGFbyW~Nr0InL7&%^g=2CyP=QgT-vrW9=`#!#*KD=92 zNES~gdZHVr)yhm?P15MYqGl}xI+4#e-?&V8V1qP-*mybF zKuP|1AE|_4SRlp>nLT|d4sAFD{})yP*Pdt_0U&TU4-F>@ zk4wE-r_g~W$X$9TYM+7WY%r6jED>dySf+HS#woZ(Vfj7Wp!+CGF#UM?%Z98sm6vla zek4yhoRA+xMFrH9mD1gU{0u~0>ZTOgBn*95Z_>*x(vC%%hL*!GE(8gh>eq+>Qk4u^ z1D9X(pQJjIEcF~0rAanAMVoBSzw!f3&514lN2bBNc?T$9XuOo zdRQap1e{n0b@|@1wG5TS3y*HT^7$7=!(q_WH<_r_?zF>6)ay6nNtA1^&)f<8pxK72 zS}R@rkJrBQ#_R81`|6LrfGs4@yt})T z{QYme`Re<(E^e>fKN#ZoB&n9#afmd8ygzXsFR|^`_pC}U2y;bwOTIf(vDVxra`*6T^>{rB&@ zdgTzdxN{H{3_!E|nYXHq$o}o0edWbxpOl=;^AQ8jvj<1RZ@zWo!Leo(abusoxbX{L z`NESge5O)&!Sdak*ZtKK+1>ekWM% zw_DBN_rCLXqfzr+y#+0r87PiZBbsjoZj#7d>vmRO{>(FvJo$|4Hl5}KpZ@LXhmCUW zjI%-L3D|YLIE-mQv((V_c^cIkp5EvRLoaB@;dGuSl^S*>g{j7af$Ms$?n*vOYmSe3 zgUTcr0xehcegOe%DJ{+Rv%~##aF|aM@v)dk02-8xLNiN+kZ6raz3>-xDScbM_jhk} zf?zP55QIGO=()rF!;i1ufAW!?UcYs6GK!=4?9SSF9Q~F5^3%6e{=t9o%NfC1c^)*I zc>;qFLgeQzo=KwUTi<@~x#ur6TdjM$d!FOK(13h+coHS)+KT3WTb<_a{lnk?=KI}_ ze|R#fmGYx#_HdjO808Rk1);nLJ4=44NN!QWsK2}{3iHQRk*SoiCDn^7{&Y)!ih7YA z=9Ix<~j!;3{D_)wc!H3>66G!4(JCfaQpa;z+O6tgfu2 z_B5nS7!$=5Ls@~>O!FNvUC|Eaq3*a`hr>z3^N4bX1HBr6f3m){LJ3R{Frram)OeDF zlTcsuTjmYv5T98eNE9I>c~V&?TBKNvI2@i{mt5QfMV`_4Xg8asb8W6)MpMF@%}zg07YVLqnh3QRUNLr3AK$Y1~WBGs0V1my|(t^L#Oy5Rkc!(-4AeG)O7MNLlCN zKNyCb0v@%l*&3lmfBHn2-f?aPn)RYficyy)Y(xfx#;&=MqHE}74QeBencm10VHB*( z$q;3WUW7IAX}LkDk5$*V)Cpz5J&nmLQp2h%u;}M=kMI2S&;G=CI2sO)_74ZEYpaJR zC!JPnwco{Q8k~$8Hbz`zP}A(KxV}d~vUm61jhnadZ1hy9Qoi`eS;q~Uoet)EIvx97 z5QS5MBnSgnkH%9Btn0aHI{WCOYxno|Z|)u4eK-OV&2mZXHOQZPWn*o<-Dy;(`TRFe z9>lXO9_L!GhB|PnNJM;+jdsIEvtDmjy}owD6E&e!Mm#mI1(k-ceHho<{?E5|h9_fp zHTc~JcVBL;x>4zkcW-}qX9$+=9q7e$L$4&9!_iYMyg<2$f9wD77u((LbT}GJhWhUJ zY<~Ct;n&~#DAZ=5`AWEHOdz;D)kWHo*J_1C}l+V@_1=5d^i8(0G;Y&4%LK5030ePvrbX2Z4rrCHm z-v2n=PZo0Wq(p^2gid;a19L$>c(5xjjB`aAsYmga!W~te^UnyzX*o4lE=~XW;oid& zuU#b|}C&7Lho8J*q684dr#|5o2>DUQGQfBrWSPuF+TD2n4$%V20{ z8r{16@ZQ7mFZ}Gw4bT7H_dh(p)ra}QI6XcZgkk*1BOCRGkIs4b^38WH-`!Yk9}OpP zQJBs5CkfD-QUh9zLCj&QTmw1HE3}?5zl5N;Wy3B?T1o@Ag#GCs$bqhsYhA%D2oyfz zfAKf|V!P9$US`h$<%!rNb{QYFUY|osJy+6B1~gu+)x*&-_@WT9px4V6m9?!cJd$cT ztJj<))`Ar;Xz3Bl+&&Hje&?B%A(yJ43HQHTtpn-g_ypQ@x+}0Nv8E(l(YBO2^>7rX z6REcku!Uf<~L2}c> z5YL#mXjMPrZ;{k~bXqp00ToQ5>p}!nrC=-2cF-_69PHt zAY-cUFqdoN@vv6+TAg*r_ciB$TU@Cx(y3mM)H7dqo@Wwd#a#`#on~d|f0oB^lIGs# zdO`>56diI*DlEaoMu|p~XJINo`Tzdk|5}o2-IDB*c!Xy872&{| z4Ig~iKMk5+tsG;pc1vgxHi}DO&Psu(cHkpmv;r5BMIy&Qz?|AsZzdN*?2mVx%GjA9 z)WGZ^LNWD?#*wpcPY{GnyH!9b>6Q(KKW*+rwJF8VCv+ukfT$|msU1BnL(Cu`wMYVO zJ(n=5e~FHU!fn-7YiiE=vi7X97)(E0SbmWN7To|~8sU;)RFSa`fnnO8_Jv%^t25(l zV5gdLj4!PImRIO`&FzK?@ zZgZs@beh3hzujxPP0wjHwKO4c9pY)d23oJ!sugX1sV%#}d_}SK#hsPS?ac2V+R;>n(;VN(luD@QJ;%M`!SCqX7p8y;h4b=;&k|rD?YvbUMu&H}9Z29oNrr zxAjnP;rRjfroXZZUpS8TINCY0v)1q2x%&X__?a(0eK?pBwyI?UqxxcJtp%abLHF=u z5d%#Li*_|v;Se}J`8cf9%k?IX1w16tP>?h)XW^{oREX-p_nPBw&l-bK=rx_I;b?;> zt5UtUH`qIlpxiJt7shBT8X+cPp5@fk?|t{Z;o<)0UVP5e*64K;zw*vTyS6Az<8+kd zH|`yN?OWe#my?~do7nR+moC2e{8QJi-+DNR`|GPmC&Mp)w)cyF?#o~OsV{uxD=+@c z&;R6Ce*EP>@s%%l?LJ{e8jiEUVXpNtc|1N&CMT1VhjA>hR_uAWp(q{X2TmbssWp5zXty`dInC~{l6)_|eyw!;VgBH1 zx_2es{V=|JCAoija{J@>{=;mjkxTk57%g}SW;5&y8DhO63qFK{Iid*_Vvc2u$q^;o#w~4_j;WGc8x;mDx~~hy`~VhIDX=D7l{OL zyeHEM;d`&&gP*V8*n8{p{f)KO&Q||}%Qp^BrfVx5pbn?{P)HP~LE!H09^Srpc=_u6 zPRo7zu^qQw8w|&JsWM4&fvKYy&~i|ArNc&{D+pcW+2C|=@ktKVtoo<5b-ApoKgMjC zWhCpzUU?qbShA&dpLrkrWb3LD6crrLj!Vnm|W)r@U zlcYtdvQ>C3<+(Q6w>|qCv{S1S*VV|^VuE?raJ;G>tu~NVT4=Z~>NAa#MV8>FNC8?5 z28vFOPhNlfqr<^u>12RP?&*C$`3-g{t#?YWf))jpgi!lcR#A6(b6MKToffjJ;%;#i z?9ZWl%jSz?nP(}xk_}N)%S$z(zv8xXiVUhO)3O-4DpvHFvQhcMhL{8yhj3{P(LDEc z(-fOpkN?>1>|G=!E%ffr`KzfsU9L zueBD3A)MrRO@;_9wBA2J(<&Z?3{s$Ft=oYbn#~|Y(YK}r6oHN2p@R@WKTBm{@uB5` z&ML%0wJL!{6}9BRVbwSu!=)IiZ@%%NW{Q`}qc{qb2*>;u7djikG;5HnXhHAws3UeK5V4+xz30MuHY)#zd4l2@CDqtnePRowyV-GTxG$jj6E%_iC zqJGGg;uAFr02n;wBz|;i)|nw0d+L_Ti;PqY9;S>3IEKhrYoOPP9A*j~FQNJ&&G~ICVUIkdP>e z!NXb8=@e%XfRZS>@aQ9Fb~fte((z!txxK+i<1}_%7gSHf(8s6NtH1b>Wr!Qak%iq= z;CSNF4uSZ!yZbQBC`>!PJZQYEC(rk)r3KV_m}lei9C}uW`WuycLyv;MSS=`5-KyIm z8Wf@_P`Diu<(bluI$4y?&$LWGaNS`Zw?RWzIyjo_9fh#tV5;{x_+K!wh^9jnM32Kz z_x4A>@dvMNx1C2Woq-o!eHwAz4CJ)IxX0rVJM!j7_ulyaJ3A}h%K8?9@Z4u#{@mwY zeCp|ErhBhH`_gm0?Mq*euRh*;?EVL@#*@M5V0ZZ7&UAdzZ1+*YZf9))4Vuka;C7m8 zaEaGh@j7ckceUa-XAmav6Q@-1TlJt>#W<{+wAF6cD~pqp@#b3d*4=}zy>{)=P7k9Vh4Q@NBG-414hJ88 zy!T*#2)OoDz?YsMP)Na0*#9FPzSOl#O3A3qnyO8)5XCt|#kkKXm^`inNbh&`;t1TvkG}GP;|H3g|vbPbcLukThtpq z@~G#bo`j!r~;U*R;%OEM57Tp4m>2$Sb<920kgA=W^rg-^j(^wPXgj3Kmt<5 zoec+s#!>v%``7kQCZLc0aEakmkS&CBWi-{FuAr$7&<(Uo`~a1dwu;N6Fk?ECqy_PV znRokV#MJPLmaVm-7prT1iK5FKOIV$PU#w-Qi$b8JWoW_vvTZAPTG#5!yV8}4T5EO& zqGeHXKDn<8b4@sJul3Gs^c^3U&&N=RPf+o=j&)5lz-LJq+spPDSF>4K(GEcJVB;8H zvz{rU(pnrexjF$rv#JVL;Zo!Z&M>|K>%hx>xJ*!}v`~j5gJTMq{KV6tF`E}OJ9T`Z zYHc!_z=35g)Kg&~{G-=DK=X@^sw)I9DQFlY!Ju^5xC_+jqM(rq78yzlg5oaNSnc=p z)KUzm$2ssEL!ij1tu;|6+R>^LnS8K>F$QjK^CK@oFZgL~6rMYop8`bhm|_)C_^!iv zg}a3tL4Gku!hilzNx zwY@B-mWx4^7Ky#dX1(j0`_xf*QK$;JvYLdD+k-AiGt|UP(?2+VnnS~aXp%wO!O3I5 zIS8CO;owTA8R!`R!&Txh4WJ!3T;n>lTOK`<*=aiSd>(iW&1ZOSQ=1@H z9K4D~%@NKkBycbE$-sIAj|9(y5~O|C!F_23?$&0n*=j<&h32Cc4RmF>6h+zXTX(wT zJKB#Z$Chh@b{u0^wnR7r<3Vr$l|X90!kwn}4Qj)=qtUqCZqrW^#X-;l+0$^U#Z8*0 zT{K&5{KQ4M7KT%lCgj`MUiaPl!O>_Er9by0&)m9ybUe<#@WLj}GuYhfb+77s0Soal zL((1xqAaMc`_;Nzso~Xt60k3)M30i?lP zJho5o>JKrH4J|kUlF2fNjmRmz^YOjk_`^4T{>NV`+%-bSB+k!nb~pOXEXf98ay*)T z=bex5T)(p24T5G9z4^EP%m33WUwnS!!V}*ZzyG7X=LdH_PU2|e{A10{v%QUTt@Sgz z^TR)yUjJZv`^xn0N7H*Zq6b%^d$(rCcW1{RhIg+<_peNEeH`Aq8s58=99)lfuSL7p zlLuEPcgXoTx_3Vx4Cm1}k7gRwJep@IVa`H(EttFzumXIco;41{40K2xf;Us7k|{a8 z8ob1de|m#NWrYwBtSkuw7@;U_QL~T=Y9Q0(3p=H9md77lx%sus{4@TqFUI&KT-rj~WrjzllyZd3B{^;i)`|a<1WL@d_i$C|pZnxzG4XK8b zHZMXD;9(R&HecL-aI)5KZmxHuBXk7ewisTH??5=h(LVlrDwD* zLE9*UJ=)QJCq;XDXG%NWY2W%P=qqXWf;7@AKXDwwFldCJ)vYz$ncfS4Y^cgKpXu2O z)*6n}Oq3{EC`^6sfH2dZs?}CEH&!>cYUOz}8N=N?jQM51E%8#7gxO8qCO-sz^`}*88Dk#mg!8Zk^1>~g8^Z|KHU87!ZGzrn%=BlQ~ z;{>(a?yV8#=~0@LL0t|-o{+{u2}rZ5@G02FwUy1CrKS(1igUeROD_>IA8ZDh;VaMY zIj)a;k`Z@CS-F%ec*XUJWo`fPyYFhw*f7b+Y=S^z1WUSRY)ga+0~)Ml>13m(kmcBY z!;GQVb{a(xjF}mR(8D-XLI(v@jUMTbf#EY-?^^%NV6YYN0UTw%K_S1N_HF+P;B;`; znlE}$acM?mSi4hLPhw0p&!Mh_M{7j0<+f|5ZtAwMY+jEwYvq+Tkw%a;Y$Jo*g(6?Q zS<_nKVKr1G%W^d%$3)>ZE=F!oKp+O;#Y``8k~sr6#ZwHt1bhh3spAu1=24ND{BFx_ z2LvFRmQX%fxB}XOr{;TguJA^%c5b5%+@4d_yK!y6;f|1j{Y20Y`W?TUqICtTdf!^*ired*b}sU>u!$su_T=dM&AzKZ-`0EHVO!TJ|^$ zBG$cn*^y&zk_tgoK>lei3Pbi+R#sQ*?r1QDDb}j)wb^9bZ!^5fI2wo9(Kr<=6ue^G zQ=B*o4uUE9EjaF9e*LXzIC$x^FMz1w5EWVOw)|e;cUtb=@dP_@e}C}pH!shphky4U z{{27ub1z?f_JwauK6oP7bjB$Eaew2?M~nS$kFH*x-uYmB{eF6a)uCDSXsQpK&laT^ zxglYmfW%9P7PA(LS1-U-%=C~WlWK#;#0XgPbH&Jv&)`ReD~IB$APG~OxE(esvJIE2 z$OS)Ic`Ea17oW5*)x$4EC~otqvX^pE)VaPS{Ki`!zw^J`N(Y%%GnYzR_^AS`w7Wk% zJefoxwkN@P?ycw(^YH&Ok8Q(}zxk~z>cEcw;Fo{W3*dgQ-RlyoY`sy4q#ys@Tx&ln&z8p-Lq#l@Jy~;yMHvAHr)op+SgK7gP=GxKH z{VwxuRQ$6%opetS@F{>ek~~^mr4llSAO^^qFNj%7-D-0daFQiHLgLpp7llV<2%2 z!;~@w))`V+tH2D+Y<1m&h?uwnnYZl>f(~|1jQp@rxXO^Wno_XxtGfzH#^Y-0ax9_d4 z^*k)S&01u#{iUE8;KpgcH!ba$d!3ECKH)^<7}Og9ptbtjZqP)*XKA9BXfrLK(I;0N z4`Z0dp&V9yb3R3l8VboEI)o!oOxbLnEan;bNT(+(P+xpYw)+`kmzKa(}m6NmjpH zCQJb;61cNgSu2h-8)5!AwGlO0bn67pQ21J_i6RC8V=ekuJCYCiCG;VA(0l{vSRMwU z4vD$4xs;6Yhkzq^0tsQZT~$*>kh$*_BXqIPod2ZgmZ zMY2c^#07HuuT~m1JEbua6y&*&(!{s$oMzzSF5qFv38+;mhP&X_YjQG*vdY>;CA4k;A7y;03toFcANETt=YmQ(H;=AMQcru zJYoodMDw6M^!;>{p;OnC4<0H0^6F}@)$R}++S6P;1!tH}5IpkurTQ#>>;3EPrqf?p z(~6)Z@!D;Whl>PpL3+K>0FNOtR;jeIwlW!yZ{51Py}d4v3ssZm%}y8hL2tN1k6GSq zws45?NQeks-_Me?*KY&HcCUHu?mjVY|DxZA9V+F9*LZ(=BAzRv579%41p112qLb(- z>e)dK4mlZmdt)(wq|;5OX&k2~!*Fe*Gp{a!Qtf0g-8-5NLYplVLmS=UJL9*aHS(y= zm6#2FoiFYk4u9(pU)yM-F!9v9&9z?BbyoVV^J|?$TqN9o!u{yz7rye$b6vCzM8lM%35oc8Vb3&7@!5Y=nnq=oosxR+sNhKuYIu4` zA_$Gs#mDjqqz18FG+Q_g;#@^0B*gf-Yy8X|J&=M==-r$TtUxhhb*4_}Rvi%lD4#pR zy*YWpJ6%cV@~g!zFwdE-)t#q%hbN=xMBjKj%#$FKk%jTktgqB-<#(>$ed~jd*P1oB zs?%<_J55^p^>2KjF75d0=PnKhqi8yfwKs`AEQ1^F`FQ5FYuE3E({z2cr3WY<-JeV& z0<}q~SgqUEBgI_^87D~PBuaa2_c%* z)s_epzD*6Qp`yH9l8^WK!(VtFhg;<#DtJ#t- zJ)ms3dOH9i*A#E1G8vC>zp&-#8!h=4C@5|{Re_*+u6JRowMLvIj_put(Oq2NUCy<0P6Q?cwB5NGX=EXj8-j1u)sQ{hlli|V1@$qZ#-O^hX!gBFXG!Oz#X;ToAKcQ9sbWeykl5jdb zl>U@nMN_I!6t~MFsRq@$4l}cMrzlL8wQA-0RdtmvD%fpni2DUwkO9fyc*m6QoRI*QB!kRnS-)J~gl6jn!=ERaG*uyPENoJ`9R8-wxmgO*7D=Q?KBJ5-7X+$QPv3JC^f z>m`IOKz`)HR=rX21M_1%J)FgpxW9h_ORx8vsFP+h00%G=4FXR?4}FAH0?9To0j=B6 zT8>%;4@ka{TZd((F=4COEDj^$OKtR}$FGeu7tT4A*~i!KgXW+mMK7&1)25j;gzkW{ zAP#N?e|T6>!Iryg`Skw718%OatwJro6-3(pNV|8VT47m8IhjmbEsPMZTkZVWjb7lM z+3XA&(+gfJ4il&4y*C*EIW5b{pi48jPyxD%^tg6LbkW$7kZJ*;vc+^h+iv=caiVX+ zR4aMCaxaa}H(TRTbYpikv^5;XQ82YJln(NXlsYw^KvtN(%H{WO?7sT$$De)bLciVW zboG)U&^&TxeXSF01o7vdKljR?_~Cbw+v8cbb5b4+4rOxAtbA>#Z6_FmybR-i89Fh) zEh;2W?20LoH=$oeyGXm70~9IsKol^EPt~CuVfay8EAf~?AiOpOgm)XQ( z3?N8+&LtXNP1;Dgg+)SCNXVT~8NECyXmxFfQCj}G;cBi4E`yoNrtd%X+=b02J4YwO zBe7`c>2+e-MjaX?%zMSbN}Gp z?%{*|!|};*|KMaW3P1O`r(4}V8Vsd0%jT0X?REp$9R(LB*@MI3?%r_{6VaCtg8L^? zl+B=t!TIT*F~;&(qR2?krD1~v|Cfs2?r;dJ#&9xPx-QD=toBSZSx&hec=aE7MH_$7 zIFSf?Sg~NGG{ch5a$Id#C{1FTgBqUe(r~pZgwW_rFP&1(-bqeSd)a6(!a36_pJMk8 zJt30XA%Kv#N8BZv%=Af?EXO&k)f!n6IetfT4K}+>RD?(ZGLT^AX|pQFqB=_xwEHYe zaRTIdYTsM!nUlvUJ`&dyBg9LmBOHTPrx$cqh<)=UQpswSU^xjjbwNyGVOn)M9*5%z zZe^0BCx?fG7Oh^N*w<^VR`uRB@wF6XxhmJE)$Y~Gb;obY$I(t@)0D6yoM4}VW}8^b zZ*}7+edo%p;UqCK2;0Vl5*1ww|DuaH+3qUS{>ymievzqjL1>*VV|mL?mM39QMU!hQ zY0f&Wm|0RM*21#1eynG!_(_iLSwl-nu}kaJ;G`)Gw#<$OqFkMH6y>cciYTWrYFt`E zS;crb0T?>)l@}i0Twkr%7AHq1=g)3#Z>)o1>N;+-OjNJs<4|+zw^tiYq^}I6iLt}c zLER0YRveB?c*A*w%~<5KM$lT!kko0d5iGmrr&7z}X@twDg-2jD!|=pJY+0y)rc_Ci zDK>#_VX!=z&e904MFrv%D78BT>OPB0RX3i7Uwch!I1EYp(~kzkQJ_Kc#WiP|76BJ! zd-0iKHg-F6S>V(3m@aWRnm}r|r55F?*XncuNZ^vv+~hwtyH=}Fn`$Dh5yRS0OO(e{ zoVpXsjwE2BhgiYd=cFnCK#I(w1%aqumN|mmTm+;H$;l@1g0tcoM!g@4;}_@E%ZqI1{eC1Ok*Q-h*5X zeNMXBY6ki!W4&qt8-?*RG*N-@tFE4C)U}q2fW#aNpLU(PzFXbFZ>aiyy^VpdYt^kb zd(jkWEsC?XUR%#%Jo%@fhvrUB#!(V=n(j)kOLQn*MFo2jp>17Yg?c^ky;dubk5xP) zua$i|pt5E@8lJ5ZK^;*CZSFTc2!*zTV`%2g`Ewg<-H$%Hq0Ofq2Vzl89>xq!8fyJH z-c+RqLh+K@oi;7^dR_5Qs_$ZKpWRVdug}uf(J`Lq!2+7So6S8R&)k7uIhxH@yAwc} z+v(vG{XQfK%kz(uvHW}(2GW>&0SVzTm=;hOAi&VKcteGZkFC;a=qPIT`pL_W?4!*CKyJ^9AwCKg!SGRIzX%&&P z`3mz3swGX3X{UCE$34@5HhgimU{ZA%o{E;p3Lgr~BtAIqa{A5Vz)IRNDvvzzk0#W)LXp#+v(?59ghK6s)D^G0o+d&&861e!y`MlC^HKHsZ zPeZgpqo%J=-oE_+AzEE+4Th7udq*e7qnmg3?>{`AMhX5ikgWE+VU*oFo-!aVHDrTZ z;2UB!21Zs4)l{p-WQQShnJRvZ^45aV++J!ZD<>_dJKQpU(*mr{fkLxQTvwN}FCko-MQ-RbMgktUULju&O7f|Eec;1bc-=MH$YXuX znU;+exa9&QA`A;PoF<+DtYM2kny}?OP4IYtoWKk0;jd1HC-V$1sv7F$qm&?{zrK!i z0E1R0`c20PY+H7{cxh-!vv|U&F-jOM#md;T#lrDr4rggvsx*=~{oZ>YkHZAuB^k!* zMh6?Z9U2(_L>k2$MIJvIgzoB=5m|?EvzTAEL{pks67#gMNb@bjtSj{Cxi}u(83Qe(uIq33PiA=_6Eg_IIb*4xmI^Q7zWFTgkY}W zQjME|WHT)rN~ZHX#&Lib#hPgCIvQIsIEHAEN5jK?V5>J=y^bWckNVDVn6YK^EW!OR z*W6hef8+HJXqjO^N&eG8F=ZfNEl{lRTHoo`4uGg=#$ZTRFXR*m6>iPL5VdL9JPE6=7D;h~nnjwn$q)oH zd=x>fLttuV0#>CB#7wIx^2`A%dWxaIoTg!SET~pgG-^aef$Q{JUdwk|L}PBF*ABWZ zc^G&D7!w)|w9JBHjds(=ldwsrik8Z2%D;>U;CqF)QP4kWj7ektgBS z`N6TEO}P~HYO`6)i$Keo=OKMniaC_A7g^O>qmB}1xbzxmR)UUY2f{cxduCmIS4xT2 zb7}IXQD3+X932M-haT}bKoK2L$)49Va8QDvNnf;3Yc#NPuIu9$xCdJkeLf3POt*m9ndh2-@AP|<@#yyL z-L~dttHd{Kbb5iIhNe5 zw(Zwg-+sj~v|BK`v8A8@o_hzw-}$3AH@p7XGaJ4aEK2#~Pp*yH^KT#D{d21?+`oRU z)oJ%PH-GKqElC13%Set!RFhvUtQJGDZp3C9g}Ty{4927B9B`|ODuUafsdnxyv|XM_ z7AJvK#3C3D&439nW$7TvImK`=B$kknS{0jsRI~DK6rys40x;1jDe5$t5|_z$Q9i8; z2};np;4<3_sDjH{Sip+vN##mS-&ucU`{30FnLZ^`38Ms2zrA~a zW*Z+NjU&W1FA={U5$CG~mlsQi9ai!1P{-k9 zKGPQC=yT6+=IHJ;fq!6(EFSBTN5ieu9cfEVgUP6i)2ekiEKNq)_>kxu-^6j7KoRMS zEsHD-aVkpldZp&X(?e}0s}EUYWwT1d2mZ`nO9a}%@ktdIM02w9ufKNL?7|YCpw2KU z3c}M2>J&dqR8XowsFa}M8SV-qpBr^+Til&~C0NLG_F~ffS);#n`ysbEH*3$g`**Zxs)N z7kEvt(F){4v^=NN^t!E}-NdYGwt#bhtEb6~3N{Ht?>Uf9Yb6C^$@*Y0;+|_1;O@D1} zoxx@J-EVvkwbE_{jQjZLFohqd#I*txIwe{mPo$Ir5AoUh#`^Dl?bX|Nb}yXUVMO49 zc2LKf*=RVKf+TH%&d>smW((pU4esi*n(3_4sJ|Btj2Fb5klk2Tf0{7h5Q8j@vdlKW z90;roEwGooO1WRD&2yM`-u9fP>&&D4qdO-E!pT@We^?tjFB8^*4wDnz(ZqxrHm)bT zD4_HEH+FyL+i(B$m!9>sVPgD#cJ$TeBcsEE?#jyO2MU@KAIW^z$Al~|hvCTL&R)UFsPKyBgz z3Dk*DEqN2F_2w#(2?ZTZ?jM`8wCm+1 z#psI`L=Gz~;URl@&o1?QTK)8-^2Jc_;@{^+TV1D$TS*&-TR&6@5G<34&JLaq%|l zPIZw*tG!mk^@s_fOP)hqh}DH6F%brhYZW872rILs>i7#irJt25^|H-?S4w%FOdu9+ zN}?6Skq95^S(}E6rkT&Rv*)5R%i^HZ#X}={;7-kJjZY5gix~Cb;o)pP^8-SQDgN;v zeD?zo1Jg^}mW07)w<>i7XQ%5%mc&&2Jb+b=Z#?a~X^h78@ zrlIhlQQ>p0DFESGG6ido~QhuWvISXjT7 zXK76sh)a#BB?BB__zz!yKaayFpW4{)nSO@_hE1fFbZnH=D-C$ zO_|(#kjT2{o!f74yzjixV!<55N5UESF1%eqn@}-o=7mB~4K*Nq_=RfuCAv1jolz zG>|^UapY*d{9Jo-physP*VPNF>TrfknHVh;AqW!SkjII$2wLzkP)YH0?D|dD^+*6G zVv19d#v}Y0yn1}T@MxFb7nNFjZOaK-w)8KhF%6=Mt}tH0%tLr0SNsAzO#+qqA{~$C zdAPcB4vRD#PZ_rBp#*CL74?S4aHr!Ttw<}lE^2B%OBIGSYKtt9?^!0e7sW>d1W{AV zoo-t%QZ7o`h>(;P2sc4c!zN#oIC=H0E8|c*c7SssTti+Sc1ebEK)dDG{ZH~OTvorj z27Kig1T|V&?(&DDVCK^_39-kr+$Y6JS?ly;sr@e^rWVt{tSl_WEU1kcMe; zBm5ddI16g2Q{>`Q#%%C*w!h*q%y8C!lV5ICS6BLQ71ZUPwzn}PGUx(GgEOrWP8qvsKU5W^#*s+=ybPL97m<<$TCf=>=X>Qy>n01AMPp3Tpw|k4;|bwEBI` z?s}drfpTT5@mK2AGM-1T>vvksAaH5Vo{7}@-L{^j&;cb8L%Apf`WB#Q>4Ol5cR??> z1rDx^=XF|bNJX9|0QAsClcI#h%>a39ctI0;53aE1v^W}!&z#++5%de-HCruc8cn9V zyAL7Z#^$QuYJ+!q2DV$qk`NdJn4N5n#3YK}*m z9&`|V437s=VFI!G%yUmIlF8S<`$5~U`+-zs&}zdK&jK~#3%`#Zn#`&+A;ef67KmQS>+SBNDZ+Um~pgTcu$iH(g*Z|;4#Q*%MW z-8hV-VU1m2Ej#mHyoFfmM`@E!ekn&|Vjqo{!IaBS1ntpD2!Xtgi%NC!(yjX^NtzF| z*{GVbwse3WF}XUk>{AUuhE7GhpDG}g2aDkU-oeQiUp^~OHl5wSx4*f)_CF74Bb=y)*IWTicm z#+6q%0ZJL5mOKkZ@Jy-~CJ0F6aAVDP2jTUDDJ&#s*Qmo`)H4hL-04^~D1%-XO6hEu zKX75m;)=8BI+f>F%_!3fSc#$lQq*mEU-;}(*j&Jn(nng$WqfFfSZSfRZv>F7;uH=` zNs?%z#A+3H-V`s%>Uc0ZI5=!*m2EYgOwm-WULPI7p!94an`LQ=exVWk8JLdlvNVw> zY7ceXvTB-V^E}4i0A{Te$>SUY9!=uW0T`)!9fUXN^bia(fgg+!mgRT25Oimhg6U0ti9?k>x7Qu#tfSVmE> zXvKaw3Z5$p6d8iY+O^ANJ;X<6Rn#qYM!pH7xQb$=4FKg+Vzt|T@!3Z(bkXSO-h+cE zoSZ#(v0AHB5aU~^*9kd~_78~dF~JS5gOiE>ERIW+IsuO+IqEfdY+fS#cIC)uzZQy4XxVRdF;9st+JG+s=IH3y@q;_J_YO}+ z+iN|?m4lnlGGP10YaeLVOE~!Czs5<9G@HgPpJRzBI~ADJh*j(KLYPr`qim_#bRGDK zTv|7b!LgDqX-S)RMPA6owBR?83+y@O3rmClAxYLEP5gis!Mg7fJvY4uGOV{rmC(`w z+>7P=qId)qExl~i^!|nK>N!PaQ3_l=5Ro&1)z_qjmSk$}h1D__!WK!$p8zLXp~-(@ z2~4{0JGdUU<^n0k=R<_`&IpZEvFnaLk%#=a%Q+H?kjcNgug2xV$VUJ+tpagj$Y}_f z@mDCM4Fw&)5qNI9*=#kn@nW;7bsegJkTqOAd#_et3ptw#hhV-L-bbg~X?EH`Ld36! zZJK0UL~$|=N^ z;qd;@Ca^wKrVG9A>IKj@iDR@bUI%W<=H}*$PoKYgXAf+5+MO9C%M}QKSxggjC{)u+ zx=ET1hbK{X?99YEQKj^7MvpRNg@D??3b9vo&Ob3*!I_05n|ZLLj%U05>G5Q?C~x&U z(^Brt%li+9N8@xH>G_6Q(OG<}^7_%SOj>j5&`MAe9Gd;$s>skE#Chqtji6e;`r+Nt zDC%~DJEc$??CS*(6^D_`@`KV^KF^Hn_1>zc-4*06`xB?hDsv4?G;MU0aH(V2ra~v_ zbuB9nT#9%LY*2`Vya%--?*t5L8Vg566;)mslY3E&PFO3P5~(@8sE(bG;U*soU*mfy~hk3V+@-8~Mu3)~uoLAe~GQHPUoVKfq?1%!4e zU!|3#sp5u4Rb&dWLYM}<%urGVRGvZW8fM^DuhV?xrKjssgSncLpG$bCmqzpqTg0l) z^?e-d0!pPGhT&u~X}3G`St<|2oG?Z@I37|%u_>loALWl6S04(7m|z5ni>A|gmU+G& zJ@Ahzp5H{8QoK6kI-VNz_+eTvQgQq|jgs*Qcv`Dlns-I%=tKFVVo>C3T?nn?#wD{j zn&?aU?RKV(p%irxE}@h#9zc@MD_Ms28G$KON*IpOMfO5_4?#xp$KeQ|(P}6w241VV z_=zwG+8~~c^#$i@DI5$@BzYQxp?sF3dO^=D#}>+$1xujikG^*~F_IS$8U^*MKO<1V zjZnL|YveiQe33@RjQ`|_cTHT>$R{nF4!$5dmy|tiL3f~sc)X~vtgY6RsS9H%>-kfOFSJX7TV3y*~-QSCXHs2DAXD!x7~0(^#Oh>GrV}@r(TtVuH6Flv8~!7%gR-v zUqaLtZYEkgjHdD!wfm?WPRE$@TEm~`3HA)q@7=pK91Ww1wibnw{ngcz<53h&SJt{m z`zP2jdqlYSgI6`fVdxP)g$+{+By4WO6pbJ&a&*?Ul{b9-;Gy#TfRbv{caba93Oqz9 z%nS{|GFD9V6A8_Wui2iQh>-T=Zn`TSZ>8mTnr=IA0iHn0mSfA^hS?!c3@O1^Noya#V{ar6f#YeM^gK&XMJC zDjYo1V)O8cr=FJH|} zm_S~zB9rL^WK|p5Mvwz{4t}S=JWl}z?FM7)-rw6kcdlA1&vI=mg!XK=n>*W^D8U;y z@ASJ}N=6)T>vYoXc2QOGDRFv|1a606+-7j@?3px*uid!SY^^$}z zu1>#|eCR7)taSX~!)UYwcFA^}HRoT5I=QAGG*oF1lUwN-^5#;e;Ua&ctvBw<-TI_7 zTdBJ@?;Jlk2`8~87>YxU@Xs0{EqkISE%Wr2PU=g1uTG@el%ewF@`ZD)*JR&B z@Cab7MYVkXXd4HFlT|A+fk4g#&Vgng7yz}3!^)e30GhZbEYlR_^=wR7MTiX(j9H_n zfQdbjTF6l(0gf_SGEy{1%j8n&!+(8kFij96+NCoJ010K@zjLgjc09h-ryqP0eYf1g zZX>W(xF$2Lv88I7YioR?vX<1*#VF1njv^Y@vJqtn3_A(XQ&i}ps8w8W6fQz>WmHzu z09HH7T*7Znsl0yp)Bj~=@ke>xH(&wK;BsF7Xt6nnOh6CMgknq0%u}u7_Mdzi3 zzO5~#h3Er@HrPOIv{(AohBrJuq*AvJxhwE8wRQguAZ8p=ePMPD{?6k%!-vP5j>x7vQEqn%%B zjd_-!!I3_#;z5Jzh1ikiAcL0gyMExb`>QC}{))DOh^FItR(uP_=Q4}NSsLeQ$T)Fv z;^|0sK}+wFMi5}v=2g8KF5Nm4LZc)^#7l)GkS-&c%#w}MOR1}k>82{-1r~kw-DyT9# z8`zewsUq^0zzZb%wRCaCPcckY(DbY_jitp}z^rs>@#wkrrysvKOUKyIGZ!yxZSG`d zW*KTaJ*Fqk6)kc-$F{H<98W{k5pq-;4^C=cyVdEy5tYSEajj;q=9pCNrd=tUdWYQg zEQWA#o@=ufOmK?bv4w?%pu~ePK&4Vo!jpNPx7~8D->uUW9#FZmzOjDcl0XW_)Bb7~ z*K;zSzWUA$Tv5hq_*~+gQKIV#9FSiOuRsSSETK}%tWU3(xm;>}B1{8vFu_{(K&mQ- zRx=IQd@ZyDYHX|B0%S9K@)$7xje6V1z}Gz$Q17l5JT?Nq-fbbsRh$XTIC>mMg2FV1 z6>j5+$ge1Kx8L@A?Utv*p@OC^T#T`#*`9##59bCCLNC5k+15#DK_;DnWzBFns`#qEAE?RgDvXh6Wf-Su|{kl>7 z*$k0pmg8yI>9$Ztno28`yWLI-_snuJJ4C`l%*B+Ern$_#0W_X%62(!13JAjpuZ4SW zegA#<6$+|H=mR6wYNn0m&An`--Esr)pV!JC z#^XYyVK|0xdX`}{7A*Ks0Zk#=l2)V{`1Gr--#@#yo=oFjzkM&8o^7^IjwbgHLVb1L z8u~hXl71%cHyt*U=!^}&aRDEFm+gfk3dxzvEc z%#E;28nD{ZfWnfP4X8{Z7Byi=lmO*5R6JL8NOd~aG>sMEh9HqwL0!`gf=2a)i~>#k zSq`c4Z~f=ro-HbFwRAE8_t`Yo8%dO7xR>vqsJtDIZnSVU>?w)Ri=UyVvYI<7h$%md z43t!y&7w?ue4RwuI7%>>#N8T#0jRbN7W&t~PeI`qEQrpQ<_{!r)`e~>ZK_~-7*33p zRA8A|97S^+Qj&(iB4?%3eC6}clF*grr06ftXX#?54MMmqSP{C}dY)3?2XjI|#=6jw z4nd~L%XAXOD0-(>p6BtRf*Nq>N+bi(mGZs2_oFm@;;Bb{AHSi3F3i$2Xtyy41f(j1 ze!&r6l*Ys3G>*!(3TJq@US1&XM1p9ZSr!8%K?Pu-c4)VFY!4o1>9kV!L3BJh(Tuu2 zD9Rm&F~yS+J>qQ;Kq1lPnnDE>nfw~mO``3>T)#;zTC2Mq!W620PALBQbEXvzi38gJzU|ab|&um;gw^J!;CO8P11Zq(@rGRwQJd-O4ef6LKMLO1o zi4CVxf;w9CJDZnlu#4x1<6#y}FjNbD2{fC{GmLI-A3MY@!jIq`Uvs9(RqZb~%VVN= z0@g&|ozL~o%UmCHYPA%26IwE^EYIQuN2gkvRnsi(cA5m4qrovPfB(IkVWKHC1|bA! ztYUhGM8!h8YM^8b_r-W6?K-Oi;8zpAyMHs}1CEPz;{{v1)gG{0a4?hbeVyLC;wjkQPkl zGt8~r4c})B^lqciwW3s?UDa!@WFhH{9y3ilbfYN_y@8&`IE=EvB`aB`PGNmvUm*d_ zlI6Ba!*gA|I0s~4P%lY0oN}#RuszIzfWg(c2k7C^XoS_Djz@?4`?DnLuXIORy+1iT z9sy(!__-eCW_Y8p8`=TKBDU{Ak0*K$jeqzkv8Exya2kujG%Z$o{P7EyuiQL5I%@ik zmU~nyRu(5oj;%Q#n8?}Q-d=yTKN*b~gxdNP9n15?L~07G-Rt){ROOmlU@y1dq-})13_*VDz%Ps8`qT6({?&KJ zZ|>#q-22^scFVFVNm{~_rxci3>9)^o^n0DwcRsjl#dSP?X%(-ljzX;4SC;V3YPC4W zmz_hubXJLA(0O?hXJI@`v>_(Jv8GE$iQd7GOT}P|wnUr)C`;R6y1n@2tcNwEb=z_c z{w1x8oKF(PA<$1RLB(kY{4>`Ll$A>x{npDbJ&#h8kzDA#GtI}+7a@V56L>gG+R%Tl zg(~FIBWbsMczO472TZcXf4zXlgu}32 zg=G{8JDcfI6)U&AEXGr?w-q&_kQ9toOI1E?Q|%WComE7?jJwbec2zXLEJlT8w%xEy zb;kuvA}&!8b!E4f?c1da6`2&Iy0FWk9}UB%#mmn=diJr4+b|Beg$L3o37Rcj2QUN{ zp)d9FG^%UiKfz&x7_m|-XK{|bYt-kZDs*gwlVLV1rD=o1(apC$ zxGD3k=PlXsa7y(~tL3kL(;F)tBTB$nq zTj@juli#K%^oTCto2ge}{$K-OjFJ>4k|VC+kSj-u+YHFYS`3Cpv?xlQYux<8utEq9 zhRw1q;ecF%1_%&u+(w$fKhWMjRj#GOY-lF!93aH}2rcr!=z}hGBHvchU!}4Q)|`zc zIGS{^Zq?q84685o9(lfV{)tNS&for>#%p)|Fb|@vG0ndC)vvz)@g4eLXs~%j4}jB2 z7$2QX4o^nc_J+cM9nYL!1AJS(gEUl4ci@_fqhyQhZEd=*X`K2(12BepX_^bG1R;fZ z0U>3U7!>rTwSm>f$v|||C=Kw+r%SGMixU?ruQJM0qByLw4%M_v+9=Q{92O0fE1kf5 z`NgMe4Xu3AGtx>K*)x<Bq4wYUW@{BeOz9fKNV=f}%R+i)O(?9G+NCwd!ye{f2i9G8mdC1!#^Ag%N7J%ZApepkQk-7Y@fa zzW1TceE^?POwMtEenWRbHLxT6TL5oBkiUT+pqG(onWBuiSSg(gdWn3xQ}BXl2{ugf zEZgW?G$N)e`c)&J7FZ@eIf^_oBoy|mtJ7}iSE=P8zA0K?o~@cyIK3pJD5D%5207;! zpSrNU+0Wu>v)zS%W=Z1dwPk!biq8p9QOl5nMqtRr3@1IA%jZLX6418f#46gY%u>BQ zUdEe%F)9SZ@-To@FQw3sd|&iYEzk8~9r#ufT*a!=*&@x$HP_3d!DyftpsFxf8ab_% zdDVqOI-P(9j*f<ZXum=n_q=MTTy|UmP@BZ~z&Y7PnsQv^`t`JPx>77O0AQQS)q4pP`Dw>ov_* z$g9xCS-2E>l2R@=kv;P;sM7H8HvD=+?~%ItevctjHz_#@E4xZMkUq>ld#O;2TjvZ3W2 zl3T&0V3TWEUA+gZP(!E}QbBDF?6gnNkK)ls|KOJ}%w{tTHd0OHEY0V6hE2%RY&s3` z8g}>gwRam~NTe6&&up&g?U;IfZEbCHW9!1D3tKxo^?GGGo^*Si{>n*`3~Mztw4@O{hCp=(Qvb9&*l*!%4$)yPX!8NRkXMqDl~C@5|r^Yl5laz{Wwl z3qX`di_J1T>xL8}*P5ckoA#!o;idCCS(b_-TC6IMQ0ha2Ez<(w)ZII~d6v}eLjaz3 zm~1dWPzU1>Z&A|m+~Hu?^%{AS<92)yj%}jdgk6khyjAd9@heWVV8}#QH>5EoJB$*~ z@M))i_+UWGoqlVarZ^l|Zyb!nbR0^|sAJ8lAu)!vr*nZJ{aF!Xdg}nLDN?$%Ov%WV zE^f9kD&*bFPyhfgy`@652Y6~;kIm6?K&Kl^@e-B|)#Ry|Bwv#dI3?&|lsljZB>i#F zml`Vg*dUS|+lotsR~~~bF#kaShK1H>84GSz`4d~w0EU9I{sV>0{L>2!*Y_9$rfBLIG z`toN!|Lh}ATs+hC>glX9P4x=XBG0Zr7}zj$ob5N`ERT{r)Eq`Sj?ys6&{KrIVyoFA zOlM)bNb+(rFQ@bJtW;wd#15KAs8-V1+=JDsC88G%pRpMtIhLqp9H!)iC37@R$EZ;* z@nOTY93k5Bi_79im{-*4$*ih7@P<#w7{{lsVRH{9B~RGPv|aGoE$dE&*JFl7^-`IKr9rr(3uXQ zhTm>6X8d(@j^;yTU9=4&w4S9o&X~Zi;9jrAv=cEgo&h2> zn^zlt(CPTi4i1Ci4%by%sG4AUUen8>u$QF3fscJ;hda8^p+&B zgx#>?G_CSnh#Qn6wV*o7qbO4_fXWC(#x3?MIkeKneLEM&Xy)GBbI47 zszAF%zA>b=uOeisx~eXV>d;M{HQgwHtOS_(q0c_v@3rG7Z8lxvQn)2(b!^&6?}I=? z&dGE@7nm?=5p7kfE`CS3ls8--k(tdFhzaqnoQqt$GFDNx3w;e3RzzEB)Ch=nEt-KB zT82z;6aWxm$J5a~i(#?h@qV7hbO#H%+OraQ6n_wC4u;2=;L=>*n5D7rzjp(1F4CeX zlrG4r;o7y0+%ha}sp3A@#c^7kgi5H~`JF9lbAv{~l9;5Rhb~l}|3$(OC^d%FUTOPl zJ-_KY{Z6yn@`vN7*A7r2=n-m~Ro2VdAVT`2hAarWP)noZPPYTYC~DNIPuVk?Z@7+4 z&}o2l<@#F5!3DZiTtys#OxouhQ@FpRJy>z>#R6Q+XNWgG-*hrel5{khtZ%Gg*GJ>& z#`-FT7b~t{%~;3CJ@0nggez$Bh90R30oZLo6flbt{sXCUwTx>7NWvW zr4jUTGi?H0Efb=o508#vQvSDVRKml{9lQ%NYZGgAgsKDV5M1X&9GseR+~mfUlF| z;qLvr{nb_9^HE0d8R#Xz8Vv?svmJ(GfD7796cJtskt@N9;K4(2-N|s+?RDwm&3E3% zmsnq4!{wk`G-|)1{>L~HD)^vXr-T3ui`9_v6{_5ZSyL!DOPfiZ*?B2RF7@R!${O1J1h$z}@_om>_h8q7(X4AY2ge>xbmNNh{kim`AdQi8A|{mD_N zOY*G7sR_|RX65k<8~@S2`%Bfjiwl9q(kDXV6j>&sNG)2|wt9K4_ih%mR#zJpM3X7% zXg)6oK~usb+Q{vo*(?bWtHIGBN_=%~1$5=J+4w}ODC{}NJdKl;_0?w3nNFsgXLjHt z?#>pN7J(=y66m$rR|Ff8hh%AjQeT3VXNy);5EF;;?zKq6%VwEYht{fkY?_3XD$$XL zej(@)<)}j6TBS|_5S-1DAqCPX%92Ee&iA=X9Haa**b%(mJ$TE znpiIM{qg=T6v2fhums6e1ZgydqQ*_nOWGBESrcvE>D~l|O^nJnq z;UE9rjolN`5o*ezHl!H^`LR=h2aTG99+7I);Bh%ABG|!?l$&y_V5xj=XInRfafu>K zZ0by}ISFy1v&;Z$N0>C^g27(f-Rd{jdcnqOr=>+1di|H+*>Uu!5BnnBhr+bga}9lG zSuhPpV?d`ovJKLz^1){aB>q0^j$fx3Pv5ig?DIs_$!o}o@hdmAUfR9|faVeyjXLJ7 zs_*GZ;LCez58n`J>MfTm|L_;xw6(W=;H+vR|0h4L9#;jzwLM1zN@t(S`OxU?%v)* zE`z3Dt5w&xc92`BA-z4HYyAXNu#v{o*b5xcz(C_P1xP5UWoMc^pe&YqObc3ejy5wE zENCq{LL3mShqzTV_4xCE*?PN&liCqqCz9*%$K_rLYrQx~_lw{b)?spGitIjCk$;aCzJ!$&KM>hd|sVrpkkV3uSVG=D=z^k&wdWbHA zsxJb-c5+rVO+X8OVeQhvtwZhxo#1{wex%;{_5bDFTMr9|13N2SBE%SviA${~{v_|> zL$WcJ#q$x)!X&qaPs*55P?1vUum1d#4-dz6r+%}R4ZOL(?lxR~?Fe!;w2Zh`qi_|_ zU^iR|J;=ii$xAs9Xx|t48(0bONh!3vw&;LRm2w?!mgr$okZ^S?H^fw&l%0ymM=|?A zmS!{rAaxhuK&UcF)IlraC8Z>rVpyf;^3#m9>~f}!Lw?q|03NEOs*C|A2mTRm%?|OO zUg$}7DvPFn=WqYkU;BUjy@lR#lo9`}U;T&AefIN%!~I;lU6rPj(et1GA&Fb5{`e35 zg8H!I?ce+VihLTGJ#M4xGSRtSze?iN$f36_D2d9#eVR@uQnT6&BLConkMBM>Lg~0} zsOP<==mvH+QAUqG88GyI5y_~nAL3aR%3OT{5cA!~W1 zdb$#`3z=dD*yGtpxBu#2{zd%eBn63S9sh#ymE)rM~_Tklj~rHM(eZUxC{aDa~qmxDdt&0FL(8wdMysGGsv2r{cADl^k^SxZuo&4 zbbv=M$cV``czBF45$}P>a;=6Z#6Ze5LXISlh=4Kkj2$iQC=2Gw~DgMYD$xgF3=&Sg_HX_Ck6k>sjzuWfnoc#Vi!(HcIWe!~Ra?1~YuW%=T9T2ZOeF2sv?GExoSR>v#h=7Fv?6Po##j9okAS0QSjX z$dIOCsD~wvBUPoZ5Gk7O_8)dH{QDX z>RVSmrxHf-M4Qew&YxS~*;sk{vGXr|=CNm=ezY6ZpM36_$DVq6V{>zDbECJ~1OClz zuC|C{R@XOCJxLhq!@F^kL?JG1mg$q+ajeI}@FUs9J{I5y`T+_5XPM1;&S#Z+L#sd- zBPx$0OCfw5M-)JswCresNv@-_2y9FwwM3;}ueQ6r!~FwX1h4|4;VQIT8pkMB007gI z@z8O61dSL=>)`ZWaXpJ8oH?vptJiz%()kRNCTZEfdR9ge9y3B+YkP6YTxh3Qi=$167Hnb8-tJN81 z7&WcIniGw|Ekul~O^)PL!zU>k8pTmf{cDCT-dkL5sPrE%n}|5Vk#SoJSegG)~D>CgSxpDZS#)PAY< z*ptr&ozB(w-yIB3o_zi@olb9Xba1@?aBzI^4}bj+1fv~){^x!Orh*&ea5}Y@`yDq3 zrxE@H(UN9#F_5zvd=pM5VHDBY(cuvwGQJ=B(#t>gGDT6Lp)quWQFJ4M*@94s6%}U_&l|)m(O>Dp8#wV8Y7`NI zUN_en(VI@S%DK$ORFBnyc!Sex$h5T>&0sCrhW~^It{{BrfPsLG)jCfaBq>NgexLlHH-KmA58Uo}*}1Jz3Erhm4|MgHuPrgQs@|r^?tNYh`KLv5HG( z^W4S{zx0%L#}1llq+P7Aj!{DBC(FzlfhYJaaPi%S9&6eU$H1Pa?}NZjWoKw z_{JC^s`SRpA&j>H0h>q!7tU<9DNPtTEkkiHXmwY%8g46}p5U|2Q+;=kuHlDDh4zsw zUmt<1*3)ogueaz~FhMWOcI)o`orn4`1&Fdk_@r+OABr(*kjsSO6KINbaTIC0UnCS8 zReSBW-L!v}GNdg}9AH=%N?@0y2{{msz^}KPKL7O^2!2lqaZv1=XU|a(Yv&MH zXIk48PNLCZD1Qh|KU*%FFoy;gvf}%CA~2pz$CI(#iYS^)^@hiAFg(~lym#-xdzY_& zaP`)^S8nM>5=UVShM#_HXM1bK^PO`$8!tTl$fMZaOBc?Z**nJV{?tzg}701 z$$Zi2bU`()06{+-gWe&YL=oZmcv%4}|_BFL>p2Q94p=|@on(eSxD&W7>we|6EG?`A} zWh5z^&5w?bJDm>prc$f)R#yo#73C<7fnyhGs)b85%Ro}hZKH-0H5rdUQNO2kzVtaB z3yprWGaU~DzlG-A-#_TITXZ)b4x62Je`OUqM`5&|?XNdH0@Z%2^-(zdRC`ET08o-D zr^89iF-XH89g~i@qRG?`RDovH86zWnE zU>y}L>Cf^lR|lBqtXu=`i%+e8?fnNkJ!d%1hT6BWs)rxcrhw6gPVz-YYne^a6#j-; zX*{EnrdqVTqb9?!sA3{&S}2Lum*Qv0Ra@GL307ToUMQ?3%>1V}JrpsGBJE{vj(m58 zl`zV(nhFaV(wZ2djQtl3{QoyMzJ%vZLpHIY0+2ULVf~4}`PGVlw(Op(x1Uh&2DvE> zELL{o@|(TY4RXW}INYVhg~y(G?CH*Ty?l{t zBm_ z6w0Ho^h^Xr8u+L(93Z)X^E|2ODX9WHGy+Og@+e-@ELFMEKt~aJm>kRqJm9YBID|uq z2J3a_!NZ4dd~nOeM4ZU6B!V3z8COA<;;eD%unZNk!&1qEezWQM7(@@pd^#Efk>Bpl^Vn^zdMy$;R*fDI zZfKylBmo+dmlhU3Qz^&eRFpVhc%Baf&~rA+$_wJ~GJyqFwAR2m4q*};3t1d}vwp(B z+x@MYqX}sk9_PN&a5Eg9MTUunm+n72ym9{s%piI;8zrKFtwIZ-v4k&Ju^@MW7ma&~ z{v~=0K?S*W;QzLp0SN{{u8JGKtEu9?<~FKAD2LkDP4I*k@EX-lD`>ZZe!uJbnrcH| zIQr>9mt|`;6m6EIu7_i;l^LjTWJ#Wi)^%Ea$r6LJ-L7DCgs?6* zoQ^5e@Z{8_ak!Xk`BI2IKN=uGomL03O@<>}{ydkXMzf4$I2sL31|MFz_3GOnp%D;4X+#wk^-}i3TlaHU9ug7`JI*ti3@D!-xw@T4I8h)8bCbfL_c$ zTz&7(eb5g+lO*l5JMc6f3@yNd_3^iqMqy?;~RR*ng z63?i0vI{jK`PaQ=LG>jD2O^bLiC3Opf9<0~0XknC#(Ml&ar6|yKve~#L%_!GR+I#N#_9eRmi{a-c;pq6@jVs^$*6Y9X^|!wH{VPB8%1=&* zBkIArbR>U8@?9&H@^E~zy17FQ&_TO;1f%&8&7UL3+vhKBU$}Vg(WlNm^3>n@@BTlA zI~{-Hul}1245R)x9oKhU#|zry_L1Ct<5tR&R%--;-gPpc=U{ZXYq%(_P`4=pjAEj%)$A_6`pAwiN+0< z3;+=X$->>13#Hpp?sLs*l-%@4R@r zr$@LDR<8@8{lUyapSqBwJN4RVqCK)!R<%7>v(@w(6@q|RJ0E1#YGY?-Gn;EyK+Vfm zD)?-s_JB)ojkVe>cm;7qpaEK+Zmv7Mayc|!vP?QGPZ+Q)B~P0x zPIlKaRK7@9kx$AOzsrMW)PkYZew7LQoVnOu>%H{cql75aaJsiYz)SR-TBrw-HEmQT zN=zol2l$infC<+us4K3Uje&lf2ZIQOPUDQAsaH2WWu+%`*rFt0* zGEd-CuT|W76**d2>EjcFDdbTTL<_CYKm}{*fGKOZAJs-By-t=!NG1(3Y}r(JH%ZYc z^r5E>sl0O39Ox)%m6@%|<{&rIY$3{zLV!3N4hF}=An?YcDG(A;cX}N?q+uYH60YK8 z8bYkijn&5n@O95mahHfvKf z2oh$R5t$;Q)bW^pX_fzQbpvpmD0349+f!Cr9&XSRmSlp*noX)8!kW{lVj zq6z{dEIlO=R-6jh1vP{tf&u8Qp!8OHE33_*MeA~7jg&-HoP|#u0HQt5!MmyxEfFzS z^(7v(JP0KCfGRj8U`}t@>3webA8u1$mcMcHHqHUjn_JgI-&Ut18tDZaE!G26a8F&F z8#_mgm^&3fLz7@14 z$J6WghbI%g{A^{^BE8d&FehW9!}{cUsR)X*u{Y%{$GWsUWo~z!5BJ96>RNgKD5=!T zjX-bu-~u9QMLP;lv{$v3P2gJc4~RJ&s=P*J?@`*%0K^%fAPuS{6%N&N&nn4?zty} zvrqcxpK!KTj!WC6GH9RYNXO#ZyWjcdH^22Se*fDaU%kHC4PJWb#UJ_Ni(mQq{~fFw zjtBU-F!nspurUJ4sPSw6=waaf0kM?`(TZ~Cj@d7w0tHS@yfA{wU zx*h+Q|Li~MtZehY+3UCaYu&Xot=_6#t^}PGoDa`y%Xh9jn!rN_jHS_>NjymstzR1+ zKWO*1f_CTHyRRqd{PSP_g|+jKz3|e@Km3Ik7HN3x_HIt-M(`p&D93uxv7{F%eq=w4 zl_0YyC7LgBTaZ}r>ze-(%i zNwMuC^^QGsK~V53?D;49LGeWh%zF6RY7};p;D=93sLO{B>{I`{DfAc=4d-9^njlC<3EITzc-x{K};|W^}6CE zoSSl~9<;RqffUO;hoCsKn$xah`bv{gNYHb9c=BNX=-rR+f-8xMfE2VQ7s9lijUo&Q zq)H}qiy>I11|@P8R8vut29edStXb|X@7l4Xrs-HxcA?2I{T3q0j~RY*gM=c=eA45x z*lC}XuzcZTCIlTei*SgK8A3JYv4LV0h$E*Eb ze{Ew{sx7i9Dq40^^ILhAj2qg|sikOPK^PipyGIlRgw}UOG`)cVP{2n2GMCbR_#a6Y z7lt}tl=FEx&tqw*Qb})hIsw97sd*Thd9H2p5qF$doJoMGHLzUKyj%$~(OpAh;p$nUz`rGmV!|rz6FO^VL1~~= z2@VqjIUL-A?9`!z+O^xVKb%3z=B9u-Qad%%)J2VA_t6)hC^Cw zciVcFY3;$g*e!;HK$l#OF# zWfmt9U1N_kJQ#g6uHIi;X?6O=wl|PxczjHcYQJ0^9v&S%d`O$F=T`NyMs|F3va;G` zP>RxgKhxW-;6u?wfke6kCiZ^^;i4b8ykbskU(i~_W?k!u5ZXWEtlg^?+DNu5aZ(DIqA_A?h8J$ z0vJr{9PVreokt!$Gdw=o-#>Ak+TrnVV}p>+0iIH+MjTzK5`ifc!U+o}p%?i1{{4nc z)i9Q;*+iOCta-|wocSrTIGOgbD=rz96p|?lGGwQp4SLBq2?FP09`PL%S2+d6)As_@p1!ZExQ!s67 zJq3>>%QAN`RRKl6OM*B%}1^`H2+frBbqFS6y`FAKk>wA0g}T>jHO)1U+S;;O&3wZdx#K!Z35;Zr=@v0jkNd*k-=~y*A;0m zlr!T3g=m*bqh4W@;Z)%YBgggk8*ZoVC3p>KQq|;>ojW#C3qb(jsgr}3Sg#r2ql-XEHYsNwEJ?fS*Yp=wHvM%DG*hY$AO{P1>RpX`8MGU0Y9g4wSUETdKq%avVI&l!j>h%r=B;Tsm zZF%dfoo=_&>9(5fwxchf=nE)OKiDFYmo}pdPNoXBZ zPln%`QjEB`QER~FNjx48PM}4#Qj4MxNyaVFL{UwzT*~u>!=%ndb^HsI9gc-s!_^8w zDI*%AM(7{qXV5CrLU~2g(9o;pVE7F{+ee|Uu3f$Q@#SmxFP>SWDO3sak|(w^ptP7s z3=PnHI7?zyy@#xoO?vYnO*gl;)>k^+Ui)M)O40-s3JX#UzYBkb(MHjv*=j+0(06cf z8?$HpU%r)2A~7ypV`|0%AYE#P^iHE{@$@dC<%=t$GxDq=HB5QW3QzWByP~b~1o>{vXoN*d~Q}g`V?s1H~ zIvwaMka5F|vLp>A(sP83pj$#!v`Sk=vssji6XrAATLx?2(i4{mFzFe!Vm(X9s7ezA zFq$w~EFSgNezf<(`OJ&PM>WUy+pE^)Lf%5Nr@u2#ELzQnw?Ddd>zap8H_Na-NgTcZ z&bxT^PdxQ3rt9Fr?&WvidG)n7UwiArpZs%w6VoM^d8Stu;Zs`D+t!$(G?rtWMJpTI z>ziki@c8xbzVXIu-@E+&`_DZ4EWvlTzuIhfn;m3pgE3O{zy0t3K8+N|U;nHBf3p4p zP?qdG4@JAXn3^}kVH!T8A;E|&i!4iH$!sTa5>pJZ{rns`a1+N#UXqt5`6k!N4coqs zBRiHMk}V#=Fw>dCv>AFg7eja7?_1U9NV%`h+0|96RxSAP`&X@6^`mfH5EAi-fOD17 zIO0(MD1lFf)WG+NhYw<<@X-kFP&X~psZ2(hH|SkE|H}E7p8L#y`|P1($A9c6{_66{ zd%#SV#k`LI#Q)ra zVVfpmFE|Br)E)SnJNso*Y0NY&2mWB>4LyKI_+xlkWD`@P#;|6n3#^CfL$|1*6e=a9 zYID6vhW<$mkPhp^cXiTKw;j-(Lz|#7by+lx)1*N5Nx57_fr0A@=Y$5K>i`NM7f=ff z2z>+C3*`b zR7NvK7|_r&Ss50j<@5hqja5GmYl{o-wx#iAG z?0HwtRH2MK1=b|XMLEpJlEwM0`k0r8c_Ap~)@5vLVV;NO@;VPiY4~G%M1j1@y*Qi7 z8+SBOBAsV zQ#Cz~J(_2ULoXcCnq(Y(!d1TF#}3CCU$lgP!1IcR@r#=rpPRt*4uOHe-~?e|X;hL- zNzwd1S8GI>rd{O-8LmbV%$}q|fB~#wPo$mQgLBt*(0&#v{;|6_g)tHk1E+Z+Of^Z6 zQ!!`CtxXVMO7Hx3$>CBHq!zD=BwiKw;DqY{qGRYKC6Yr-Fp!W}u`f8bUU&3L#j*Ha zFT+_r(m?sbO$uR6(5NVcTlC`~Xtmlv%K*ylZU@oBV9<}EfZ%(?0fxU5J|k>03=Q#G5-0s` z2j&lrm&E&pgTA}B->p?0#O}?R2A~O$jiIWwnFeU>4gFrXv%Pl!VL%noYQ82CK}l0@ zICLQduqIg+#$(tNPz0o=SwP%VK)=Gla9FF?&{G&w5DjLMWGIzz56lg0C!-*80EZwi zC!K+^fCv&oq7ht(s+*=P^Mk!G%7Xf1U>5RdQbbQvD197|pOB$AWc4)eyb(S_Gjt9Y zm84+MH!TOY9{JKNhHpbh0OVk|Y2HMThhOmL$dQ$XqdoKD8w;~_G?K5j^3xHV69d@+ zL4X=*r~sJwpahUZL61;umW^TWoHD{7J4^i6&tGr1dyDf;h>6O#^^iUDy+GIazzC?Z zR$tt6cepUnXwsQv{ZT4<2dzdxpKCC^Hb^Qk054+4#tKIODTdTRxCs^zB^XxPgZV3E zT4^Ge^l|ah^_Cl?UX0LL+$p48(21ymwlz&_jfx zY-nMUajYD_utL-|gehnO?mMj3zpMEc$IC|Gbz!0zP7=cNabh`IsG4QfDEAK5!CrrF zdv|wp5+)Vf+&^e}p1ZrdJ3m`n+iJh``h{m-zx?FIwdZcOHizLq{*C{J4hmYxeBhFw z0QSO%l_c61cp=Iar<)u-b@w*kH+qeqHGhFqWi zcfTtN;r7X2{#(S^e3q)Cvl1+pqOz%+g9UvNL}RoqK8fl3{r#;g=U#s8*{5E9?xj~> ze!aDOU^?da|CPV?z`H+Gom)kdN{TKirmWf$XKsR8*tv%@^m($}kqe z9Iuga-10qX3aXN`Ia{HLq_DV-Q<0f@#NAB7dDXEVxaWiyL{GhZ+3WX!=rxrem{c_b z1`p^4#+?r%063(QX2DJ%oqPw$a65rbp}4bi05ejlm*E5e;-D!(5XK3>QD&7Z)M&^F z;|d9yE=h3h2t^`THmDWf4Usui&aaSD_kdF=9%C4sLLuQ2f}Cs~#UZLA0D_S%APj&7 zHE{~_k8GebbQPh6qRM;QdvIpx8>axs{ER+58VoC96)_nFVGGLV8*ov8x)3fpWg0_> z9B4#fL*G{NJwHv0aXyiSvukbG92j1XU@00hdxI{7Dhwh}rQhwZZEjzm0-a5kpb7R9P*@s{5KzfmZeCB0w%pdSWB?jrQJ9+5%(NzR& z2(uN{0L!4x0_~yaCK&Jj+QQBTiRGK`EdiA7#Tm6($JiGjN`(-y(5b&@Be-p6f!CP|9%7HJpZ~ znP?sOkB|m&3+D?e+V)QCjVoK|pdgx%c-$bkf(<>(86?K+*ky>7==*eIsyJotoLbCY zC@##+iZqVkt(nI9^&7J@P0iHfFiNAycI+fc(OE^; zf!)g0nxgVmbxBdZZWm}$w;V(dJ|9;mn>#y|idC(Y8*}p|RY&!p3_{X=YZn%QjuZ;d zuM(g)#mM&wO^0?Z0RKpb-25#)i5U>gJ$(^04ndQxjuP3NQ*h(1xDFc9!wY2tBx(lR1eP1RuW z!w48fc3l@7hMXw;Sic5KV^{z;0NR!77vFsGwTlnlcLtCJ|Drg8G$Sn4DBB7I6ospb z8U(KIdX+{Eib0bJf`X(7pLK12Yva||E(LM=!2Ne3?m!r8&!H`G`>hVKf#BeL$hI!u2_D7niqSYyrc|CtT1D6_>;tQjmXs zc~64(g(g7{N-~fFkeme64j9MD!K%bh2`WYT2j|}o$#D0#qrijbsLwBfI)1-ZUpxV= z+i-7QP4@3G4^>i?P+!(Ayzs5i50)2a+pSh}rVeA#ZTG+U^5siIz9a(5p)F|GfBE#E z;RqrI;y^?vlN4Yl<5GicJ7fu5fT@B&i>P`8(}QTisy1)#u_5;@Jy{BN#*zgeFdtrhcBR;kBGQOv<})I~bVWm2+%*Dh4fZCJN#OlnD-g zJHxGwckyh>eP&<-SSeDF_MQ3ho3{tYC~bO)_X&%{Ys@biE-)*rW1;w$LsxF1Oyq%W zxpmCU`2WKne8)p?xeK&{!M5xw(Vi@uwmnMtf{#_H0SxvJ_9Q8-&mDsY4?^Fv`Pd!De2CDP!ey3oHaq20{}2Hb8p5Zflrv6uJFgFdRb9fi?oy<-jitWR{$~{5(mB4d=zX*oTgxi)%m0ANVbbDR9QdVV-x^tX^TSnS4Q~>El_B zf(4d<(&M%pilpL+oHK)TTP=9iWVljO5sczUr(n27G6@$7R7 z#3O@%^!zZCaFqZnx{d;I4#66oT$T`uOlp^pvI&JBSw84IKrED3pxhV8NlbLQRm1vR&+E*;^`&e%D`` zulEaCQ#01q4)!{M8;LLLQtT=0EI>#qNQPhV0S;dj%it2H*ib#BI=2fv6&0YW55Dzq z%#eQ!VTNFwzuuOY4flZa!xamr8zr$++DUFyFqQzi(AtZ3B526b59%G$6vEv=m=d_! z5oh7h$73(5gxx`*IK58)dGVIe}oh>_7e#hwDeLvkO#;*j!2;lfoyLDFh*rgHlD z^7{6{(p;lnvEn#;>tf1zC5&6Pn=!gv%1p`saq z4=;|wp)q2KkXlwv@nD1#5XE#~E+C3q5rDwcX%?4( z#wp65jA4o;@j*VYh!b%NA|8gFrKUqBQckmj%%O>jtO_!4V2tnC@^dL;N#nOifFVq$ z30IhAh|o}VYBy%ha>M2-31iP?G|1-?oSdD)l0%Gl_YdB7*GT%6HXtrJvlR0XD5eR`*-7&dN_DmD*4OPLrqYtogLRAr=aUBk5q38%&C}_vo zk<=a7Tuwh6r9e1v9ndjEWylYQ4?BpO(>Uq&yGM>50eWx;eKu(_IoNN@s#2>};3m=Y zBqBIaWDTK)?*&fTsx}(%z=L+{;GpMNhGjdtWtKEJNd;KM_v6LGD?~JC2eH&Ri+s=p z0)YNgEqz}vS53!;U?hBjjkaTddt2xCW58b{O=0dCnvX{lNtQit2vtX4(HOY?gpU-s zUMg$`x{5G(*yF;npd&z%Q1&zq6O*sbP5_U>BvD~#Ip4O>@ArccAz}<_p+Hy!xKG$J zLsvjeyJA6-fXtqH_KolP(0fr5m<-&Y=!nlsD7gqj6UPI0U|SaYg=nx^DFf%8d*jXZ zjlGJc-*M+jmCLpk#q3BGrjWu?Dk++YNLMiom{c%@&#<}7ImBnowNoU;N393#_3hTt zV}}rjf$xCToUFuGZNo2bKmAT;Hchi@h2T}O6bE`s4pZC+nT1s0A4QR}CA=kwPaHzy zDg=Xe7tXTq@!8dA*R404D+TvXz46o&7oWedzka}ZxI*$-D9i*-gz|&hOyiMI15h5P z5VJH(HSsk&p(K0`LcuN+KK;w@ZWS{*E$*+i0P2Hy)Ro6yd2+*=)rv~dUN$7Xr0QHw zpBje$Qi;oX3e1FhRfC?J<8#1TrIJoigM<9LYfvueOaZnVc|fRfA=BxYnEOF{>6A3T zee=q2IGkxVW)B}%O(!a*-{`y<6*vdt9((1ad7`^C3A3&aPax8Zg+Q?KIx-}>pFoLxS$cKKYbIX|;}6tJETfFP7y4iX`ec(zrNIF>*a zgLo>S3R(`yPm>^Y`&|42GA65rRYL=SMv)Cxy~uBOl^QGjn0vnMUw;|ZLsA$5@jXm9 zOFnrdbzl=E1v&^yli1ubS{RMEP?!L}#KC~CpedX%uA!Zb5C$tgQ%Erc-+y;0&L3QE!m3^anK zQ53+fmsA;q;Id{TPF;sVK!ZWIaTe=9^AJUx6~s$+Y)CPVlGUS!5uT=D1gimYiQ@om zKrWaM*h1VcP!~F`rKz9s-2t6%OmRJ89rgk`G%4`4Nm&IF;tb%}4@}EKY%}QhVY>P0 zmNXqFVZ=2B`QfUAy?vo#g;{=?!gBkABpa8j4!J`Bq4qilvLuCpkG{>$F2H&LDxu)^ z{yxATASj3+Dd-r?h?J##(j-Rjq^F*G@!40eUfkHvxq;F|$O~6QG?cRhWEYI!S01`g(t_R=t;9bx%vkV)- zSXSYufK|Ws48n zK$DUznT`cvG8+Ap~>dl$j?5t)Q zLEu4o%4MrkubLHGw-~s9=xAiC)!|qvb(m-$#*QT5n1lo<0A(&$tDp)O%?Ldp9)tp& zgOeKI{*5#5IHkfvy`%%ZrCrmfHshubjKO zI^RGca1($yV=+wxt`z--JODz0__&sYAw&Qvxt3+(Td6Y(^8iqP@|RB?UYP}I@wv=1 za5`+PdhOu)f@1QKoK(6Q`xGunIvqB+47ySdVQy$ua`}W9q8i9FCsqUYQmr|n01=)P z$|b2;)Pf-1F2(bz*=e~qcM(?8WuhE5o@H>$3lk0>TW%RRe}(l6@1*kCmbl0(!fF(X zk3D*PFHPVX#_6~}2+$<6s=s-)odqL%PD2ww$q6Uz!KG4Ha2F!`38z35k|0%een^wW z5t4xCs7iE>@TbHT#K?Y%ckWryXmsu2GSS9sFFbd1Z3jN?)LnPF#^f)%uV3-^M#c0D zXOBKq-0uM0mSi28~^dCWH27NlgyipJ)l9kR&l_-nOb?+Z9n+l z@3U)-y^X5}JL{HHX4BASK@zwESOrj~q;Q%7jiN9ISsGBmIju*EBOjKIln1#?r%0z_ zLjKY6fBC8ZgS`~nKlry=!8ZX!dubAadiV!U zLpX&Tfu0FX6rUy#uR~>waz#ock_gdSgQ*A7+-gWn#58ZV*4ZXFk z1M~Rn5kQ3r{1X7YmTWFniZ>R z8&J=+o$lqeoqo4h<%g;b!{P`n8VMPX6IcbVF&G62Oif9A;3-Y`wE^+=8T@hT`|dd9 z3cqL>CzC1)2#tt_hNV!n%Vm%k!3>mvYcY++s$s|_86q}@4spKt|W2X9}eSe)a!OeT&Ps`y`ks1Q4pdi@D$bRqTc)%Ij2?dc$zK}x!q0|Vv=vS9HQ9gnb3BQTL^v0aTUmmEZ;67 z|KXz{reSyQsUu7CP1ki{7r=2i2uaa!1Nev{ilZp@`+U)o1ta>EMvMv&1&yI{MLt>_ z@=NMDk}pE=AUTsHh+#ytG>c*np)o>4xNGzrdZqG}>;fP?P$5x0WA%wp>WS}lqG6vy z-UT&_{gR@O#b-!>Ajb&H2SA?@HGVYKO>28=@6}6dqVH2ui+-`EQ?iJ@-a5o~iv)sB zw-RwQ*|vk@DQaPHA_rKABVmc~PoPNBSIjx01I01^r&$4*GL`uC8CX2yMiM>VYaEu0JRfE#U-6f?kjq4LT_jIG7Oy5%R!Mp=r=%js@sW z!f%`Ll^L`gJ`!+&9v%Fg6e?9`c40}=wASu^qh6U?JqmZ&oSi9TVXb1!&NS-_%aW#n zr;tBtW)uNM!gp!-v^)Ih$9$R&u{G?%rDNtEu-69uR91LKcL8Pky8^&1#1gi z0r&zr2qSa{MmYx7w<~4z5&1bNGRinMlO!M ze0}rg+TQugo7dL%04Ec^20wuMg440!2mWGW#w@<{xPndhq z3Mt5#w+CHAmJdF1=&9kssb;g3AVg5ZD3L7njmrn+RZ}+kNCm+IpU-f-1$-dvK)JXv zKah1IBs2u7B%ZU-GG@5O9zdOluo0%9g=hleL;RwLA8g+H!V^#S`rbYFo<4KW-6hNX zLhB8*sUj;Mn|p`Sc436d&E;Wx>xHj<`NC_j^*TL2KpY$Y@1J>vqbX1u2)zge03=~v z=4P4?Km1UwG27Z+Z!9c3wMMl$yM5!*`nB^&6m%CP!Jhd4g) zmJB2k4>wbQeUJ()`WN2;IDX+L9*cus8hBacb8biwB|sQ)m;qRbs&G4rj6a+g0U)@* zBESTK8BRnX7Z9HtlW`2P%Blg+ipu~V&^25HZ~;capWkoO^6J_s;drm4sExUWyY9Z@ z-V;mPn`?bHMqY6vI|peMB7^^sHP**H`kV`bXgP9lT)N0fk%*7Vl`3=|6rGu&<$#i# zx?~xpV=FUg=!@rXUcI^1X}1lvWLbu)0Z$uS@4gUZk5lY>5f0hhzz+*AwO=Nwn*HO3l)kJ zsneDqqp&}0lemc=?|k1o?yc6#h+0(y>ZT3J&~%IQ^G1_lua(9?Z}5~b+c0<#q7vr; zL-;2ni03E{Vd3HOV5x!7p^IZkjCct^;p)t-sNy6UPb9862`^3IQBYI~ zQ4yrnAEr?#a|KHS9tZ6P`UTE_SA;+Yfh#&OhIiBv!H%=7t=(5It)mO*H_RqG&EqKz z*=u%R{2-AjB9AyA3)p=mFi%bk0xaAL+e%{bn%EfHvM1OCLCIa@$0jN&mK_I0!pI>^ zR3&MyZoT{dBX57(JSqYFKuN3JendBH&h4eDsfc+uzk(9Cbt24O$)l_^E~8xpKwtM2gNARX7hr zEy;#>6^)8VYKWK~l$@A#UCKXak8@aejs)=~FZ2#cnod(9p008EJFdbjOLbNMv84x| z`T8?BJ9K!dR&AXe<^TiYZH^k^*stb3(<$V;p;fSr7w?f_EaKKpUX?5kw0T)$Qze4_d9=oz`Bf zH*h;We!hU8zbX`Km9kx}z*yAkP0K89Z0x*t?&4-Efij{jbOrDoejPdUNNL*E-1g;z z>hwMABd}JzUVg{JcRK=V`>uEF*rAo>MLyM?jQ4gn#k0-Hjg8jLt^F(Ot!CAJ;GW~B zj<2q)E+cedRinxJ+WI&h*J>4rbGJK`iW7-bm5PW`l5_$HHcr!ewSqE-rH2pCDS8o8yPDWd+QL$Hh1p zB4z?;8@izcBMf^s&Jg&hnh9qY1%tpF0HBzTIZ9)PHOk&#@U^c$-5a`JdgdZD8ni{F zAnxk*(+k^CKB*#6qa^(?Ud^Y5rSRcxjq5 zXT7+WIYoZzYMynvi41umA_J51tlzQa?E$Eu6TX!oDxEeJJ1QpV30fBmGd1Uze&S=6 zW5!`vl=NEFK?X%PIXK}D!=P70G*jSLTcHe+UT!p+lOn%fI2s2~6;zatVl)aS3mQ-C zTo?}qL&y>|-!x4Ky5|ky&NwB=GSRD)rj!Uk%pxl$K@VlYwkB{Z*+}Cf`*9Wqe0Z)K zq2~hGo8_LW14cL>Q;vO0n!E9nUUSd3i|C%CMx>m8&Pygo6*P8Q& z%?DJHqCqu4H*pJ08*hfT()SX5aq(Z-N zOxu>FgM(HQW=BpR5ByEe84jy>uEQdOA8(1b60jgkxkrQ?h9v$IDmKMS=?DdEBW<3f6 zewj|?lupm{(9LSK3ax+~&^D_|HcnxtArPVOJC%x4tBL7#>9E(!vaDRKqb?MS79uW` z6&>6PgP?QJ*0>5XTaJ5yx46{BYSoga zclY*Rd;Q{rZ{d;*hKMxRE#x%a!up&fYW6pF=$Nu6I7D z@Ox}9@$gEebUe{Djh_RuYzAIYgm5j56MjIRLpwg02q)K^orA@H?UmQMeSdXzsa`EJ z?&-dIx@gL(@;`c;TzP;@L^}|TLCh&u1+Fc^I21i17RocgkZSB&5lD&S2h{JHtUl}- zAl<^z`AD9q-uUT#VCj)UL$>ks~U)cT(KQh%op9qV+xxzqE1zOi=WYy4#5^4puBAa39=1)IZ>5;%L@ z$a<)U6RWPnqX9>_1ha{0*0jBaSYAKmem z`~P(CCg9D-XCHwdf)@O@|Le00Gu2wfG<4;+|L_&C1LYtcXdhwfqi0XP>%H%)&8`3k z=Gw}DhOaMi0ql0W4Xq9X-!4~IPu~YpzVgJEXO~vymXATE&@iErXeI#%)Qa8+Y@p1G zWA=bKpbyXhRpY$+(Kuec`+dA7wvBU77A7f77mksKQy|6Q1lv$j##xkmiISuzv8yO1 z6bqea`Qj(^hq2%HhW&o`!0qq%JMAzS#8JvnVLS=K)DIE;F&x1L*lp_aOwF2~b*fe1 zZ~yv@eLqTecH4Ixn^{;|tko;kN?Efs@FMm-A1ZMGYn=4D@%44T=SOaw@{0rHIJ-?l zS}fjqeB}clcn9=q&>N&-Y#Pe;LEmw#Bdbf*h6A!dRQLR|B;zFAo?PW8?p!nGG3rMPc!~68}m>EMY<>xTV5|V zmM?D2@8ne$cdL;6_&eGow*2b!4zckh4?I{EH%=BI%q|v=Ezkbck9-);V|n#3hxfA- zsGT-Jj9!dO;DrmHlZ|CW#ENEHl`8Hl6|zzxhNVXpT$O7QQ|V<>H4yz4yrBn2r*MP= zZdOJ6z}h6q^wLP;i{*%>5P~K!+Z>$?hodwqO-hL8AxadWYy_N-SQTDctT9Ui=?9!? zFdP7#0Gbrwu-<(0{D1hPZ-9ERuILshgo=!mm7 zxF{gNAc}>%Kt~yniI>rV46a(KpABAW?j#Hp6yX>IXI6xy2%~UH@w101F`~3OK z>szhw`QW3J6^hLyj3O9Q&9qUqqU(|GBd!`H83YspFzj|RcpAB=%D`WmQ>|RRc;$b5 z;n{aRcn6fEqwcLr2Kf5s#D4>k6XjM2ps*XX7sQl|u7xE*3lT0z$N1_#G)@@%+a zRCt$bpktNgdZ-p=C)#KJ^rf47!~J0@&}`wyKKRgcZ(QGQ59jL6_Q8-Mi@OBg1-WwB z#Rf3|DR33X!kDm8`2UZ7@}cL`{(V+0>IPAiTDE>*jK99Sn+9n#OjUC>kHI*uc*pYLe@l{9gglUCz{Masfz>F9Mn8M(f&J?{ozCFFx4y;e z^`3k2{KtOqWB=FQ^Y5-58os`H|GVGwC%^ZZt-T@8#^;~933Ohy?S^9>U2NQU=FmM4 zJX~%rfGeW;LRBFQlt!qe=qs8uh62W$>l^(}*A}0@Is2~f0W`e&`U|FI*BUd1)1Yi| z-d_nw3|fa0a6TFH7>==N_H{y#_j7_=lXE6y4&4& z@xsoRUfUc5jA2mG1NR(z?Bn0#`ymPq_%USdxmPb_6Gs21j?7mbb7p=XHNxy5;_vmn zt?j*y?bh|}4%&R?$o#!`9zA{Lq^fG(u!j%=#D`wP&;!^;X`(8C^?deW)oLm%K73A+ zL9}2~eAk6#S0#CVVGbr6)|b*$lF}>zKQ-}9SEwXn9hkfkM32wuX;mby)|pv`ly`*dosjAmJ-VRw`vXiIbh}edrKk3|JN5B=i?}%k2+fX`!}<4lNIRKGXrG zgu}oQKVmJHag>ZA z)1Ju^f1HL8098X6KjvsX+DS7sQK2%3%m_gUKm?`u+JCXcr|_{qjQl=S5@=MCwE@E2 z1efT$R9%6WB;+;iUT45SmeZ;VLq9f6<>G~F|NegfaFF!G1_D?xUPb3`aTb~Ji}d`Q zX7(2Srx$T7;0P&53QdYfmKF$M1b)E|j*T9aipm?%bRG-3lCy&DxS8m!NW6ns9!IdQ zvo&LNzB1n^BP%q=Dubp%7Dh!)RS|LdTpoA?tUet2zw&o}K{sq5dse|u2N{Ush3ut; zapCgIPpXDilGG&d5gbEHp+X=vyfU4vBzJZ;pw$bjD+dSrwo@K)J=Qc1(@L$Zn>y4U zB}W0D$WmAVEs6vjFr;V>a0qY*>ZK#l^*BNhHtBYI(0|+qP+uuqj?MXTFb>6um+m-oY?N}@E=0#>*+}B7-|biH70Wj1k)jx- zIkh^}1{NEHy>jXD6VILhkx#rIbaZUoo)<)#!a94wZFExyZ`h{7XUhH zW%Ho#AwFhlLfj$PLK6x=sHUm&4wNp;2gLMW{Olv)WO6kbHixCb!1v?i)TyPLg!>W4P|T@pCCz3PNQUab-NNaCRa2I3{xK~hneDlU7O$R#6G`(`@%za22 zW%1UPb70)^u{*_sNbyD-A}W9?@gW`@lq3c5Jt8CWrG##A86gz7aQfSiRJP843wzWG zfC7R7sQ}P`Q{sceUf}n={((0*7_@iY;V_B$K_avfoiYqAwz$600U=CXYc!mN`5Gh& zy2lA5x=F`Mki!+{ptmqhP${@Sxa5*FkyT5E0A-wlAcg($sGUg-X{?uq!$}ks{K>@2 z$V4) zriqY&pN)lZL~#n`G8`*H5H^Z!$5AzX(ChON?W8zYa|)UPqoH!y4_Xh%OaRAFEZCq! zapL(Ndd+uDI0%ifNA0vaFm9Db)%T#j!Q4E*5DVjgdSME7wsutmG7e?cy_<=e|EJPhn4s-^SVVYpg;w0MK z+I{(ro7XlD?mM%5=c$$T&GzTN^*UNktq^E~JmVi#1N(gAS$<4EL>ar~Rc>w5t71=o zz`e-D5j)z}Jc~%D3ZdIVk#BFzfK)Mj#Y;lfrZ?rF;XCqAQ*b2W?ffGCi(9y{Y55Jb zQbNx~dy$s68V5+vQ^YC#CjY}H(t#FFA6fj!dmfmZt7}$86<>A56~3hmu0*b4I2_s! zVb!U2XoIAhj$_D^1idfu!#!|TsabAv^$E9+$_-O|%D7O1@O=2{tN8IS^8U z>?Na;EE%RVO4G<6B+&qsN{Zt4Jvc)yZy&jZF=A5%GQ&Z1NlN319|VB=P-Jis-MV<` z%D?^ZUqdhR5e}h->>bB5KnOu5LJdI#(n|b^WKsh+%jG;G^87G`RSp^QhPe&2&Zgy_Sb zB5XwIGO$$|HfvyDa_#@DXYWCAH*n}TBK z@S~s=j1VlN7+V=Yk;La1?7(#q$>~;kl8rAxW`k=-R2$wWu$3?Mb|8zwOn{txObB_S zR1tLYltiYOQnbW`9_B2{`8P-yzW-olrMJ^vK0J3R^X{>m&pmzVo3CzzD*FSDiv7ob z_KRK+1+M?xOXt4)!bRM4XrZ>*8iFGzm7oN4kOZe9Dn z`s&V3r#}c!-Ldos2WwCVXfi+TPaQxM53Lh630Q)ksHd?(d#B1!oTvte1b9dwLaT~1 z@rJ2XQo#^jfpte|XwDDKpML&pZ{XH!J5N`bWR_r`I+* znj*b;xdREo|NBp^KK7w^o<94CWjHV>g(A40a!_poYXlbp`Qqpuh={+EcvTgm2b@8Y z7K@u}Yu$DW@U>d6-TlaW*_rXEwRwGa?b?aE@7K$GO$mYp38u3r_TX3=ih@)C!qc`u zJtu|vQ;(2pV%xv^oVbJdI&j-wZ@;&<;r3gDUW>B_1hMjv2u%Q3K#*6dShad(W?@k^ z4B&OnTvknlR`7A?1t^KGO$zCxXz+`+eC|A%1REu}!WkfANy)l}VzU^f<6$^!wy0VKX;d`zv}&~nLrqZ(O;bygC`np7o3o1xu#;ELUs+h1)itBLz46Mc zH`B^pD~k>q$#sz9&2;}ta10vI)QX*>!gNdqhaTQs{`1ilXNnpI&`OzV&ILJ3Ndzy$iBP6f2g*Q3qf#FwQ5O1>aTs$VK^Z~|VJw$T&+}0r z#1a)FdO=4JW1)F4Rk_7N7<%L8#&7?@SJ5T5j^>Yb=Zk>d-f||P*|;tc9=n~BOI**P zm^c))Ck-1G zUwvlSInYfL32_jawgvY)?DcTFrgP4OQ>h~>CtPOf*4le&? z3a%Os@jRC?fNF8#BzgoJmxQrD93o!gxMCQ2Zcwf`oa3pPzV9*Y;Nw*;x*`xf6d#?Y zbg?Isf+0o=KpkL~8x&l@++1h&~$#W8Cd^O+&9& z>***}H3M!N?i{vBQ)KjwBp(51h|f%OG7zUIBMlb;019+GfVYXvOGV%HQ6n0VaBSGx?j}6d5w(kY zVSOp)g@69j4@IW(_3ibCt(ngLV12v)@LP}n_ts5DY%s!bdh8Q@E@y;O1IJJ-@J1i2Faq#z=!O6xu=t8joQIH6c-0aVBtv0^(hn`3+2$AOevt*UA)OQ&-_& z6{XUcZ9n%I4+KYNxWH77E7gCk&DZZOYx{M)+e|Horrxd z$qiaa(nK3fqFt$c_gDw+9!;`Au~bea3-QKmR_krLX&I+ zlOl06g%7a8FkD($MMAgTfsrD>1u}(Ei;|hSCc+6IAm4Sx)G-Okf#dO~BKU~}1d->8j z?IRQf+KdwEK!l(`t#O2QQ-|_n@h`u0J4s|0`jq=B=0}Q1#@X%6;-5(Q&PyUcgJh)U z=iKx5e8&q+x|NRnG^%U{(x-)B6E`z;T9Rlg+XE$<2JYgX@4z2OgzBMH$Vu_1*Q7Fi z_gnA2@AQ#{)kQ@skF$vHW=TpI@+0JY@NU*@6Cpc8s!pe?Xqr{7L*X=y@1>ijgIEQa zDoMgoIG8@9C4z@7gx@M~Nn>~=4i$~#D1x>k3RaB@ffGYzM14M*IF5aYO+nJ&5@1Rt z2@ynsvd|kC?I;vuXYhs`QlxrKN&q_9fAUa#-^q30*fMs;a*QH8bBO>h*d1uflK zyP>Mu%*rB^GVnc=C#J$=@QECJhFBf5@uZ?{*kTXco0L zj(|VV0M0+mCNK(UA1F5&ah|*ihi@7JwFEFOAU-6p-fX7X2tfmIXr-DjO(p?$!TI%i zZTJ#LtS;vcVK*i84mJoW@K2gyQ^a8kOC_2r@hTKHz|u7TUO&IikVa9g(#UgEKw)4w z0jfy(4tIcK+cL*R<*8?1eDmV^M?dgLtx;t}IEo}$&(etV!w3{O!vpROM0Wc_nx`1e)h!jD_6GW>(0TD zAD4kmS2eDgfdVPG6y?Hi{={4QdhwFmzpLbIZFTx?c;|hqf85z(pvi%DzIi6vjs{cv zICc!38x=y%gu;rjGOiu>G6rfGrZX1fpcW#`@ z{E^du9T#4BqF(3712`)w8qf@%l2AiqFd)AdT|*^vCmv*%#rEI-{<~2Hbipn=f%!^45|xY~UWC!J0V( zekAb_CQ2<8(DhM4tJt*?WFYKFQBsWJ$F0-e4{;*_M`)_5T9t}Zt=qOkgPA6A?hLR;6^ItV1L!tipbKNq3;F{Oia&6@ z4fJLh^27QH5h@sku))-|qnyZ$5Nm><1qQ_PLSRP(b{xpmB?I_T1*UY&YOPpOHrF7klOq zDYtJDdGjA$CLJj%x0)gH<2Ntn_FcC(o{F4;0lY($Kh1?hQ3en45=8k-g*QYNqyZK~ z{C?~skKTLd30c=<&Ejkc2$!r32Log7uDd1+C-|(%t=KcWG`2?a@j1D z_?WDa4rzGUd}udc^pp7Uyo|(!&I6(f7aXTCV7iQ0uQCgb$g)`1v@{xuR&&kBFwA1V z3k=ObF%BVMs=^P&Ap(Jr>!wE3EsXHxt2h45?|&6t=AeeKdO06a5J4amPaNPJJN)Ey z5Id0Re<2+=USML;0!$DvP29x1JQk$PkH3922RI@HN%+IB*u()`D0Un|B)HRLJ_g{O zg~F%)!QX`SMf4N;5yCBW((?l-XdEUa7v&)3({J1cQ;i9j*%_&QB`9e3RrP;)sTL6#Jg*dAob9nPw9K zC}M@9M^?aI;0e>IKr#?u1C*4wx*wzsRq#s+L^DDv1u;@X1C|Pq8CEBaBlJ$Q%*klv z4u?8m!FX&{>le;l`r|J=`D2g0vpLh0WuSXL8-ikh4)CCwVZwhp_2xK9+IxF&8s$bq zGjz`#Mj>C&9gPyZ+5{=U**MNr8Da{N1=HCd@S2nO5I+?-p$|Sr)z$yczx{(xJa$*n zjmL`g=k6{S#S+7yPD);iG*}8NG%)a0yQYgmP(&08q@D7>V_@5FkEgF6c$+ znjD^P%s83+Cx7|!`mVdt=99I5`13yqlWJP9mPQ);wxMlqZ9n(gwQs(5{hlKW=hs{F zb$k0@Fy_~E0e~r)Xo7_5N~K@@#OY2&GK1Mhg~(RTu!f$UkrR}U0T%!%fU2^ypq4I{LMPo5MM-GGAUgS5cPJKO6(+r4(X zIlBmpcleG6##y{^`E`gvec`aIo2UeQnQ(MXK?&g0pZ@D#(KYFBe2Vin^6k~n{)(y_ zoR7tCEOTOb8V(6{Xds{)Xly>M;lllqZ7uT0E- zp#VTUSJX7Lu22lqtXNXjf$QsrkpymQdkX=HDrMd705&BE_%v;PzUtKKNtPTwbyioD zNzf`}qhc5pO3palK;T{o)JYZqm|r21bEUwOjiKov!PJz25NpW@~GY z&l#5&YNwAb9Y4Cfys`iQH|!1+RR*+CWVzex-q_kh-%lJpRBM!F#6o;DIjU4`bhxN! z03Xl^0DMqb(4R^Jw*s3n?DtHIbG8PUGW_!CjqJcmJU6`;ld;;Kac5 z{QzVf1ztS#BVW8~3(2oKdZ!o6HmzpOfm8AP2qFjS!4WRZR2LVU0U|m^$`GWiI4T@E zdKk?^nB>^j)e9GfZm39w<9FT#Fn#&*byV^Cg&WVmc@vT;B#xc}f763}C)_rb2xpX1 zrb26wj%y;%w>R=g%AOX7&O=T zt~=n{PMUJ#+S>o`cfNo=3c$g-!B)}DTl7S?^PY=6iAHlrAC5dEg}5l_FWN4)qQlR7 z-eR6O$mz(^az-$h$SZCU=hJlL$PZux^CCqxyoz5Eo#n7fa0UJ;uR)PZpZ*6wv$eC^ z8M-}!k>SU1$eDkqbh z=U)U60sMyDg?jY6Ei@cvVhkUV!S(p-Yg;hHCr+P2N8r9yO$Ovgd;l~6%4cZ`l+fL8 z!5_>lEW+sQZEu53h(+cW7Xc^$3jpnVodZNF04U%$2g7U!IiXKb!i4X7Obm`jkkT;I zh$_%6-7-fBB?CqZu9o0{vm;3y^b`&ixl_mvS35>Ma>&)gI6W4P+}+);S1Wd<24jb? z0hVH|s^QASaa5W_B~5xvc@vr~XM-g&~b(wV2 zvcc*s4mZ}fFI-uFAK0!12$2^yJO*=%MOtYm3hXa8X*Y zKiAqs9{3kDiqpNIP??A^NN|oW;S>zz!!kCH!)-tkXf}I6D}>vyM--b?7cE(pa4%oC z=AJJ$|JBBc8`pMpO{v$MEh~9(*#7awJKVkAm211-czFW_>~y)v+o|Q|PyNt`+#o>F zdk4M0c=F9S!^cUJQF^d>GjIpiPrx}vYr!XT51?)&MbSH62B1lS6)yK)8IFa}f-7G1n`oydkC4c~k z44McFQVL?P-*0QW3b+8Fy>az=eWnt4o@te(;%Ah!0fo#OD?g0sc0tG0eG@MAU*yXZe^U&EEd&?>kO^8B5|j@V?^qB4Sntk3du?MkPEx4p$>Xav+&C_HzUzD5#?J2k ze!tW6HuwAOfj{4@JbdqoyYD#Z)XH*k5=C(-8}<6VYioOe{SQBMuT!l-zWhNCb__Le zY^qpv`#o9HTH8C-nOR*oA$goO3-DB6;~+#ZT&@@GwhjjU!O(TVP(Mt71G)o05$m!m zGN5%S%DB!D6cn+nsVh1^<{-_~9a-kb-ezaY%|>#JQFJDD7sM;VdsZMp_*)2zBgA6QPHBg2;vZj7E?Q#l(%gz&uFfEjWbJ<2_;%IgyQr)Ju_+mnjaXiPJweXhLHm z3S)w3UeC%<7DYVdZc`$%K`r$`UpFUJ=%$f{w z3oy$t4agM+9qN3mij^6v1N9hp$1U*+hbWk;aib2(ocnsX3ypq(Zmj zmSnxxA8>$8QDDs1*497uJ71(dA=DtraELbNOhLU!LVC_7L4lmnq>&)l|3a3$8f?Xi z&J~t;B*@6(aD?J=(<*YK4E)0>v$2Fc&-4O825%4#kOk&IA~w)_TC*rbJcr@UvMeps zoxT^Ey6T0=zxd=|MJn`k&>!M|7z2bbZiE8Eoi;EL74RW{;&S} zFOsL?Ra;h9ENxsp4?Bxf)S5(q=UlK4YP`39;5hc|{G9I%#oS3g_kt}(i#SbJl6)V~ zCqW1WLd%8$v#0Ai2o3Pg2OnZAfoqYeT-!K>rK{CBXbBU};Rk-jz~pC2CQ%qlD%ZyV zUV+BKvH;F-Rt`uq0*kYVt9Z#tk~(DvM2qN90_*K`7=I|G&>!mcwkf!m#glYz83puJ$i#UYoVqPw2cM@<^3eexofPZ`5x@d8 zub>z(9!2yEG~+xDkU*$wd4>UZhDv~=~kAk(0JvEWRyL19!XB$M&RUU8%s0DgrHf+0l6fp!Qx$FUz_dva_G zj+Qtf2R?H==}GddR@vk%6DdqU(L`0%5d@esu8J@t2x3iBgFarlwux3$E9FYnwwwy` z`>xmNbk?>GprK1M)v^xF(-GVFp(mCP%)B7n+UvY|Woy6h-F0l~?$d|Lj)fK&x)%7@ zqEQ^*bI+-2tp;A`rU9|Z6$ed3M1+|2U=KC)&|XA4Q3Q_yE5aZtj#EEOcUpaZrH!9= z=6mw!FNDFd5K5@{4@NJ|HxSCAK#r_v0FVkq5VbpHL`NVX7u$v|AZ~^oa~uak1*58) zrWgR?2(0f1y-qIy7z~2l{mz*ahn5blT4jXY6tiGBfPozsIr-zuPrUSvm#&~Rp(Wg? z*WB~}bffAE*?>bKM{*sZHNZdWKQ86Dh1r?r7di8tC`}jz9^cMC%_1`7`I+Z-67w@B zgKwdi(`z`IR!0wljd}B8vGI~9=~nUibsnRKIDslNS>y)`m?8?MfrOSzrBA&7ZKsYc z%q<^A>?ao)*hz3aoYc?9874o_1L9}8VTGZeru>MIW|&eDv9~N2q&VtxRhCJ@_4D}o zbW{y_o&;xsQ?Br;s3TVo=4XA-3;GR_klONf`B9zwd^%iU}p@iO*X!s}_n1B0duZ zv4(y$^u1sE9HI(#RxDJn#?1Vjv61puWe zMF2H-=zs9B@10p()h!1ST`Y1+4OuP3@7l{>;Va7&r&6!AhuBIybtr!E>bLGJImops zN6)!EJ`fY~#y#XsjQohbMEQiHqWDlo&;k?;T8Rn>*btX~ytZP*lSxDWR)6=q8!Ku& z{+&O0snrYG?&wE9^vK&EItyY0_Q{CsCu67}G!!&LrI5k)LGP&--efQr$D0SkT!yjz zZ$Ezb#5DhBcm3Uk+HPwAP`j{D`!jDR!kyGv=p1TCS2*Gdr4XarlzEgs&M0V{>5+{S zFV$=X!8~8|noRfrTqqjyBi*o7bc@zTp-POB@sG_M_IrdrgQ5R&BZUsA<{y_`p~=H^2j^C z%d{&+7&k8Gg?6KrR7V_3iVA5*^=v%ZjP9^|1c0zpF}!EM1@Gba4D)NB{nzl9h9=#6 z|LK(z_l~l7Jjv|xV!<8EJ^FKkY}|JK!{0A94QY06GW13VUCAC7=8hHQQ9(&Z6OAkO zaDrs9y?ZG_fKe_-t_wbEy4l&?K?~5bYNJ-0KZ*v1VXPNZDQf|n7bT-K3bRs8(<(*J z1%bvbciiR2u3!{lK|nz;nmI?b%s45kpabBJihj{b36n=L!VlaGpW@~GK&xqyxPc}m z84~%1UFJhdS@VV-2yT?EEQ&=PlPKV;hLGepudNSVe`&U%>M~$DS`NOTpv9$`QI>gr z-0$`vBU(`gJmU8hRYgn{PlF)3vA%tCt9AM2UJ#{@rI&5ZHg(9Q%|{-E`C9qV%3QTx z)eL@G+4Fr^o;XSNTfLzd?ze{nFG6=<6|)gX8DaX+B>41-Ek0SKN*3Ufj?jXeX#y=k zr(wV#0Hc}441onAhvLM>=GY<-B!M~y9duAPbimKR55p)zlm&wfgrO*Ml%#vD16X&6 zN)m^Snfls#Cxgt4Cx@2j=4R30GMtmD>S;9G-QMeY;n$ve<)!oMBsXbJ1s95vdk1N)JnwYF z2{VXPQ7ZFe!$F=;6gb_B(_EbLgZzFZ^L+?$z!anrXNV$lKZvV8^CKUgpQ$z%meOP- z6%xP;M6Edx06{3^AjcTg2<1Qoa%za=qV1FN`%1{a1&LET%swW401(e6a@$-anCb)mzrZ( zhH}sgkn)dy@dtMIdpFm278h!>%{utk>2N*)q6Bm=P)!BvpaMq3m&hhoY|= zh|EYNH~^pru%df#0N9ImLUkcrkVJ%+VlGH218#^S-w%Npz%{@o7zLEiU`6GlC$?GD zAzqrH)~e+fUU(Hi_SBhEFf4IAj?=)j9WEy-LcDQ0QZ)^Mvf~(06xHj^S6+D)?7X>o zFgMfa^?Cp?rfI}6zzs|ov-+lx6mxm23000Fnjjd3tzu?VuAH)-TLFsMlKX66Cy%s$n}^0LmcIj0`wu% zy_0lA4jex~Q?Wl~IQJLJjm6nNJGlDZ`9rml`q|IDcxj_|;AS6s+g;!Fj)!1@L20lP zjv6`$ms>;xK1y|qs~{jq+}+!M_O;7bx7*FK*>yvhv7hq zj^6SW5YRW9%oVh^6dk~iy}h1o8;|P)NW+iK9s>k<<&Cu~>pkdwYrwfpa2>exGoN_Z z{SQ8XGjBLVcVJzVQek(m^XjF|^EY;Z$Iwi*r8^fYO0LETiv`fXbGn+%OR(1{9Y6teMIBM-hMb)?US4Qgm5MB>p6hoH z+S|MBorB(XyT9M@bX9^VrK7Q_$W;sRi-yPnT!!|=DPN-(wgItCkE!-B%ZF$yRu3o~_Oh6gb1^7#wbR}U=?`u!`{_Q6ZO0@kS5 zhBn)DI(_fr^{wY#yRy?BfXH+)WDaLIQCmoO;rMwb*nk}wmYe-Nfme_xXXTWk`Nq5Q zQ>L@PTbFn+l?Rq9(s_zqo=4=rEj3KcQ*M<{Eza)|CGr&B5&sC4MIN+}8EzGr=glu6 z0s18BVg~#JP@u7=)-wTY__-hdUI5S|Cr+};F&}&&*Epk)Q=1ACtK7sbf#;^9%yMeB zQ-w0ZS&T**Y>QzkY1jvbg}B2VAU{MCUIM_A)$>6i&`VJ>0Q*9|y+V@-On|a!laYt- zwoDLH^RphRCIwyyO^W<@Tr5B@QKGCFj%DM%1eRZydYu8HQ{1>t!0`oIJ%}<26o>=e z$DcqDq!=&clofShj~1Z5+w{qao!`m!g8p+=3u&MaQlWsLA5(e57?2vYPy|h3HAM;Z zI;rSXI>QDX9 z`!8Nwzq)ZSTXSY=mSG}9FoD@@9l8gX3MB!gF9lKJx>36~{MAqXyaFI8bLGeLuRISW z8Kt8Nw8JT*v2oxc6h{THfP6((j6iXI4SrP(zFAd`Eb&1BN#fE;uPdqczgrVUQNg~iub$$*8O)SGB@hh*eBz(bA<-|ZK3itszU=Rd8tA~z`$Kjwi zyms}bsVlSdT&~phJenj9SVQwO6^1G1t5cwcs+8uJmtjo5@#SwJ<{r2q@D!NZ8AKeg z(N){jVD!8wYgX+qJ$vDUZ@Uv>V3}%49g{)!FiF4nv5z$tkBJv|DISnW)CR%0{@T|V zj#D5=y7O;5saty3_v-Vt-`l&6#&DQBms;>zJlCKsOeI7t0tPkU7ZM@O!tik*v;-)Y z#hh{ZJ(I@5%FLT_w{Dup%=)=ku6^m5oAAmjOG_Vq-`hY~r{aK&@Rg_nw7&zms@@R2i`Wna{r<8oz}^*y}8wfDX-S-uV$?w+_iASXeDt! z8Z5EFNXk_N355|m0&Ibk*iFX~pAg9gSKR=4K^F9tvNkC?b4C%Z7xMP8<{>*O+`PHF zyx8paT|?JCpYEfuADKHA4x=~Dt=-rff)i`|A*ceurmsd&JDU8P-~8LWzgUdIAokrj zJV@o6;hf$9 zGxpLK|Ih1dts|?oJ0Ez>fAMXWo{gC4I+7Q8GwpCE)^E;{~KskzHL4K??KNb z2#N^U=_oU}ET>5^9>;EF@gA<1Hc5&}JPBQn`a<%G$|wb9?-vW@!mv9@;?a&*=!Yf7 zal{(AHGuJ)K-x-NnV#&2bkNWsS!^H{pPSGm@p}gpBqZWS+8tO(3M6mf25b)ngjS|p z9uT%S4b#MrQa?_I{Xy~g(beyM=b(K6ZrWv9-eIrD7j2|c6vVW%{E~$xm-uNw@oi_% zH1%4e22;>(chV$Mq2U^byJ0}Xz=O;tQRG5-xcH;AvT{f>OhZ@P4&<)e9lG1CZWM*x z?jRkFYPNZFdDipdOKW?IJPqwE6PAXL!zw@n5`thpBerde%W{+^4#K2_Zyz87C(#4= ziqFpi;R=|<;AGGrOp1zOIl*u+a0ei~By$DDZnqDdqAD^NiKyw=@gwy7K(rQ7$*p1d*R%GmmkR8h`5;2f2$A^+H&th0xCv4X{-FQRRRI zRppaE^Zn3{#Un?#`sFwtrIDiB&?878;040tI7-C3sZksU)tNs`JVTL1kag~e7dT4g< zpod^qQnhle&Jp}5?hghFOY>maa2QaRiiNF>?N9ygUs91#DAj;H7A&R3L+#`#(sI&p zBOPK|n$_9)`6LQ6hGB5=K?%5+Lm~vNARcT==z9praFWJRniUXglpA%xqH&tN{Nk(E*S6mI zj{B{09T_<7je}KO{+yKJOFAm5fsuUarE{Y+>v;hX+|ZAjWgVE@HVr7iUaPAcTD@9* z?)6LH)mz_ky5D!(ecv(^SQZF#m2)$-(2xH5&;PPjwBknV+- z9&a}6F>HJ``r5ebA>JaW6%Zd;c^`;B5&}gzfd3Ot8r>#cagpKxq98dNO|ruGTgwnu zWx;vPKX}jVk)-4Pw?BWiHH;T#nm_RaABJuPQFP?ku}-_i=OB|&qd6P+LsZA-XfUq@ z#H^)3w|n8@jX!_t^?&-45AC%F?HWS!WG<8-GYE5=Gv#L|ogJ>_$5;UY1z&ka``A3X zK=hK6zH{k?7f;|53+vxFn7gx5P>`)8Y0?CqFoQ5tFviK)s3}@ihc3va!e5&`dF|5n z`fhiwW;dD^;@WQ&`e~`~gL6j`FM0jk`u4sXCF3i*v<@JU%J~*W$QBgg?e`q{`1igK z@`jf5`<|Ha1nd!9U)z1=+&Y^iKI}B#sC?HWcfRujAA<0xDi^GTjPk(>!X@e}*@Ufi z?W<`t)Sc$+;^|4dmA!tWxKfwq>m%E4ZC%OIOgGKy%rXpRxt3Kzfj z8|W&W7WELCR+>N@Exvj>DkO2q_eThMlTo1;6~nM-+Xb~W_M@UVgem2;+Ywhd<-;cp zk(eZWln@|v11%(y0I4~9Nyrrd9OMBP2(2hE6bsI-A;9HRp(F{RZ5%8B4hJUo!n7Ae z!zk$nNk1L=8L~~h@kl9^ELkySsgjN6?>uqzBkv9RZIrAVvggMzZzV;d>@l`OrCc5Z zCPkAWBp^h(v@#F1f}M$c-}i>-J`e>ZB^@2??jZ0eDXIu1X}FVgB1`(h@?5=QQFBAL zCB8gz{=$u!x*kWP(??feMt1jF=popOusB~S2Qh}p^$hu?Vo1JSE$fQG%zLYRX1HB`o`-&jSD8Wl@l4bxa^@*SC%DK1jvp{TuZTS2kQ>hcKE!$r zyd+Adg627kbX%$-{l>5SmB<|!mAY;7Lu-%`pif09MR9656&Q*%N+B>YC>R8GV-7N> z8^#DoQiS>d$>ZPvE=I(S02~TqTvi8}1Yu!n;Bz#-b})SnjatROQi0$0 zjx%Tn3~=W6J;Y9xT2;{uw?}Ej4YMp3$5L+PHLxa#(6a!sm-J%tuC4z^dZX_ZlzakD_?CkF=s$6X}U|A+b z+BFV)h5>qGS{7&JL^0hG7@dyLs{Ph}rQ*O_$8p*n48HQM*Y7&MczAi%u#F&$gdH(K zFEBC)f@Y(tap?#ZU?^W9A2yYrO)@QnMKaf^|Gt8y*L9%@}me4Yqn8+@U0IWK6S5Asd5@8 zuo&4WjkEOTIlepzx%uWdo>-VIWMMq?!<~BRd@!JmKt4W)7QhrINDz2{wd!#%2!;_~LKJRam8QeHg zBjbC%93W;>>5+g}sZ$_e5&zI72x2tee!9JIe+}7+9E74>m?Zp=^f(?{H4R=)HL4w_#ybru044za6i#pnIzfkZ*H}^ev%g6+~m7(x%kt5!C%P|zz3@P zg&+CuyY9P-GLaSz+BMZ`|008|Gk`B%NAmzUOUc?|R@7-L^H;7M>ea zN5{rjp8o@fp9NZ|)>lT?Z%j5ilTtx>@Q#tL_V(8SJrRDvMRj*KhW-A`(qb+UUcdM7 zkN!5=lW*J4{+5(#leo{}C@?N9O-fk-vDwbvWEhX3i?ouozUT!k0XPXeN*5&l*s)y5 zIDwP$hClX~21eL2@sGG2fEj|$fkNO$j{HMz0RsFeb)jW`)Q0*r@elSf{bVZdUUs4}8=9U}=4P2g>Eu}?*Uj;nj5=oOW+yMj; zw?U79p=TR40H!!gWz`%-ZkE7Q;1;ge(r)cSNgc;Zxs+$5$_33S7mG@#-BU}YL#y*M z3rnzxgKqo$xeKaV>~!3tN0y<^5~;$k7H9?#AwY%(PomRm6~bUR$~a~Nf&g6tY4qA1 zT~mR0vKYpPYu+Fl0o;OVQ#IAGxLz{U)^k0;=W@jvQO`>KK`+T-pbgvRH%o_|js>67 z;0rRHb|0Y*T46a3JRkg`SZhZGF%V=Ul0kTc))dDanUytlZhi&|4Dx#eSJe#93(=nc z@`rzU;l?f)C|uOkWl+2GKQLANQWCg{i#fY_Rh)=S*k}uc*dvws^CNLXo}Fbeak}N@ ziX)MMDdObY<%>J=JR<+J{975sE&QPgn&uWrMQq=h9~oF1GeAg0gTz68uaI<>359hm z{U84A9|va)8%ACa+&(gDhDL)@D4BL0J_vHJ>9RWi> z8a5{{O9&QJE^b9#LtP`320Nx34(E{p?r2&R#?UI&)cJ64G8V(48K0(QW79Igy+OZ+ zAalf7#*$Iyr@;`3Z0&3x>~~g=97^JhPLFSZ7B)AwbHIUa(qs^P=X4Ml1~~*#c|@`a zGKwP{u@TW+5N>CBD_k@D3Zw7xd+l2Iw0E z8sz}E;0%62<(FXj>>P;@Js4|N23#i09q>mkQs9ng&h&WNUeOKgZ~dEY%nOhtINAiU zy20t$hM^#y<5bge0bzun1i$+K{w38g`Et>uaQ)Kj zus8JANeFueGXi@K=j0CuLw7K{u&9|1^cyZF@L*7)nS}*}FK8n@Rg$3O!NBWb>ksE= z8?YKdjA(~2(=gQ8X0=?lRK6z4NnTg3-CSLoKYaWcTtE=UVqH|20}0TwbFe?VFh7F9 zh+!nTDh8jpB3LLDxxkKPgG#8dC?N#lb2(gt92Yqq6~Q~dLk76vdmcQ8WtS0Vx!nWX zslp;(zkY2v^k(N~ok|4~gq$GmwHs^8i?eT@zYZZn(BzoLzzYC&P8?qS=l}K3(OFha z>XIdb58@=}wOHHqQ~&Vi&cAuNKO6$sn@=CR=g>DhI}d7ggkzo?21(XfuKm&eb<|7A9~Yo?Swx>Vl~hus zfG}nHY>3ZOD9)2~vhl5h*?TK!H$r4dX9p%30;bX=9UG2XGD?b}+-=(TmuI(bw7UJE zRy6<=$`zwu$e!pOyuUmPmwfrk?taIQlkpoHF3Lx(*kIAf`a*#pIFo^42+;oBKl>%! za*{X-+}`faKD4u9**CYh{^V<~BeNK9N7Plk`}pDy{@_RFmX4wE2&P~9;(wS~Xf7Q+ zqwCf5>n{o_7vy(80JnmI0AZ8RcN%jD6o5Khekqir^ZmUo-EkTVhgTo@1vW@*JOBCD z$2(oVwG1|)LF3#GaQTv=E>E}?4+tbfbVV&<`4eF`aE1dqCDN2J8Swl_7QqZMhMN=F_0=ldkTGdp6Ct$KoP18*k{~21S`KoKe zHGmVa;e0KM!UpcagYIXg=L+MRQwOTx-~?wwPZ+MGoT3A6`+flE=v25uF?-1NO7QcA zk^%#QVkxwv(ZC%@Dkot2uIJeLcvA9RK8XO{a%wX${cCGCJwINYon2U-pPii_XA#0b zx7V-L8*!Xyn!qe14DDyT(~mrGUk1eTJcJ(zkbre!;0fp#mrpc}+1XhHuu&LvJADu? zOjC|!srraRL9oo?6$XM4u96J1J3G?=_tD;NyMsu$xv&5VfRLSoF0>H+YSbMDPGpn< zdFCW{Y+Mmh@AbN>ri_wIm&bZz9^C|o`tFdgO8|TMzEO7m%V)l{)#_1W^57M60C_+s zDZ|{5LZ(xmc_a?lU#e7oNsJaSRpiQ#u(335;i2EOr+wBC5aoRw}?#NE@7Hu zievIKwnr4oQ=af<4qV@^>2`baJ46XQWPWjDZXZ!5g^Twi6BrFy1O0L3u4Aj;|B-i^ zh9Z`b2d`3i6G?&PD5Qi376KXE=U4_}NO0M%Hc?QyQXWqvpiBraxUCc-pksKP6ey&~ z2^=UHXrm~}bQ~jS7eEYx7QkP_8xA3O4nO=Wp|g`QzzX8<5MUPJ2j5(Xecg2UVXuUr z)Hcgy7%GSe!x%~4-P{AFfs#m?v3}#mZ%rSma5x&IKp~`4P62#HA| z=E>quB$9NX+bY6O+(+6HVo&ozsKwkR%gDcgO3~pxjI`JjmWF>cf`oLuoq(1T8iX;K zsA9cK;t#ToAOj`^W|HjT3JFX{#|WVEP1K6KqBNGAw;xx?K=ypZv*>ZEv@pKfiu-u{J+bsnseFdpO0R8}T@&o zL#vB$M34-Cs5DLy1ekW2FW=gB93{KETiC~OcI5COr(Q0~QWS)SX+!vcD*#%3&vl$K zjv!8?q!_D*&KtU^XbO-JC&o@dOaqFYCIOrXrznp`9G52+$pATk9;(&4%o)jrG)Xzr z1Lks+0<>PdbbV!crc$i~w?+X3mru7Yym@7>-B(}-O%t~qU7h>NldnI2VS^tG0e4`B z$x3d5)Sy2cXU=QdZ3yVU_~hSs<@F1mAHWOfnl@9f+}P?yeR61iUECTVL($^6OrZ4HTO1#dKPf%rl|8#2XwxfY!i&l>lJa8Z;{h z9F^6{FaoWVW#?tD^TGKeJC}Dq`_&5@o#;1y?gx++aXXA4;#hc$A@E`p>ju0Clt_bR z2U47=R}l_^Rz|k|>}zXxKYZj5cW!>4wK(W`LpPdVsO=irv%LeM6l@e9L?x3rN=Xnj zFmEFEh}}6|pqRl4qGF(nXcP~!l7hHUEIdjxGfATn{0`z6$v`9Isw#i~+)>~!2qZk` zLED9y^Q+2BgU-9Evr0JL-tO$QeQ@KYYXfM(R3mc7CWbn6U~PZ^Lb1vFA3pN~Kl}+W zcNBZWzKc%TRda29e`~k<)N418AFcsGOU2gSe)i;YBXM2-10VkZ-|rdc_eybARAu$y zyAnoAg<%)r>Da09Ya>1RHL<9yrtCqGf(6^08C z4cso?HI)dmK#vjqI{XfZFQN>Bwm68x{DDV03gX1`qbSXWelm!&VVv}mG!oAaBbbX~ z!IouHF4~gZFtvsS1_Ded24ZSWMqB}H&`n+7FFtaDYsViMMp!}U!+~4D7fub~j1R8H zA$$>&Ks~uQ*&k^y@DZT})+0HBPIw%4nSVegvl*N3o>nGC&ld zk6?{>D#)q0gMpib9uSXimr2}3;_0GZP353h5Mu^(AGe;r^8h)~t6apzcKZxBA_MNiFG}Pk-Y8r^zQ2_}-$Xq;6W3WU zZ=dFzUb=P4ZCX@btSFzlCK9I&5ZUtFqD*0*@(egZepn}QimUt+c8rjN>QH>=183gx zz}*NAPn@{}VNVu{brEG9Fc;|BD%Tam7-xwrtAkz}z_GEg3I#%nU8y4$g4%Lg&xklU z@nH%$w}DS|i?IK!D~!3Sf?N_$^d=*~bXuTFv~ElVZQ|FZKnb7{5u`w(*aS|}4Q~~iCH`b2&MH`q6CFS{%f_{r2mq{#>8981u z43hRBz||XTJFe?p-q`!dyUw;cF5CoScadw-Xq0tLMzA|KH}k@CFFf_FXYRTC4465$ zxY}qgzWCHr2dyp)??H>}_xXe2^=oU-zkFeHtJUv&z!0#A2)<68SUqxR<!h1pny;e=nXD50AGCFG!38xCc|`iLojZ`rK3@I0AM|^EKQXPh%F;;fUKe0+q!w~ z%GHh5-u}K5*G3Y|VzpwO_xr>UXu04lH^#w~Nbr|ZghRdo#%KCM403&Dn<%)G$%3qv zf{|q#^R?QWUdNWC@h2Db2ktx^1OXyrM2#qcvljreIR7sXT?#svsX&1o%$HQf z?`*&&hPD)B$%<(sB*eX@t$%CSy7fjWg^l_qrn)A84rnb(1FpIy%lI$ETx3Qs`0!;c z8x(o8761_yl6L6?8Uri>s@5U((YV+1IJkxwMU`v1e%W(pWn~<2X^q}6R7#~yvC?S1 z*-6|PhZ=4dTsyp?e5i9|60#)xluNm*s45d>!+HPvrF{{mS-M4y6}#p3vWBM z{MI`TpFO>D$I0bm#}=1YoAZma_1S8*Suv}8VMKvRhWP+XK@1_ofH!>bK(k1q2^N6~ z5P)2W$SBTCAySCB z{xUoS(Gs*9>S62h;p0c<7Uv92H!OaM2EK%^5ZBALV_W=>cmWL8Do`rNLJOhnkarXU zVRgz5Oc}xzm>NYz<$CCOC`SSQNK(FA9D~a#*Ikq)yIh6>12e(AsD_mUL%#6KC8U5Z zj5HaEMY5CSqsMAZJ}#^`DyCINXa&^;{2C5im?4<&=UzVFcRBfNN@SrPIN&Lc#lKvF z#T8~?9-%h*uegtt` za@P6z9$JnPf?F<9u4;g}K0-WIvp{*_AkkdDQz-^E7{+jblcQXvAm&;%j$KB9kax5J zHGpi4&x&0C9%`N@eAI>V+pP|)k!9PUvF{E>e6<|aF*F_YJ(;gOa!yhJKV@*95QKQp z??3;>HIDMJ&pAm1HH0q~$0QAoZj)Eg@YW@fnRf{S@h8{0LyL2~fucZzFBFweSP%hH z>r8`lHDE3&3oHn~I0hl|Qg|#J0|g!O!7(C!kO`N>&~Fji7^jKK(MT2zlMB0m+`w5N zC~g$I>qa#rn|Dd3#E8m!l*g!y+`lAQfOZbkjL#cP42v&c=N=+VA%N{y+LPVje}i^x7-sT1_*|$nWiKZMP5l z(Byifs`I1s1%)%?lx&oilv3pT09$bwp?$rB{f&*CBuSPQ7wa>1ei~j=-2Na4Li##> zvjbd^I_*xgIRh1ijswVzL`0k<9 zUx7j9h_CD-*dH5)2|(-zLABZ_O;X*dfO@cEh_OJ*TBFH1Wodf;+{J3WV(3PFruqCc zFS|b10BzQ--}#@<)EslS>*j>TMjiHs=RAAI}05Q;b} z)N1C9jf0(T1bz6xBPSn!;mVJF?>nA);lf8h^sYBw{W9YFUe}#FHv2!eZtxk35C9N? zH4}a?a1l5J=oXSdtIank;3#MY(H=w+Bd$D>jvEE-k&#nxl&e+yDSr>{`C(`7)fX=v zI=a+4D9tr%t#+?kZyr5*1dWDYhVl-3od_J^crtwqBSj%QPksH1cinyHyzEwF^@L>a zZ1oVu9yvb$hl5R0f|;lhXek1NG|m!O>B*RDQ!}LIXb{dp6F3P_3Tiyb3PC%9y)h~r z&>kTMhD723#$k}DWmT(kYRO0Dmvw)8G>X2(hzInL?5cBM% zUd|a1p3Nl-#3on+!LW7svoVwljDV>8n}7Uss`y%i@9{MQMNFc)boItxJn<$nKlaX< zgM;vI{PbTL4BM60_lgy@;8^nM<1at?IAYwlf9Nr{yCciGRcRL2x5hX3sp=rT&~$Ky z?`VoA!;7E%k9lLTJ^eR79L6dByKclGiY&$6g=rO%1O)+EEJ-fQvZ2bBA~kdkt`yDI zbp-(cA20ILHw@)yEf~O{Yp^7oRt3Uyq#~D1njQQ=cIdresE`6ejA15We;^^u!Vxm; z!4T|3qsc(#7iFL^{jU=fCpQP|ZREjE@R>kvrx>06;G)_R)ZWpGh@UQ;QKWg>7 zJanN+A76whNmjm7rD$U5I_ctmlnn$ zc%8*QP%QKh0wSxrJM6)IKndPYNhv^{&~o2(d)+o#*lac= zdD8C;Am5y!rAkrAZ|fMAk)}l;)I2)P2#U0u*KU0JcR!Dq7ri7;Z*da>j}*b7oNT1Y zt>fF2pOTU}Z>NZy9O1w@3<+<^kp%g{DFRSg4fu$pq$^+%WCooLQeYuaVo}V*h7gd@ z1QU$d&^r9*3Y)OP0QevrK_X}fnu@V<(Kwcf;ktA8@IhV+-|{&)<0J)oTx&Ir7S-byJnNnqHD3K2QrI*l0tS>$S=^ zzWLM_zx4IDK78N&>ftEn8~^-T6<>`T;wm5myp*n)qcnE=-86~)VV}?7U?TZNCBApM zw6c2i)QRTYJUR&*}Ft@q63*uk7bQ5?7=Iijv{L?R9ax5K+-yVpM?+cc}eUd3ci%>a-srZkg z!{Z#};U9+$DMPsBZ~pj4P`DRFI|tp@E^Yku$KU%?Kl?Wi9Y6WlV;@~UcIF-Le(%ik z>I=_(#kK%~O2B;QgFb2ygH@0vw3lNK&?wH;5E~gw04&;pP3S(eBUK!P+2kIx3ROX0 zW@^s!?!j@hQYk8?0WE7RFD;hFqk~?*IWr5TLNvp;5b;5lj*^25SGRhn z&Mdv|cOSN9w>R6sH$j*#F4SN1yQrQuvYS*es5G0&by8pi;G0MUzB3`HL#lwjY%m09 zBBrvGWR=6MARAm0(o>fxQ!y0mt1^7B=JtJfEK>&pDSBQUYWz-%k@A(`{R3B%OKYt_ z5P}q-FbI-B*Kk656c`xc7q}upKv9T7{T!UcOIFHx7~jF zu1D`iGtHN`jd$NWUYdz5_4XftPdsKpfkLRVagmdtBQZk$C)#DQCYBdc6BO$Nk(7eM4e5THx2 zJPH@%(GZQGL;Y?aB3h|hKuC;dCj62Vx{Rt#*dfCT13zV)Q><1i2vlG_EW2V@7CMyj z&0a2xj8@r>LyxGa#QL-<3Phc=*ZFm8ztic&aSAE5?HYQ7EYLwpx+*7W^5rL9O@yfj z&B?8NBe(dE{LPh{5}QAX5Lf=!jr?P7al~W(@LsY$&n!}Sz_O>O`4w*BRz6LmIEeG< zwY)8Pev$g^t1$SYJ^8&9HL>0L747+s{LBfNyp_gHWD(h>{{c)(XYV*NQ>(^l%oVD2 zehPF9RKYk+f?Yz?Vp#lQAFAg9OSlPefSwKn)xZ=;FQ1+e=cnTgUWsrV{gM=jrONRq zLnr(ADjcUAnH3vA0Rkx_I!)s=OSet_zbmoj{CW3}M6|P>OEGFoOWEz$?jU zFyO?FFbF{jSWY0+XI{NTUoI$5oF^Q&*!aU^{^cYDPVptX@#9ppXc#7&BPiE_jdOYxpd06e`o4>h}jazc7c6AQxyhc3rpI?S)~;1=ac47ewcMbc&Dr zN7?4a7FYjDB3K0Y{5Xtz{UK@xci%jJWqz&-l#^zu;vzA^ZYjwFNPz$7`qINrUFN?mNi(a^%EaF11kJQzTcS_$#=ILqc$6EgJsUwr1Bk32XxzYuudKlyim zbaJ`bXf_aULnwd)yMsa5u@ODyOLVk$u0x2OSsUJ?GEqD?;!6vql2O$!_*}mT+q6l! z(6R4aK=#>Bc@^^T-*LZ{nEMut-4~!4rC9Tt(`Qt%Lvhr6-Qfm*0@ty^u*F5rIPiAxivX7(2YVw(islmEjxvfJWs5_~*g$LH=tXA)$%Ufh zkOK)!1}+s(a@k>#ORzxDodyU9Pr_|{T7pZ&d(44iSZ`hwNr>9PpHde1K|1No%vt=< zO$m4sJqL_H;4v7uun$PgMx`X=1NEZ9k3muk&}{f&c4e%=>TBvQ;C63Vt(9RVOv6C^ zgMrV5tPwvH3wEUpnZQB*n2zHs5}%yT&Ns`oy6IHnFhct{JwhrXT=9opK%4@{d^6oJ z3=W&q6r*j!em}|5a=8kKfVPSL7GoHKFm-H0(G*xCsU%sBh3*ADAF1SK6yAv=NkysJ zsPogl<%(_++zbZ8{=jp@Ck_z!qI(?bhw@1h09L{Y$x)0*3{g>*#Zl6jX=u6u8)*~*-lf2-f(1@sHpYU`$C_ipU@Xs6f`mwow{thGkk45t+0!u+aEJw#Xrx7Zrme>_(YyKLc${`t1(xbsP)282TZ6 z0C_-$za(tjL8}k)0?Kd-4TFyikqlfEV9NT&c8M@au2d@^888!QpeoYR(o(hBm|d9p z<~N^Pnrnbiz_189IOR3)5zU}zxg_muw?clyNqPL4H_qQ|ah@5e28t0_@BsCaJsj8| z^+-$pLFCYFv_UjBjadDqvUKfgL(SzWB(T-#WjZ$Yv6`T@$WGJ1>22=1)uoIe%93^x{hzisow-#*YsBmneXDMTP zrBFOlt_E3ZL;=)hp={RY4+GyiPT4Are)n@voISAus3(Fjg+M;#z_p@i2UnkY_T2WF zlk+>Jcu}`In_Yk*+tedC30t}z^7RnZN8BtLMe;yICt`I`N}K@25;z4nDzMS5eAAB5 z>xr4+0yZIO`{}M~$>RGn>`tv98^B%SZF_{aky5-82DT#4mQ?hSt3ect*LJ-r6Idj7 zC^Ih~2iOtQ@H(ji#>fnJ2NmO;PJ0N z@gy7{Uv24kRYTsmu?84-;_e5MS1y%8zG;OvZ?EnDA76U)8&|e3bX?$8D9Nd+^?{R1 zA3l5Rx1WBKtr6Ql{h{~S6$Lk3Lf z6aRo0@Sk%%^O02Cie%iv+$;-)k@pl!oQivR2UIv0B$h$CaF|5YEE14gJmwYGKsbsw zv*UuGUlRH+9fU7={`}%>X$)g9;*b^pOG%ssVFZZ+PVn7fmPBcm5d8857QV)kGnMlt zv#eYxBf8kw+=1KH4Go3wY#%^6>a}VZL=aT2aXx~p;a91VABqF9u*znw+0b=sl=1^K zx^BbyWMkk1`27%SQEAqN+(W{oL2nR+A?E|ee4=lcffUhw5(gS|u)ANb)+|fo>=KTl zQmz8mYqh#5td$>u1PD~%PLLlED~cl6&tZ1}3sG;>tF`Lh_TJDPhQmSV`I@P#x=991 z#>k9@>!u1kv9;O4U5!RfRiM{0pLYSGr3`+cSb-mX>07VBEU-zSZvKxO_qou5WI4Ye zuX#$o&(o%i5kE{9B!(HnA+O)sZXYxE?Ty(%5N1UN>O)@0w1)58@=MGQ^?*V6Up-8|8i7NyuiothJBsB=!EDm(DEUTJ7>;p}z8dn|zy&#Hc66}@d z`T&%Ysv`(_{`IRIL%l^$@K*Gc{bicyv^dVYFX$t3ik+ZGPNr{X0^qQLg<%LF+rl0Q zB0?#kA%@1KhH#Ec`LHl>2GIfYbFmHRBh*1bkJ%X1M}^O8#TB@Dkm$&KZLb^LeSB`e z>z_F^JMf@U(rncT6W|p-$`Z{&TWDi&Ga8EwC=2#I_e0FU)(Y4QsIG7bngcK7G!n@f z+?BUH?<)I(GbZzYn56AtxOw1y?ddmK2c5S+bPt#T4dEwkIAEw&$_{#rA}fyh#FNk5 zd-m=Vcigpg;~MIUfK2Uj6a=(>#PVRC;(5bfr>pUSi_z_NalEp;Sglu`Y8mBdVv+Kp z=jys1h9T@Hyd4D2FvK!GlTymB*~g$LU|A6OX#e)sJ`BR<=0V`cAS~dgW$2X(U$8{0 zG+l$soSmDUnVCUs(pZ>RbdLHq8#9$^)v#p*xSP@Am*Par?&gwUw1ct1<(W2BM9#H|-F-jqY0p>QF@I?nX;{n3bw8DTYst)rK9yAP}S11jJUJp2VB|Z^s;kx$D>pWJI?Y%4Wt}5wo34d zCukUW3RZ%LaPvs};^WUp!|v&mM_+mAjk`}Tz5VRjWbHs%sCc8e)~L+R&m+?D`UAw` zm3n!$HkZC}6CpMDy>@x)_g=W_B@D&B=g9mIKYG`9-+%nx6U#I6P0chu{k50a46*&n zd(R?Xq9Fn&2{9CsLZ~I_!C?f^e>4yONEczIgsGvx0qmC$bqHDD)K6X%H;6t#0(l7v z4$%-3b=fTrZ=f!rj+9yCMOjFpba045RDoo0lzf@i2X_Rlniq~F{6cwWN@J<#uBk@F zQsX3nS%FU#3-HK*>2VlC&BlP{=;L^-8V1}0LVeLg1etKRh*;Pje)m!3{7|%Dd$)yZ z4Nb9a%PnXT01z|nG8&r32{a5Mk1iJZDP~!>OzMQt4nhtY z-X*p85hw{UuWnlCWfHn^6rx@IP8)6J00@IMIgPVP8v1U~>-23yE6Nfe1?obeV!{BG zZ4d+5;xH^JoH?_*b6{G0*y9CZv5+CkLTgo31sPzYV6Omwd@qW_6jVEL=2Q`si30Kd z*rapN)^zwXHBAuO1dl)a2JHzqG@k$RBO#*XZEj}-?FEO$ah_k8dT~i4BL$~Z?&bM! z-5|2xPMT)R%NOZ95f{Y~H{LK!5Q>wh;UF(Z>~3eqCdw0cuvn1~N%^U`L0l5IOm7pu zmd*?8qz~jl*)kry=Y*jvpd?V>D2ZXQK|NV2L_wIusb=Vr&lQmQE(+~h5y}PYikm@G z_%+xVUF8%77(>f0ODbd!o+AY|_l85)^>l_(Tm>$PeVQE*pO0gTQ2!F6Xz z=y|SFX~rSIW@#*Nm!$7T=?fl@P7tLH|i zmTLzCAH;>a%-3x{N;$(pGzR~{92yT=9rP2$k2w*Qs)d>fZ$NIiLu@q2>>C9dEfi;L z`R(?Ip0R74LGbiT7q++e9)I!TyC1m&qJW|kph(C9+*CT-}NJHu99+f;2n>Z;?c!K+&NA2i68L1+8-tqRpo;K*7i)T6Lxt3h%i^!R{FUS7YMNwo)$+9nfx8e;(E^IpEn|^$5dUdB z71SmKfeoYA5iX76(syc=D>quRO~*8}kty|(Q0a%Tk_EsDtpON>zzcd@sW(oq&H!az zy}5C0c@ESBj}R&R-XH$iX3M?*u4S#NzZUfFHY)xAx|j_8Xz5Vn`9YUvUWl~lG&j^s zvY-X%HJu+ESWW>!8qs339tWfey({NGpm$6amvB6|;mZbYBX(WT4YO227=(Zn1pp?l zd;X{}IcArEFS`8@Rt62Yw&&wsmYY*LC)2mvBevU(q){LhTM37j%=q%}uUyf0rH2y<2Hj6(wtyqt^ z#4#82M>WlnRJu6q*D4icK&ZnuLC4@|p?!Qiw8$9>{5%jpJOLXdjZ#$us^$x2hQ%+M zAPR*|O4HHS&VHxUSESOhV}}$)>vy`AZIo+G&e$jw;Cob+Yd68jpb;>q2y!Xc#gSM? z5YwcPK_G_?`$V0>aQV9lG4_4&pQg1!405;?a*JjbWLrvZL!zw4&Sh`Um?% zt_l<;NjkqUPjaG1Fqx7J$Ao^Nsi+b~HfLu|%j)+BaS*{sAVBhc0C=JzSS+BTj2~Dj z0>R*XW?`;UtNOzMXqS6Ppbdx_Fl%v~sk#Xd_w{F9hk*sZ$f}$HWD6Sr(v$uISdb5b zC!l{W40*O`Ztj_w=g$wsJyT6$z$4CYrBgo0U`kjn8$9NZ$RMoOH0QK5afLhNzttY* z74npqaBIu6ViRY$4rnlKHmk$0s9j{E@bV~6soLhLqbuVib}BY*;&Z8Vr0~l`e91YE zBi{=M?;ydFhH~iMVAeSUj1vjr-q2J!I}RxG?N$mW7e7o5(+<1=i0?QS9K=Bjh;w2) z_GH9QPFa>&L@q!a`fCuJ?|DnIW>?B^L+y48lBsd6HkU5Luwaud+j9HeUU#VJ3IInG zjgYzD@4j|%opqxo!JO$2aqx6QHw6uNBrZG)T3wE%Gow91MGn8aQ=@ zak;Sd9MwY>P(cuaV?Inl+JqDVmBlTvAp&rKR3#2&>Z%NwfBMkOY`yI8MPGGg#)0kv zmf7&Hrq(R0P|n4Mz26TXy5sQLZvXInW#I9hG|(2vPuR_=z@ce4P;vlyYvVWvH`EA} zhFs_*{RD&ZyqAJZ+zX0`&KJS>_8@>7UA?h&^~TOUr&mHhGHt%xuPFTDKoVya+eBRW z=b!u9$A92^=T;9L?C!*I+;6w9UAhdVR29v#ZS>gzXe~|J2fec6%*-~xWR94n5vm__ zdWemBy{@iluIH|=Z@==!m4kK?EmKK&x37A()%#%oKyR9_H*}l?|p0cd%gE8Xv_>iV1NNYfaHiEK?Dd8 zqPa+-Y1x!bM>xV^`yW{fM<^_qA=?rqOCrrBXiFd=5CaS_=-H>I_x1Jbw=eg;yWg|c zslDnP|9)Sd`?}%0=T>E9ewq2@mtTHMR#sI})NS+f<;}M~xU+jaqT*I+2{cmfAN)V2 zU_O)J;88VsNM%{oz!~s30t`o7!)AFxW{@9MUBA2g_y6YC&1!kvpBxmMfG4aPBsoA&1|l-eZISr zaw(|bYsl;M2XEgyJPCRo9|Qki{p;_(bmbBRG+HEQAz3E@K!hn5fRX|8QR z0N|f6{0Fw=FqFfTVF#rzB`kL&pitHAnZr+}n#%`#F1oHE5(ZH-icI`GV^~ z;s;Z{LlcD_$~F!`La2r2gpCo9%XV3_A}0rxcnSa{1+)TczI?9!7k}X;%;faZprTit zm(B<2w7ar#a`5>0XgD56%gdcBm##%Wyiug(n;$*=-CK|05M!eK#?u=wzkCG^V%(MK zM*>vl|M{!$A*RCr=4W5Rz<~^iGfxdAFMtCJQ6|8kl0oD+iue*KM3hBsLnU&jA;D$3 zbFQ*mgqQ9RMwOTP`PdRIU#^TH4Y_>3H1ko0W+074WYM%h>uSizNchSA37CRXukdYU zn9g@9l}@XTxgU&%3BMBySx*U<^V|JJZC;CmAqsPyg9QtNuZ&Cj{l1`Eod(lM5Tbgx zr0u4EZgU;RL4{8k^9~1ZkXLJvoRlrq3cmxJ=U^|(;lr)-IiE4%78c$SgY3w{@d;H6 z+NY6V@&=D-!1vG$Cr!hGPt=2;B}5fe*swo@rcdJp^F`BbfWeK1S>rg)^6CoabI#X1 zSiCFBk5Zy8ST=mMf4$B3YIDIpE85F#NCDl@pr#tW55ZJ#cVL;6X%14twe+Dfamig} z)OMf&xC<6oz16Udc@$>oc{rW^>1!Wi2rx(-VREG~B>9Hd93C~o90y)-xFo`nkR%iY zTL=vEaO8+9AOS#J>RcpJBJnPRG$>qxn$F-QVAMEaRg?_@iZD_Y64@|oBY?yudR9Lw z#!Oe$12I8Cvd$s`#vTbfGm(-(DuiNv?y2o|i+ecWCBwc%fG*IgGgzEiUL~a&BNt7A2ucmyQun;! zaP;Bros93U4sev41bE^WT z*1TfM7oN^4rz)c8!4-rpW&|V(A0(q`q6z{M%`#5f&R}8GU9;D4x=p9ma-bqH8(qtg zN5tV7fEOw_d6xau_7bdXc&ARWqoYIEPb;x=hJv7YqbL*>H8%Mi4kvxV9 zQts{TPq^s}f#!m1oAqY>{MNaR^_8vj+r5=84AS0G$8veoDv6U2Lp=?Kr^ko85Bf*P z(R2bii!Nw}>DQYuF}TB=t7m-P5X@mY4k!a_LwZDM3dsRXxS6z4SzccYr;~sFyMMa5 z+C}}xCqvlRP0xAjqdRZj*ug*nhVxKObTw6kK8kuUD3Jqd0}eSz6mvw$0+~@la1@h# z#)Z5D_~ReE`&)ncYdibLkbMsy?p(Tj!E)-T?X@5NabZ@n4kkGpP@pZi;#$4Qhdm&p4Sg~R9dkYy#sFw9H6y3I*xQDC2m_J! zil*7Fv%bCgg{L+@_~ag3v;W6G`5%GpD=)0Km+K#egHFxtq}3?O;3LLKwz}53nM|NU z>3K@;NFOROVn;MY{!oPhVQ$o*$WwP!xZuXXU0Gx{6cbKF4IIo$b@vCS?TcQSfs?CG z@v{Yd4J67WX;EsG#|03soyUAk`NFy&6iF*v^VMGgavJD>m%-m{p`WTk^aAu3U#hkpz) zPbh@5U@wvhvsJ8`1z(~#^N10mnsA}G?E_>gvNB0CP1jOrX}GYf;RSJ^W_Nw)xJU={SJm znHOm=nM?-bSyjidnY?d|DaakHag650)hu#uNYZPtY%mW-gE9QrZl~$jT^P{QX;$H# zmj*xMP{I`{FutG$xDx^%UFULImT*c``L+VXc8`ybVe7&uHw?YeY}&f+damg>AWIlT z@SYIi!E1a;P8e|%n3gWYzE_IqOT)p7%n@Tzdo@OUG9F`g1`z0F4^BS(-0JB1I0AJU& zK|gqy_*0F|C==iz!#)8M7Bp3B{BT6Q>A@I{!eG!JfjNB7f_(i#upo=Hm`sMY*BB1COyxWJQm+@mt3kbKXgW6RS3aU;>8%z&8MgQM@Pk{s)CHtJ z*%oKUcqv_`Kqiq%zy)mZ~Nj3t`jV8~)Tje;l+!l+sI zxc2}Pg*H9|G&{JNF;qcU4?kJP9S=y5|_Zy6gKEW<{q3m1kR~={iok)q<19 zmvM2gHRcRI8H5?N+4Z18&Mh~tKRW%w(`yHV=;_VQqvHuDSUJD2)|tk9+av1du*$ds z6LbZJ2reK$=!=W1fiA*Q6Q(1|fLRcWaTXMs@V_6#re3>qZ};WrFQ-K%j1nj^`dzHC zg0Xz}?RUTPo$vp|Pkiy(OD`Wk-bs?g^F7;k&{zf91C`E7_(r3_5WRMN52ncS%Ccjd z-KAw1urM*>FdR<;3?4sk-Qe9av>Q(Y$WY8@{2-IKtHZFuX$-7fSUMmO5J!*XfowI4 z!9bv;689N_>2k-qfniJ1+x?uF2CKiwzY9-V>z6LJ9~#=oWQpI!ykSWgvmG( zlZ9QUSqe%Zq5>#KOG6?uniK(Q7BTURv4{u;S|~_DF2saPV_sGN^tJcD_T?8py1CP7 z_?=GI_v^Df|NJYz@c8Ci;3tUSeP}x2U{ML8k6E*-p$1b`4KDkRBI^WYs(k!* zvgoB&@APESX~W-wv2P59;o5qqYU+1J!Lw_ZK{ilS?k;DnDlZ+^+um4xxVwMr?xUra z{ki8iBXi!*lb5}2GEL(+5953sBeV zo}Km8)!;{WC&TD}eDCgCM?)a&Rx5w!3zxUfbx|Xn9n=UJhx*}dammrp{%_y;Gbc5tt)zf+tN^jyn_ZkH~Vn`OM`_n1FC6;9-N(a1k}b#&r~@ zt+rQha{C>)mqW^@L1u7`=CdLULesV(d*_u5V+tOG3Lu{p5M`A{-4}B$gt231du?A2;D#j6i}ll z^rN-FN)TtN$Vvc8T*(JdBSiG#5h@ML`fwqhvgpHtW9Vcs|=>fbaPDmSSTy z0JYOeczfsg*2805pIhy^E{qO7Fv)L)_^wxXG_&ejb{#{)M^UPE$EthwrOkHBcVKNe z@-h!p^}^*f%pZ8Dpv6kdgL)8cDBYvL;$0PqGX(__hHI6E4IZ@zcy;qJlH7dJ3R(d(W46X0xHhVMEU%h!JJ`X7GdkAMBQfAhjq&)oRn zyD5_-J7ik_aA`0u3X&EX8HSf4`qB6MG-jeLN_&GINsRipc1o8Y&QI zOek&P&Olzw;qfq9-(H#P+Rb46!s;c=GS~Is z5*WG(7O3e)7)~Ik|H;4leX!8azOvnDxgSP@!#urO@#8py(42%BM#$~Dx1x!35qJnq zI92!$lG4P4>LS^YD@a3afn;(ivl21VlQEK-b?`ImGB@Ee9=aUM z(|2v#HFjq)hVvW@*_1ZU=_od8)q6)=t7o+&i#kbbLVsygB?|&n!U~R1g3bKlVQAYG zN7uSpt^3^iD2}&biHcj1<)!`Z;cy)N(|7LdL@CO34gGI@?$Ua%iK#e;tg}#4jqWk} z0pg=G`RpJ4-~(1H;UE0W%g6%R*-w~BU{OGER5^x3g93{PlV&szNU{nyP@ov^jxDAz zGGvrl3VD2AqSz~t6msW;1=&GPgtKu`)YjP|pTkEYEFP9;4KPi>uxOpv^9pZ>OCek! zA5|esEZ(4)J)TuI&#iPjt)*TUAg~NzZfJZ`tRRQ;>#Uh>^NUur9_AX{63{M*Lb#GJ z5nz^4f9HG!7KEF4qFgbi+f#uTpuyfXO-e-w7IcA966BoExD!w_;Eo_2R*3C+rU?ZP zI&gK9t#MJgV!PgS8o(*!D_p}##OGA`aRV4JZrz7$7{ilD3cux|L0Jiz&4GH!RT!P` z>M(5AO5y~j3EUMXLIv4wp2Z-9?IMEC@<~0A3i8{6ZIn#aw&}ct4P?14_ji?;y0apV zlB2_8P||UoG)pJL!K_Fu#~TjE-+%LCPSq#6nIpu@v!4WzevzdZm?QEe_X$f&A_HRT zVllZSf#p>tPe9d4(%Ix0^9!^DX-MWFrzfw^4m=B(AWyKfOcszVsaJxk5B~%eY$Zkh zDF(nTtRJmxuP;4!?V@RFzPviY^{l+CKv)%d0jr|XYy+1$?hcKmTN=u+Fl)+8g?o)FS#PmmzI0*61dzR#x>Jx`TB=99>R(S z(ZGaiOoa|=;z_4JBZ5?-P=mv=0GA*+3TO^aV@#BeB+3b$2RzD98c^~}L_k;>35q!H zg@V^7!4#GU%n>;8h);r`(`*1gaD0^JV|dj;6vX+x$HQLT_Tm2Xy)!l%z#K!&lNa*hC79RoH8c-%Dfaz1!oF?a2n?anZeqo5v z3tguVgw}N^7EV~E;0V$~OCd*@B{(PwqBn}q@E;}lD2V%~gZFPd_`>s-+RgejNY>Vt z*VdN!0`h9bwe)Yl`rY6E{cry5|KRWJKYWOZ+UvFs9z95+m`(;iWf=wIaWtJ4S(;8m z3tkS8)-}(s)66t2iZJ=aUy{UWl2GK&3b;WeDf{af@_4=DIgs*jn80?rX+RE?WkLIi z(xd>(fmzELKfi*$QwK{14*)YP00S9_(;s~2H3&L%_>+6PpFBFAgeh5pfao!)q|8lJ z0uC?a^+ZTlV~t!yASIbk%T)Y$;S3hyzpx9>l;J`}VGPkr0$l(A|MW>jK~w-#s=x6U zzVhhtVXdk?ym#-ZD;Jxcm4Es6A8eW4Xf$;?&T)}ZG_owfr2x9OxKcua2jE9Hlwty| z8@B}j3ZfjQiEY-lo89-`e*i(z>$Fae$IY%^ShXn4o?5w*ihqWYg^h|jxy)Uy{3pNj z-=T?%>;AJ(Z{$wxt#SWze!FRyr^l0{e%NW+=P#_hpALs5-;)d6&>@mWJa6fy;6Vcj z`i!GN?ZymQ0sxj6T8@!|X8|fmW>+h`fzBH2jyc_nt9I=|*H6Oybex1yYTJ5d*Y?t+ ztX9^+{y86of3P6r;|I%24d1b9`_T_?@7)X{jNf_B`K=c(b=oe<&p>x5>6|-$*{cGo7d(fa z%>L02d8=N9|LChP(xlLIwdYN?M{qnzD$qo*BaslG{`qzuvDb(`*opIyniU-cM}mkf zOxoha?xG-cpo|16;)AoSil!F%NXD z;logv&G;Q!ZkD1~Uzx`VzX*-lN0kiS>^o>wzG@W9vq_*XrHf{07<0GIm;I3nVikcN zhDj2j5eT|DZZC z5I#LHks^iHtctJ_muyvB4xhzG;1~5vGNQ;QCJGE0yk;d#RpM&_$r`mR_m1mtZ}D z#$aw>0_zrbul>* zKpa^R1TcU?0$~9|Ajt-Q!;1o~NFl=Th%0c65SyX~gk-)X!qwqD9hF5|b29{TSyB>7 z>v?4qOo2B{5E$h6|H=y&xN$l!?>_Fo@!^9k3740ez!*k4j10_v*RnBxZF!ZRVH=oz z%S+3)rN?RZM?b!O?OgXKUbwWf+=HEA!DFpeJ;!itV|%sb*m}oz`jd2{*W~ReNErI~ zylbo6PMCE#vMD+k#F%2TcM#yFh)*`>fSDh7b;y1T9xW?7(lo{XnY08K1A(YdM z!v#?eBZGw1s+iB<01ODMN=V`-*hZk*bb_Wa@lhNsSU}Psz_=am?W5aQpSrMjaP;2oeGaP(l{%qQ2uK92Fk>)sVpWLx1U4K^ zNFfBk5Ew@mAd0;CBE?Y|!SK&2i4d=m`1%KTu3cDz0k^SShtzEKI^X^1`zwY68?L?7 zxE}>zOi>;j6e;_eUF*1_l`)4yN$IBR93hMxSsfIF8mh%wx$QRWVm_K+?iDbry34JT zS^n;H@N(x8lm>u#HyupNWx?wI@jv^=m}qs^`1zlD%5T^oN5e^)e5tcGJ)I&yj5F6U zt1I@+V4Pw+=rjWvnN-kSLTC0AL=pu@+<_?adEZNDrC5kL(lZGl9TQm!>H`=N|6nIx z@$988+N(OIR(Gu|D=kf%Lfg&fzMgJ4QF$qY_Ga0S3Is*FI? zkY4085?0I7Q1&R`|mJz!oYYL&F%pUC!q%xzb*5`bomqb~&yGy9FcWwHlCw zpf}tPG{N`r#5sHum?>zQYc_CAY_5i4?qqoZqol}kAeg4vG=PQ^kyovv>u4m6BJ>P# z(vSiIy)$6K0ITD12=i(@9>0D40ckA^iUWp`W5L7Xh#<5VUO^(qiYd$>)BG7o36d&7 zU3})q2*;4Ivp@cXk}|vkB>-}ip~3JmjXw^v6q2A~fK>S43SA)MeI`p?pn62XImnbW z;V}MF4dW!os%N_wHkaG2I+S{uik(ES9Y~_HLOa1&N=I3~dpz=O^R0KU zzx3SY-N$G#3%ZNW5v%G*;lqD(rAxH;So6n*++}l5Fwd$|E^>LUKyGM(+XCgcq zUj#8JU;}}GLSnTmThUO)9xMW;QDz*i6JdN(j3Z7k!h?T6A5|^W8N0o6^!cmjf#kiN z$LH2M!&|pnZErM~t}Zn{iN#52Vn}LmP6FPS{O2H?K4--}$MZxbT2Gj?&MqUxV=qWQ&5Yn?pta@qhkb14+pLpMT{7+{DzX>?h$% zZYSyU_S9e;4Mx${R_Bd$FyfvV5$(i}y&!up2&P0C(Bq}>6|RUL6sf|N90cT53Sn1bZ-!*~>AnKs|>9oV1^--5G>*|lO?xn3!?YAYwB+`z$ zTm+eqDk@yhKh*rVZA^M~);yL^5L{8{9Mb(H;R|1>FQo3{PjxgLuG z97bI^=R{&*Mslkd5o2vZ%-b zPWTPzVClf3>NbGAYjmB_O!!%21fAB><8A!m-=*X0Zast`B z2H#0KI62`f4du;d=rkXx)wnqfng)o%C!S7WPt4#j%S$awK0wHQicT~ckKrj!r*OVa zw+?rRJkISMMUsF=;7@xnQecePt_yvMksS>uu*FtamPg~sn;+g2OC96IL17SHpvs}R zU>yIzlfxi+oJR{#1m;UvNFa5Ah^q6+I3 z&|`Q~5Jy6urHhnQQD%^Zk%$9Fn^FO}I9$EBwY9N?akt@AE5mPAwcI9rYTfUe0^gTh9#WOMzaa^7zAM)PLm`)JUoh{6!0+kbiEc# zBQQ;?-9e9sCkLa`0o2F2?Ts`F^E`ppnT$>xzm-hGy@S*7csdz!6&~C*4vSgAS3y4B zKZbn(6|lD4?sVH)RfFwvdUE>aNB4jT`lKX0$*bstE-Io3k$J#L=#OOK8PWvMGJys7 zEO6qTQB+mS060~(D#;4fEC=f>iW3%OL=1?kDgZ9&j4MFYBH&D6Au0Ofj~kVPP+s{4#Pu#|J@H=$2zyZdVlBW zhacRdqfhd)uQC|4H_=Vb1sbH(=pYjVhmJEG6$&Id0n`BuOUhu&h_WCcQB2WK2)+mc zS^ZnT_O(0rcN|;4{_)+i5_ejdOPOt%H?oPU0!JmJWRkKV4rN}_&edpNaO6NVRi#u) zEtAa?t|>NmB@N8pO715+DIV(V4$e&m@bl30uzZbX^q*!Owxlm z5}G%;NJXkNq(Y6NB0AKhu-Vq9d&y{+K6}1{vS7x#XE}TUm=+kNRnxo_F0!_>&o??e zI*xg(g6dkPk|MJNxfIedSx}InQBmS8)02qJD}zzan^3xKxSHRwd}ugBA5J2(U%YEc zR*W%?b_#brBThboLdBVb4U2&f4QmyCUJ0aR23L~+PtyQhQ!K&lK1!q`o!wcof)%5ryBbGh)q>VP#SD^LLeUB`zs z0x2l=!!f_;=6byKQ`6yemhw~)E;YW{0D6RXbMw3$Po{@Q$Cx|ydIQ9SSp=&Y(+frk z9Lln+faBmD@UBR`1wI8zz@M+W<4QFEt9;CatDaFD1@J%6Lzk*WFG@ZR17)wn&Zt%! z&AM)yP+G7U;Fhq%wQ3l|7%Iy&PL7VJVT2KIJv%SwVHoj)xV8h#AqC;N0c-fXh_Dy4 zN*qnc<4FyI!_fIU^CaPWm>FtO5G6V9QTojWFoYoDgaMW4f_v>w8}uFYM{j@p0Mtb< zKv@!#BZh(HKQ3uUkp~b2O7akYl8ApmNStLD7Z6n^j=11)kw`F3@h1s5E&d5F_3;V- zDw{{Ce*q*X6_eR{mb1WzL6t?o=?Th&S9k(?k$HAFQ@3!+e*Pz)Yt}u8CT{lR*Z2xh z9Yo`4rD1e2a9cNteA$e z)N52`d78v)tIKtH*WB`Q2f`RWbsDEPZa#Sb<|DBLR3IgBd?JX*HjFbOS)>pD#UxO~ zVhSM{K`A8DT_q?TjRiC&G$H^I`6v>xn-+iu&pj)z>jWLucQ@L<>_?#D(7 zI)(m&G*q!9hp$R?vPFadYU$H$U5io#&pAB+Onx>3~c4tFqxe2dcl+|`%%quqJT*T>x|;Clf^ zkV5lP1T`rBnU`#Z8d|UuDEuI@s1T6!$-muSd%k1o)fc*J{nN4U+jn<|DVM~lXTJOT zyWjco`#*Z~-5GOd)XIy`Z^C)Km5y)6(`TKg7D0duH*%%Fwzl5>Fdh?s zRDnOF^GCc85ezxIc!`Lzu@EDDKmk=kvgqmKEI%qbSZPq9fMtEifBb_!&VYx3I)mC$ zZ@M#Ux2xf}ELY%zin}bIzSRC9gI)_N*TdnvyWLd`$44}AZTZ^z^B8&q;8N|UQAc!eh7Bvul zE}z;#6_||N{Qy{4CdgucMaJ-l5@ZQk6(Nbhf}j)2!**wc)oT=xEChN%2qD6aoD$;& zn>!vy5AxEe%xVUd_zX%u&lWy;9!;w?Ba>%LlXwEN-f>+R7bpg60~(?%p-XB|??Q5@ zjf|h^4JObyK5aS^J)>zuE}zW{?!Rj^>XuQ9pw6?>w2XKfrE!P`;E|w3Oe+WqPy=ep zrh@NsbA=8E%;I|ixFxJM9h{a~02=gQfP17C?`%xJ4mdCtOZ+O6v&q5R>ETMYc zkF!}EPMWO-YEIG=bJuZfnAu4bVdBrYi!n1TuV&QzMqM-bumf5gjYq+77l z1%Gy2s8R-2t&*9Jq5sn7oVL-3h zh{K$PV^;Ug>39Ucy4~xcFGW#sJAv&!-raM3Z|nTF?>DEx6zt*En@|vsAMfWGzuE!g z)8qSHW`3gu(Q@nN0~iq0ewEtl<~CXxk48X~@1?9((~2LA+*2tia^QVUvAKmxC7Mi6j-v0;%3=;{>4$2&J3+<16$ z(4RmxX7D}d^A|3yz`}m&(iW_ZBF9YAVW$HxC|8&ou9=E6oBrXoxM zv!-t>HJ006!!`Sph!PEG6CG+x{6ooN3Zo%~0OlTg3jm=eg6cq}KaTfKCR;15yASqX zxOOp^L;zT7H^!65v#egL{@w5V@Y_Fn=huGYw@wZoLv(;S;6qe&_yKu7tC_Y1^9US3 z3y!x`=t5>0U*$C%jvU7+bGSb9$#?|c2(A>S?qoE9b&;g}3O4xT#?8Ao?>zkA#{EZ+ z_eY}uXu*K-Jb!(4d3$T)%BAzqT)Wa->iC{BI2~g0gB0lJXf$+8YdVc?Jvkqj~2v|6& zQYX0saEy>I(FR8R#~&%%i^cnt3||Z_hFR6@q=$~ta!JA5%eVKqR7L3Aci1HO`<%IO6pSZBz=gz zpvOOGm$O@uL`lj?D;yxSi&CinjC2&7EOj&qJ{gVEIIcJ91?()Y;+W70m7=JX zsaguqET_o$fCj{cW!YgcM!Qh+tY%isG88e2BmqjDZnM*F`>v5>uo?}_Q@IgMv=N8t zqH9JK)zMqh+MRBhac=|!s%e??GB-GLD_NSurx=YU(+O-BE}smBqd19e$A_u0e{zE1 z2r+i0e4uJxfue$0;(8ti&M>$GT^wZ%#tnBYrupD>01F=44gCXMVd%iJg!jT{XvN-z zGOVD(7=38PVn#`7*}-_k`7^7_%e!!hQOW6K}1&KQ$mND-!GAiSIv{33;*GSm!&Cy1&$w8_V zFC>K}2_jCBWJ&c*T)+@o!7buA14Qs#q&z91{@J$hsp^aBB>3c|YZt*?jiwK|n&+|C zXkaGUd}B4=--QA4To<#;vg^Pb8UYTpZ94#WK#0GZPJZLph%0DqzU6w5 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%0D(i31PXkPp>be&@Qw%) zX``*;LUGGoTC4FG(|-zOH^p2Y#g#2Lx(F3Dr?#q?%bV3|e>@#dW7uiXu3=1%A8Z!_ zg-%u@K^!NMH(v3UB^$)U@#X00>+@hbF4|_fRg3h&voFW}i^Y7BM3_zz z$cLt>fxnoJCub*@uV0@&dvW;itCvrnAHIBj-s?}ggHar@-(_bl%jIWux#ousm`wcD zs?ey_-o1Z&cYEhs-+KR(Z-4NuPd@(O!@K)8cPq7$uB^ipGvYD~7K?BhheJh~8u_7} zyK?f~QvST847`HsOy`Da7hoM7uae(PmR&1nDN4PV+piXGG}gP@185~xFF0oJ@+_Rs z>$O_3RGg2-U;O5)N=cveuco8^-mSYJk{W3~bIVoTvQ*uQ0!SzZT0SkPY2LsXbu0Bo zXS>>L?(A<>TH9u+;S>xv7r>M*^hSk~?()&a^6+x?@Rc&3YW~dis(W|d`Oe2T@9$V= z7sK(4A1y-Hi-w`#i3H8A zI$Y?Q(yo_Eu2rqL5Y;e_k|gEBe92O`^fcjiXXh6K2$f-|VU(DvR&UqQxp~bjXog$# z(r_LG6h+h*r-e9-i=LH-UzM(^^&0wA=a-^&SQXr8K~o&tb6ls{X&8DTSoo)xSEv!z zv+Fq3k_#;{S%gZyP^^}gNenYCPM7I&3HxR^n-9kkL>E?35GTGLcA6!0O_D61zBz~d zO9MHIso6x+A!H1cC<2*~8DekwiE)=Pj{{Rsu%H-!1j_tBDJl7g;JDFw8@{tIaLyvo zgEDRrVYu!~1SHB>+5|ul9;pnQG+GFxUTR<};{q#nGl_={IMcJ7O)6W#sSLJ~)WQKA3qASukarU7Xg{Vk28Nata$cWe~kGa zE}dS;6)hd}Is~Ow^c)i<@^D?d{fTSaimIJn4u@0UGl(l(AZqkph*Ix(B@#pYKe(X3c|D_Fqb_xJtm07gwB>ExXRDl zn_Opxl|w=aDt89}NGe5H34TuhB0)S8N-7lsG^YXz1z}&$_Tp`2YvY3J-)@Qo|;rt}M<7QvL^j_$iR^=}ui&;TF;C(-lQcqR=q( zof|j5b*J@j{^qkk{p-*7JEeNP3RvHOuxoT@~%Z zXr2Vg5Cqwz zlep1n?QPW?MHAX;>T^eQv?Pd)y?+m6vs1#N& zj;;<*FRMilGV<`Zzi}+J+GuP1o>^Wq_>FtY9jsT(CE*-i{%mmBzdSm7^<>;T3FpJ< zcsRJcfRAbD8i3)>o}ZjPd-C-2-+b|_Up@TwZyta3ALa#`X=qBh=wRTZfsj&o zC}lhVpl@!H=iiLjy zgg}|(P!>ITeR*Agfw<&|1Eo2icTA&X8>4x!)hOQFZFC0<$D}Swl2xbOf|iD>pet&c za?iWvGHui=@ZjLd-Mn+4D7o=)IGZgnkaDZgFfEwB+}h8!U;^?1z7oH&W7|4qI9*}- zP!$bGfQ{ef3jH8Vr{i%Pr@i6W)Rp(%f1n%OVjm(`sg%o(Rjd`E2728AE}*^9?W(GE z`!kA7xa7N9^Et+a>sZ~v?5sO{bJl~^lC?N%Ae$=TZCsAzPsT)vZ`jC)B=LfQBawcq zSQ|u}!DSICCO;Noka=`-h3W-%la-}P+FNi$7_z9O{CgqG{JKtFZXg-o)F5czf^9H< z^C&@JG7y$~o0FZc1?w&NrYsLaaW+3m%BCLvLz=SG>wwHnMT8TWEH20EgbV}$jHL{N z!|xunfBfy+P(ZWEa5@?Gdp#6zog!uqO_thJT}u=0We)M?9JyL~mN}fxZ9^N+0${Dc zSoZ9AnZl}oqANiKdnS-z64gv4O;<-}-I*WnwW=t-2xG_8>lMebv=R(s8y*=SI{&M` z`nfz91WSSoS{O(ihin!wF#^&zb3rZ_$9(FEeJ-Mg$TE#LETKcN=C@#$s|kiZ!-g?( zWDo`cl5wt_;Fs0&4H#Z+vNi|&bJMAoruZw%)IC#s63uC^kPeJQ9%UK^q^!(w7GqgV zd3^R0YFAo8lY@&Ku%fugk5arPkBKe&z8f;(V6mXw;1|&*Hu4kJ1RtnGaZaPGij0Eo zf#R66u0@GuX^3#eF1w`20s}&(;9zZ__&av#^v%%ccY{~A_v@Bzd=-rV_`X}6j24(p zU!P4;HNQ$PXax~_f#N6E_!nb?HV3f&y-y!t$}E>FNdm)^4;%W6#R_^0^|Ot+O1-() zsawkOU;gV~-@n;3Ep80O<*R%imgk^902mAw(Eo3L@fEyoR9knI_DY|6tFxcijY0@tu2Qy9G+ixiUDbnCaGp91J6F^e)2LLk=d)RutYIDv z=G^;B@FUP3q=Ybm*IB?7I0`5siO)Mz+(i;5GykiXXT#n=UB~5e$t$^zYX^QX^`qHr zy0_gv*sgnql5M6(RNTgwo0Fa&9h>X0SaiA6q$-$S@7+1rs(9nc96mw>)1&8)9)I?$?#Y|!)!B61i-I}aPEH=n)wnk~JwAQ&=Jff? zHxIvh{@X8}e)ZLh7cY-`-BA?9mSMOqzrtvmeEo>uqlT->?R1r`M*YTK`;!mf`JEqq z@cmEU`}F%CfBWNmx9;9(Z*SY4$E`r}`6TZx*HLbr=&Q?W-K`sEbvIXY)dU`fMYHuX zNaM&Zaz$quMYdav7xU?O@XMcmrWInt(igMf_Jf0qv+nCxCxhO&v)#P+(MP3Pt+sto zsWfx14wcDL>CJPG`m58~`Xb03&sRs|_33cco#!w7)#)%ljp=@9E7)0FiqEVE3^N5o zZmnSWd+k+kwtCa8mW+G1Z{JDcU>07AW`z_&{2(3(7%0%OSpspb;Mpk;Zm6-MJlYrENLH*2+e7_SEX;WA15g^wA=fRrkQ@o;3BYOz#=@zQLR zP1Ar5uT|W3!}G%xM5|tM@;T5PJ$ZG0)t`__Y+^Q=8JMuS*rec!jFYG^4e0{Uf|7Mz z7H1k9$e=P7E@rq8rD2Avjj~{%j>racc2|P18+DIM>TIbDBxd*lkf|762t@=%@vn*I zAG-Eycp;9LZ(AU$3CyL=6(x+ak|QC57w{|@@Q7si1%f!Ucy=wafonqJB&wXnnI?Z( z(?qobC@Tvj0H?ajE)gdlK>?Y~qFH*@Kmo^r_l&{Y!Fcxl4{p9b=|X!Z%LGQQZF`ni z60Dxj)F1vo6 zgYz9md;w1yh8dUB1g8`kl7M56=ksy0c|V1%74~KqD2OOIi68*vP9{H$3eOQEJUf5| zMVww-O#k@Xd)1nY8exOISn@4C%B{?}sGbEW6tp9dfaZYfd~hmRE!iBBU#fYmWiG@DpXQa7y-(ghxJ%9bWl zLXm_FN(C+{+dGakJehRr&fJeQCEwbu{CwQ^6m2`zxC6&m7o&NIva$n5HWnCIFH~rO zdy=A!NXV3$sw&^Qznvt@Mx&1T2~9{scqS>#8A7TWM_(alIaaID*s0q8xBv1d!F<{% zd!VmSP%YbrecSI30Ut!sFFtz-oXBdHm3QCYYPHHQPX~M+nAhlGK%04oiQOsmlTlplfRMmY<0r&u(||)13UHoG;xL1G5wF*LRTP|u)bSA+ z+9JiRMp{^o$8!|N4}~mMi6^Ugv{u$}al1N=16@`B_Hp;+#RBb}EGVU9JMp3_j#iMt zJ`owq(#b@UCfBkh1FmpQV<(?Gx)?q^yqr%bwpJ)tOVvgd9{+SQUCd{2_*(T+y~M>s z==XrH&T_%|yg&W?$!mXbWx^Nt%DSl=w%zP(-M_QbE}O^agR-r|MtSmA6fA;XYq?|$q1Km6VY-+KSf{X3og157Q;b-|BQ%xXap zhl{?gFC1&>spE=rTwI=(*Jo}#FnwJ&%Q@d$_5Ffv`Z>Lj8!R;q$|ct-ue|)qQX<~rMrciW}ib^dDV zkKwFSGDJowsiTgphC!7=#_}sc^8Q=TpR;O8- zFQV~uQLTEe$7eY)37nh_%H?9g?$2+|}aK%uaVLxXQ8@l(-UtW*l6iVH3D`ptQ-Ka~%H77RmI@`YZvkC_lI z?TW8VgKFM_R}{5aE}ouV`a$3o-K*|^ZC~YejgQg$L4>A(Bk^*1b~U&f@?m!2K{j3( zk*03=B4;u{*dTmBO&nz4XLkFN>;w?`Cg5@lQLx}l2qKW501fR~I-$U2ZoVLZqLB9iWw!wuBP_L+JY;B#9HW5%XDlr~Tob&dJHe|LfoU z`lAO28Xr&8muUvmb|R9yAkzxhE2CP{yUsVY6#s)nchW-rB& z7M2kB?>)n@S7&okJ!p{IxZpZSr?v(+L`ShO&9#gtv|NEN<1)GrrT!ZIy+cBSlAYgHvl7qc1s%4((5YLpw`QZ7GT1aXp} zXc#B2&-!0JejU#SC<32eGxSoey?d~Gu+;>}U_6H#+8e^ExaePWPmWIdz258Rhrjss zqc0vl`|TG`A3k|~a?$M$C*yg*y+bX-LLYPeDW9gR={$t|7BIW$#*LlUgWJ2Ge&_v9 zKKb~Q?|tW^?|kdl%~q!aHjw^s8;VXL^RGzv~hYZV94Y&tkuY2^^3Zk6yQm&tfBpU+2q)3M+MVeZv* z-Eytr>1i+>VJ_q&7xM{b%JbKUaK-~ag2%me^CoO5MbV4RtxC07G5y``gI2j=FXpbf z=A$xN;wpAA85XUg;it=2N9pIs>yy#?q|4<=ODh;Uh9$-~&DcEofT70`o^>6MG9VaJ zsE05S_`#}8-xE}VTvJR$EVDpqy@udW zWZ;-boK;a~AnK5F!m%ts3;9;L_~ASIJKOv!GcupOz9^TxI8OS*87xK1G}Qt)xvDoy z{MLlLHno`h@EF_M+fJznV>0q*u4|=ns1*1a&LtN*_|>42=ctk zER*(b0&|6ZGPdNv1WwJ8E9z9huQ_=A zmI@Iuo!Vc1C?lANfaN8EAE5;aud^{MK}u&aoUbzwXC+w?Br}##|2Bg_a$*o1%-K*Z zc=*3Ty}`pEiIjv)^$Q~aE}0wT5@)-v!3m$yE_kF}Qu9W+3xlMIteKc4o`np*M80_t zM1mhdM3WJzjxmTP()i2g|LGroO6gfH%atN@U2dJog931yLesV|6C@a{d<_oLqepQB z6U6u9r*AIPCK2~7TdpuY z@J6JJMxaaidJA!83}syYiC^9kU^0g&U%F(Zdwk8O~-Lw#y)q} z;H1UQCOipZ$VNVU3)Vrpf}KGm)B}K83C;o(Hy@{vs-cxoBYQ) zb<2~>dAVr)>Z@}whg*J0;jC3C4g}egC?r*u52Y6hotpRk@4OE^Rjrm)6?z#SgA#_J zAs_Mpf$)sz;9~0L%S5YkvvvROjhmg)|LOnnuYdP@9|4K2s|!C^FLPJ@;p5k587uZ% zX0zyor*pStM#}nXut0jJ>@F{T83(u`>!fioIKJL#^iOu1pq0H#4KIu#myp9Ykmryw zIyGFY0L$1W@L!&fVIeM=aAMI^mQg7x%V?6biz}y9g7=RJSl1PPu}#V4W>GF#DXy}1 zR%%{P6O5I#inkie!v3DC|5c^sm77!&*s3%o-p;d-rF zEIYwuF-NE|{bsQ6`{TK1DvfFh6Zvcw^hUGk+~;?eG_+5x6kSbK9i!k{g`$vRCS=tK}!KM08!Z3w&R<;yGp8E_>~w zQ+LgZWo@geuBBWOi1TJXudV|vXF7%3&HSx)zUJk2yinyc>8nDFQImvz;x91M7^X{+ z$6yKXj$#;fymeLh)tC%JiCu;G*^kbs=3a>Q{7401!M zVi%#mvSEpiDsXudQI18b1A<5&W+(QtWaA*kL3(ZtH3g}wv?}+vYxfLwHVeB^BC4O` zJkth(l~RHqrC1~Zr82$2frk`{02EmQ&3aId@ccu_|M=VYYPBK;qgO1V?9s{9VjfH< zbGR1uO4+eA+fpq{Jw3k~PsXcsU6#+y!*SI3VdvF+JO&mhb$C0u6?bswSF}o&Whl0# zMd+iHd)PR(3FR;zgPYt$u6sF3gGH%U@rtEl2{pUW7I0u8ikRFnpwOYy@f@_KtK4Wf zEqZp*wx{Fid6=+hWk%ME8H=b4$So$YgR?W{v(#G3s@Y*kcdm_7@}h->U)W8E^*p~tY>ki z*a!+jWdvl9o1;K~4bPS^j~B!*a>c%t4!%BuWE>b57zhNxMNbp2z+BgYe}TWwB2Eb` zB?Ji&Gf4t-a3xWm8JH%B2+leg-+*sGBuFdK5FwINh18t?#ploJW$z)EIvOr_=vwom z;rVr?-a5X^!qLO2mcAx~v=I$LqY$zYh~B63 zm!+>(y+vLvEV(#BKC$_T>vFez643Lrvvf5>~`FU?F%!%sQ5sH6Q~N050+q6Da}z zVLr)DjI>2I63;85qR7zG<-4^|G0dUplLfexn=EVwymk8NS^H@|f`P<`-XnD@b4M!dB?9Rx2G*bB z?Umb5nz#0*vw6K%l63HZj{yN%*VU98x?}Q!^1{>*p)4F8l&w@Nw>#zEe)$6A+om>O z1cqt8dUN{bYyevE_h84hExpi2$6N$-4}b~;V2FPH^r&}!QE`oWtEC$j zChTgxez4mp8@c}2?=Rval)Yt6X1r0A=8gN?%6>~Ln#--a)~V^Yc1v4L=fQjXAAaZk zTkqWI+}LewZC9#gXEn8_6MgbZS^Gx*%G3*X?WSS5w(DWQoBB%C^n&W-@_K$9=L_)U zr>k|4FB*A6UF!L{KP!~0>ynjkR8!ksEXKNRj`|nTv|m^!FptnnI>%;#=;d@ci2XUdA=r3ctz#D*&#N}8m11F1vT{`??<$eCj%(G9lCBGfN9kuL z>o-@c{(zQc3Z^GHJy+17?-D*3TuAde%uqG26<~30Oa)QK=z8=y`+_$ZY2zXd-IhrU zx>BrOb|*vy_AFvBxp9S58FDfVMb7fvMNc$8dlo_jbIWYE>L0dBTe;jJdJEoEraoAL zG6-3vO62GU6+BZsL^vRVLI`Cf3!qQLnUaH=GYmRETM@agadOs!v5LgmB9yngmr;+8bKD?E!pMJp zbPi7y25YrsxQ-3O0y9yy>}gtV?nkDj_DA#AC%y4(k&Tq=p_1`lPE3&+mu3a(2@Tu7nga7~kdP<0A8f`CdQ z4ge5cK-tN3#0DVyU6;r*ge10&M6lz(Q9wcl{7u?W0l+e<-)4Rt+0-K^B%+}ZqpYB) zPKj(nmb}T#iV{8GIRL&+k_*IWZ;07}l%wKUHDMTBhT=1ulv%VQ{cfq>*DPqYxxQrNg`cmFO*Y8C%1E#3aQmwu+W&%#{(| z1O*3KiuUpvV+6jX4AY#Usk}RYtc(Hpv2*eOW18H@VPWKKI7ko)NMQRVw#l}YLcFDS zG-EOe4$7rx;XE&XGJ!a_p>cSPo(D$A4hI&DdG76id}<3)357=a7J~q0Kg#d{v5g{Q zK^h{IH~?h}q|ia@LJmV!Q59%gR7IKrjAjA_c?DFtmAn{1Vo5YPN#e4_kL<9{-8bBJ zqz)#&u5#;4cq*kW@3*rd_)rhg^Xli1FQ*I6u-yDepdhKLZd88Up~ zM-myb3ZU4L0^i`SPkaBsTcqx9eykzF%R5KHAkv(SZBFh$b&FJi#S1{c7CKH##Rv;yNO zOwyMpy~DGsLK1Da>ihe9uIH=^8m5QwXj(40+nq+U>aCMx9wr!cilzl&e0DW@^5RV) z7`p}xKMj3hdBvTb{ezvBje6EOP1Ra8@7?=18g9H*i7vb2Pk;E~t-CjN5B3||+dFse zcMk43#fq7C)HEver|F~f{ACP>x6rtecUw!dwFuT}8VtJUm_-);bXl;Mxpk06)A=xG z=<#|n7+gh3WH>J9^5ID*b5&8M{S!L7tEhkY0>)^$wY^@et0dNR8}2nkJ6ueI#T71kFEQ_o~d44jVW&tg7Zuh3HxtPVSk^ai$hw=r0OxT&r{cuFAc;ZS5tpoEnRgwCQ`qFgPX3+oJ+cZ9w1^te+jjdGKdcsA&O~v7)7EHGSwmu z64LRX5s5^OJyJ=7m6@!b5m5<*q1NQHQ0{qm)~LC^esnmQ%)^jhMcCV_RLbt*X}|38 z>-^BwNt%Rl>ia?LC*#RNF>OfFpw}Burk3OIp)Hv2x%GTDgJ8kqN)jmOHC)hMe*wp| zQm>9iQ`a^e$6lu4ELm>I$UeMl*{a~wvj4Nb87RxROEh$no zgC_#U#oq&ek&R{|m7U0rm-5p2>^>0zq~o~B5{$z33@n3ZL15$cTLfRjQ?STPDa$0m zEFn{@oKBEM0=_P|ftM>Ou))8noqq(7NJfvqBMNvVG7YCp=p+CH02G}JKoDlRf>y9d zeHq$o3>(x^%ccUk%&sM!Dc6YP3hl+{C6^@)1LB|j(WjH?9G0xYR8W=Q-?xRYL(Jz6<_owBQd4XDlU^k zdRz-?+%eQjh4=6<(xV{w>%VkuCE^<_wx-Q>}yp|ufnfK|5pJJDllPuv3YvD12-|$h`n>*_H<(u|{ zdSy89F|=N$evZor6j_#qzCtkq;*2%Jz>zp z?Uv|y^L`?qIY5-O7Wl;I8j49#RZ&kQ9r+*%{~UTai5#J^(io8+C@aWPHj0o!%Pj}C z{Day~yx>~|P%PjfymrOW9{H2|ZfSfttyS&cKDt1+LX9pWszPawa6pct1LY5&>%2D7PHCe=|y{ct6VC_ zQTXh|VYOEK;_2(&2t{aDp$>Ro<$1n!yE;wcxgWc>)+l-5>73NCdh(ma8yG7Jkxb)OI`NhG(VO9Ts`E z4&D9m+3AbtFZ02uR*IGHeEg|d zZh2PV7U7zDg{v!V66S1eIen4KPu0$T{zfOPm;C%PTu0sWL)!33TC4ywZ#bpNqI>uz znhr|!+O&HCnxo0sE*1^bj(i`vt7M%fenHhEe-X}St7SSF_Th-wt~=>prD>Q$lXO?L z_<;{cR~)bGR;p%syO2v=$8oDo+iCFamB?Su=IP_p6|D-NPipGm81LK_;M&7GMo)`d z#SPssZZHs*DSB8;2jRbXG>l-}f#F$S&}E@^K#~k4BvS*?SfL9*Kspt8yK(v`CB36CtnCw0PR$;6QH6$FJ3#WY%#_qS^|6y?o$K228+y%$(XsDssT%n=9D zWTYcxgInYW#Bx+JjkyA~39XVloP~|5`};q9e?IqjcIt+r^!np^t=MUnVK;y&z5cXX zw(FH*5X6ul*wl??(=henaFj%;rLEX&J1z<$+qf4(S|%f*soV*DFg z5-tp<=oE{55Pp?-9^YR=K50|(xJ{C~ zE)0&fYg@hEuv~V-IR4e6qsa^wQOJgK97*8#Vyh9z3vvK#q4q>nQrH7oM1Tm!-jsnR zWx1@L;dzUKc@h;dkTOzMB2otMS*-|Evw{pM=xZk9it$XEc*P?LVglmqBcc={GPERt zk!!3>AUKOkPrq$RhE-_D;004wUVa%EegqKd2bkbRb#LJjB#jLL0OkEHB*FN{<})|L znt?Nt;olVBlw$rhmQ4oI2?_C)8}LZOi~}0}?JvK&ccV2JOqz|Rtm&&5nd@a?KIQ(( zrHX?|(Q<596YxlaAY3e>TYJqY45#zZGE~?ZTysm8^B}5tR<+{&<GAk-0HNs%wAAouKxV<*RE{lL4#S`JDF_wZYLBaE~Oz7kc5C~8tNdQ zkCt#s0Eku;mU%vgzPBrazVtBfFI%%2X^U( zfn>XcKfckP*z2moH^n{A=&uu^6#E~h7{@`zew?XEz(n5lp>7h1L^7%3dnnX-utv1( zK~oUG*0Wtmr_DgpSTP@|0b68kLs9_?ZAT?ZgN3S(bRq*rOm-82)ktwR4zm;t9rBYD zec)Dq_P4JsOT)~BnoC9NWVPtU(FeuqV!%C#AHL{=z1;Fkc#_R|(j1&YPNvP^YC?QC zRihjK=l|>vwsv;ly}+=OK3dV5qyQ_SA8Mu%MS;fsIn*c!6;%U`pbWYRC*6cuYu9UK zH0aIo`Lj0{ei-YT0*!_WYsEq}Ry|!ki{RowCfAN@h9^_DA3PDY0Gkh0eshK6zkkpa zhKNNWb&)Mx5Z@%A$H+1cCBVpJLb9(14vuIk?T$RA3-&8NXAcuR@73(QtMlvFD_V+} z=@s8kM!Hqjat^vjpUuP5t2vB=AX)Nrb<#6}PXgpUks;D4=C>1=8XKdL9I~5aPj{mMG?p#^g{79PtlVC{eZTv?u}A3lJAPkQqUG=1 z2oLw;$Is@+kH>n@s+vKy3M}KZQ^dJmt#PvR@UQ;kzsqy&OJBPE;Jx>wtgx}@-wRRsO zK0DaYV`w#tCgX56hZ(rlI-e`9cCcJQ-T^7iZne>wFJ`via09>Ax#l6#8&8ceF(#RQ zG&QD)VFX4L8>2;eI@Hbv#oEY%#n0;5x*qv)Puq(h+QCC}3gR4R-b z|T~fkBPPhgoEZK;Tiym(c*F z2yS_S6XnE6V_^A}H4p+nWoXQavQdB|Dp}4oF@8ZMG%r>7DV4m$jP1(=csNp8oFr;d z8Zxg=KU_7q(lL8sGwFf?z>k>O70Okggje*_T9))Hw8#Q}2}X$sdNx%Eu1ZwlvJ+t7 zF?b}lmvw?qCJFS#hN`J>N=sDqNZGPal%W>ePo5ln>HhU*tyW~I>+tL95u(YEZxyv1 zm)|G~_yz))R~W{QWgMPfCQ){DIl(F!lL!8byf`1tJ2kgebw7K4QiD0BMLlpa!eK@T zDEh4c=GJQm?M~Zrd24i0WIz1*GZ;{k$ajT!0)qnLzZeyLtI&=DB(A+_E4+pt$@eMA zzuX*=X;z^bScqCqkR@|C0o&lU2&kDby5T!!h)F;sz^()<Un1g8M0C`?V_e8lwSq6xwRh8>uDZ778^ziGljDV1T|8a#!A zE=VW;;D9)#1Dz--&&2+@Dnoox;XNHdW^nP1eo-Z(7po&aH)|r|;qAam`*e#iWVLuh zJCvi-ciqj>k49%z)4FHX<1jrvn>|0|Jz}#^t*qEJ_9O{eIxxmA zs%z{(KW5_J|Hj*mZnxW8`M>?&|KD1k^t(;WJdSH&{aUS6u@nL%Ka|6p{Pir$_$(5o zTZU0=ntTgTnP;2Vwz}=s=Z~KbCUGlp7d-D;Zm-yP-dKzFrbXW&wXPY4sWymB*f>ZL zeW}px@4SZtfgY)+y~`*QVL~wMm1<&;O=$y21is`|Zp($P$Sz+_s>8=Z?qCxdtH7AI zYL&V+9LEqSVzsO&>>wzo3%)<331c&{K07*}PNF=?O8f(-Sk6-2z)Evs0S!dRlWLT+ zR?TFfk2c8+!DxOjo6ys8swBK=7+4rS+CO`~e_~{5qwcr5O#~gL<&{}>dUgh5UtjNT z^&75bO&1APju;zBoSt4zfAz_;axti31>xI`?%M3~_&fjc&yG&c-*|2N-LHSG&~?Xc z=|{ul!)Mtf@xHQYyng@qHG5JTGfc=PLL?*6?4qdFJTo`#MXH}2 z7mr@#kI#$!v*P4Zo6d{Ng?4gY9-J2^7us-EUe3zd3^90V^F6htNwLB@Ar>`kG4`lo z2_8_vn4`iHicy9*)eW^{U|%-JaD?kL2hl^-2o)6E5doK`eAN};xy&r#m_|uc&Ntj@@ z#hAWQaWKBw0nc_|(XX&`050tWKT@v9xT2jE$f9($T?^wZOtZmgrt=MR+3WZ3+*qq0 zoenwKAF$3Y=JSKoK4{E)1+v(d`Jnf_iwCqnze_|4imXO3apYz z#pla$BpFFc6e1jEms=D;q*5$UvgfHJ<^&2T1c2DtL~vYT0rX4rAv{e)^+-UmA?bvD zNE5V{EfV37S0pM@q(JsphpP9h+P@3`1+*mWSgUFTzj7i{h|<3U&Lc)el9p&+$tXbC zsvrNaP%RU$@e3(SPE|Ac;fUi%@JrFFXjunTaJ*=UEm!AS`R47dv(pRLsdRh21;Wcz zgQj69L9K3?y5;bkL=q>M{2bdZlC;;Yy*N1MZK1ZsdpRmPpfMdbt3K?b-K@dKHL8A! zQ7r?k->Tibw!XgBd-CkX_RSk<5@UTo8^To~ddO-%7HC+~Ae1MGa3;cz&=2BR$_k7a zPSO=E$}kNeNjxEZF<)wq-^!v9W(&**9$%u&8kvS+`gX`SkkUS-2{03th>++}NN~ZZ zuu27*%dS#+dOZ1q2kV$;=LpG*%2A%=Cfy3pVUR+40X9_=J-CqXxkwHDl&&MfV1a1@ zBjM!M4qj?O=V*g?PNFO{ApxA}iHkYzNFlLtmgS0_)Y1Y(GUAFDE2Cd3m$t_&woF>N zYPp?IJHK44^{N1<*W61ZJ4~XU<#^*FsJTDmCsg=w$~*>@(niJoA_pAPUc?E4Fqn;L z=$PRC;M?ybFv8J;d)LlR&;I3~|MZv_vTk?(*l;y}if4+x^B!)&{ zIbrD4{5%eAy__wQ={y96TkF9}J&2DMjoa{5x+N?Nh0& zmn1y$ZfN~lW131cxi(FQ7=0OqU3_8`o0OV486~5UCanU$$wmdCx#*+~82P00i z+VJvhs#Thm(y$^dc_~?a} zM9rG>-9P`cUp#z%dwcb_|Nhr)FQ`~e^Y}Ra;lna7+%I1@Zf#5^gXa%FvRzE>P8ux| z_<$_oOSBfVpxLaoH*~MoX!U%vVNF7N7Q0blJU@q1sJ7i&r(f@^J4tRuxn5-D-eLZ; zJ#7%?Pfzmwi{kJ^I~W(cr^WuIb{>{T=lQeK@@%Tbxo#H~pRZ!jZHs3QG3)Z4LbGBS ze0~HP;zX{)BTzon6nK?kgRp?_=~g3xGKYVpkK{!P3_>nKSjZYGgoR=c^ivQv;bp)B z3q*rR9v!KS0tJ3#U*chsa!DwD1)jxM zbiM>%S_5fY;vu+#XBpuYglti!N|yZ!z!$aB2zl(6TXwgU05dW?)RF2@@CYdPJS0*< zgky?DyqL^qSSoP~ANvR2c*ig*twv3Ef}9IrzmyK^h!cC2AQx`sCy131O9vHgt>IkuDt+C-YGEbt z&I)i07e*HY2zoD`2_vYid3aw6PdtD_&UOXwM&$Jo@&lQ}15-YH8x>F9lVzFFQC-M) zP=O7z9dA@bCyX-~+$^_DygDO3h^`bJ^85+-1-CeYd&07l>pd>bZa7u#G67~&uQZxo zYoqbg@kOd-U#hiFo{u)ynjie?0FmF>SX_iK4lZO%;n7%tTEY%$8yQ3I)cxQ2)>q(G zupH^Hu5E9vuQ&bw*T4L$+nZg}G_joGJt&Y=#?!@^jj_T;31h2Dd3iZO+;V<)-fRS5 zVKy0KqWJH>^U=-q=F#~W-QK@eZ`53Unw?kTPNxclm&2KHnN@gy3B53f32D^-boYyU zIR5BbAG(7`D8}Vli2OhTJOjlO1foa^jU35XqeyWGE+BG-ZEOaAts{&EsGe6$S1)Fo z>lD?k)*^}_qV=^#y;4c{Kda%=#RhJ==DO)^Dd6#?>&Y@jO#u7jg8TCarEr- zm9>ple{Fnu5@(gQo3}dsHOweMXT9FrvP#{a&a|UJ_Ss(kbYDB4l;@KoN-Kxwm04zm zMP(LcA3e>Vook0@1!5q()bdnwH2y=nR&JOk-;bANyurePOM-UQY#`TGRrD?@qi7@w zfT}1vhys7e<%NcTmKRDWDw--Fc}FVm$Hq`#D=rOWs0%V)BqIt0AO-Kh61hX(lswyV zj#+91#lT3X0Z#%GqHPEkl%P3(@Pyp6bA^AYJB*Y(&(8MGfBx}{4<7CR{K?VxA0K{y z=k#Ys!%qgYo$=xdGE%?cs%{VFLv}iy0*32Y}HM}JUzMmv+w<4@9_L$G(9~Z zP9`yoI))EO`T63>XdF0J5IFPsVmO)cvPz)^{AQhFSyr{~qSq|1)a!n&T7}P#7BL1m zoOy+pn-LI~YioVL^2`p(!*DX4gp1i^wir#~)zwb5Uc<_Ba4~$kbM&)M_J-rh=2~y} z@DeK7TCMpWhMy}g-Vyy zBFYqMNiXS91h7Q5OB72BV3{-$>rrqoyCme2?Uy(dHgzDDb49Qq0R@0(oq#D(s#^9U z1hIr=EEdcv9ATm2kuahg#To(*>|vdM`>V(Q==Z)6G}~~y8Ag7Y+n(nKzUO&4DNM>= z=6U`v+0v?=Ymkra)$`erI{BaGr6+GE<3N!F>z6M(FcFEI>=7|ahGG(AD!WmR0kD=O0(qi zu8Bk^2thEpq6&Q$jE?xc9;l-%Kn47r!AL>{WatnX=J>7l9GDqoAZQ1>I$b2A4Na@% z#y1-Mj~^cOT0y51z^zQ2;#o2eD*BuC_TY@S20wg00I~BJ8^tS;L!NO3e4Hyh2F1XSSEOYd|cl{JQZiz$?4_l zN`EvO@tdJu5G@v8e)BpU>%*OsEX|%C&hBm1jI*?Rw{wZrd09**(b{UW7=(1d^dXcV zr2OiOzy1N=G|I2i%OAs5AzOKF9zgO+CL%TNsR;&fR66;?s8@}#q19k0b7w2PsrVA()4%I&nMSjbENdOPfEdiyF zI~*whHGbug{|W{fSOQTICmBJW7M@|czj$$YW*2$Q_Z!VR;2VtqO9(`Km;;)%TBC|d zzKk%I(*m*h^`3q?nswXtfBc8VxurAAqARH?hFf=gJm;R0CU2jh>lQz*BQYx$1@*Fi0FXl5_mI)9I!c* zNkZh(DyoQjP_^QZzss)KZhfgtE78>9zjBP>a%q8f4F z6N9eB^Kf_X^glj1dNiCLEs{~1M_lm`jP<}S1PP0fli(u~aXkb<60@WjIOwq0auBMc z31lhdNZ4_VDS}Bag$=J;vw0uS>-V;+)#};B=;4dw&3@yf$A>TWkB?3-9^AjZy4GoU zx@)0@-mLlP{oc(@aMJI$VM3$v*fz9!vyo@Tc)UQ5tDQP8g7Tw-h$qYoUOHJge!#cy zhw;;AyR+F8>xvneOq12sUYR7L(HsLmO|_l9gGI1`EogaO6GMobjQV>TiQWk!BxD=pR39FD}{i_M$ zks1#0Wu0VX#Vtunc|cQpmE7>oB zaD0{JM$k(Ew=4w$STIj=&_*l&;P<|Uu!4&L>a?^>yIK!S$BY+qI61V?ZJtg*crlrV zmQ%qls842d%*n?m z$JlmZzG=SFZGf0G%`e8`6&<*Fwm_FUV?@fY(;|J{gFBW1D*bCR>f_Ldz#|N*9x>!Ub<2@*{4Z!$9!Cwt@&7Q=VvnKQicUP>Zqx zOF4RDc7aNMi-LoJkFLff^W#TngDF=Jvq-$ZVioO)LggfsqU}|WDM#9VaSaGFTx`6jqxAWDv@4=D$w}1NM2e&sZ3+(Ey<64dj))(`cV;Z*O&1NCj zNrna093H_K^m>iYLt+VjcrpkRuBKL+RuGn(cRGtKpUvXoB-*^*E*H5LVvPqnIWoZV zs{zNKZ1%)_Ni)2+8X*u4OjfFtgd^Qasr)%8wUtJ#(ObTEH$GKF-8Q&_9GMsxvC5~McEv@B;%lm&8@ z)#5!_OB$|{=#($PEO>`r%(BgIlkONT>u5y z;1P2bjFTZAqXe>xQv7k$Fkry)m<256PQ@j{Im*ypPD+qvRAM|pe9F>^gIky>`#~XM z05k*8mDE^Hp8;z{J#QQ3kP&GD8_j`|1R?AXHil!A5Sh@=a~KRv=yil>o`(d}9BM#K zxxTkE6g9IoKTde3(B z?dzMHYu)vJ!#2!D)!W!w-`w7E9c%B!KK$fLuW4EKbb?SJhPAEs+OVC$c#8F;Woq!w zjk-S>j*=+c**WBwQ+kb+m5%RKs)5DVMPj)r@&9-=mKsMw2g@ohCNc6^2+xIyM8UIwNLadT&p;Ii%nLj% ziGEW`6&!oTPRL(coQHqeX;y({_08Pm*O=c0((+Wj#MS2mR;ch3z2O2nP6(HqU7{ z{TF*@)jFU2M?Z0zIJVWUS6j`%^=vF~`zzg#K7R7SXU987x`nmM#_Qi~_O`P$^y>}BYYz8+P5?2C zD9u4f)kT=XH!lD=#!L(@z&xszMA(l?A{vn*x>M2@`6=O*P0xAmhBqt^lPp#b57zpR zqZu!N0GRuC_Z>IIQQ% z{B^&1`h3*wRDbr#5f;IKpN8rY74nSs*(w!ChV$`P-@e)JG>7Af?>V05gJ^^j zc?ue{)s>!al>hS2e{yT9#~b9b%;US0&B5R@T7>XZgTd(Lt=pLA5p-0m{&YIU7U(C_ z`QQHXIeJG={ifMY4R6K&n_uoawsARzyD9q3AiInJNnsh-S6+QA_D`;_D3fRO$MGfc zgCr4&T#84Mykd+HkS0aO`$jp6czz6C9!xT-gF1+sC-^LP-AEm~?mJk)nP580MQPl*wOPr-y`zgbcmC7Af9oq> z`|>c%T9r!p<0ptWwfVUE+ixu_=jh2t!1~i4e`sp_EYQu@UvD-yif50KpC4LvqvAV= zc63WOY*#Zi`NRmPR|t>1fMraB;IQOV2{d4ah=|Kk&!B|Qi!&Y&wsJ1`MPs}@f=qK* zV{Ieh2$(bEk^_La#4w~0KmiXOAv_^>ED-^M26V3EBPeCIR>GBb#K|2y$DaBiZ8Bg{ zfDW++U{Hf2IXj}Dgb>0Ao+U3tLwsUwatwG~-s&Ryg@sy<-D=mjJj>0BS(+~LGUFVi zz?BW~06VI2i3Eo-@OZ#F$sk7~l`%*wMwP{PtI-wGRjRhqTsB$_qtev(FUGsaBZLTF zdTsN~`!{E!1(qIf-{1b|(cY66$H{yi&F0N!&2{}+qq%isyV+_wu45SHcsNEt(d+l& z^q=jWG^%c|)mTg>8OXqhC`-gKwVIb}`s(_M>$zDNzc{#<%;#B_bULl!V2V|q8`Pqd zYgEi58|yu+cA_NfbvscSpC27R`TY6r(GW|)=^}dl=4!XoI6fN;$1@P%%EyyUo)*V`(T5tiphx;a{P;qCp^}n1zJCN>ZvSDU&G2WWb7ADnS z9G?w8eR2Rpv}_ZL)w<_~Ndnt%R{itKabG?T8)IRWra)c~{A$(nU8~t@!=~z0_lLiD zR`aYIYmJRgaC5VL_geSXM!Q{iPcG-S$@e$dmcd7E;VzYoDGZuaT$ot=>joxGt~tnI zrE>Qxf1*29mZ#NP6HajD?r)tx{W0nQkMS903<NFaq9b&+q**xKGt}tu3Pi0WN zlY=+9@iq>!M%T%}3aunULmAzf+>8m=&4*H8YM2F)1u0mLLtW?%2RyaR4^#4;s$3tU zA$F)#ir09H`!|~Xe33rcy}Y*GMr|$dF8yp`WT*4R8@1->Z0QUlh7!<@qQvD!JA1E0&Q)>C=NT*h~s7;uj}~ z0ey;4NsPkzYEs=WP`Hx3GBcr#lwBp(2vbrl2b2Lnr2%?X*g=Ozx~x=oYzgW`0%s*lsyLTZ6>STN5A+Wjbk9& z*jPJ1J=Jt;bNgoT{ImIYUKlmIwDdg6IVIbe?|7P}f(aPP<#SaQut5H>UolBOF3#|R zw>}`S#*y4qBrX>TvX$plIqt|B+8Q`!HD^*N%;M<`p7@qQfc(-M^N}Ki-im8%alp?w zA_G)x&xN4Ki9+Ntu%k-EiXp^>tx!doiH!g`M9QsK+%oB|mkc+A?D1ksLR zt;}$EVz~2tN5V0C79pe3ckH!pgKy11l#>^mtF5i|9)h;R)8UP+RmZ4Aaq{5q`gAgT z{Pe|SGCDaM%*IoVpG+oN7)>1Vd~+0rri};$d==+s^TBim$Ib^BVP+%pg=!14Is9&OEM$!^cwo9BWRcK$SXHJl?lJNLj!ZWc2p_HL9_cDS} zjk!_KQUq3z<;>;P!Z0s>u_Vt! ze$%hPZw&H%=ksf;t;^9IgV?tCQH;@?_hbf+Ih)OG$F$(>_-;^OFaqCdH>=fJHA&KD zvjL-AZ2_@DZUz+ zuYcno=(-6C{UE?P+cKT@_PZC)f67{N$rm+(iEhoSxmJ`Gm|q!l@WvEYBdLrMwfrv= z%BTvFrYha;R*kA|6ct{fD2lrq?Wj_WctS!9n zf9)-V2)2vGEzT>N+c{>NW=`%WD5V@{6aW?421 zLl4V7myf-L^Qr3x^_n*xPvasp-n zTfTp*bvjyH4(2uAo-7vCMxD4cLr@R{Bz)R#>!eD_nY=Rbq%Me*EuiE1`5*PtMj3QK!Syl?8LVgo4PMHWaA zl_CPv2d?c~MqV+n(g4`TN_%y+A4bX3-II3R$JGAgU+#F8xz?}4!cWgfCl|xhi|MoH zhh-k09i9y?M|+2-Pj`-@G_TgH11uC~i%zearrE`CvcAffPZwDl_(84FHjEsa-?+93 zXMxp1)vH|FUJIw=G=n+t0&%t3z^Zk=STt(E`dWWwt>5c3?%lr;G&;>rquc9RwqrYd z>*&Xi_HJ!<@7~$~wXTnqEZ;#7H#->55h00%10N+pQ7Pd_VxC)Ulsx4?fR_@J9gkFz zv}ELooM4nmaWASBq=0{4kTqfQYKiIO;zC+X-N)p_(`6HYL9ZZku3AX z$$=&1szkDELXa;G)K<(z&?p=)OB8^z97XLisak2U1QbZwElX8~Nl^)d!}cX!?i7F& zsnVB;MQo<~h-atuKmY5W{O&j3cQJ)zl{8~eiGU+X`Gr7?a?BQuW-Cn- zpv5>wm^xp?i!hCnl*aAZSXYgvp=TRwy;jV(7?kj0nA9<=VMSOAf(9l%%S_YMGR^&? z)030oU;gwN24t^U+v+#EO>YsU!zrJNB(Xv;=S`Xdtpubam)jjXXj`_&Sjn(Z^v(b5 z|6HxN$|3@)s_$yLZA}lS$Da-_4uo0#Yyb5BviZiho__S#r<3rtwR$_S5h)}&&pOWK zSy`fHkAFvAOiHrYh(F@rqy4dV-G0z(jVB=(^i93ruI|JO3WW-#a78V=zCx05Kwm`n zN9Tx2!0}BX5?}KU0vfLPJu=C=S1|kGm^gTjgT}&nM4a+VM_2Gv2haf&kgR2rXhWS> z)3i4mt#-S-P%A;&y(bi8;lQT?RN0fkB_5-k7!K8Oej!-C%ck_cS&+# ztAfcG8LV%<`I~Pc^zZ}Ew(XoR0YbdT3q^)yPe&t|O|9y;S5_KTzwT82n}7AaFWtXU zt=G^)mc@esCTP%)`LD7FL&Tl%TlIQvG9G{DgGUe){K`hxS!o4kp~0CYX;~pYo}B}+rNA9%1xtNT?!lJWJ07SfFfLoQCOqM8ATC>F(H_e8AaV$OvyrLg~eOz z0Y%;sOBt}Lj;}S#%+yl7VrR6-7<6V(^Bax5UymZMj(NA1%dkO0Hjy(o{)C!g;emr2wJoYl2H z))1e5_N-I0wzhi>FEF2;hrhf`3*FoD+~2(W{P^6gq_4mIrfFNJN5`Lk`s~4b?=;HF z{NI0SwE`neO2l4rIiq6gd?OW4>zjp9(6afSI!q3ri4rBbv~k)js(5-pj0;mW#0$EW z9hauY*LE>lB3iP>2t-aHkO{KOAObtyrzs&g&*V{>yvl+dujepwqb!ut(LE;;B~^|) zf?-(Dm?<}au^by-t^qwfcfnGSC~pT=fMOj&8%;(Pv5Lj`+9d>N?H9#mK&WW3AH&PA|ustL-c=fBeg5SbuD>>h?CiTadbY86P|)q%7jS7Kp=qS>Qn?q zA&Jyb!#s8}&aqe3%drL^LWoT$c4Acgie0NFufn68Fl;DzUiw+ol5s{GKmflA-zspG-~ZOT zmhQ|K^ywH0oJ24;C#h{am=WM95OBc3p?p4{pPWzO>*gU`Fi=?#D*W(zr}^gn8y|i8 z#0#pRFUvA4w%VX+6CcoOj6LgdD6w!%enHUvmUhpHFIU?B{vCAw zcIO*=pM8J#V)puGs}&&BDB`R{%mIU@K}r%CQdU<+yj&A2A&Tah|IL2O^z@VC*=E0X zd@;Lsd-bE~2=g#NK_@V9)(5p{j4(h>)T1qhua%}{(#!Eace+4K07S`Pg^vM>Lb;Jk zFzgOtmEd7Kp&Oh+fG3xSjgd!N2wxm7V9`I@(-pF>O2@R@sUBmx&WrIhMwHN6ZTxt8 zfo1KR_15T^Ux@w1qf>-Urz3v%A5^k?N`eU)P$ecqu3;z89ZUuPSR{S(YwsfX;FHD) zQfye9qf?&Up~MW6%dIra{aW?zoolNN_rLyc|FYX~iaeW)=d)?Jw%Ws-9n|VDJ;bzN zA&Fwgv?kLDrfsx6xQJi7R(A)ufE!R1%pvz0cH7mUH z8qUoswNx)G%8`sAuoXt&bpeSujaNtzgYaG$xif;ZI{+bUAypy_kqSvhB??duQ$s-} zZzTs87%%+D5b*NEK01Wp*h;QN(%La5z%a*%&L=eyZ4j+!QYDfq;TS96Eg9^S2Dxgf z11+$~kf9*altWV)3!9Kq?w~HckfB;~P8aNo{AEI05UfIfnxBwKMzs{W!RmF3be6Hg-GIyNl`0mYR4bE51 zPs3!bSLajE$AdNokBgXia5|XX+3fZ@!Lx%)ORwDB>V{!>KA2og<}Z#WS(e@0?A2>t zyXFqZi$xSYcx~hEo$YqB>LMCg>8!11;Te*J@t4h>tJE^Du$}sGMa?MjwkpBMP`wBxe4i>azxDjK%iWgY% zcvzldX=}TvjKjrfG#O1o-?s*%IhuPm-}x6tNwem5n!)bDakJUrwV0fX#ZKL?Rs-Mj z40+@erI(juMjjx*kqwNrNO%j*Ze_?b^Fuwe5H{!}bvRltp&) z&95PhK}VPh^&+iQFt9UVhTj6g$W(p4M8>v^jeB3;{q+0KPN#R)>Q&E*(<0<+$6Ur7 zNbOF>=9pNCSSiYpqH2lJo|GTn8}IL<0beyiVn5iL|hDreag)bT~#ETO|?EW}p; zDAM4S8EnAlK`R|5r$|mDa7v^e2Fg$aOCXX!q(#B&$8b(yT%;psc3CU+l7IosD*fGd z11l^Kk0$6hN%Hm8#<-GxKAZGh_m)uwBZo)h7snIOI*oYd6N%CmREh!F@*)rHMEzT^ z7DK%9|i1#W{zb}s%OOQxzNCQm>?63fYqaFspRRJ?lOEvJa zO6hy{&1-ADUj0{(_Tk9aR@>dWdwrva*?qOokFHsUdGF>5fa_JiU9aB1y|K04eztdv zmBaJ>OVg^{+UPxaaMN~OcvqXDwr5#(v(=c-=43L>|Mt^^&v#E>?4Rmo`m0ZOzx$IX z4<8?V^x59Srw1Q@zW>1|$Q(a>di2?|ljr;Ak9W_vR$7Q29`2mY=JDBJ`lY)Yt-Aa0 z#l_pV*F4WUxtOl?n-*_eHTL(<8Z|dblkfiY^T{lV5-f^nzT7QUO*12yK&T=nDM3<-0W3!tl9U4nwIYbrzYJ<(rcwnD zRV59USZPrti<3q!U**! zTEYtkmA?|-lA@RRm8mVExl>0=M6f+VBQ2p65+?MF0-%FTIFT^kz^YFFtM7mKyWf5v z(aB!ODdCJe3!l&_5ZQ!!%*~h1YcBjp6 z8S8xeMZNC1K0iGSW6knxHW^PRA+MD4aeEEX4x;`lR*v${4A;_!(};wFN+B8iV9)=u zY~B8bTWwo*;5dP9n!oXf|BMhqaeBUAacht+zdj;7YkX0H;TRSmO5S@=Q8CBU)A8#Y z&5d>qo?)KwontEIxH32(<5p#*jP8%-!-~qz>EuS$>#x-?-nuOx-K;n3n1#*}?kL%@ z1!g;XI?g=6fha+_NQ4^7P0-Gij(8^kj;IAmFb*=C^VSTWE5Wgx$R;gxWQb09f2eWw znH3;`hXw%9qkkYrv!<@U=e1w#Tp%iPECWLqDmpXLQI_1ayy@Yr*RB5e7YCC?0{UkW zW)6y1`e5(CBjZIS@(yj7Cc{(LHs5{y#n$IS*>#|BBh3p)-t|Ek9GV~Ttw7T#PM^!y6Da;Xy{5bM)gY}D zQJRh?alP)sPSVQ=G;vv|X@9o+$|aTU749f4kc>Y{o*x}2150&?S&&0AN3%i^CC1)x z$cE&d0I~??#6+Z89_3!QX4dS(lS^n4#ssr();wdDYe}v(EZuU4quI{!1X#jY-p(YV zV;>3y_Q>$ZLe@!NO2G;kKT;u7qEHCDs^VEmIY!B{wUiSZ{mHTynjdrIf=aRr)L4mCcPLoPGGyhbtRvYc)Ol`EFUU zD@IzHMw!Z6JTY7+Md_LRkWiYHHf)b4iTFM)NxbV)=)x?EJR@pS0}3HeN|}>A$RvHN zfit8L1BYT<{yFhlG;cmaOHe@$csUEj3o)G3@y`_u&wQ{2-6|I*h1}1i#6fx_)>M*X zDcZ9NBaf(v1PB;0z~t#DSb5bP8TO7Njt2=P0Q`W2U_e%uTdvpbweGC9Z&f|ND8_LbaftyWpi*H{_|XuF90N7`1v%;@jc~v^ z{zQ7xVZGtc!YJ_FW~*AS1*fOO)4>!2HqBy7H(iV8DzMT!KD~?(>SyVE5l?5~VllsW z`^HKe)LI%ZM|DiUe-Is|_Do5!{ z9;29`NU)tH8c1BuO(}U|qi~>}dS?d&A`A^fx}oNknTT7FX50}>pxGINBlarz6^I~S z!hDfu9lHZ61qOJP6A%Te@XFz4Z?J>fE7JJvcNA`0ZHG&U6J;A(qJmi-}g=BWDb}$k#c;<-| zVuhPQXlNL=sekh~-l)~82#I*}2Ja>zW405_W|P^JFXTvazMuucY6YvHEQt}R&O+Do zwy$qnW!h@D;yC`-fAQg+>nqJx6VgbM2nOJK!O`)>v%NFXyXKk2d+%Q}Jk$2DEMu&wYvqLN?H?~&b2`XCt$*&$;Uhvk@mdMb=W({D zTNieCZNlUNj7kSX7Eds<2ek)5zUL>a6Q~Jx+XSM;#h14y|4;oaU&KepeP5S zh~IUMukqkp20H!rcCUV~;a3Z7mgexSFj2w8pE4Ut7rSK{kBMK&@2Zqt2ruy5*EV}M zuJxboovid*mxIY*vOtKlfsVFT;TE`D(JR~Q-Hm>$+i7NLcH{b*VS6sthm8Qb=yw{f zV|JTW+vaIj4#)zYl|vr=%x7ttX`|^JQd#LY(c^GDpDhx2_eGrD*#^!wr10jQwHuo~ zxUNpke)rBg!qhnCJJD}$c4~p=x$bn9taWSa{TlGj!fdTyZ?#&xdq+S0WN&M&eRe+l z;G-SKHp3{Hhp`$d5CWJKtxyp(Gv=7!s8FJU2QfdwODzGEaFA3H@>t{XKn)dEu#l=( z0N9{L>Zmj9&^L8Jm0V#ZK?;7U4?;s#?3)&)G6ei05elUOtC|vv@S=$RwWPqVknn&E z0!-)@n|N78SrGRXYT2=Y$}%G|k^@P|ssC3uVnYfoGT5mqg=4vsgglNEze z0f1@(v%?>PM`SQN0Q$yy`>ij%mL;iQ^=9KSuZUkrO%e)j49O#a^1O5&Eulyh#9!dI5Bw>qdi$zufuQSoFdU+{&A3;qE9yl6DN&!b6Dn!UtuY~#_B^W#AXDyRHl8*K)OoO92ii*XBap)YP#Y$|~% zmPg zu6F##&kk)0G` zR{Qmhm9<)y<SCnRIlz~wu3kXH$ z{laM_D-efbsDl~#)(Nj6l<*ZW^a|o-i7$9Z2F1wpA2>#a_Rj&p1GLTgNDCMNsf?0H zH;-xGfH$$E#%hA026cmf@W>G^(gTI+Bnk1OAHJgn@ajN%;vjP8l$L@WT>}GG=(2h_puu2D506@I*@Nf?r4xFck8G$6PMRot-!) zaRNruM7RbZ$8)>A=60`k(|3YOWtODS6y|o}K#{{O50+F)kuVGx_DGcw3i(>qSzqs2 zw(I=x1(r81){43ohY^BpFoRemS}b;sM}FWg!gw^9fBEg32p8I|V71p+?RO{R z#pXtTV{>gV96x%x|NeuGcfNFgrBiRU8;CPZ!-~_`^Smr8+I6p0_Yrg=s#ss`xUTc` z#nCSwAFX$5v&AC861Ic~3lVUDKMn&9Z3>psQ?d~$Ba0HHamt5@VnH1WsTvU?>ZB#% z!K)an;9-+16v$Gb0*XWaU8$Dpyy_Qw9-oF&Tw1we_*v5>f= zjfbjM!7s%^x|SshLhO@As6n)Y`QGB<6-$-MwYAnSfAPs1_itl}TDE07uFfwU5`$?u zdBQ8VXbMm_aPuO&xEMyMdP?t5>&c5*oa@nM``BJVV-0BAOAe1}Jo zVUSK|p^!xJLOUk>lvdKz$(mO8>=!2^7#hY838mPVl4;%jx@nRsxrhS~gaN`c{b*js zvAQ;a(1(%@ytV(EbT$xXa|ptXtnNEqB2eM7suntXmM-1_b8mA7K~9vY%V$kq2LZUflI6r zm`D<+VTPRY1Pn@*o1{(|u^`BkM%V}@x^xUSQ7K0sHfRhw0i&eHx%4YRcW^PG1cdl{ z&7IGaYRi8TPValw{l|l?&GwIec`yliURXU+Ata#>vZ=%lDx_P4bch*nfTbAiByg?Y z`SzCuGbkfJUEHb1vrixI zkH_Qdn`;n7l4jFIymxpy5%YNGcGIzp{Mp>wsvciVR=Ty5^RY3^P55U>R7m}`uf=Zn z={!VT5DGW~Wt=D&t8x5MT4*Z5V*{$SfC4CW5(_8H7egSr!n{U?`JSq!D&XR>2(wFC zX;sHF-fugeMfJe1%rY%3oPLlPR-VL1yzMdtsztoKJCeAs^v9mWCV(Ctund73VN@Z_ zOz4sVwj_iqSvFmk5CCX_Ux7&r@sO%oz?T?MA$!S}aZ&`1vC*kUk1z9FuU!uYi}{Pa z)AREgTL0w3=U;vQZYwLM-`(?Xx6)CpyJlq;85S>&GF;#iLD!N&CrH0)!36)lS2*%+*TuwZkg(`>hcz^ON?!{Oxd&dJ?t?K`(`G+Ui& zqaH=k&fei7MvRiq7vZ&y&Sbhc8;o4XI93q>gah~Qver{V9Cc-pLF9);1aF%9>PD~H; zO<5q|sPYb@%CntaAMUtP0mi_05x2HG4HCtGwKAE^g*1d3SZ&*&&2b#i0&fQKV48Jp zb2#PmFTyP)gm$op-u-WZ2E;~uW&-{K?V$wtKvNIrGX@)+Ky-{{I3^5Y-}U6Q0v0_y zOAfk6%Ylnk4}@5q@QG)4?g zBy#lxG#G&Ay{6I<>muk3wxs+Z{defquubo{N5X$AEa@Ja1Sy! z3^UI%!?Y0vLK$VABeFyWt7_AB_}a}PMW|uH=h>Eg<}3-SwQJXJzJ6<^?ifG(@M++h z?Pd)!oQ$Vu7vs@n0e+txhSyeH=Q3^HXbfi|EOj)FS6bD4L}?4dzy9K{Yb!#&3b#*M&=iu#!pkD>KW})6mWH424u{hSwlDbKc9x_-R+J&nkVP* z{0VK%^Yk#PG%Umt)*>rByNDwrt7x`cL?H?Ftvrq@QC_j_(sZ>v;|xi|K=5Jkk^`a) z*Z}Q{Gc)KUjm&{V3~UV59IOiUkhPlCdFBA&4Vi&-va*83K3~zZ6ara611e1Tk~Yf$ zfC}K?>2eB-)d~PxrFGFN!FVKFbc5(0Io|k5tXLmNYdOcYBn+j*E1jcA=x1AE6h*O^ z=0Yziie+s^0NNV?R9+IR*$jIbYWzVkMGhiO@s>AN8@K9iQ`e?(7RoK9(n58GgdCBv zBXuA<%2(5>H7|_fpcdS@vG&Q6!!$|n-`;4oYG=n62Pc=AyfWOi?bU9>uLi^6`176P zZo9Fyu`-^7NgTEKRmCEV!Ym1~3`3w8FXA-ji$)8+9R(hVUuKxcspkc7U{RRE{kopz zxmLYirJvF(C&!n3pJ^O#Z?rqT4kR4TW{;jc|9s~h@w)F-TJ?IFm(6;$+p44C*>D7B zmFMU;?zO9{eJoKOm?aKpi%6D&GCGB8#Vc}4VNjk+QK&q3N+c95<%FLCm+{BOobyy7 zAe!+HNrn(7OAJ#skb3zsw}k(qmBKEQtCGG{yoBL^mx5&jXwrZZIS63>Y_P0YViw{= z*T8{e1)$)iuO)tVB=E8onpuT%<^^5=iGbw68+c1X4Jn==F*%-&ez&y}j1z^_uryf1RiQOp{MpWEsZ2WxE)A znBBsV-?@e>=KJDCQ&2n&FLN%%x$lcw4ED3Wl9?1{|DUFWrLL9qxhaEv*vM5MuGHw|Y# z8OxH2OuEsyEQ%pN~fymg`Q+PB-|)r^mx7ABCOs^ahbBe#xq^DNGWf z`Umw;5CRSe=lWV}dutV{tkoJxG-s$`VRk7Oi@D=@FoQUbydVIn;bO)MUW`BZ?P`7- zCo2F9n*n$6V%lH3W@`Dpo16cifBS$*dDe`kQOyTrp!eNLGrR4mGh1K!KQ8HgdsA#qt+G4Inw$-*XhhN2cb}$^yV%Xy>1dmjc zYD@K#u$MBXU8n^4suFaq0#oQ)ltL(4vuH=!s@6gg$(D>bPyv@xfm6*%GY-hFN{T-% zPWgg9C#!hd-s7F4rf=J3Wqq{;fRoGVwT*6TqZ3_Bjp3rw^tE{kv(kNAU%>c48yDrJ zgTNv;(oCx%pa(|GMGPH?9aAj$t?)y;jPTe9d%2TTzOBP+irC7NuB2J+)qpsJOdOJ8 zTC-ZZDFQGcZP)Gen%7sFHv^|$(WXfj!7i8c8d)J7!UK^m59Lo*GTmNn?_}Jpc@OSx z&KKd+-LvQWXODJH_KpXZrNdcm^xG>dZQF8T&T+&KX<^-X=jPfYr2lW1@0m5b!#i#=F{iLpFBGr zP9n@BizwlUCZCKtnNi}CBnnc86vLHZnS%l#6QwrTzl;pPQ~R7YULIf%^z?nwP*q(C zl`RE^;Hr3uiTz%6C6K=c&(bSWP$X){F-_#x8=w>!fn}23m6;Gg0P#me%+n%&jUK^! z)sfn~vcr$?OO}{eMRHz+QF~HEc&>Q?LtFg<4D`Gr9~5y5qlI*3IK4z9d6iR~%1>rE zZcW31Az9{~?OwC)U%Po7&NhuB2UdcxHZ9a?R9G3Bp@Hk#2$>N&EW+qwI6-f6E0BrE z17_jcCU1;5xIoAZ)A1auff>&>{aOueVW%H|`n=h!IyM3ZuG!^j3*H@q^)D~ymqnZ; zSU$QAPja9axx^_xrL45>{T5m|ymZPpK$@1zOS2l{h$5QJARex9cr});sNQPobm5t9 znRFXOz-y>>rD&|)I(YK4Gzq`?&;F|yAAj%3$>{eUth<&OCRv!}fop}y@;#*d{hGZY zsr8F?GFBG#5kN6{#FOa#&Gn0LF_llBP)Xv-I9hlw9pAmd3p@}g*`jM; zC$iSgxQ>AUgjlgo)S_#YVE$&m24+LrA@C?pF*yN}XK%DhpzCY4d%xDXJRTEJZme-# zGi#gQIXiYW(OhLZraNPf^?ONe(kbkwLk) zul4WV+)Vf$o}8~1izCpJzyNG3O;e~ej%KFiKo%&3TLB$u9GjTaz*AAAal$iX8dgP% zXg=pH9h&~`{cTH6|JDESVHicNdN6>G;XM$DDf4Z^t9AX8#iCJlXN#l?+dEp&!Z}{b zSD#Y(<7+)AiiR)JhRirDfMuyhDh(o6x}z;1@iIA!WrH2zVe4!~dy{kEvhswik=1S8 z?6{XvoTpj0<$EUFYMGBquj{2g-;%p`JUSgNY(twY7&~0`B^1yOh*-)x5!H=8_87?h(eA5&k6#ekXX?bkEK?^qMThbzLDmH!w+cbX;C5~CEmP( zWefJa&H_{zc!~kRByH4r6=YaM>P02oqY-+6MY07@kSc|fQ5DaLpaLwxpOuxYV(>m# zc1>8#cI@e=JFUv?X{ciz4Y)x0>o z3>>Rf_g#!Y%i3J;wHj44@LfkY%a0x(MsZyC9l7s{a>QC=HeJ9(0^hf=+)UGIJ%GcC z=GpP-;PCj&51jR0e>k|Dhsk&rUR;hYE+?yShSOP=#Jc4ey4~$m+pQ)zg*VvQ*`LSx z_3hO-O`h+b42NN_UAwl{-dJgY-OrvJlvq7!MXgcqb=r&R{6`->NBHhLW*EhM1d>u? zqJBlP!Yvc_RO50$u|O58Ev=umqA-@>#~_fQzF5jKiP*2;q<~13Os@{PV|yj=ml*}H zq)I{XP<}6)a}FR1nW5QEwO%4Zl5whn9wyQwIEX^<2!0-`E~HLnxkmzmD`YGep)BiF zt;+CN0T3gz()AKRYGltGJ*I5&&q<5;XxmE=deVFwF~{;ehsafg^@_f*fgsWS zyIW0+$N4aVJ|JJqG#b8pv)Vw&a-Jb7XQ%{oT-Sif=q{NPEN##j~Erj_#56c%qRMueK@P^IU4zzGZp zU7*qNU|6j;pj+rGNm52hAU)4#!%?kX_X9tT7vKEK>;L92f9@kf%8S{8m!&WdzWwe- zWs+4_gD_4{&!_dOqhBn^Mtb!<9DlUc1KAK9EJ_N<7mQ3KCqrE#MIghG+81c?{X~YP)P@g0@a%|l*B_p zuE3b2Id`)tYXla?8{~ODSU?k#DaHhEep>0(S5|7}*?hhmYDSK=NQ`O!a>DPyGH5RJ zs!@iSkrkR}W|J6Bq2lv9VhA{LxB-`MYbmiFEv+(1z!k?3xTF9}Kxq=?@Nx3?5FU{Lmoca^2}jcbj3AeDHv%uy7g!TAs7Yj`Ijcws5DJm(obdco zKf@`MP(X~9xRjz}V?CPzNddBK$FVJAHk>~j%@-LzwFuKCgyIByLCPezzF{ERE}W#+ zYx+O^bhpgX2e03F{r1L@-aMzp}V;R)Zy8<&2~ z4QI0?<~hIdX!6m+J=nv|>#Lv;^GL7VL}+t9m|_S{7qhi~3y#98x~*0nVmUfF|M}x1 zto`8h;FjenLM3XZm+T^`JgdaGrHDU~zp8o_p7Ox~$~Ya0{5UEU0JdUm?BL3IKDeO6-G$YSam&jPR@Rs#Opn6OtC zb_LH``MqQc&>|}Aui%MQ+@c_tv^+#Fq@0jqsY=QOTjJ*iKZ;dasmThJ0=b&wR$y*`5AIv%;J-=Jkg<* znTeccU`g%pEPe6fWQ6$}Q?hd2;Inc2oA4-b`+AYlu^BeTKGz#sxtLD5Vx=d*q6=(Q zz>(Z)#&4~{w$Plm1V>k9v$y~bEM;v2)2q34_cwPw{_g%@28`W+?;nFZt$HqRjiyYQ zlz5jyCN0$0Xr7FRk-2WSR)U`AA;eo-_C64#o?`pvp6)&YrJxHjhwZvz z_q;Lznh{y57m~`y|B#PhL~)#=OFmwkMGQ}~{PsrsSD)`s`B)7Fe68oMwW{&+Y4c7C z>u-cmIg|$aXHMw06t2Pn=Y;IPd~nDSvlj!^WC zB;tK3XOgDMSww1(Fa|&e>13z_TcyQ9?W7B&6CVF4TjubCSzT3uQD!8m9dQy`O4XJS z=((&orvBPSb?0b?@m8z5fXmao)vh9{Tki#dW{!V#W+bVGpd>8}UUt)qx|c?&8yK2Z zR_0k@>bYqaNutZ}(o9E(*0sEVC$K@7jR;gP%u1SQo?Agpl9zlMi6J(Z^LzvdL%fjd zEF2@G8XNEe&RKUwJ^ChtqExVXRmIo>N~TgVV8|YUd9z4`^SFuwWbCjIrCfiB5dcz_S4PSXUw^sTpFYqzUOxG+uY`1leYKJYy_(CKWkvDQ7im|$Mt-d5-!)SqJQBe&avuYHQ`o$4@VgO=eWb+I8S0M zM`Nv+&8BIZPbSm(Z2sxv!)M3CckgfY`n|)W)6btC>SZ~eEU?Bw^mB4HoK5DV@vPr# z#c6SIIo>(AIJ%hLyVk*)=W;ydt##y!niEqer_`O~)uC+h6HzY9Mbpbtx&kR@6-F6E zx&(m$mn~#Z4ANrh5>yM>D07hslo@HHzzINj<^`4DQWPvlsr-qNebIp1x|l^lN2FaDQ#Bn6K{)xUygW{F0X zNd(8~OhOAu+)Lx-5hr4rvTGZN5=PU-I}dL7uG{Oh5YpP18!*y{Q;X)a;pNctJjZig z-@}{)laG^lJe{X`Hji>d;DLvgcNvE9dat>?wf>ji`$@0UM5s~a=?$-13(y7wyT6zx zS(N7Q+-UD#hUhYI<=y8ZYLP6(EC=F8n6$|v`_fwTqilY^**rd;`kuK6)3eKY+qWCO zdxl6})+lH~*P=wv*Equt)hA&&Y~W=;I^sNdTPh6`9;Y`hoLIrGMUk8bc>zlBAWsEi zs`zrX<4m%P)A7ma1QWC4T49)WJJm4CcGCGUN!zCVn%6i#ow&C7;iFSJ!%~~c1F__a zf6~R3q1;$&ZLhWJfw$7G9qjEteE9iKfBKUTe(;0u|K(rYySf%-c%4HEeBDmA3zBO)?N-@T!@C?4S{Bv$KXFr;(DvyY}Gq8 z^Vy4IehIKB?`~FGHGUzsy0q|ft!3vC#aYq*^p$U^{Lxldgo5Er?v|bc zKp1iY%q69;M`>fn@QJlZbl;c~xZNoNa)9~s32$G32ElSjF1Vqg~nAjcv1Rsw{m@Im(RtV?}g&-5fo zsmvcwbYH^)00m_f>rF?~Y>d)M-73ea9+pPe$-@-EnBiMmxF`#q_jWNP)v{Tt)oePc zG|U}CLs%K|vqQ973Ipb^f;>$(P2TRnS9J+lw0TAm-9!p?JX~SWpbKf|$3cq6PVc zg6w*`dTXt9x8~Kg$~aELjGU7N;zW|5lO%GBg-w)1=+|}a;b?YvI(l( z@#Dkwx_fwdzISke05XX}9pzyh7a3eDIP+bYmAP0XyN8z_J~{SX`^#_NtT*b%hi6Hg zt*`Yk$E>Y%_YTjCf-h~F$7#1+J-wXmACEqHae|4X*R1ZHT(T-mmg1)_UN(?b^%AKhfyZ8cSEi%rAsO@(3O`L9iS3Z#Az_wA#f{K|(wF4Ro{g936;Uc| zJp7`Nc-Z7+gR3h10M8603%-{3qiH6VAR-c!U-_~M?1}R&U&vFzJQO&_d#`UJP?#^m zMzdjBj#k33_?43^k4F~+$Fp0#u3;KcoFE{;3J!7hB92D54Q}G#H<(?6H2f5Pqf^OgBBgm}0pJ z7Q_-N)$3mo+UYtC9c@7ammp}j7>$4g4n?^bFYwb`%P>ihWhy{9JB)gsTgl-dK?ctR zV(9pOaJchWGpf<#Ajyi&wzt)-9$*$hk9;RsjEoX86(tF|m+AB({PLadL{D#a>KI)R z&sx8R(O5N|q>`UySQ6{BC)f;)1n#GR=wU=xcV>_wnbqhYvG`#gy|J7=OKF^MHKBGH zVL=*P9r9iAnpH7=XKizEIypX{j%RT#aGG_m-KrU$IUUTMs&kUW{22Y-czv_^lV2WQ zj3VGU8Sz@XN;p)ro#F}kyIZ}QXMxVi6mv;|WfchXYzv-f_r>n>hoArLkA7&D5#l1t za!6IF=UL2Xq9|83Z*JXs>)`1}2=v6iYkswwCJE1rm{g-{+b|y>L`N_Os>^V1 z{Gb|mv*~2-cmP&koGz}fdHNvn+upp)2IIwAx8_<_axz!czWVXDKe^tcaDYx^2N}{d z;DjM{N>DE7F@O^x;s>*VsUWBT?^&5i{6sj~7bT9BJk^~kJd_Pudi{2-9So=QEGt&K z&6ep!kH=+UI$c9Ucsh?h-@C-{0Cs-#P=*@2V`Gj$b|FVZ&As#_1<?6|I3}&{&nV zaf#0E$r=5UVnFC5fGCchbQ#VPC(;tFWRRpgZUmxTwc%}{5D1{|n^-ki7;!0Qk!H=# z;v(9g!R|7XZwf}pgOJET5a~HZ7;E#gXgb6)jLe!<=oL(Sy#K(liYzi15ah5JOv(_30R$bQ@X||v*CVmnE1Z>HCmgV)j_h{$vax}}e%0|E5 ztoh&j*^?}s-MV?L)u@G0!uNW4ZnK8Ppc992trlS7pU+d<(Hk`v-_v;5rg^kA&tDw;%fI+#lB8Ha3`UFLXbMXBMsv$>Ee-3lpFZ5*JsrX#-nh}nBKGOt8CHF8 zs>0t=kz^QI3Xx@$qQqMYMpR1SQE$u1QB}zpq2TF{=|^QSDQrwK2nAGG0P=!Wp_4+w zBe6*Iv_!pxmxCod^GkSDC#abftcrRdD2g#6!*af+%A*g$DTDpm}CVRaPg1$m*ChDUb0N8OtOkLx-d1@!peEV7N#w| zLe5-&3MriQ4aRy^*N(#QH@^H9NW!`k5h!44?N*7Hbus1h1z7IrCKLqU0GK$MPiM2E zlgpNT18t715EdbPS8C<6b2~< zXye1(=+3ZQEP60AflXd1OfxLHPKR^HGC7r%W&P$iDu^=>kAQOn)0Y902Y#4IG?~JR zN`4hTLtO6pwUiH`&UJ9})|P`;h93^Z9zy zUu}3{TBLdDV7v3T|Z^bALR+uVVb|O(s-0DRvLab#Xv0MG#d=(V6xY#KAO+a z3-k@4flEwzXe4*D@YN$g$SIIFFQNb>(166h!iTaQwmh96J;CEpX=uXp*e06sATJ(x zjf1^GHGnFOYG45imfAtX(^}@vVg{plr`g@zx$HE9AN}$GegcT+5ziqBPc)U6d!WZ0 zbN5;o0aM^ZrCbJtNrosbjxL%F&h1eSS~Pz1+*2}&_) zd>J|9s{DhEFqC(3Qq~kZ)PbRE>RM9fNo=lp2<#ADg-KD>t>iccl(*t#c9AFf^Mlc3 zkwEpch^R!J=#4Z8ZY6DIffrjEg8;((IaZ~fwX|85E6ocGX}yA99-@J2!U8F0TiKF& zg@>i$D?}>bvBmQZrETehX<85LX2YG#BE$ zzaoPLB9w7VElK(P50IB*M&`V&xIqDjn<;7m(gHC$qjKgMv@@y`*3h58Ltt{6iU&X= z(on(>A;_`F4t_&elgGPI#R*@Ks3wR)p&GQuAq&NTP!DH{VtHZ~lq@?!0ull;QX+yS zdDsyJz)*-9UXoLhk3z`XRe8h~B%PXC>Jw1Jis3IgAdwFE%TjNeYPo|wtwH~+#T59#` z^_w?0*ZXFf-MW3vckRVuF`0z>C!=SF!;A5JI*(4yr_Cys4=!8*+!BHSI2?%6cCcct zOvYo#zujt1=gDY1Yt(EktFkm3jTd{TBP`^$SL$#lfAzPIvF3U2-dd;G!0HrCVc7{G zB``n7{os=W^aXz5v#+nV4$sDmFoG)}TS`OVOo^F?N>qP0sTw6!3?6C$fNUr`NkAcI zI9^GdRghSxG^*N2V-6kmO4}q1w>acajl);+%M7=K7nEwtgz{xh?WC>jnG<-08<5=<29K*A4thH;-yYIXX#|N@p&uw(NpfwIdEKeQZ zH*_lq0&s>ULz<)?{_^wFv(e=^^c=T}`37zp)2U@H_=4}UQ42nKw6nS1Lm2{X%rUhp z+kys!GF4tuNipU?D~cc#@I`l6@}4k_PuilUM8v?w5N|BYVXr8wUH?t`O(g`V6suOw z%Q(=Z@u(;>&UTf`%KCPiq6O~}F%Z+h+hL+h;jF=KzDQyYWmTn>v)OcTdJs;AL38E$ zyMJ`@LL91OAYm@SgJ?__x~k5aGWeE;HP5U1OoZw|9|C;0fYF(eB z&5v$fU-27l1}7M`;D#rg^R?^ud+WE3UwoGH5?N|G4y4VO6{Z;=uoUFF*jxn?@xBtAv$s zZ=u)t%z2*Xuq&*vQwSx^`5qg@fN^SNWnw#JmY4~nY0XAt0Ev~As3;k)6}&k+qq&xF z2!@4tN=|o`y6^LHNtRa}%=>0Jneh{VFdYOaFb1B~;8voI?9~FF1*Y{Q-a(h7@w%Q8^r{Q@Ufn zG%kWOkYW_V5LZGFeqTUUK^KBpFbJ$LCYWVQkEifUd0^X>9jvcdG8DXKz)#x@E=~X##+Dd9Go|LL@kJYCm==*xr>jw01wR7?~CpfdPd9+3vxjgZ2Us|t1SvP4S1ND4bQ zNWg&BDzC7!)zwDTVgn9@NR%=|+Qc4IcG->_q89WD14{@XX_;9bEz`@RmpHh4rHQnX zvSkr|#JM~aSp0D-8}X(bI1*QYn3AUwxZISO!{hw>F`c zTC<@$+s8BTae! z!}F}i&kr}(yN+ej>>S$n)yk6xq$M<5GN5hez2o+CP-m{~2uhWB$Yl#XM5 zA*Htc8(cksAMn64F0$t$s3>)_h!+$`1>+~+3;KuzKTPvjYQY=Gm1FtJ7?-mkSH}p6 zhZm=xfADA3&RS>fM$l|--T!ub_15W&UmTAX_tqOn!v)`WDff^z0%s~=hnR!do07Yx zrCFIq`K?av#7^CL*{FNN(W2e-^P;F5&VxqlAeu!5=2W@yoSupA?4m2YdN(rX9Zp1Y zCKlvUDs(J_N32mWc4Tk{pK9=(S~bo5l2f~Gcq^v6zdsnyl3UkWm!n11x4~wsU44{I zj>4#6T3@QQAAWkWz1jZ$Pxtxgji#Q4T^f#9kg^z={VJ8;e)r}Zch>7wf9L3O_hhib zH@0be$Cq?U75$e_4%!X>yFY)n-faX}X|=1JX7#hrpZu$T^`~#Y@tWhgfGIK6V0z}e z&x_S-uk|)=9`8KlyIOe1XiAQ_n&$}~&kHcC!H^xtoiFC_c$RGs2LnugI|rALRNd3R z^c!29nm<`2u47yb=NqdHuhg^41V-|AUTuz8O3qeusKWFWQ^F|ooOhJl0sw_CV8T9p zJxl^&o+~#g%Y7-3HpryQ2Mi}wr~->qvF(_yUv%9b^zGPIc90RZ?VjP$c? zN&6uvAymMj$~llE_J;!GUKnWs111JJVE57z01BxxMU<P=Bk|Fz>?d<%G2?@YQ3iG*5%1rIG=v!yC1}9`qi(0`QH845D0*W#Vq{A zr%&7U0KFg_0bOAX%g#FWU}x{7(`^S;HwgGqpc?T|f^g!tELWX}6w5bIgc9%hvMo zRh|`AmvwXcNnj&G%Seer;8qeZcxzgw-VTDoQum7TJ-20~+ITd_cwX%VSXI`1tJMnX zbq@~x$CFD)^QPn5qavu%DsZ$)tO$VcI6lpwsX*#BPHa@$$d>wZ~$+92sIxFn%OB6dYlY&?uDK$B(sY zOTkK0z+%>tTP5Vq234e1ql7_-iY+gOVbX)&*1c7~ur1f9V4c`)*3v<4)$H8O4a>j0 znC_qPqyGRy&j=P&C+t%OEUgkn(!HRUVb1HAqGOWwGO?DUnBztz#y~V70xbb)-apKiI?KivF$RE|{;Yyr0BNLEg63R zw`sw9kvhbmHqQ@fWX(j#QSfeJi6pRQlHp6)QMt%VjINjhOFZR@Bfd$Dj&;ee;+11ZHv`NJOL&J_96{0U8Xc7Z&U!ZQ4bg9uT@=wyjriB&``JM5tbL{LHxM>JTZ zURcA?g{;Dbyr_XZ$>Z?`x}+Nr#DFKYgeK3zHHug#<+4W!jRLi_NpOK+-U)o8)9+!K z_u5AH7Mwy_>|c)gm3jn)uyDxOq>Z_JK*s3SgIn8c+Z!Ep_;~MP|8#tIKAH@Nj#&w6 z0mk5?$NP&}bnnK>>$g@`dhPjqz6euZkn?N-`-x?+URes@kJo;Epd_tAksD+5- z>H70oggC8HwGnNMrVEfO0ucELzaoI;DGI7WWF~oRL@uH*B_$Xq)s7$2m4vUF6Ao24 zP$*DHrHorSR69wMT^4{B zG(ZO?@0Kv$e{kzx{LlW$AO8NgfB$!X=j&g4@2g*a@Q1(s-tYa^JHP$4x4!ZIz2A8A z#?6iHkN@;Ia`E-)8pNb{xayS_cv}gI&CZnp8f1EqWL&aLr@4YUQVMd z*QzcbdZSm>%39#gM z_I@~bv3i4R1-6{N1`Kc&N7b@BkVppvejpGyUnmeVk~j%O^U@2$LB12>mgQTs;^bt4 zmXKeIXH)3-PtK;uFKq3}blP>D_glTs9-VG)w!ZVhUWBDyK-Z+qqX;PeG}h0@Xcd@_@_#bC8& z5~`75Ycd!-Px)Yr?GBV&Y}o?&E|2ZS5FV;!1JfCylM20_~I{d z(dvf|Wb8sLV3S%RcihMUM-#^gwWy4V_wd4AAQ%&K6Z9Y>6B?jd^X_Vr&081>BUq~% zeq}ZXy+y5N=<~D;=ZHotmOY;?_D=Y!RK(wCmSs5tEajDCtc%&;$3BH5lt{QhV&srX z<{pX9*-+YY-M|qoffNY?$waF*su43#pLDRqj=ZpsBD!5j2BoE$8gJFG5)Cm6hNzK? zpd4LG_<{``O9sut07f{chpFaO`O+{%7Q7YL;A5qH0NpG>6V^X^##!F5dH#V-bi~3C zI?R#p7Q@m3a)hYBznppEO+lP|WZ;0erC1D(Fv^*roS=9{1sQBn%oY@eoa0yuPz;|s zAqJ5m{v=T3Fa+}D(8M9n#*u!ulF$OB9HcNT$XW16Vg-_bQScmApbJO{!?lF;f@)D0 zj0{asLVv&&u=qoa8yY6pS5!SMl9#>2TYw!P5zR^%#CzG8D>F29T&LS_Tx$eQls~_m zm^BY82L%3@+RM|MNsKEqw!?t#p$O{4+7slJQ?hrT<#tXqHrPi)o7nQI~`6jy3&WwkKu=r zUg}C56*5Y4w07($-(?|%&P3rdFT;U(r4%_LJhmK1#7Z%%M#Lyc_$LMg1oEf)kWN@G zTPap8Siy3319PQ~L1(C!e-1p=fmi_H)v@G-L@82u*hY$(Q9wcvIe(>Gv8)oRS*q~h zPs(saiU~}DU&0F>m0`JntAZtdHIYlVihWg%#4OOL1EiPbD8ot!hsy85n|%BI*Ls~+ zt<%S>Y`eZ+YxuQhqt$PAx}E;&+Q!zMyZ7$iy?OBBal6yndvSQ<&W$9DPxcN!`Q%Zr z*ZY%y@{h0IzFn4SJP$!AFUrC~i+nPj9Gwn$(QdW?DS=~Sa*R{0Q}Ynows<~6K16nM|{wf%3~T|H zYRsVsxu8ino#`fsDiNXQjA-Z=cl9zc4uv? z-fqHUG`iPTZ@<6y$#=i@pZp)!?tSy{(GMbw(ozdt3qBh7pbrdW1Xv<}VS_)c8~D=h zi($Ok^nO}Szr4C~c|J`t1WI`n=c8%Z^6ag8ZPW5vg)zrUnXg8scGwzjlCLa7;vFos z9$GDG6m~V)z}IMZ%%D~p+m8QMy?1doIXIox0w>K1%Q75{{6#vSC+l17zZx6^_5Gke z*`N0M)u+$ScTeVUR*-d)^12lx81w?mf&^(pUr<7>Fd7-vfshIXscC=uoe#eIlZSi9 zmrwUkK7DfVKmTWc+^AwDoSqM7yypv3j%A-;PB7SNfw%MQ`G5a!|MW|5-E;k_VY@2O z#EgMHI;%H&8{6lHJMf;-JhW{aN+I8f2TR`70YkvVAII^p9_^EHty1%}Mp$WXRA&M? zT_nA_pB&9G54?I0$3NO$f#J~UGWwM9g=6wZPStp0MnU69<_I}vGj>lfP3|VH#<%}+PsEDD+VbBPj357q9ttvQ$aeUC-iC&;M35%#TE};3ZBBz)5MTuy3rAgTd zPU%#`nB3~P9;_uUMpJ%{bG7C4n{J%sK9<6owto`w{db5eblo>9Ns{@Fh1OY31EYvA zQb*I10v3|nc8-An$rTmDchNA-3H<^)-5ZZpA^Qgqa+sMxDd^-sj>QoY z4?2e=_!rFE= zE`t^f1szT&Bh$3nokqLqw;Cv`42H9ipWx9>PtHzGFK6TV7#(4u##>Eih!uE2B}w^h zSc{*}+&?_}=;7}E(a`1R<`?_NgC{Re0dP5*pI%Hlb+78%y>_)0_=_;zIXsV(?CJj5 zER0pW4tY}4lnFN?3$hMBNms@ak@r$|OL&wlTZuRYh!tTYgz ze$hfMaxyyM8c+$r`Z4pa|*vww1Qv4U`u%U zC!ET>gl8iC1bDf*0wNr<#PQNoT)_(@rLjaMfZ$7@*gy(Z_KG+;zCuOhdE8`f79`fObHwVbKfADQ&>=T&ggo#bKQB1*ApPeg}M^R6St{2Z}955{we% z;bct5qm{Pf0t)OQB7sQ2DN3>a2S&`17^ls49hIkt`+8+$P0ruj zUa<`$3{wnId534=YWixU?q~Xn=aj%YrS)7TW8qZci?b9S;)bH3CQmRT!7# z-bCP)A0MAR-#u)W#bUVYIms!%X7cJ|v46DHlaaji$Q-A1;?zE{ zqKuI(bSF%S@v9AhS&KY%%Uh8U3sJ|w5Ak4np?*xP^*N(q^=^5F-YpKMseEa|+-!T+ zB#XzTyW&S>WSiEr-NBUi=4MH5)_ofh80-g~qi5hmKZ2gpfIKBng4ii*DRE>ecXFcO zb)aK#Ej$RV;_W1A5q<#676{3Ndu5hV2vk=|^&U8S!{a?%aaN4xDdKpr>>wWJ*Pan( zXs45SzDQlOGD)&PZuQVhzHdcCbTvXm$hW1XQKkh3UoopYnVuo0h@un&Adh*9L-%c% zn-&(OWguR0F|HFWM|_8XQp zk;3Kh;0NU3pTwmK9q2<&-LaAq2Gi6G`(sBCGf8A%#9K+kz)&N)K(*inJlK*F+9Yr9 zDCL|9=_kBkXI3?)*CSy za?5-?i?S@oq#hzdOjF-=>YlyYX*jlVI+$W{6?oR}!5LVDzsSNxqgj)I$#)kmqBzRn zcKvx6ee`hWhac@e**kx>fA)Cy^vTZ2{_(}r-Lp?V-+T1*=;KGbpFKGo4M!L}kDs2L zk7n;axcTOr_uIV|T$b-tu3zgz_+gUV*zD8-cRGo74o;sR4$j7L&9x5CNAOu-k75IR zlofcRJXB(VI1mOUtmiT7MH*L!kQK%DMN$CLkV{@wqN|(g}Mbeqg{Bg+OzKTaw@uNMzKp5`}mHibj>HAFBzk z4zQCv(9&!LuQXKUqz8LxAoNJJl+)v=62Y%pVlPdlwIY(#NFrJ(gG^D2=Ks_G(YLcK zL#R>ad}*}pI7t#G@d5@2k4~)$7xUS;)>^?L4u*CAop<`1HxZI7CZps17nbYssgX2x zUC(ly6j4ANzW4Y3;a9%)ji3GShgjpFS41Ico@1TeX;xR(yN{n8I|wc;s~-5ZS`~dD zrU~b>MHo7c%Tua3+7<9=@Mi`;IghNxh>^@PRzhF__Vh~5fFsMs!P{A-uyu<<5jLq^ z5un@?Fu+jD%eYYu68TCg1qO7K1}{j%X3`XaeC~SH%kzuV{b%#x2^LtJuf5~@m`30q zDrtfx805%1?1P|&Fzo#FIEyYPi^S*GlXbXhini2h@+wj#ZV-%B0+zG$Fuj~5YrfrT zx*ttPTZq{$2l2k^Shi)PNpXHL0Uf?++-SF6L~{s#)v!Z4R9;agVgv-Bof;8`Dcr%c zyxnMaa=Ws~M;FuCG(wm4sss47ZZ(6%@&Xf^EZTemvD`uO;G z5X#f~{A{`qtV~7qLP5$&hO5B^K;?7!$H+rs6w*ad3Vmr*fA&{@`|0O<|KmUTt<&=n zUzY;AvJgrZqe+A488i|uYa)J=ad8GkxJy~ zNia*2UKt^X87CVj0Vnys$6SDLzZk8k zPvYq0bo6vEKh4tV41r%6)LeLa=o&uRaf~o2qByMwPT;#(Il`6Is$REQb*+l)7_Q@- z4#!+{RR;b=4i02XT?Z!}M^NL%T)EnR!EcX~RQry0CT*=q$D_(@s>p6%LpSuyIq zXBm8rDS{fvW^=Xm_LtrOZ&*TR2Nx4W=Ud$x7ORt4 zC;|gJOwOBIl(;1!ueL}pYt=?ZGwp$63TauZVcL;n;G;%qkjf6ANIC;qei2Zi zrU@-kQln5gTk(*E(gCD%RlpXK`J#l|rSIRK@MxLAmfjG5R}}(^jY?>*nkoVS#=ekOP~uFLG*F8STqUH8 z<#-;2Z@u}N$*UGlz1hO_5!8Yx3^mvg%vfH-6))zF0mzCxc#N}+IG-dsWw+ytJhYxUn41c<=wA-?aN>P+IwIA^4ssf|CO(Q^Lu~&-PL~6 zwyo2PNxkNKzFqY^p5dV1X5bIEDx3fRA#Rb^wEen`y1wJl%U-W^)WCTi1z~&I@!@s-BI(L{3UA zN;3xwH7rF-4Ws6Mx3-%n&ll7C^+p=;1F0#(wJdjCYv*79ztMEPZNIwWI`^E$0S0BI zsF}u1*X!FZm`5+Lm};SK>h2_pJBGdG23}O2o=q{r(Mi8solIkh$92qRGpJS3vic|) z{Aw_qrA0S4HY?u2@uU?vKl=D2kp~VgC;SSDSPD%SQpOh30X>v5CuLh=pfn!vDMv`; zijYXWQ$)RljQ?8YM?d>)_we-Z|JHkmVCA`deozMRQ4DD&y>{b2{Kvn1=iLWxP{%-l zvN>F&|7L&vZ0~WNWrheI?lsGEhJAVd;jf-x5vC@C&AzwMtED^Br02-@@jaAgXAwf1 zR|Aee*WKBbtnpo!2vaTwafs#bSOD- zM985&a5>&qn;uU9B7i81gr8$GTpPia;m8~}40)3@;L*r6bjv`%08``pGc@1G=Q&R> zB0%OB2?~fjvu!g?E0)0%hzQI}6JrhQ1-__(0HvN5JbRIuSm&6ogC#_<$TiE+9H<^a zip-ZKsHqfQP>C5?l%AobiJqC7Z)2gLFZ9yIu!I%nWreRN76CAv;Y5oJyaDLpBtng` z8eJpNqmprwjJ-H&p5dkZ=`KY)kd+Si1R~Ikc7RTlD+XaOS(rzP_Q)v9fbamyrb>%i zE`br>z`=2X4$vcAuu#bs%DQMlvf*8>JlTLQ_}&i06TE+1L@w(;G{BZ@irGS**wPS@ zVF3x{4%oP0#%PdD=HCymq^R+4#gnX?JpLCYV0!|GxJxH}| z^XO!JF`CU53xp0^8$9Lke|EXmUZamc-E?Uh#j z*7nLuySBO3?6zx;VXpP-z|-qAYJoEtCt3wgTRS_u{P@$SA3c12bTYggP8{1x5tYl= zE%r_styEpRRr9CwaPM?T&5@s_rjaP0CC}W6u``b?rA=U#Ni)%sv_=V!l{Rqa5h|)OXgL0AxB}7ewf%m!PBIqa@kli=v`V|K@(iTvjLA;O)l76)-h5>uw|7H zAPp2|ic%Vb9wjI##JuIZUw!+|=H}|r;Ze8WiRKZcY}o-~0ZbCGn{c*(DMiu3@vzW0 zi!vS07Pi}T{APFKMsMRrt+QV1u3;7rXX7}Wu_NglbUYxY^#&AUxHR2BdAUlgQORK!qtsiH}%|^9`fyGz+#=Z}sJ756Y&z_tX zi?mg-!fEt&ufOWp(DCbDyAhVxf|`?;N3;1g%X_=py64nZOxK*}3;BA!1?x8|E1fFl z0jyErM}n$rcD$dSAAd9)6}i@}SZ@Z+pL}@u&Rc6g{OSJLD1v2NPT*E(Zj=;Q4Qqi0VfqLYCIB9pS`L@wQ- zM!c;sp$<`wDTU!6hKB;6o=_?mPgPQ_z|R$@D593p-$vVA?Rp69U)yTE=2q)bKEVjW z0MN@;&6!U5(ZWTPFXEixHSbHn{A9pQI2dQTmSvioS5mBC(^9kbG)^&H==_wl&PUfx z4dX@67{!7HFj>q~-g!`!3uYBe%T~OI%e1gfJ9W%#I@h(df(3D`Rrm@^9kXGDH%M1< zMCsrNGD%`R;yc9nAShI97<>sE44qryj%PDD{n92OLeN}7jkI;@3*Ay`JbOfO@EB%7 zbt?!yslrHwcFad-8Fc^)WJs9;NarI=5`tDTmWcFWD1Zk-{39sg{U<7Rk)Va*7O9f} zfH0mw9(5=M(d_<8qeO`$ff-v!*U%_X3xR@;8bygh% zMd0~ZFCCs7ws<|nhP zQMEkJnJ=QBKRP%#8(}Rmo-Ss5DRaUPS{TOD{flWB2mFFK7sO9to}O!GDPuixI>**T1(=+loszkT~AylI}q zu3y6n3_+^p*no3=A3lsxzgASMb+nc%9(?6zj38w(g}FN()v6M37E;@^>oY@Pk;7ryVrjs8ovN4 zTlpHnjjiukQNox586lYEwq+J+@z!c{Cks#W*l`RaEWD~SnS@ySO9D^j?$HQQ)@U$q z+4jv^LraRi{Xx^U*K75zZeF*mWt_!f8qMM~%@0rcyrpI7?WT{&CQFM}!*4W#^Rww^ z#pLOH3Wn>s`BtNOu{+*as~_x-KieH8az+*l5zxy?o-_ju_X?Sj;20{^+ zU{vA|2>}2FA~K=qAVaS7MdN%Ah}7|91C|~#&C{*pP+Sx2UanzrH3$DdYX*=YgoFXf z5*)(%Wx(qQ$@$bDi+L#nKxjk&YD(D;kg^j7LMT!Iw4DNf^hr<o^CeBXI1ywi;X@x$5yECsl1) zVS;6CbUK(~6%nPGZ-HIib?otcad0-KgsEUq%F%%gtXmzzCvjm)Y0I=qF>-$ej=rR| z{9#uHfm5d8qJA z$|C}zPSGPJOL+CiBtUeos;;8NaeD9i+G?-;?Qehk^ymOfu5P~%^(FDba(x>niIHkq zv|TntD=0}$FW8(Cbh*F6wp|>G8-#Pf=&<1qFlRXs+CB_OEqU zw|@LI6CVOXvBNmyc@bGP0N$|uY8uXf$@E;F5&FgNGJ_I{(>mqE* z-nUI&Qn9&AN9cp9(L5PW6Rc!!c59z3rn}+nu3PhJ-jiqNou-HAzSVHoRvV3)nN;oO>96LYhnv62lap```i{7~u&1>Ic90Km3>fd6}k@*&J|~&4D)M`o8(+ zfB84}?_TqQ20EzlQ4*>gc=}tnPhLEPC_xSgJU_kI*+0d>Z@qi_;Anv2vPf17%UJW^ z2@yHLZ?1Q1(b?iZ?D57@l^AdkBl&{XI3!_YFjr9sA{Sa1*g-91hyf@BBa_1u!juR> zzkv0YylRw8v5CywN^CS#*joy-OY3XR!927L9jp3QvuX2vqnX_c#^K1Z{HJ>tvoL|6 z-g^C3r_=7W>NU@r&*P_ir&XUfNPv? zZGKcGN6e8YA~Zf_1Wjud&oL9sq?XQL8i9pG^aw!;h$GGo406hvL857yh*B$v7V=W( zOCSo&V8%4g;7$s;H6cYyBTF*W0x5)^nVPl;HODg?ub3||H|u`D_jbfsd1x9e9V;C8 zA8?rFu=>0r??mT!88DH_RR&DKAn1y7s$Qv>74QIDIiNYuMxYP^kx6Umc_D0R#B#a9 zCVmKqLEP+^;SWB4@#l~C{>_WSU+_UG zz5ppKT*HEDFbH9*<#hi4W9vVIWl6F$F)T8)NG+~bPm6l5R9AIZwN^7b)7>Mm*xA9Z z8GtpA0F6r$G$a4e_zV0`qfr`R#1CS@1=rRVJG-NLre|7J>(c7IdRknqQ)hGm7&wC?0+>akWe*F0Hb38mEBKS)-+E@TP`AS0zGdXlT1e@jxEHhTpABNCTur@+| z0+Wv|#~6FuRvbgi!3W&lZy$Gt7nhs+$GwL;r+Y^|IE0W9N((jF;O3^C^4q6{ad_dR190Ef$pSYq7c&o3V9h?paA#b${AJAzOaCQax&45%< zLF{Utkivc-V;EHsilHhZ|G$+Z1{Ma~ECUyassSc2_$OXqC#=9oNeQz|{DDO2e-RCw z)i4ui!ZTNDkOIA zQZvkOJOWG=po#Phjwc;GNUs-svHHAU-KL?&jlu zJnR^Dao9dKEyr;^&QUNTl6aCq`Q|}&u7A2e8jdi*OMgT8@RK|k4v=>$s)D|L{gt5H z3`$K{7>-}_%S|{-<53SH<`+wEd^|*l6@y}&Mz(Hp$YQ>OiO)N-Xy+7K6EWZOpwKYn zWlUoZ1M!?rQ5_|8GCe)n&y%RRc4=|_5(b9w4F6L&MpD` z?+iMfNpcWHXk1ITt~%v+KiYr(x%s!=-#zL^yg6Pdz=#2zG9{5sN_=Q5@XR#cQea=9 z=V!S{PcZS&*g`>$Bg%=ogC$LarGf&gAd6`lw1pDszxumhz5Vfn|MVY!yWj1P!vtdy z>Ek$FY*l~y(>JeNTJ?f5lnZq<$1EyH7dEb+>^#VTcUriA?@6yeg6O^S!j%v1JSA#v zvZ_1Qf(KqfY>!Td&7}$^`@i^jS8^G?v(`cnLfgaA<%2XBO_aDXHNj&A<_| zC&Z$4nA^GdBMrw1T8Q4`ghmJ=K*Rg}XrnL&{yXre!_a6s?KnF;9j?sF=OxTAJ(xHP zl|eT49S{2Vq|5cP>lap*mKSZ$sTSQ@(TAUS*zO&i4w|)cx#;qam}R808~{>L6-PBq zKbjq&81$fjq>Ip3(qA}`SZ1oTbd6g75IU&@XhN!dmSxM?q4*CRqgipvfqj0X?xbe4 zAL+j1)NPoUFbapGv|e^fCEGQvUMI=&JjA%wG&r9a=9m=%(-=TzlRS_Yd;u>wPEra5 z+V!Xxy=s^fm}HpCva}FRQl!G7;h z?s>3342)O4MH?hcCly;u3)4|b&z!el)T_%1+Bl)v!Os(MWs=XogCA%ujRVftvR#85 zAUi-ZcUy8EgXN47F0_+J;1sMi-~p2XqZr5Xbi*hHL;|5#8d0>jI#Fy;F`zOi0v43M zGC5|@DVR9n=HNa7G;aAL=U9+L$n8)=jQuHw*+p)^u1`E)KC2c#Ab@J2fA z{=Ln^cOO3en}<(+xV!u7gQI(cxCcit;Ctz(x)tYB&$WE3FzBaYn!>BhvP5(Y*bnQI z_pqWgu?%kg<+6c{WfL9kS{Sg7#+aTKbfv9 z)QVjHHuq2a{b6*-O`l1t;*VnLH=u=j#1zyYM^Js-s|^05dawioFrSnIFHQ69!|qWx zgyDhKD<++3Et#xmQy%%s9u>(~S!B70_)P4{MrKLpiZMVmECn#>J8E^rB_Czj#RMnb zXTMnysZ}5e!K7It0*bPm&HxZlP{`r{o+S#nRHBZbqEcvnE0O2V;Kxs*pMnb#X$BK< zqjtU($tRa{WbN~*vtIpQDHF$j@@1R0Vd1qcIyMV~OrfC@f& zwD;VFrA}{jc(i}@`jy9z9+j&#C@I@<^GRB7&STyHB;9%l62^UjFMLcOa`89`(x|Toliu(I891BpMI8{UGqhF^mZGb@IUv4g4Xe@3t7S@7#vp%<4ZLP2+u&;aVlcW8?plh0Tl17+0_;GVx|M9I6 zrbXEFveXZYg^a?VOz{*rkb*&D+YWN*vBH?3ug<<;3U6iRaTp$K-Z9-^dHtGh+rmUL zD}x1}Pct|I1^RF-MWQ*|KsSLR!*q9U{rx07plzg0d2fXtKM8>KhRS%$h;tI}iHZmz zf?<}J+4S}Eb8nm+AH!X6rx$DW;V>!|9q!4njln3Xm-!Y1sNBov=aM8l?t~@ZYE-G{sBqDKN^fiVZP*g&-s(+hcU=eVj%;J!xjw&;Is`e zHE~jk#DVznFF(5Tbno}R^1^ACZ#ad24|`&9q5ji1-nx2aEhtu{TYwEEP{dp~cja*F z0eJn+2Y1oxQqld~3)kNH;2w~dedEgW3sy4C?80c2oc6|pVZ63f|Fbur2;mIhTb~0m zS+FKfF2AzR1wR!!wLOUzIYFE)K?lnYc{o0eUi#M4pHD(Q(oDfvEpwC$q-ChX-_b9s3InzY_1mpl_5ezoF;aq@Wk zIPjeLM%lB?ah#IxLJ`#hrd!2ySvO)k$RUnmGFJj2l@65)1R3u_r2>>@i#h=sCgL9# z&5Au&bC+Ae;$p)dO$%9EzFLGef&MCElCiWfnIuW7<%PisEX7GW)!^X*g>M=zx0D*T z$t?|OVPYHH8cfzeWymLV=mW#jo8}Y0Q{Nt8?rdBMl%n`*;^E1CR`%S0>NS_b;7{;E$##%?PrGs|p0*`US`*%lMQMoqnK^t_KR? zI>|?tj0~J1aC{R!Fw@9I%aACmcfud$vYc4CY#I>&RLDU(cd!XsZkh+9aDjZ}fe?rQ zdq9N090#h&C&wN5LnCe64f?;h*M4LF_@@WQ9}dIaG#%--U#a`0s_O-=i}`v2rQuA& zk`a2Q*&pz2ZK#ssuN$70b|XHn0>$Pz#vp{|Nkw&Wz?0=voScr?zlEvCSN)SbNH0tj zkONkwG&{M7%$0CcpR1SA`^TrFc7NO%jG@_F$1J&gj1JNcWT=cU1GD(5e(=tL5|_RC z%9^Sd{mvkIa?tGzV_EeU=|G**2`b=%_7F3{RlY|AX_y1ZE#hI2Ba0B2r5OOhQNRnC z77@cGdit4%BpjdB1SoQ!z=DVwykaT~s&q;X(-b_9>R)IP*k?zQt7zo4U{TOBa0P=4 z0T;Z|5EbF!41_SMUXk2q$!sA@i!ciK>?f_A!5}bADqy-KDEx9{@mWj)uVzueBRb3e zEELnv5(NPRPO2a7JlUVG7cI-Wckl7d=dNyTZPy!h7{X~Z=92=t9>H=Q^rL7TjmM*Y z&!w5n?G+;kD#5To291a`p?=2XlxJxYW|%E$!y5hG$h3@Nwc3{FB&nm=KoMjN zV45;^V{x@qZ&e!$d78#?IOv_m;{haC*F89<<9>HEXhVd{)dv0`skZCcPN36AE&TlU z0AdR74KfOXP2GU0uJ}!zp%oI42UpDVkWM+mI0qbJoJM8AH%o@y_UYaOzuIc9T=X2% z(hNFekUQuZb|ha#0K4ZuL7m;sIiF)uuu}1EJ)5uk>?D+4PRN32m`byVek)eQxW_@A_bmV03r-#OUNU7QAq$LRBM&(W(9^$v0g9b zN5g4W&|CV`y^+JmUU{R!#{9vFKkFTgOvivT46lBe=5|33xKDsg#rzXx81Xr#5Z%;x zM~Ix60zON@Ld`a&u-c|6@4o;kSf!}lpLAFYM@isG!@8;3Fc&J;ip zJBUY|XM=GhljHUb4q%aIh8$3UYRFRyTscvnNESD_l%bB~=f~ z<*X>-rCvm4brXQ~sSe>G+%-~r< z9eF8OJ_Vbd6^t|xpmHThaY@TpiXePil28i&3|<2BRl!-Jif`_pZY(v{m*y^Cy?p1B z+nBfAAVAkN7nkEv1V;^epY|$h5}Bj#3X>uVlX!e`c=+SL|4E}>b$usKVu&YXay%HE z92^}U9zJ^f^x*KQ*YA&pgKE7|snw5B)9ObSpq z8gz%fQ`i&5O2hTbkh=*~l4+u97&Vq{Vg&s3_E1`kauiB#PUq;xJclb)%Lt6HT|bi< zhkykGcr1AW1!G{6MD2skLHD5CI#-`xv2B~%wv?}pAxO13DV3@iOX6IxXQ`Gf2j&r! zz~ai)iBTCGKLm58Mscd!SsJ=sW9|w6%Ur12QL81Go_+;9n5855i|fljE9!Xs*+WKW9(Z$Iz0%X zeA7w8FfUudlZWkQ%}3|`=*_J`$Q26Ai$H!R9^@1_4#~jK;mBkho{E?nKrL)l@`73f zfSe)2MIaG~M6TJcS>S+2Ktzt@12@8C69*P2yy408?T;UQ=WCxE3`fvzpdpTvbL$I# z|D!iH)>^(_PNGr&^k9GU{-ay(|MDk4dhgbgwBRJ+@cde{*{Xkh>mj-TPFSsK2C>$$ zC#9kjM(N}2_QqP{e|Y1eA_C!i8?tFs7>)q;({Mmx^e=S}n}AkPVBo0)f*4Wfq(b$ zQ5a{KVK1LsPLr(H>G{P{xmxo4pyWG6zN^4G>hvG)oRl%3Fq%jO>PMxZmaQQ1!cwxF z6B@chGUP}&wpaBLha-sx3KGqT!X~}L3$+{wgQM)5t4oz~(Vd=#$w}h2oKBR0RT%iF zCQoyCv3x?|CD@&n1lFmUy3 z3>QJOCb{8omm>5^KFkU>-%cAFxsl02Wv)ZqxntBf@-Tz^O>GRtiH^C@G1J~S4-0z5 z=M3KKvqtn+GMPH&G*0DJqy}sp)}9xpd^(qmVTbV~YhLVdl;ZOyeE9_}Bw7z(p_;GA zX(XYe!9}oEIvd4kkV<1QTf%<=vqd0Kq41fUrW!&5*ZL?Ph#Sx(h=EK#_llpINdQl1 z3MAKfAWZOVzz&p!`jL)fh2Vzaa8U;u!P?KQkJI|Lwmwap=ky8}l`7>5bXKY8uU)=4 zx4ckmp)1Yfb_yG(WQ=`1vAG3p0;lC9sYRL#(a5rtf6;2DtxY=%xt0{X<}9S+J87XqdIU=LpuY0xLbc!&9sWO?B7gHjXj%ICm`rwW-= zf=5=dtq=f-6nH}x$Q_df_f7^!z0rEzDmr`sn^kZDRr~|tGw@KxNCRn90rldDs?l{I zmB3t*5&b1FOc7}OpGBC22(^OT)0dXA##xXdhy?b9!X#MK3JC*XfD5$jhR3kpyfLQ{sNvhN;?UhU^gzU@f^Sg&9S{i-f%U{XT z7!xCy{NUq{tBqzj9t?-QG>)Sv9Cq4+emfb5;b@#Cakt%hW0XiHPRbO8cfs?FIBUvLgmxlpUYUA7#*czSr!?Y7_Fp)Nr?rka^U%nTdyjN!nC zV>rOIEF6NVn3HMGb8iRu!Lbc>64L^p1~}e%9FF_Vl}q*J5~K&?0qK}?F~A`{7!kf} zL)SsxG3`KtG1-G=%eAsB>mTjLNz^~xGu#R+F>n?(N0NlT%|#bb4Vpj?>;`nfOorI= z+S**X5}2bfD~5$?&D|SBnKkh%o>y@zRZlOOVK**Uz2oCy;M-sY@3Yi`(Ktzxyj1nk z4%p+B#Y)eFvMziS_4;{w8pUowZy44kJLn$r{hpP;`O%x(-4R#8F(32>F=-%`oJ_f< z%(e>K$zX2XL%<-z3`aOd$l0Oc60=5O}(d=-VjGVR`rt zW+?5gj~;&Q^UnU^B-beR2`SZQf>AdCa#vyv={*$)Ft&95Ky}Kh+Fnn))4jd=bMfimn zww0xkjvwt?NCnc^D0pjPW^tN8n{cFBLjq<;K*-C4zNeXE%)v86`)2G?uT(w>f z#!)f~W6!cYM>(2&bw6t35IPIERt-^cg0Ug8hk{)|0x4!x%+g*qlC!=d9$Y{aBZU@I zmS0+_tS(o4+evoENq6GU`Buefw?~j)-?#W><|OlMyMHutG!0fnPwrnv6W$?!Aj0Fe zpxJdku7S}AE+rUQkeY&)!8*affz4qs0qn(~f+?h%Hs<3L=5jjW?IH|)6p2%_#1|8z z5tw$+9?XL2lwXF^Ebi^rT^nuC#xWm+@qo;pc84%QIGv}{f^G0EV`wQGpK(h9s*Ljj z9Fttfe8`7cW-L_uLuY^NVX$PzM()lPDFG%C0ZmEh*tu50p{lzfsWCe&r%U)ZQF6!pOK4^PK?N28-o6d5($f%8JC zM!)c07dU9cr8MBaz#U0=FKSwLxC}gqCW8=i2*Vhiz!#`+a~4O0Ecd`<;+7`%xG8T& zV(ZxezC6aJw5oT4k^g@hIZ6)xK?Q zwCq}-Hv^*{n9!)>go~!aRCWj?4W&eONE6gDYo_3n&wvt2B`RG?-iuBqlZ7w>$)zgB z6%H9Bv#Fg!3wbOi{U`xt6av7gM3Tb{CNt^Nz%-(g3l$Nh_^C9)BT6#WHBuC%O5Qk} zC|1NT)JR+bN_|uWGbCpbK`wa$#mW?RMWcekpQ@9XRRR7K7!E?JT!Rc1VFgSie^iiE zmQ$okRGRdept?kIRDr^St;1KZZ$J+Yd%bfP&+lz-+O`7>Zs3l3J*h!zvZkNgSi#`@{1?7pxxx0< zW|k!IfaVsL5ce=e*dD|XG#DlyiNqiinhJ*47U%%eux*Se--l>JVT{KK{9sNzDU6~l z8ix?@EKlCwaYO+~520(|!y0_r0f?YZaY{!$jmq%VrBDDw_0*A z?fvl0?W3N&{8THn`?NNm$-`{+k!h9Fk5PzHzGz4UoPnaEWC_{?T9r+s(|}~Mk==zL zEtV{{ikGtrc+KMs4#mix30%+#Tp6Hw=howY{Jk%qoc6a5+w05CgX7+#o#QBuz-m~r z%X8Hit}K84g^S<(%FEyV?pOc#kG^~U+KcVOop;{94Lt&cqcB=bjjUlk+Um4>QD?x1 zckb*Cq;dur93%nbMJ52M5TS>Zpn9>2XqU3ZqzxyKPMKhiczD!cQ@%Qij<^_SaJFrc zHq0C7O5{T*iK`-gpu0pKncU0CSD zesqda2dWE&l4i~;hXf&d48kx{exhK!;N zu?dt9zBveaXcnd?^_&X;DcC4L9Ap~H5I-X%P~0Sf(UL`{up8t#Sfq2JW+LSYS&%gq zhbU=<)C}kMvkS&#aXM|R72v;_Mfwe-lRI#=DYu|e9>8_mooIfpIq_)HIUXGDcK7x= zx9=W)^my2fwC{ZPn|lYRVU)v6f*MTo{H1H_o^74>#u!bYp3mi(7HkgK7p{@JHgBUq z=HoPv(rK9F<2W6LDXmZ5Fq7TlB7ca-l*@u^$sGyUwJ;)J@ntD*x3OEfN&y-v$22eC zbxj_!|G+Uy9>l6}I!s9_%0Y2qgaVFy z#t&7H`v?#@L}=7d_BmcqfGZzoGduoB8D(F}GY}ygpsu9jkVY|3c`8V~OvArqF%u_+ zM36H9e^OipRcQ7PNQeQ+JUc!E#7TkbqFBxkohQ^Y?GxeS}^5VsjHRuncC_--?93H&?{%x7*r%@Ew>$O^=**-oAN=4cwkON4QX(mZD z=yjo6kN_oX^;OuT~#=5XBsMsAM5hRcce&bB-RORn*;Ff;}t#c_`m zdD>t+9B?>V4pa_r6i~@>n-a_vDhMdLJllKlq3H&#l`C$s=uoZmF?isF4?W4C9jAfk zQMS2_5Uj+AWn>Do-LYYl9q-(?{mKMmIZK30R0TgG!6+)b{NfjMS}ON=FfgS%6v2h< z|WfcVm$xy{oVZzpMu3C+UZBE8MKK9#U&sau}~Et z4>ZT{5ji+1EFvV7c@^PdCPHWFYyiiF0IOtzsVK`aqLOD~sRGh*37wUs+{N~bckcYr z?|u>e1SQhx4Yv+D=(dgd%Kzq{|LOPs=s$Sz)mJWDzp=1(u~co@UeR*B=F-Lw{`#+h z5pI@cOs~JZWZUNc@h}Li8y8woG4DQX&uUUD93tqvz!?X28t>l=`RoN6kdzpT4|TsR z+cJ-qBG?3;5CCj3`CT2NVi7?IMoeh05*sEU>G(pSo}CO)CSRBIc>+=1K6c}sZ~uL`5xyJ+bEPc2WSZ&4li(}QGFgE z%S^|VwY3S?Ghv^@n3sCwQ>z%!yTW;$4BS_*$0RZjG9!p-EaHOVFh{Ccz+(k>GUs-U3r z-UT^_$(!PV#^A`62$2nuZN*_>tl}V7^T}OW4SX>uEE2J2nTvUrG9y-m*bO`qJ$j4% zz-d_U3pxNuF3lSD5Awx=;X5rRTB;e?2im~422(-kr7Y15ysG9%D{3nZIic7_&F8F+E{9!)3_JB z!0+q91Q~>B7^P?&Fhf^DTKI?xpPQxS$$ODx2k2GJrY8x#0k1Tjc+?P@%sCaOcA_xQ^g#3^}DoChG z$Kk9S2s=yXwE{+NcGR-R+O&o7HdrgWuhJ{IuQaqI2P~Ep1$s4k=`51UsE? zkAQJ$+q&sM1=F#k^^8G~(7KQnGh9G&af!ynP(M-BZFhe4oA=x1d2X&iQ(lgTG*ZKt$-=MCGdWD(qJ79v~W_)U^goR(b+CIO0~C93CeU{~Nz z*Pk2=HjhR}y%-96eZIK2)xT0Mx`v(=ru*aQU>J6jw3BC#qd`~CiWTRbLHogI(2i4# z%7anZA15$1nx?s04zBs-yp#U)?cJAd&ZXn*S0C=5^b>j`lj(4js164+DI!7#wSpK! zI6+4sO~e=sp2-h%1;>}b6I2GLbQ~Lr6NWPi5FX8A!$dxjL7|heKqetVU?fpKr7&VZ z$H#E8{GE^Pef9I#dxPld?lHU^=!e%|IRE~KpKPo(%JrsUI$$%VKB0~B{OU_Le)^N2 ziw5LvDMvTkqw%G+Cg$;OFM9t;hgC@U-dYQ!2^k~-5hOB(0J_*BB+{`IP6%;0pcbCt zLPC~kZD2A57cf0xUPKNPFf8+r?+ zgW-6&Ro&h{J?w;|aeQfGVPRpp-|K^?&~lLZL9Zigb9t%g)tilBZ`f>9fxkZr(e@-w zVX4Gv<~bIOGYku{n^mW)!eBSAFl;z8wGs<)P`GyftXtgNI zVN@8HSK%DPx-+?-luLmV4IM-ki^@%73NswG14b8UiZR0Tg5ylIN!A$~j!7E=-7-lh z4r~F~YZm-{Ogxh$Gd(lMoZAUu$HAZhQnZ#2#3BU6X*|>%c~>0>tCsy(mxQ#4fxR z5s^QlqSWXnv;bGDbLbKf+LFTVQR^H4gInE9g-&8AfTGOkggIBluHOm=rK9 zpsbKSjiD$CJRSBq4ywf`I=*jvzh@0*>R^foEZug2&afbnW>oD^M}5R_)65 z>p%bHTL&kdpcHJZ&M(f@V0eD}cfR=-fBSQ}qaMNMI$FYG8o&-#X zUT+kR@r$E0Nstn-YMt2<_93C7T{Q38P+V6JqG!4UWJQ|hD-os%F4VdJzOiR_E zI=TR}fF2h9!vXkYnHE$D+AFLTIm6)2GTw02ru$DmE;rYzttAaJ3XB!L${ltHl}BNk z*|r0Pz*Ru93kpnjl7NXcUGmYOmqwxG*Sh<64bz4HK~{2HKnK}{DVJ%A(4kD_3I{U8 z<|h9s>cV_9yEjTU4@dJ=H;VH8&Ebt&Wuso~#fdx>!<*EDBs+@Z;TUrhy}+WO7Y(an z+e-&LeS`o2fZj<&K~%1HH7MsN@lW5}2^{0q7nYtp?!0^VxG#{XqCY%)#}{g&@Z&&B zLe@5z!Z(y49qoZGLlC%`9Pyd~7a*I&pXme1g{)GoB1%ydNL7ZpX91T|r&95%#WLd~ zL6q&S`cdZw%E_<%~?z^`xonP`wRk(2mtVtFkfzxUFcR%hbQdduwl&PcsoyGf<9?-B z#9(;5+eLTJ)yh|&yV`4a2mKz*2tV-CB(>qeY17@kLzwdm%ZtDOx6#FD6MB{;suq$%jIm|vhKtClM9_oMN`kR<2oT2ACMFL3@oiwco~7~ zQ`3eOQZR}}0mcN%RD3ws=xmrX^a(ggX_Rctj%m(&Jp8OZmxUm(;0VZsc_EE}VxVpi z>jYA6AsZ6FQ763%hRIVn$ORiQ@=;nCjXcbOe7lAW6&$C0_ZXWuk^4jJ3h5CvAWOBB zpPd89)bsRt&nWtN`^2a_g|)I?bM*PTwlJ?R)Xj?vg^gO;)Ti~P&E)~ABfi01GvMA> zPEpgnxPLG@{MqKuJ~-aGf6_B+Yp=ZW?LU0|_y6Dv-~R4PU;Vw+%Qq_vtA44jTMqhg z0!yW>aF?a{M?(r{Kacu`h2P6+`ISi!i95}E?;`?`jv}ots9p%Ru<|d zj6*bmJ4OrXlsi0NctHC4!>B)w!z7O*WA`W-)Mm&-P`nXbsXU`=DVN zdp7xoM9VbMgPvpij_o=o0Ls1tqi3At{c$!h2G@jZR^gG0gbSen8+LkyP|9!!tN_sd`XWIAp*`ni?=t5O_Fs z2o&`LK_Cu*gXG|1hDai!9##FSN?=DOD-;X}pTaXk0udt!3=y(`REd94tpdxdGt@XT zeTGHiykdgDt7>O3(g>-HNm4@4%cW#1XkG(GPSQjr-7I;=0bEEr(WnAa1u2vW16lVe z9|S-lQgI$M?W6nKFJ4(cY`2H~-j}}c(&p|y+^INLuF@s=hfF<9dx@7A8#!#G)&7Ymdh|VOuq69 znu+&O#Q%UV6Ayd+?d`46F#P21ql5kZalZ!|Di#07KYeqOM$vfC@ApRIA?C}c@d~KH zq(ybc{qkHVmD|4bzd>F$>ViKQd*BB23b((L1<-vu%o=h?%l4n%Z7iLuwdOIlbEqH4 z5H~I7nph7Qq_kZ44KFy1V4`Rk@qSUsI2@krZDL03A3xSjYmz}1qYwp#N8N$Vfq{g9 z2c+~EWO?;0s4n0&{-KdN!}PQlzk0QK(hcw5?ab@ebB*fxa zjswReWklozSpbrli28I2qm#r_VG|jxC6OZmkt6WP5kbkM8e{0wPi0j&j(?PvGxK3* z{QR9yYUNUxWN@?2Ej5Otv|91s_~~!1UgkT&pxQaX10GXHmjAo&|BO#qa#UuOf!5ln z>_cWp@o{JP*;^`qaBiOUDIW7ED^HBgw2gQ-pp5;E$%Z6iZlD(Bc;P$Yn891;C}#1! zWM~u@IoLRq61y#joh<9G5MqvsCHr8IKyN*NdC||);iIIGXAaE6xw+$`cCF;!dVCD0 z5B1-C?vm{|!{N}jY}d8R)hbw>i!M zR)8^r38vQ_%Sh7vz>AL)N1Jrwe8gAI=oRYwLeVx|9h3eP&M36P1d{?+Ox48d`4`SHR1eRlI!xXM8hMZ}b(?mBse#R}@?Pf@>9m3% zFy*o~*U(og#--K5#_FVQ8LM;Cg<4Xw^WtP$ElrE|q*9zXaW-uiyrS#a@@4_v*WlV3 zFcyqBjQ2kIk?Ak_i@(!){vRy9{EydP{?6j1m&*%lLAg=jo!pe|rHG;fK>B3R-V}K# zSZ+XuPtgSy?2dY)@`LaH{nGNng^L&O-FdLPd$=&)SXf=3TV9)6oIkg*d~T&VUoS!U zswJ;n^vi)4_~>&wW55Pp@-1*l9>v)>&IY3x8W=846wwZGEbaxtC3HGH8yH}GI|uJT z@q2IJw#Z?UPXA?P;#ULT2E{g3qu^08dMMlG&mIfMLj&h_DyfGOAg$ z6@A5{1Z2))=p)rn0AcIpcb3c`JiRS;5muyv!q3R8 zlr&9BOI&_{XWh&Ieg%)?jD9&H2r9&ZO5taw!Y`$9oHdG9_@A{G0TodbAqW>|NI!b8 z{o>WNEX_XtojK4&o)j3RzZFoHK0hTJv;l?2xW0>SNKbo?>ov#q_O zAHI3(_QUOaPxc;d9X#CJJHN63b@|>0w|@WkzMaLRZf{U36&tO&-sy2P8YSWQ`0(h- z<0nraKe>JDUJ?(Bu$F!A&;Ro8v^+8kiCIW7kVd`1`PDh-zOQ}#ix)3l_~Fmq1#Nxz zA~b!RL)}40$qJYT!US?{3Ogno!BXJHF=2r)1fWwxn`C^*8~Qpu-G5qNx>PLJAay7V z8;O$#%M%}Lac-sOe!(dk%JHo86%jCwU`QbXZS?fr?>E+87#?qO7>InbTiCHU@cjcA zHr#tcZ;FfIvyPAs__V)x(~!HtAMFi~yV2@gajxz?-0XaOr@gz`FZA*tElj%E_%KRO z(*3Q$hj&l9-Ovxr7p}Jo`Sjh}NAEvupA0bISvx9k_hVrQn*sdzLtuB)#ijT`vm*tD zlq4aMAZ#@X2t8*~L}1WH;su6`ma&N<#D|!aK&}*)b`c5xC3l99>C#?aszD)f5ZN7! zpbFty!9yzf-f6$TvfNCDVPmf41SL?xeZH6_lIYL>>W9e$zE7fS*<5Be&sCgOu$7(I&v`4AWvp0-r%RbL$(;U zJRHn6l3{2D8XzlS)J2#(vtjNx{K|a!;m&EC<*miK4K;ZZ7R+gJp}E`Z%r{D#J13n1 zzx{pv;%aklZf|Elij($9I~s>^k|j~xT9`jQIX*h+`JUhFbwN@P_{)n6zQ-4?gmKmz zj$5^&JVuIHyMTEOb`H6!IR0#mvnkSEG*>Nn;LP{7q}7vGsHEhdSK|&RDmed z7(lTs$sB#6`$h(P8Res#wmz&A7&Yj|I5nrnB(V5a?j+G|gV@Xo<~QJnp5m5L@F~k_ z7MP%%tNIw3oUu^1Z6mKRf$0Dqp-PS%$OO9_%mlk*a(HSx1wLR0Gi9RrMeF)P;oPFW zP{*t_J&wUzW2s`CU)CCa!FR2-IpfMkVX>5!OIF~7c;dj8 zyFcg!PD}+Y8jpiYD;)K0$A`Iqj>iy!Z&s=_u3mrc#YwUe`qFL z!D^#9x468vwsC%a>B9Qb<%_G&U0HkX%6V8JOU)Wwf@ZT?^c|QZ=n`%MEfgT|aH!C^ zurtst+@}SR$oM7?7$I?*jp8gyQkaF~I3Gs%!zFKAf@*lM-0?xXL@#>I<+au~U%P&J zV}6=vZ5r92*T`XxPUX>HTx)uiL6%U40U}26pGh_T7$hzOf&Af+eU{3Rf-?^+2EHlH zSHJ+r8Hi?+q5|SDQfC&HYFI`pWJFc`|2HUX2&!sTkeBL0jw&FLrx|`BQW8MWS;{Pe z|1)G~_^I4xWIO{Sxdfd>RX{@kJS!-53Sj~-1*D}cCPkS|a1bmjtAOWhF{@}+=Gi}s z3%o?cpHM1RBKRTm?8gf*9l5yp>Pwd`%UEhP@@WpSf{ue2p%8Q&2S4P);)mcvD4lV* zd(^%A^Z?>=Mg}=0)vBLDuK&YtzXGeII|%>i4}bU0N4Ffm^e_JXFR!1Uzj}UoX})3T z=D+%PKk_|$zExgqmFm?h4E$`G4~FCCUw+v#t^e-7`?sEJmCM1xeA6(jFbrWzhjIFm z_BCCbL>biG#Nwh02V7QwbYf_6;)h`obq-)HLzq3jEEJPH?H}qA%tirFahEC!YyW;nG_y{PE~;* zgfLObGgAJe@4ODZ1*P@#4@>L|K%_EiZB)a>#shCDMaioVKj|PP2N}u3h0%hDM&$6@Ix-@ z=PnJ-DKJwAm%?q;;jim_211*3dv+TCIK*yr+P`#uazGhjh5z1 z3k%K4RPX&}$8zo3i>ptQ{!%S?^Zmzv_tsMg$9KQ=g;!sBaX1*G>d|N%$FTuxV477M zb;B|r-g}Im;r&X_X`gnMSC+x6Ua$Y?>GqrNKX|&|0qKG7mW$qLca&t1L&%kmCUM=4 z)UmfX<)9DHQyjnao^2Zik6*DjF=bom zWs_frF&!`$)YB{AmOS>lrx88jdI;pv| z)bEcT-o9h|ZnIgRUsx^q#qz;c*aj$JM~^;&HdB_Ooh z?flsf-`!ZKtuHm7yLJu&{a^o!zl6GR?85OVA7>y$1=zM=f+!ZIu@e!fmpIfBXyiif zF%VRy7`=cOeI;n9iePZ4R5Vu7*=hn&3Rny$4y?1FaLHJk=`Z|abV-#m`_3{Y2YI6S zBy|QaQ7916+>$8OksxNBRY4fBA0_@QRU(pj7GVXd-WjSHLjIV3R{b*+3V=CE7oL?u z9vdRaYJEHch2c@!=d;@ZR7or9DicNs2lC@Ti+)S<&aq zLl0VifGICZ_(4~=MyL#0HHF5Id#li|#w3k;$A)V|ovE8FuqyZ#A}CZ*gvvbpHHjKt;5@Q^G__zNr zw>%g24uJp#c|(`IkegZvc7RomW&ZJ3uKw#E-vUlB1TNi-eInc7nJ`|&Liz>fJC2u@ zihJ!4FaYpf!?n;%V;I8LhsmUk;Y3h0W&!Z=!k z$OTRW;L6mj79#(@{a639ciz1P{qf18-LJlKrQ09>qkr-zt>tq<7;l!H>^}J~|M&k_ z5P7beyn1;-??k*b-Xtyw|Ie>o=bXVP=;5GNvoG2PL7@LCGRy}bFo9*f z0WbZ_1hkFMC(%FWwh7+V)8)05IO2Y$-f0^RX}0QM z)b8d%ty%IspSy_S#I#J;cVM9G?;X5*>+vTK_m55oe04`97=`gsn;$96l-@!U`CK9B zLNBq2=nQz9AR0Oyt%mqvAT5^7G|&I=%WLcFbJig3y>nE)UQZV+SQvAyM!P-i^~RNo z6NYK4R$gj0hKJp)pKqJ40VjW_H-0o2V*s{1t77vh8yHJkny0kLbElxYx>j*)jc>r4 zx(@vINj#kdj+vxlx<$EJSMyw#Pe6+9=^WNnvx@@C!^m(cpH#5;c9Z;5buQV^KjbSrcPop`+n` z$Z`fB--0j{0cXgVrP-rV*C9VQd3SV!M6HN!oRf_V@dP$P0=q>x-5bK>x+#aMbDM+}u-XtelI+qp;tr%`KXf5jSO) z7NgO=ye%l`9d0G#QRndR@vXhR-9CoTwHu96Yrde37p{Jzaqjb@UYjnz_*ZB#G$h6l zEok^phRi64CQ4(yWUbC-|zpOov1ez62Dg2~*1I!XDy01Z~z`04$R@87vw zDi>Sx^M+vz`uzv@ANzq@DHrRtdTVLP@%d%}s89$#e2g@W;8MkLG=U^RDW@^jU=l+W zU_``8gzC96s2eadAlXzGlWZJC<8f>j3}E(ren8eoEbwg0F)b?^4+p(o_oVZ1Yj172 zy1cUX=-&M*D5!#|&DQcN8uoAh`+xB_KYq`%^d#4g$E=KPp&a=W-a1Lp~Iz$;4rIUmM?&$ zgyqnQyqM9ij4hUsM1fZfo8cFL z)B8{N?>^msw0qne(uQS0f-#UTj$BSeNR#ajd}$1(w2)s=pWqK#1_E(;eG#I$w$gg( zrJIhHRZHHhuYYlFY4L|Y{!Oh~T9|8jwsUf_f4qNE^n7T>F-+!R*goxbI=xPR(CK5| zkNSf!Pg58Uuz?DO*7vTVdYI7i?rg5*Vy=Ly5no&GuTAqP965Z460HsV!BD_l4U2-! z#t+th99J4m@GhTl2IZuz=@zuN>?!empDR2tVmOE~R^eK)u@EE!4#EVjOOBr0sw`eQ z-TV;V0M`(NE#L;YYMOA(V-&PZ*jdb3#ZjgvtF zKOsu;FqsUZd>rR-DtEq>*svd|L`I`H&(wV)b|wNEj37~38-f@v2tP@xl3A%b39tzK zSpxY*F*{3_=FhU3i8{E#L9alGqKp@*0tGqLr9yH8{eq4mTL_@=BNwSNN~Ix4MBev* z^6r1}Pk#^0KRN9c;RIRw&)@j<=U#cia^N;I=!W?hfAa=Gos$&HK6TD?3u8iz+2 z+;_hj^pdR88F3S32uqT@9QbIT{Xa!rJ=;lP98ZnIw3JZHY1MmdQI*bZy-f(L6_MU*$-tj(ci)>;H zPYz)Y9&GOx%f;ipgT0*tNLrC^?oXD^U39&`vOUKsx}{S0wC&ZJer3*agL1KAb`$IV zPI!DWX*f<$)~(9c2fzNsU;O67U*B&Zb#ybo^1}S<|KOF47r#&mj+b8elgjeTM>`KN zgJ)?tI61aq4C!VZho)&-PN13G^uTe2{zk9UK-PnDYnE$0dGz%7;IQ9535UISJYd#7k+;27gf)@x^S%>(`sMBnhoAjvp#XsZszftrVDXCTyXysv{et`v8eA%&=~ zG04Rs6KJKpDD-kEkaAM!sNLBIN~s}TWzHah=UJS9I3WnAimI$x1pf+BP6`+?>MYbQ z1WC4{lMUjLsWT)h5Kxq?;9!=5X9<~&7*&XrUZk@A47?x{7={^sNml3}bWG=2vLuia zgl9+jsfNxtEaHm1CwnJPcTXPgob*RyAmL0b1hA+;;Ha9LW_fP}_bi3e7V@QpmXCOm08gxq5DK%ugD&5086|R%G1( zgoy^^2s}yT5*tj2tXQeS%RxFyOvPQ`bF5ri*F72>vS2^~q6s4FaZrLOgbvjqA*j}( z;ShAhgJZ>iv9C!xm3RAqR=%lI6l5oRsvkxa1+=wBHF2I^2lG z477`jhXg?iO(rJ7kICYguY+XG@I$tfY=Yts<(qs9**(3yiD!QORHb^xZ%C?pM(J zBYwswu9l0wIjuD341Vkk-K725pZ`6b8b16Owo)r-dE-gmst14b*0w^2@SU|LS9xeW z@TQM2ch(}(AgyJ!yh+HWf<&|dy$Kis%*dPa!OF>m?-<2oC!-L?EpbuIh=$WT3QCtl zKFq){TON{!!)TCBT1(~ZXgE!#!CY-vNK2mK==y`5HmvZ4R&8N^4hD1-MM0?y9-~uh z)$(vS0tJ3hc5KVGt)rvP;r#gS|eS7Bnh~1`Swn}mGb$^jlgyQg+O}0v%O)`%U}Wkq_I*Oj3blKQ_pkI zxQ6czA9jbkBQxM#8)#*i4)B9v!lxNIQgorK3zlu#8gDnk^(KQ&Oy4ln;0J(1n%teB z)k_)1n1xjbTLAVx%42{Pv?1JpG%s=z#q7`5r)wtn2+KN}7UhQJqFJ&jJ;~TP=u|_# zY%(ptx0%FK$G736qH4~Gqu6k1<QJm0UaZBM^bQ1z3ggP#-}a86n=<| z?-D8S6R*=kp7RM?*eY2%O*MTXu&yqfFKooqqtl)JC>$E{&4AA1`gZYgOBirn4zgxcYpS#UV5S5@*35pdsHt}c} z!ov^?U@}S5IE3vFvxCEoqZPKX>gvh3+nr7#uUM(n7fi?AdU#8p<}eX>i%OH*5`3Lv z>I9Yg{PM!m^3wVRc>2djhll&SuwO4;xn8N&%C+jS+u7ecJ~}u!KHNJw-0z$mzywL- zF*K#D9&ij$iD(+kFAyOdGC1%tY%$C%ex8T=0DTgN{r2g}!T!$H)19rY?Wa#3-hWsu zmajd3H5^9|?>&SFefjHOZ7pA*3&$sQJv0R!9uAMc_WS?g=Wd>#X3-~4jv!wULo^aN zRn;8R43u_WsSAppO!N>-q$@zB2RKrgf}b)t7?dtX5T!+MK;jQ&fJI8q&@okz3AjYm zk9D5aFEyPdGpH1RAfi%`J*!P!ihd;OGyIBH6>vF&Q?bwD7yX4qfU}4I;-Km#IAT)x zSzQQ+EO$^F!Y)4yI>piV7u4<@mmi*da!rv!QRJ@_V4eU?41r# z$MW^B@r$mQgdhlz0C{Q^f`iuJ(kO7J*(4koF1$`m6b4_OL+%}fjw|q7*MQz}d@sv#L#z-m5v_oD^37wsSpat}tJZ7dQT*`De`f{NcyKafAp^jL zqrwE=(O-XlBT2HmTgR3upP~9xoM(k7!_p^U9Zo7yF%WSX*PxFpMtpXdZwlu=5~xsQ zNkv3N`wKxz&CGu(kEq;ip^M1cKOLW}mluSR-|UJ-RA82jH7Jl9=NER42e_`690)+s zv*D^D7Znl}kn=o~VITu-vI;UoB48juB{0aP(5xyFKK$Tk@BjYSKlkD7r)Yv@8^{Jp8?t4pHT=Ap=%#1fJ?^-U`O^>g6;6cjY|PP;;!YAFNGcNKU@wP zGuRxi>!ew-xxJUfaiv-Xb-ix4QmM3Bt%YUtTeAC@`-d|ysO@>WWa zuLOf!W@9;4Z6mi$>*7*zZMo{`cD~z9!>KoC8SCZDFe^nqhTG|m2ZPY{;l;=CW{R-` z(l~{KLn(JnaX3*v`8G)yvzFWkv`yX73phd&V1KYNP#dzjhec;X&doGu5<<0tKco+{ ztjL#Ka(JK;c7bDJJi`t`b?CS(&Zi(EOeV$@h8x>NKOViMrTi8_rls8X3GD$(1Qqfw zDw;aZVK?w)=O&l?jdZFz7QZ}(+VhO7J+6ax6yk_Z3i#vNe9mN;a1}Al?Hg%BH1*2XF1&d9&c& zSa|tAb1Dnm|B{4ICARN7UQwH-U=XJDEDgyV)6A1Nr`e%;0~(j|jh~@gzy8@9rK0EZ zSvRY-uylI3eXz4xu2v1(#XL*Fg}0XRh6oVy+1bYY{L0$m@`h!(fb93W(QxGY!Q4Wt zHMh{5YnCb%$Mc7S!Du+>cTRUUH+OfQ9`ElQAMPF>>~&6#PL2*cM~5eeN8QuY?(yNt z(cbCt(f-cP-qz;+?%wg?;jq_%gW70Ri+)fkS2ix58;^$VljDW?#^+xD@`W2Om1^@e z9WfX1dv)|2Vcej{VZ_a!yZX}Szx3Mk=R7TaxOE_+#QQf8A@-?=6&OWH|F24jP$xy@9S^W&^Qb1V8S$QT3_asQG zk)n=r$&-LTE6nz)$g>oLS)7L%{Hy|%r5Y$rBY@x|umEzv3mDR2VZ>=ZjQHRK61n0* zfSDGdZGn>&;tFKEA*6$>Y8In}<*LJKO|KV*oXA!Go5AHeI>;)g1Z=sM&ZZF>s9*u_VY=FHFYa z(4-QAwN150K;Au>>hy!S(?UE0xl^lJE_X#Jd7vHeZV3(HFGo z9J`hI{ZD>o`!y&9;U`q66lbTP*Z5(nFWgw|58_WYk7x(btQP)DzzJ8V7g>^^I)}OV zM})#40!$*ALJN41QLgC#3HR>)PNWFo?pU%YeorE6$+Dk1>W)c`=pN-J~i zEU5<$jOfjy{%9OtIk%QZaj8-QbNG&Pv(O)gaPlyh6ia289lB+q3FUIBR4zW*+-aY5 z<`);DFpb9HBo1M#pFg)^X!+r3@5x@LSq%c$O43QS;zQc`LJC+yTt7hjzLoEM9RZxymOck)7H$v%0&6)c3W zfe9D*VdVSr-UigC)-5RU(4d$m_@Xb-!y>MbKOEuzvM;1RATEFeR`5BN9ieu?ip?o+ zVUAP1WC9l-yqe_PGa6@HLdjSWS2oCgj`%5;NVwJ*Cr~6gOhePA{W!6}1&-z1gUg^1 zA;<)Ywu8aI^%=*~7|-=M)HH4fmrt5P-x(TOzEU@@pVLd>2yW3Oy z*5>ZPIH{EM`Bv5Uo0eO7^4@Qt`%0}v!zrP2ckbT*cmI!n8Ba<#zWvV@u71&UN@#(` zyH#Khj45u>v>h)fRbk3R@i-ob@pzO?(x6z)k}w|iW%)@dK!`uYYWn~E|NJX>2tla? zM%3q5T;F?o{}y_~G+YZ7y^}xD*kG9zNx>0LoGl?;5n|3P;WMC%~qq?sMafxNjQd` z9t@V@5Ozzizq-D7{iV+@ZCnK`h)BXdD3SXX9FCJbjlwas3#fC84P7_u3oAEY`rMaZ zzG6?q$2&)8Du zPchBfi7V3oj7_Bf*+J-+aQD1N_{3I#|eCU+8j_9Yd5|DRU=jn40*0|020cd1_DXo zLi!{cYIXo5&@+S~KEk1ORGYF!Yr<{fJcA}c@1-=TCithlZ#gcI$s#W&Qz&9kMSDe) z1%m?oZwHJRV~{(=H0}h|;h+DffAGU!espQI1(-&?_@g)8e)V%NVhT@_@GpMw z3-rrU)u@%-=~2|YR6`X%e1BJvGJNm+0w*}>3Iy6gY!`7$C1f)IL?MWtLLRO#U?D)r z5?%oqniz^zQ7_#C8RFbVo|AH>LRQ3=*Z6HN zwXLJULfwB1lO#+ptS)@=$)laU1J84lBt})3Mibbq1-(+Mp*qX7qZm0cch~uTm!Ev& z?K8-rWm(YhhG`re95B68ElfwT#vP7u zy^U#Cs;L1r@eg-bSF{%;N4H&{pklE{rsbke)ZtW7he13Kl|s^h4UIWGxOFM zEW$hsp^*$H$N|ld_vp48!0v!K0;9pQ?a^@P_`Ydb<6)O38dRETW6lP*C&~C1zkGY# zJt-FLpbYE5YA&vi27}|hM{zXHv(&OZ$dY0DG*+lNp&4K)qGq^Mj#sP}YxTzb>iptr zYjJgMaivzPyPoT~-Z&f`?H}~I{Xu^ajrgAR;b?SvbP|rjUiUPEnb7MbFi)c7`1rWr z?GF2Wh;*e^DV6I>t842QE?>NMbN#}l#_~BdA&Wz(J8lo>24}utpB|#7h2g-q%p@H6 zy~|N3m!A!vNfZocmnuTNqpr`w zZ^5e(BT1@Zl7ine7<9LyX9mq5FOV+K5?B8!NINCMTm`~^VU%R>npv~>48Ne0h$PRh z8RUY05`K%G*^!4cdOp)!b`v^J<;(&S)sNbQ`Oh%S$|Ax3Cc-V-OyjRV{|#AdAzx?^5Q9|9 z`GV)^G@C{v&p{4gkSNYBhZC~Yj6$Ue(Ia)53Fl2#+=Ugu3#I4COT+k32)h@$m;?ZZ zqoUAg)u1HfC_LW03lZ+_+`^oJp&)F~T)wN3%i1Uazss`<*U!!MN27swQ?{DlZCn3G>hR* zS*G)E|MG`m^ImsS^C#u7&{!#Np7wuvn~shOzrWUEQ-P2c23sp4BW#iPsT5#<@%b6# z0E+Vkj0O-uBUcPH$P8tK_YU}FbrYrsv>@66ZlnKsD}}ZcsFg{VMYtH2PMyVIK+{88 zU24qL{F7cEzMkHX({|`KtC=$xj|;v2aOY^4@?pVgny2$~<=vgb;jrJDYr!2HkH^s4 z2_MU%0RdGGo5Kry&-3{B3_2js%9XO^IIxKO{SIu1=dY}fM^N%{uRo5ne6CS0dJgwy z<`c&@5kVtq=4yJ$j9@~|*W3#mbr%Z#a2R#c(wbLTDjl5kOpW_9@)Y>}D9VDsDmq5A zpJ@>v^*B!sp5EKr zdUO z(p>P$D{gcBq;s(U@RLel76X@_i)Cg}(mUyOk9NFbaQD53k8kfDZFR@pbkvSlu3h|{ z|LlKWYAwUKBul}*Ji~lQt;TUQ&C>*y3c3N7H>42e0FaobGwdFZ`<*zBY!7CK9S^%% zG`3ye^NaOXv*cNipKjj%_@1ppmWsM=ma6q?bJ4J@e)qWFJB`M}9A=79fEC62XYvwV zbPrV}zv03Gqv_l$3j43NPxz=pXFD3$@n#{PM=~xr@u^FD-9eYRs=yTZ=)d?D{3sbTO_dCmEmSg^=i$i@}CU zrjvBgIX*tzGIR?@Pp^GSv#wO-lfCR@u2;eAD7Thg{`_lS{ld+PJNe|nY$MmF+J(BD z!&xa7kvFPMs3WCsAYM4BiGWH%ir~OFFTnr-kwC#mF1aEI1^(S)0 z0%voJL{zH4%*r7kV@AJR@O*|}YEXelW>I1ir7Lk>5gX4efIqk|CYYteui%WPPytvW zKorX4tUqUIGP4Mf1Vo9MD-X0X$5CR-Ee`nWIFnuiaR$l$@`;v=R7DDe(U0HxD-EEBG=eKMwCWHCHvDG& zr>bT|>Y^Jbi8AOESpfX_!S-=4T56PR$HM$^aCB0(H7Ku_F3kPtH+;r8Fw?o*L~mt* z@sp1}-NW&F^DQ`zywN76D0vH(2;xF6FJcsZ!)`|ky>dzv!5}#rSWWjBIow*uJAleB z#~={l7otf!Nhil41$+X+(b7@DZP>A63`Vi%#Ct<7B`bUl9Q98 zQ`>PaUszw6uU0&(T6PYP`;T_o%X5`l*$30$P^Kx&2oj7IH%dAhbZNP=v{bY6Y4Wsh z7A=3Rsv8;RR4^e;QZD!Dg*c|UT%W6H!zAzXz6Du`-l6dhmwQSEW7JSZ6!@ARFqLn2P$&|riU-8CkB*p|GejGI#q zON@F8mj(C-)Z+9KrkDvaNx>FDC0ds1x&<302{Xgt_uw>;;$o65XoDmVC)0{$_&)ca za#aiMNwQclPcQvLDp3W*DXGi?a;2T6eP;v+5+;Uzz`J!X;APi!AeOF(uwm~ObA8{C7~t|1$$u+0D0ir z(4oEy<ZP6Qp0G_L{wcIVy5oert4S{w4JQD1)YY4$@h>7r_k<#e76$jw+Ith0t1_`1>x%#G3Gg9p(RZt zjz%Y&cid8od#{p+JXGQbqNF(}>^X%mzpw#w>JPsD;;%n`Dy^drjg}~;lf@!HY$c5b zwFya3F>y9j$YHpEBC67zEInfj8AM!cAmb>frL@%ugHmV#lGTPeg;SL#PWCuii6Hez zRFl_X32i8d2L0;eP4+iCh_Aj70n^{Vzqh$}di}y8bY8jaC*$z$)+sQ6hV}VMcNjwo zAR)jHXjK<-C5{N=JTs!&tMIech(IT9EI0X)zk=SXxbJ=V;Ntn^Ql<3gfBP#4-Ni*` zX|~Eu@PlF$hoFOgIVKgGlyh}O zD-=t`z=vOJ4f=gsH!9U)z1a#}tzIrwOa4iB08_1A_LrJfP#DIkZJV_3IjGY{$+~f| z>6Ob~oJCtB|6-*d7(08XXn){)=qwELA&8HXm8E()v!cCzKF*aqa$gcSrWZ2aHUcwL zXYmasN^rSt8(gN9$9_hXET=EZ(yEs!lcF(TiEzyh2XQ|j5^c(B#QPcQ7>( z57!tmxj>K-a6ZpXKGy4)Cd`o}GseFX2lv9wXFwYmqJhdW!P#{9zixu9HcAM9*BecJ1d zmN(ATinZ+NZvM$$dUrqDJptxvAvNM2+UQ+g8rsgSw|{*7`s(Vi!VK(eW!+!ka{=u#H+Ibl1TI@6%U;mH)$!mY~C$*&wiXjAy%e|JG)WKZvi_?4> zn~pc-3q#@djFAd0M2^Y;^Mi`*x-c0>z3zC}pJbUIlwq1A;n1|LYIA;ZdAa17+dKOo ze0XPX^Kll9z&08aV3)3!tMziF?ggcgU+){m;UGyt8*K`77W#oE8}%npimo+W(1ekJ z9wB+W@x%A!aUf;sB;#iz(H0siv@Cea(lIqF%qVG)sA(AWiD~kiTzUb1Mb-G6H(}If5kM}n7iPKs>Z`cmUtBgg_+nkAj1eS~8xV8D!=U)2!?|k#~RV%;q za7%k5^XsyRs6GK=6z5YF@+`ifBpsrLq;`RUAR_`P zVVdxh6d}TFoT2j&n1$o|&2LY5zdfhN#hC+>3e^|}13MRL2buBMv~4uSfc1e=kWLXW zj0}CMXA>Cs781c38ubv~0w|OP6D=>7N*c$FU=^OBB#QGv*)&D7f>H^3Fykjwbf+@E zd+SG*SC-Glf<(@jbj{Alh3@<6%NN@HQKee_qi;U{aW3`!6Y z2B-&O35FD4Rz0IEBcxHBnXYDZq7c3jk_-MoBSnZpSwe!7X%bKjO;b_Uf*dXkkpBQB zQfd=;Ioyq=8uaxS&KJ^g)zkm;fBcPKe()$tCvV-}{^f^H_7B^iY@VPv%A#C?hkj-i zi!jJL6J^6*4)Y6?fk{FgQn_L$eSvHl8u`?)eX~%rw+;v3<=R4>cU7WEU>4HtKDyKh zY`yOD@xSdZhR3TfK8HrVdwUbM#C)?>tyO`uSoDFm)9E)C<~`4M96RHa(9pV>=X&a? znK({R9FzO$$?3}4VzE+1*LZHhhOICTq1-LKaOcUP=Q{I^GGDiaIW3!(9Bq_nD-GxR zr8&bX+PzVHn7DN(ai+cQFpA(XC!Xi}zT4}M%hl3iQ0jbi7_}3_rVOFcm{6yfA-MmE z;^feZm#+^%Jje%b;^yXBgZH6$=ZaD$3+61TR0^%^9ImpGFiBxA$n8STu5gxZ6OIYW zPjq84&Y(r6@)|J%?!E=Lp^zt;3F8KizGY5yYcft`6_1;4w1|&w!;?U_LhRt^zz)=+ z3_f%|PITKdicSs!h8(tL$xDuh1wVIQ+_0BgSr(xA7WEB1CLB2iIl2~^&cvOWVpP$O3f<15RSFhiB@24B*E;Q%Qg;9U+ z@kcO<5-v66<5AS>Cm;W6^YN|2?fb{4JIAAOY%FaAtIJwYyzzxsYjew-AJsY$^(2(6 z$+1V))2OAoBLq0gjnce;*;B(k;tDLOAN0wxjcfK+qPkA5`ZT zmlo%nNjMq~hg(}mw{P9sesVt=v~38MByM^iX0M4G9}D3(peB7DZnz+Q%Z5NJEY7k zOMn#&FynuwRf$2JX8MK8052&s++SN`ChL8E|zv3#>wg&%ojc@sYSn z6pChKVQ?xcBukEnO9j06%t5G7V2nMBs3;FAg(sp1;#sC7@q%ZSGRv9eO8-jMESnU8 zpAZyfh+tOZA16<87=n<)lafSvnuFaUU9Q5}bn>ho2GWmzz%ucycGxs(mO|YmgEk6P zK62M@enaL4nm$}?G`X1(W0lHG2!-L2$1vfc1bNAMrcj_W%JEC<1m=WgTJZQOxll+z zixHvc7|O^*4jYJ}46O(!rbr;|}`mz_z)aUJvIbPz?!%Qr7-1!sMs{IfUjxURXfJkKS=$;1zWJNMAQ zX{B6t9XH8B02`KN^Y){WbIU{SdJZhWlcQ5N@QURMe8xDA9m`%^n$Ht{lHk%>^UmY_ z!_xt`k=mTBiw-w~zIdU&IA4LmtsnN2?$q=RtLcYHS}l9SQDpO@68c~qEw^g0$7SFQ35>&22c8g}w&9+c*bw&je+>8-u&plt?R_=4hq2AK5XX*PAgbXC8!c5CmFdy<6ES1?wf5pBnZ zyg5$bc#J^UIBL^0iuo1IBo6Z=N#ilyDvSw!jYywxw+C;#aJXSGQG?NV;z-0%H>X*e zCZjY>$D{sma2k&L(YTMf0-ec~kUXn5=lq}qI@35Fv=6r)+VjO+oJcvh%3{M!Uz6r{?V8aiz1m*hr`AgSUD?j zsxUu0N}?KMqM^>x86`calvy|s?5rvkClK*7s{WB7psE1Dc@1c(0w6dcN}3r|=Mj{3 zk$nuLSPFoCUI9kwb<#%qg@m(uRg~38dx?zgm7L##M|j2%{HSlm+h!2{5OT&7Ud?C| zaUdHQXfKj9W(W+|35gE<1`^9YBp6r;3YDUu*btQ@?UhpGtEvlU^e>JiLf1E5_*OQZ zaDyo%hC2bl8TwAhr3eU^2D>}wXUe#pQ^RMT!FZlzMbDkUs-cAHwmzxYY1Ph` zs)abSG}DjMOf)B|gF}(gB4PE@j9<5(1VJ(3E^txrYsmMcCsvv_@VKrkj%j!!_0g^^QixY{p1hj zmPMIIjI0!K_}ro;(Eb?@6_y#WK&yYw2(nV51PMntDx7lJIPHAHS**Fu03(@%aQ%EH z55!3dc{_Gmv~|zW0!#C)!Z7A@ryvDvmWPU2DO)O9lLX;{T}FgVAsLK;23CSNurrO} zt&bo5!Pj3rIO@PWMBg18cRqQz2buxVDo!gWlg4`ahwtwRNetgPx1gi%C`A+*@|T_w z#fC%>D;rDsk{Xzdf8z&My0495;ec z>dMMeHXa_g`<`PZS^i|djfuHlwkOd5z4_XWdAD2)vT3|IK!d#tPFu6H@yK>8h;z|* zA8#JTY2K`tl9MPtjVydqnxZrpmEhO{AH*0{z|E$h%8kqktdI!)L4ltk2vf{R1y~-i^(mp6 z?C|3DQVd0mY<8n#Op_dbLWThiW}zaGl}!yT(`|lnH&1dtMwCt_VQTr7?%CNWcb}_9 z75B$~`^NT@C-?4d?e2AxH0yMSaJf%AJyTETmKGheU?f(d-$~zl6n}Iui4wbF^J_Yo zK*K`bOS~6Xtj}K_IriP#Z|8A%<;Dx1Tfv07b^imKRu>m;F$zxF!@v9G_D(WAEjYu1 zJ;jvnSSw5Oi>qtp=3;ezZEkfviNd|z#~6B$BslA|DmXSp6`-ZOi=1+SiE;=52k*%a zDzluQd;%o*SfDpxr@5|&$-AK24w|2gheO@uW*dmA=lZqQ;>yPPR;vLA238uJg28b3 z=)v~G2M@O%KSaamih+yp9*K}Qyd1A+0I47F-i_k|6(v9@iN?dxs0V9;uQ?cpI7Sg) z8I;B$-%&PfkGn@{)SqTi93@IWS+?r|Rf$i{Kw$6>sfHz+CF!u&escGtH-7SqhYy}W zEIu%lO<>T3{gdv|*5<8uCP}|kZT#vt zZ;c~9t_5Qqf`$my0Y^KBFpdx>PDUKj6fGIY%siU~rHM>LT(UUNNiyN3i;#mHVI;a)5J+atas-yw#3I+sU<_xboxw|#Fe-v06x=LTE~W5q zUE(}r4+M$om=9106a!+8#ZRHCGSEmUCie*VS_oK{P>a56^KK&M5Y7o)hfo#D6$v9E zK~IIpMithteHj=*9W)y|ny@&^o#hEXHZ5h8?}UDV0mQv$taTDI7d%Uoa(0YqEI z23x;hY|H)f_;g*`?&JJo3 zVo?<*RN)=VxVHA(xw+mrT4+^&_lq}w{N^3zP(c6-N#sN>WeH6T%w<_LNz4jdcorQ( z79bP_4n+l?3GAs^G9ONvVK_r2frcOyG7$2DH40M+!zeIG7sz6Ut2c}GT#3fN)JUT6 zkH$*%Od?@&zl?ckvq%CiZDR^;jJ9Dy$z*W>T_BIE2_IP(Az70Yd^VtjR77V1S*o%B ztN-FpJ0~Z@afA}CZTYsfeb@nH$(=UBLd&rK{O+d)$3J{wF&psB8AJ+!ks84#stR;H z8w^SzA*`7<^3VtPPfJMmSF5`@7x~u`|xUu<*MV_BfisCgZ&}g(y}D!^~aTRX*3wcNepjwGzfd0 zQ%ngLE?rn`RfmJ|g|*hwT=}RyXm_c=Kg5&<3MuFpMY| z(@7jp=I2U5Vs-EJbv@Om+;_>*g^3TH0j)j344=b>;9?bcr1M>!Kn3f7qf^ET!7&M- z!wqhN0dpB8w_v%`Mu7ZGarJ z55DnIzOeA*;XC1=zrKET<-&`OTaCu;gWdaw+nZkCLyf@UhAiKEaQNQCt{SDaN)cf+ zjt9No)|1C(9+gUEw^%M#8>Mo6Jn9VkZA@ewD26GwpeVc??WWBm2dfD#2)ltF{Nfvj z(kN7ye4s{lQFs9)$3=b|jgxQ;g9Z#TOc#uVOHisc7S_%$udgpGE>w!H>sYR9g<*8} z-saSy>;^V(t2;Qljt3i< z-_*2VnYfnYctP26OSW5f{W2^WS`QQ^!HL#E>QL)w&^g+>`_X$p`^nFqJlbqEt1o~4 z)t6rT;_}8-uUs|x^oq$T!f*_JPS=6aLZ5AtM}zLTdptNj>K;7ppYD#khx#O~%`bo~ zu=Kf$i#1Qb|8yTR!zqRiCPytju>S00O{O2dI?kj4y(B@!uZN-ZJj({j{T+~*}{ zbx`m;13=Q`N|j~)vn1l@m1MD)I*XzQdK?J=P&qPHQV2-R!;%UU{-mzo3Me7?X91T~ zwMvBxejF8gDJvO*U%;6nweu&C9M%ZD{4^a=W-|S^t7C>-vO1xeHIZWZA?LT@$xp$< zK=!gY4op{^mW*d01V@kr+C5h`0YZx(dtJzF06^ZZPKE5L)_>2B=F^LXo24;(cI@ z6V!*=KqG}s%V8VCW{2LJBnfDHX)gNe#qs6Rlk#-C5N>x49%|`fKHN--*Q!Q701h^f z{6dfp&d9=v9>{0CI8SMM7RP>dDTzjWRdB?QX|cJW4cb{`4+)-Kc=_6jWttnyi|AIflSgKo~0xu~3qz6+w+KYA(zX%ZtMFi0VbEM#5 zp5i&_CVor8g400B<+W0Y{s z8~!A3sF6Nsl@#tcS1l2M6M%I z6yZPxJjz7LUmyS_h@lTQj4`0-Ps!=IwLIT?W$w5VA z1honoE1nGRnM{pr8uP;qmQL4^JST0KNPz~D$`v!dNQ`8g{DvF=QsT9pIe&f#o~_Nb8i8Yi;jP8_#d>0GJOGWTHh79iYl4d|a82vF3)umr_uDfa6^FFeVuv9EBkQ`@w`Yf%51`!_&Az z52u6X3~U=)9+#QIv4;II$zX555}i1?fQ=A zma22{!~4Uxw{$zV$|d*Dr;=y}ixb zw?2I5Wd9%tigA*~VFH$Rd!xVo)ozkb!D`-p%QCdA*{I!o;pPi3-q_yUzVq?NCC_p^ z&#yN9Vg+5{=5!M;Bq_fsuS0Jh{NYE8B8C zA06SkeiDtNXh_8Yb(tm@PhP27uD4o?tIKQa%WEsGX1&>{1ilN)K1q}QVDxlr|FnJj z^vRP)4<0A{9aN~0?e*Q~eedTh^ z&hI?g14HOjQ4LVwL?eh60A|P{4k`}^CLvI+D7XMtg`ieJ2V+WyilPD-iKFyWj(G`A z;3SF+M;t`M%nBkX9wA3claW%ALE$HoXQ_y@FBM80IC2<~w0{2X z12&1gAm!97WD41WHb}3rFe|0l=nl*!S?(7e&X_1L3`)+?a#&-~s***_3a4Z$*<%F% zss{lg!;6!ItlbtaG+Zf0Fw{+BkLv=nx)28pE|APOp^?;CP5e}P!ZmfV31Dr|l*AuO zr1>;XlK<%+zy9IFeN=X4cd+Mh6yyx36E*gQT3IKo_mAgP#j>{cfsjVB`{7&n^ekOFw}RS(qL*cu8Ld3a!Vt4U z5h8~H5l7*0FfLb0i1#|ZY7OeR0N*l$(&u&vOz`Gt81{R;oz0zM$*tCFj~;B@y1!ko zxUb!q&#fRx((q9qBf%bW^_$Prr^gV zA&vJY&_6KurT4%Cim5Oh+@tvqYBa8b-~_Q0+Y@SneF19?IN=Ha61X{z_)r@~0uvXy z3V7gYK*&IM008vj2u321|?B^X)UqMk3M?zbn|31j#gLdwPvk*dQz^IPmbG|ZcCM7 zelj%TVIGb&$1(g;x^wDYTPc3!`p7SS^5pScW97oe4c9M@d;6P@KZLFE^vV8Y5Q4Ha zfu4v*X%+=EiUS&f9~S>bC<8Y_x9VzyIXLz zyka>hH%qk!_{5J+o$gXiPA8`6g8kqbcs&~&G>nzCg+Ml&rU#kiUECb7re*U%JdDP| zGG9s_!4|j1Y(BpI{(B$1{r0_2K6!BW{^8!<&h|buL!;`K0;d?bi}Q2WZoYE! z)i2F&TrSmGwpRihVPNFs<8atH9QO`;M?0hT0Tesh=K6ejykR*QBd}v&j>qF+6pkQm zoI&C-oPuc7tu&S!OB>gpf8pz|UH7NqoyP|-iNG(2BL2WfSvnC7%BfBQnTtQUH8@M+ zCDV|?i0G+Vs-!C*Cs$U@qSnc>Vk${3PFlmaw3|TJ7pTU@zdI)0EE#JK`Ycbeab z7Z{QbZ;Q?rC);0qv9Z!9xVkpV zU^Ey>#^nTCUUSH8Tv`t8Kbb@sjpITVN2j}w%B_v|_J@XP!;g|yK&HtZGz6p1efT6C?y{K@--0;oV^y2k|w#RV88S7<1EP0pMap=5X|;(;QBXMZslbs?SY z_jV0p-%Dny7=E4+E@NjQ$U&?;1tHu$md7c<&NB!`?})dW{{&zQg; zko@r1w>OUlD~+=27`GoCL6bh(A75Av>~8*d+b1j};g4TiO^;H(JAj)Gz*5l6#sUYb zB|h*>#Ip_VHY;)KAjmcu;Kk7WXzzwUPr?n5G^0&hg1OjG`*M%4YTQ#^T3! zo*W(>153TpD3{BF-T(qKjz*xoST2Dw+eX>(aD+J{KR#_^oQ=j|sZ!AmJ5OWKz~&S* z@cHDZ+v#*Y*ZK8tK8Qjvs(S5QvpbH9!!+E??N!foOrz#>M??66=rRnBb_d8X&rVu( zUms?9F9kYwJX($>(*Z;vyWew_9!a>b79)&q0r^QS2|I#Hn?^BgN?QIonFZqm2X6hr ztcVVPcwoqayBI>YJxjuryMt-4LS{9)kw`@)!JK09La)&P=}qciTZ!hXd%OFNsUIEp zFa#R4G8+HE2M@J8nqORa^zg~T+Pb#4n-2^NHczpfgnba_f92Y7TIlu1SI^(56pLBh z+1tMJ!N>1yK0YWIMQA_1oy@c~w`c}6KPYbQoc#FhLsG(}BX@mu{*@Q6Kll92`Q?=$ zC|T2FcXM;x>t4Ke#q<0JckXCeqHC$;!nbd_o`(T;xcd}b24-k4gP-fCz|lO%QM_-O zcicxrAele;5EG;qcDrS{lRQNSQm^yr+>-6^js^;aqanHlRbVV&!k&_g6W*Y)`S$cm zy;*BDnhVSGORKH<`C6k^tCcI2VjU4!{kBoB1Vxw$j_E>1xqTJ9K?kK_7=;Mq(P_JL za(vuAK5U<~aqgU+LYx|vG78pf#Y&}At(7*;ZLF-HU%hng{IwS^JomY|wGFRSg=}R> zIO-mbx+kOV>8RgJlgKuv5ON1oi&uogilYd|cMR)k)PwzDx#c{GV1-&H_*RL>BlKB) zVHvH9#zPZEN!V$wU%&C<%isFabAcY+dvd^O1)>S%2LG5cE`fs_N2&szF|0Y+NiNww zQ+$k*rz(-@%)pT&1Zh+(47e6Nvxv%LDFRS;t1Bv10mJoK{Y;mFjPM9?XH`_<1d~82 zBoIF^5+@6&OCd`kc?Qp11m+N4;Xi|y0*uIsf25c(!6R9MaCRg)D2Rayc%(?}GXPcd z3^R_3jRMbtI6Q-gs-0DgBu+@2<~dJDjZ6|H!zx&A1_*%l#d^J3c=64d$XSU455V%= zi3S&kEwpqKx(nJhW%Z~-haMXbdM_^YA3uDQq^ak5ON%W`rqxP$b93|BrBxUtTAmM& zKEA#&|FBzfjXV`kmZhZ&Y6+wXd<~PyJGURb zaAkS_=;ZpfOO@ulm-at;bja37s|<1eRKv*w#1Oaupi*c|aQ0J@pg2STR>BSuc(9r? znnEENeLbfFAr~on$_Z2%0rY5Sc;Wo-+Vu1o@a8 zCt6q=c_PYP5lG?$1rCZ}OiaRh4Jrb2A+G4c&U%)}DN?HW!VpG8HWZzt9~YYTzx>m$ z{p-K~V0Es#STAiJc0gUTWHod1?>}R3eD4b@1f5qcD?oFd6jl zl4b;^ES6*&ClkjoGk%LAe332}(tnEROG9y10I@clh7_+0Uwu z-r$=NYKCovBfh|Zx9z9XQYpZoLR~Qz>IYYET$v=vV1Ox+AH{&jO6y0{;HGLxxUjT* z|KX!wzI&(QTc5kKXcgVE?Pz-=bXnGY6TmWy(+mT)RPuYh5Ytr>h_8sqPD5FjuSm1IAU&zG; zFkTWXj1S5xrT_?(ClA*mmAJU$ zbttc~;94h4T&e_%VkXa!P2mcIPV>C`v@^CR@sp=VFpFQjxxTuwRIAsO!a6y?<~2{ZCF#x>3CvT-;bZx4Lxc(uHcZ;due3|G>YW6tW~q z3%38@;eAlK^Yl^YfZuR&AQ|=fQn`wm6^VS}OUt=#ktJN!;afr3Dn<}sWKinCWS#d> zEPC#M=jcbiZl*Dxeg?l?FM#0^j|S0b06R4r4;;S)@!{L!rg8*zmv5|IeqsIc&GXk^URb|UZZ7)emSwvb>tX*i9CSv*PRe`J zsNb_(4@Nc=1vjBa!!!xwFwCMb+FTg?$@^aV~B_)Mf$0R-m>7R0{@M@M^`up~UkJUkt3ZtY&Xb|o2Zettdn zo!Z{80EZ6MWBg)n#Tby4O5QZ#Mw29Y^1T`>=ivRteyw8?N^XMw`)k?gUmQpVG85EE^ok9fznM!9bD2tGBGK|&8HU6RV z$x3z#`xHlDP<+J?1;r*%d=~LzS#7Oqqc|oPE={v*DBHv)lQAH1z~Dw2A_>==Dc?2HFIj=}F*|I_*gl7^bm?BU)W(vR{gZ)3NOF$(6mmF0ZoA%bN zr~lPI|HGq`E)1O~du@#Li_7H;=No_Z!KW|&`UkHr8vQhjfRwfp#D#rNvrkyS)^WrT zr!0@LmAE9OQX3nF6K6PX)d$>EgA>dhlKjjEJxVUFaV~?O4<*Lc4oqnv1}%mu5G<8* zr*QYtVc=NvtrCzg&DZ9t#qp!#Jk0WNl6Y2PYqmatNK7@jWC8p`%X1-~QJgL=G+HJ5 zU;WLS;b5?~(BN_k%w>nq2eeN+=)pif?UqE*!9ja@X)Y+2;)KihP{DZ)Q&{(07xP59 zQ9nI8`uE@eIcBnzR`KTL)?PPm>1On(lgCr58-1XD#&DGwSaJ|&0HjWE&3Yj!*Of&8ZU&_O_l zfX#eghpyX(p2E4#<)Jh50?JaM@Oya(>^m(P1{adEX} zOp@ETpX}^*npL+^xAX0&;G6Lv2Ce?*R_;8!zqT}AEY^>Y_kZ^5UmlO`OEbiBWJ>w}Mf_OsicJUARD`9ibw<5{E;KRC%LD0hcI6b%E}a z2VWUw6$R6BVcHrtAG@V7RBM*&&2n?8RG%x?Tjj<=rL|m{TdvJ7S6eu)wU#e5m(Dep zFSM2~wpK6Hm(CSyEx!aa+677smw41q#{E(6IF1ICAGFQ!;jyx3CwVv;q)7w=F-gF) z5YrK)37!qCgkrS{3mdXho13@!z9qixgE}5yqmgmhB)M8k@HVAh$WQs)p%fDsUx zrLe`%GQ>J(T~ZlNawbwy(>S4CUdRtYAlTgmIjfEt#5@b1O`HX!pmnTE&`2&}2(DT+ zt3hEt!$5ciEN9vU%!LpzQ9)qv3p^(n1x9!Ui32B|8N8$j=HG@_;Jh;-AXFAOD)u6s z7?`A3G0R~Y0T{>Gh2>_w#?ME&wq5jP+p@0NHXo^npK;n7%^HqUQl4#8n1n(E&_gbt z;Y5^;?YXYJ_tH1J_;6gsDEcp~fTi(A`=u#RC$|MIO*E}vg0nB`VAyt!)khuSzZcZ~rAN$qiWZVX2v%vN$3qT7T{rQWFu(wxQ)#l<7bl?B*mv2Zf zv+n@Q*+=n2dI9N}KNRyVzONIq%$pQ~OtK{)8-FMde};SF&axp3h>@QhO~g-3A|X)U zyt6UGP*|^XF@}8#z0DO1Eync4^;l-3Bn$>z<`ZokaSJbajLBG4LYfG}x)>Mp0vPZo zDTGmha6AfC{3cU04&xx;^L0RoEchAF5hNTxBE>&PrE0~q_>X?`$)A4b3kN6t$Gd!` z>2vFqa?$<42cLet?RQ^Yv4;6{1oM;L9TYDu0!jlE9AyHWARL8|yx=1>yrh(%A!M)u zqN3sI89(6$JTi45&?us<#jnw#ePAPRNPq)|o`w0;DwO7mX31`M$I5^MEMSV2z-;>7 zTroS1w7{G~ZyN>2Hqwc1x%}8GCXYPJz_MDSS}nT1ssHfJd*_!bwF>;<2K-gmwIBk6 z!NBnX$2PM#kCWIhmcU?03MLQV54yenXb^%v&v#+*{o)tDx&LGr2K48y%+(tKCgb8D ziBA&B(J*!A%FyMbC_OyxjmEL>IccudYr$N_NsdS1(~%l^>`3qiM;P)kU!jNDoKm78 z!oEff;=m;e>2z-LfUHoB&}`*<#{gfLa(@&DDhF*EWrn4@@p!_z^j=-hjF*zbi`udLagb?bvq9zQ+U-s{4Vc<%YD<#ZU{i*)d& zZ~L#zC)L{BJNIu~yE)CqAAk74`ybrTQ+>YWR%j8j9#y9`DSGm0_a zwoJb2Cf|k(XUXw!1hIs?Ewl!53ylrC*V;Q8Q>L~Np)Mu^=TAlu=~o* zpx7#68XJZ&4I0zfKI||;`##Qw^;Nb9Z8(Q{R_uqQ0aOu+`Mjgu(Yz|2R>NcK5bi8p;oK;fz$5|V5K~M z@bLRTdJ~V7b%<+YOz^P6XG1AGS*vXBhnz*8*Ye3-lQ( z%eeTGAxv^>3cp>4U8kifDB{yG{OGQ3w#?M`Z{OdAu{Yl=z4P&II_|HpEtYB}%uUZ< zU4P}38!e|3z1wBwuwhP)y)RyR`|Y88V z`~UY}{rD&Emn-(hO5??gYp>k6@Wn5@aOvvRN^{Y6T?{S1Tq#y+9$%AD#9TN@ViPuJ zP%>@qjm6xoEYWf2=%77ZKYy-N@;-d$BYujGUsMRZGK`fu%W;qP_L695&^?D9;(8$m zk5bKm1*;9%c)Xnjoa``|$fz#mbNw4Ci9B#%SfFPuh*=tsM^Fv0*0@ZW z#D?vfwu{M|`$Y^}=NH%H6TLKLAR?#~T~2)hJ}KP;i2$o%-@radqhZ)P8Fr81W^208sLvayphD%TgtqXD?A92)o{31VUk%U~pH26+4x~hN&#T0j1xS}o) zM8sM0?2`FxCWBlt2+L6v3G4)_1* zB*h;UXS-PI3|{?aMJ1PVMK=#J#eR@Psu&DvULcdjwJYc5=ir%@yjrEgmz=ZrZCc)X zAx#n+4q2Ka`Ls7=Eh?z;a#WY7fOCO#waJq9^TyWwt6KfaR@7)4xFZZ!=GVhQ8;Mtqt>HGZ&_X?jRrDY zF%URSsqT5Ox&M`yE)K_$?^sJ~D}Jd_sRV!a*S|n3DG&Ts3w0X+^ zfujdm2!Ip;kgifE4hn8B;3c@ju0c5#r8tOGOD!TQxE6ft4KJ{vnK5!TSQ{)nTSXuN zm^PsJR4;QjmWpXP3N8TPM3X{R5IM4C)=^tTX}G|bWHZs_3L?w#B#nluMTj~}h&{oAviK&gv7aoB^$cysJaLM_QRgu*oj|}0JU@P#W?R22> zJOW27M+=j{EO>KX#J3FO^@<TU%Hx7AwQ<>C)2Dxht3M-M-WBc7jrg`!?EL@Dla}2pRMSJ`8-<{r@#}t-VcK zVc3o@@hxt8O=(NZrLY29Cj?>$A#M9GrhVl%?AL8yHfao4N406drm?P!!YI;ETeY;g zlEiUh$CqT!bDWO3ki^I5`o7P5J3c<TL#n|ct&s-}$Pli$K={GpGW704H9Ce8dS5 z<~zYOE_ zS?~}M-Wzj{R@4uUJk#JS8g}=am3jxVzi{Hjv=mRMvK>XemyPa$s!b_zC3bHvMdRZy zp1-64$i~$uB1C{RCMlwrMAP%YU*NY*+p(R3ntHzH4OBj&+Us;0l}i0+ z&oJ|b?b>6-f;)!M?C<;ozano~AP5HughKXsD**uJv&7^9cQ)cpD9W^|gM`rN&~IoL zWhujOJ0uAq5KMd$^&-wagi+5^F*vi3ggz_d_T(@G;4r*CDryQ4Sxc!PPcSy_4nhwD zWutDV-)$oF;&aqM4e$*;i38pQ?0mk3&j*_YJhQW8#?n0C=H3m`0YkdjMd z&LV^7@EHWspp>}Slk*?L6n+@`69qkxl>K2Kfbk~iv*@9uYo>14EX9}JbwLp3@3sAQ zt^Q^Q4h;atVj0|tB7r0n05S1foDt|CB2Kcg;sS_%d@85`VvDkbXI6&j6;+Aondu^t zH6ly)J1dnPLZjh()MQqt&^E_K|GSix!X@fNB#P8g9-L=g6+g55qeFC6+R$IFhoLMM5kC6zF8}K zK@Q>v`iH;F<{`f*LdH!NtjMJ_0ZV4V(u?`w$&@h&V#s*}Ol#aH&&;3dj!zF$M;a76YW;7RJNBws$X_nXNUv zvx{@4&Mw}IY*(KtyT=xuapG@}@=j7gqm7+zF zm$eBYW|Ab`{b}>d+c!40_mO&T-re}0!*P1jvRuP8)&8L`=lP;bw8@1KBjR!~Js+AT zmDoB4oZYAeUSlAi(_pk+P!P;M7^wJ0wxO&+iRqjg;v?Bm=6v%XUmPOF$z_Z%AVVb` zM9QGxr1^mPEyCtPmLGw!w zak>Pjs9t+pIx*{xxf@Tnqp&+ZQN-wX_YS}=!!VAHDwCzDI1E))bqWqh-|Ys#nq?V} z9{&F4i`S@de%iTmq4a#OdU0Vc*{R3fq3Y(X^S0z@317f5+%LD_D)pm5H;hWfg4~FL zn#a2(5Imq4gb-8!m@r73apGYp0rT(-AWg8Cumkud1e!zxEMP|9y9{Ia<${J>H^F7m zD4xxd5C+s6{0FeX>qVUj*9#DJ2&Dl95E|@8FcHdfKa6>U203G~?G7=(+Q2D@1spr1 z8N9>A8>=df><~T;M!*GDFy(#Ttx@oa$R3rd=(d`7<({dQtBuR6r|WgE>GeMTXnEz* z>3ChSvN@~tmESvdDzf$pQpIYKBy%K)l@KpNT#P1MD~O zUN}UW0 literal 0 HcmV?d00001 diff --git a/images/comparison.png b/images/comparison.png deleted file mode 100644 index e379f8cddc6eb3f906fcd0cccd7e95e7588a61ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1055873 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

-rp002QzHDMLTCSG0+a}A#fmCP~pq6n8yC7 zVPACZ!MXb3 zf{si_P;*QvDo$GH6-pjl67diR)voK@3pO;d{AL~H)+o(og$_Usw(AYQWI`PE`VdLP z1PK(S^0o2<`StorMcrmPGA1Ymiv_)13&nZmbk;l+HQSZ`(-6|5L6DmYx@HR5lk9XS z@kv8Bkyb|_MFLb$rz^n5ib@bpfOjm#TV9zNQ3CIgNF?|dB;=)EyrCwO$8$(V-HuJ> z$`V#c%zjTrR$3ixfeDm=JC#hAYrLL>CoQg+v+zCxXxcyW8n>TCL8`K(9ALxWvWl^<<1K}uM$iK3}Bq@=)A+h5cD;X{0 zzQCA)#42}2obuO$JCj=qzwzsjz+(!LdKVGqw(yvk_?x#lgKmVp@yBlp)TLUakU3R& zg!oOVP9X_q|1vOQzR4$B)!J`PiiH}Z)`5^hJs^-+VyW32+C49u_4eDV`g$Lq;!c?? zf6svioI}#vIoc`0**}LYA7^)8`0Q>+;3pO8tJ#xYo!*^7mcM%9B@kMg(z&T?+8(xR zJ`EVaD~#J%aedN;rYdSh2BuMuf#kj{@<9nNs(hHd z78sx!(=^acq48>QQ4Xe*lVfAdQ_!J@0}2>67}-~K>3rGHdoKc`2&k-EO~c z)@wpy74f)RRb)rCsAgKr7}VI+Bw{(kfdkJ`rMB)qrezlDX~R76A_@o(eO!M*E*c%= z8O)0cJ3}2ee9|sZ2h-J6x z0%q3_L0eIl)at_tlJ~|hf6g)Ih;Je+Dx4nqojpQI8=e$$;n$NM&RL}=Ku-!h|K12m zZuTcQdx&|8te9bB^*qKD_3@0WQ#{5Zx@ouHp`%~{?)E#7_OJX)FZ$S!X(l&+6W~uS z`4cQXvp>Pb;|%wO&u}{eAEPH77`(J{w|jgM@eQOZEONkm8SPZf6WozWg7o4d{cV4W5Dxj) zYk8%qZInX~YVxA0y!Myo5}n^rOJb9is^--0_Y5CKTx567Ey3)+{Tm9<_4Rd=E>@BI zAJ-mu*_N)2WUzWI7FYcInmGfR9(i$fb+x^!%etSu4g*}EL7(iC(T!e()4S+VeVLW0 z#gv5r$}wfUfO}BZ_ik~D$$ht+HBBR}lZcYkidS$C{t#BDk3ag#b1Yakt=btnwwgPL zi8)Q06AHU_mn}nbc@=VK+!!w-1`=Lg_eevEkh(N)T;#Aw-r5M#qL9Z*i)EXw94WR* ziJr7rk#h3TI@CuP<>6e}i{j%g;ud2%6iDkp4)mEhud+i-My!>pD{QyYm#zv`rN`jq zg%(W$?->IN!|@njn)9YIn-&|fwBzj-D~OfV_U?>`+EJ@O$O3K{D*NjLvG)C*?2HZ) zoMsXk@@wK!Nz~EMS)ikv;d2w$> zOx}3$6y3vEBX**iVeBP)x=fOeq4{+8#ll59ALSfjjmKvvL{QA9Dt?eTo0mVv3p>M) zhJ&R-Z$&YT{n)uq@;7iJ(|2-TKE&*aEU;H`48cx_3t9pMpQ6zZJj4<&V!|H~pNtbc zvNiV}wAhwz*bC`sKfWDiRDVGCRlo_mb93)%_uK<-x&lw~&%fb}u=K~PIZz>V_NP}j z{0WvJvjZfdkfEz=Xm=j(bIU`9DuWRm($48g%Yc~ThFMe`9{R4ke*KE5q=mP_pq))c zRWDcHyo~b+yf{}3&#AVC-uyj#BF0V6>D@Ubg}w3UH+l-X3Gwdy=1mNI@er@#;q-2i z&m58h-?;hJB98xHevCdUndY#OHjKcoM@=8g63?Lv*IZo0Wj=Q#YIfAvHX1MDorj%erT zb{KK^VOG53QC$rz1p|UN!PZEMPH)Q7f3xt9PnVw4U!PB5sW`poD0=2sGbHc%)s0^u zh2x@~F(ASduk`Kl#H7a7AO;E$gp^lOJMEckg9^*c$4AI*q}h5j~TqR`|x z$Wwq>G!C^2F-C0FLgK~fleT=I<&oS}O2$Y}^a)EQ=ZDgOuy-_dl!wMJ1^H1+>>?9W z`mu=Eq^8Lr;}9pMDXg9PI9Ec4Y6xK*@Ia4xRRT3JH4PzVSQI=?UO#~P+J$OLtG(N- zrd(Jm5&hD=X%=w9u0Xbv^YyFi$}eqe?UO&<22Y_J0xXV#lTDoHFW3`3htK||x?c;} zVejXj1|(2BgZH}#2d7J?x3y8R73p#M;>C-tG#3E3KuZqZC;j3l3&J&5qH8~USmN?M z-8+E8PbTZ#U!wvVgjY@?x8}_(u7r0k=$jx|7%Xe`rgBo#JLOWM4UQmO`PCbmQhz~PfS7lRc7*m8*wJair z_f+l;&-{CijJx3p{|(RiRr2diZKpWm+RZ;HpHnVC%5V-@c#Ij{edATa*c_5z z8M1ig&obne=SHeKU3!~-&s9wUdgfQEN^%%gWfeBxIjtTS$H|(M=5fLsbOjz(hqg@= z5*eD)ZO_^7FD{hOM6>R|+v`*((upO>k)(_?s2O4?kN z6-o#GA=a*)Gg2k>fS}jPIz-4?g;jPvK__mc>{@RCJLPjTbN%UGJf9#vlUs%*(YdI? z(>P*pxZ}k0A($oOez&7L=m>=LdfhOtNvYsK^UkxlJE$ zAr%D#5*Kp;rI7d(6aRFC%o8i2Hu4XkccR27VL0YPeymm*);9y00;{5Ij^6ED*^@-o zhUt$*pA~~^Delk0E+untK`t~ZFCYObu!2E)k!Kh)3a7Z4dd?mU>^+()Y?&*t$yRL|9#^|S`yk*<~C>fONCU6cXm&JifkXnXa~LY!tY!vQC@4 zh%31@*`aTzTHtB1EywYZBM223r0^ehOLUW+B}+U0XMg3tjr^bCqktpg3^%dNn-rj~ z1I)kSc@uYj454Y{2)XggP>clp%n47P6afh7oI+>M3_d|BkVI9jkz`e~$;)+JLtMwa zs-P<5VB77g%3&M|24mYPOH?a|Gb%&9HjlFQw3M^OymZRwlzTwb^Hh2YT)evbHvzdU z8uI4%BF+PlViISWB*7eV`_)_Fve008Aab+WfJ|6nANmB=OUy|hQA4i}?d#p4-Gj`U zcX2Z4lsds%tMNb+ez|bF8W2@KAfP)GSt)1b{#7U$^);B67$gqNYPWOOlLF%m_~QMW zY?CLE&@y-#?ff>u$mJ1iQn`}PxwfZLQ-id2SXw^A5+=@*EeVl z!vsoz{~`h%FypbrBnvf!G39}f;LjK|+s(G^oV^DkL;tF2N1ow1h0J9b5r<)b&EO<^ zjt;8+gF!HO>0DXWZQJ&K*rd+$${T)N;uv4NxZ)+%vcgo7V6sQob;G9`KAxhe+PEv( z*@<*$fINznZ%>d zE_G$z;>EA8qCF%bqN^d{r#rNXe2C;`jBG{1O4#jqtjioTV1<1+`R$aV$o)7yEu$5Y z7yVnwFtaW#rc8cYiAL2$?iv;&B6iGDufc}9M=;`t^0a^(h6)krr_Fl3zdnrp*mszT z5J2&p|KJZr8G2c40Z#^rh$m|js3pV~T4dRyGb+D)pc0RFkA;h5Pj&APb-pH_u<}o< zE5Ya>2RBdCwT#d9;Lbe*1vh!Q{nfb}zoiNR5GXB5hVKn?+uqGR7A}&#gZuFCvUved z41~Av#55?t`+DPsRKzoJ=&61f7(q_tV>-%Ez`VmZW%Aj=1$y7vJpqE-fWH&Vgo-si zz$Lse_*yU|XYx=2Fd^aeDco@qu_G636|_qjDa?$g;L%?dT=c+)bvMAEBq{H5DFXka zWj9&KVk2w}V=)cZbXmqI3|

36$9o<-hP!JMMyPFE#n^2#O%@6X3hA%jWPD=e}r5NO6{Qbpi3} z6ugX(Jp6`p0G|E2JHQvt^RGPIgULAlcWkqLNayQk=?Ubnq=DB}w+14qkPv%Ng@l=%) z++Y*rj4envrF)8LQy*uKQuP8OHq}x{2@>M$U_mBFKd*+G25A!Eq;l zg^OfQb)P68Wx!`uqrqsRxW?_Ekh{=31Y)98Mfv~xJAc3{1`A2#CZ9x;mgs#4_e%lG zbS^pxJ2%}N0?qNGlw+hj)wPuUZ8}*d|+9aXS5Yn4GR});SJo$X?s`|oGi}JA4j*&q~sqD=6WYs);`ra!7}_Y zuuQ8r)C-#I%>fPqSCaS}h6=LMPhK)us%q2q9dWWE+qb<&29$Bm=#h0mJ$0g7xeH!% ztO&}en>CgN!x;iYC+b~bL2jsWBOr!huu@b)4Va2)0gRy^Q?1IA^s9EiBR+-}uiC?* z>-wr{P_a~csjhNEbAU37vgn+w#IR7YA_8yeWZt5Uc`>ynO0f>p6xP9TPzYg;s6H4? zmBG4O6+vA+_5z4G@ZgnXvD(GL96xIY&WuE zO%ZVprL8a-$u#d>E6_iTKxn~d66!g+Gl6b;Dhf>cuI|eOSyG;P6FgXsmOAiaVilc- zehHmfD2wbr|Es@QFflq*h$Pei5hKooa+kK5dv*WxUJaZcQmhtKg zAbJWK$>e7lh@(G7a~Qykd-sP!e$KSI@;1LM{kFEC?%{G)Gw<=dBK<&-uE$AC%6e-L=5PRt{kN2lb+&ZWFBXZS()G!QXY#a(AA;*1Q%qSHqE!yarnyiV9dbZ?bQ{T z*BE1zJM`=Iru3u4Rtw96vQZw;_(v@$H%(#9QVM@&6JCpiu#@dU949^TXt($;C@mrK z3}OD5GjAphDgy%*!R*K?Ha@xEW`=WTkf!j|XJld z`KS2G z>(Wo_GEx}>3TYV+zdX@FXSSf?fPtULpcj0g8i z$kueOv%nW)wS*q=7jfc&mJ!s`3o`CI>M>|bzs-Js{L*^`uRwQx>5#{ z2sxrQMe8(s#<8Oj7(&qa;K~tUF}0EBsM2JMmBJ&n!&6s9I~Jo$Dw+a$>h74m#Cn2> zI4Fqh@WD5t$5Eu(HZ>QWSr7GK>coXz!xXx|DS${BPvN2EWNWe%NxWg zihHTa9*DQ%CKX8X^cR9lxjhUi&12kW02eiWs(S*MfKlX_q;u8E2mtA0>7L9AbH8fp z{eJJLgQnfNXMY<{CLuxS+>&Zv&@)N{y1*%w%)4ml zD@0BVgmW1m#(gZfDCP%nUkM<>in@VX$Vd3ZCMuZRgdKoP?d=M zq4$c_G)uw2-+5aRlmu&5M{&Y+g+lhojnFf)msn5!IphHka9;>8dw@AY@;u-Y+!>N! z={flslj?SbpO!*pIJ7NzlNCBh)kI&seA%qmXsyKEU+);gek|{eBubu?Vajv%e?a$e zw)5NDp4)Eu>cW3g591cJP}Jf-d`RuRkToH%r4-aZ0w-uJPH(Fjn$XlYU=CS&?hH)7 zvs*qc{qtjj`Ehm$X8#<1;pSH-z(*U<_+iV?zhcov$AeoBP^-~o)m%|Woy&7Z4{%;R zYc|c#es+yban9>ao@cmGP?Y4&PhC5xY#&FIaUMKU@0g}^<3oc(oD?NRYz$L{UN_Bt zzl*Ivp+$M|12W>c*G`RBw^D~uVlpmHLEA!}NifOX-T z5h*-{a2#@s7V2M=RKv;?8fO9IB1;WAcVWf`nkl7PW1~=iCHp|k!RaEHg0B~F!%(T6 z_i*Lw#W&xg0lh{wa}*J#u_e8-W>b5<7L11=3F$J=obcU%s70JaEYgPbvFU#SXb`X z8odk}UWf_+qJgxKCED>26Rncps;Xn_7tAY7*=W=d4v$Orq@3%_z&;WzTl4Ne_#dWQ6q$Cs(lew>@@U%vb{X+jVaJp~Ww zlCXRfoCxvkx!v3Ie}|@ zi>r2jP*)&W#fqPd@NrbLx%MigRqm($OnT1hW`>tAmkuobPlAVwxDEHkrvewRQkBxL zoX81gXHOIpZY6a4gO;tFN?X@C`$zyip|Bzyyo4Z*V8i_AI~_%po^ly@44C z$%AU4f-HN@{4R<*y$o5c61I#H5-K|SdfB#n1Y@4nVf$}9;uV`Jqx~1PIM0}q526$r%S!xRLm_^YbwM~~5~C1VX%9u#4ywtaWNcxn#}VAXMKn8}r#zCSRW z-G{Q8FiYSGs#l)#wp>^M8g@;dZfkV0NF~`1N)~N*Q0P|;F=-&OUhywbi_$CY3Qg97vxrt)B5CSi>XKJAX-PTBE7o;Y-i4>=6Y@P;T6cm*V2pJYy5Tt13_lMb zjb(fQ$4hCvFmB8luXd?Z_$0J<&Q*wn?_HT+0;C4Kv-^<1Q1%WRtqz9^e5qMC+9?hZ zw~OEj+g~$26dcBt_6fp^K8id(Rk%RgliVF}5#wE)^b0BT53vAFga6P4A@~F5GGB(q zR5zOI>YcIO>r@42H^~5DqxEQd=h94bdVhw?g5SeE!$*LRf>+t70~Y344?_pL3{9qu z=~c|*36%@VfyZ_|D$Qxvz|eOs{|V~67alQ*B7rjGw;)3XpCQ$GCW@se!R!Hol;3!k z{yRNOSmu*TS-KRMewS_znLWu5%z^25>6Q>7)+jIQVj2h3g}xu^wHx&DD{7QDD1sgl zTNO2?-M$-~M0nGPi#%_`GQ#Y?jfD4sIol^y2H-J;oWfD9U^wZ}MdDdbPGPMTiU!OV zWI(8+o_)fWp&93WVs9<_{os)ZyB%$AB5?9p-}rleVJAcTD#j}AeoZCR^f+|^$8~lc zIjAlxJ7(;V2%EnsGROgC<($#^@1-ZS>y_Sj|`1z{&y#>FJbu)6~2gyIp%Y zfb&wTSbY^p*{qvQsQ$J>%~k18>Y;jZp5oB=N%I~1t}3e`z7!b@fljM={N}|A@N~V~ zV+%#OZUjZRn3xx?u=+VQytJB}s0O8ZK0{$8n?NVyZLxA67x9&6UQ8@acyPsOIFs96 z{v2I&AK5D2Yf3v4LSMU0?co&(qM6dg*l zeNe~Ru|x6Z(EBKGfw?ESyWt|zDGZ$UfM?n}9}j0nxrp0JZd zI59&=kQmO!Q}Nrs|Fe|%|M?&O@8^o*p5X(*jLtiMlLxr%Swf1rboT`&xCnowQ(D($ zG4y`o-;s0hxhc*2sDFqPza-?1_}@w-RSNwlbu*v zK+5xBcW1OYFu@#t;g+x73RG;a+lA+%&UU!!pyE%y4Gl1$j%X!CQ6-k_o+u}eMl^EB z=Jl)Beqm*<$FfQnb)&2la$J!+eBRw$o^Z&jf+5Tx?nl2~ZK?#L-mCt|7dH%y zf77K~#dz=PtL=>V)Iuw@9dfsn#tt+MQ_(|F9!#eqrpF4>t~)K`oKQ=k=9NB@sBw^q zIpncwpxRH6O^p-;xk2q1Xjip|0@2XzmS{4s2<2_GgA^Ey&?&7jKZ^GBOT^UR5ib<$ zaZqdEtgA*yw1Kx+1pIS#nGCVM%!%?mNE6XWL`*zXgQoTL!kCq#8^0#Mmw)WS#=}%2 zCU~$hH_!Lo(3ZNvdu^MB9;XT}_D~^I;OIz|10DDyDw$9N@($TgU}O+DM<27G_W%lb zqULQ4?wYph{O#NQ>+im+*IPQpj)WI>10&==|8IUR{PPux@k{#^Dw1#6!}+1f7fqby6}3JRwOc#HZN_@yr;y}Hzr0Nml4lut2`OPp^)BGS2?nm3gv5gy zRiUt~*WG?smuNM9`GLY)R4rFmdWynkfdnBj)rO!&XtV24o2EKj{n5`_$SY<32zd9J ze>%Vj%!|Sy7c0#6M+`ueoFZHGZR>`dB75=j(O}Z>9%$M}|rW$w%c&ocJ8Zdi+Ied00@Zxs~ z_hDmMy$gSGb5`C08z84uS%)xUi3yhNrYww>vhw^w*J0eoc8>(hOn}>Ui;yTy)PgxR z-lFmdj6|h!gC(**92k$NvZ1?rDzU&&=v$1KA+Di=Qd8bajnJ-}dV{Wi5+jP0DGZu| z^dVs^ihgo0**H*2>&;WW*RFc4nDdfRFNBN}XH>+isjyqn`$xC`jmM(N`%1&YXsTnL z0wGs^&DthtGMt*I+K_~lm5dv}Y@*yI79kvBpqBrUFo`oGlP*~-v^vYv*C%~xCa#i1 zgDH(P4pJ?F$&H8Z=IRO15F$)tLu-We67SKz^brelg-KJA9NS7ZXo;Tsw>}cP1*#WO z$zzE*8cI*4o#Ks(gXejVR3kqY-ZFejg;7X0sk!O*1ULCabPVXm`m7tzdy&xT`k1#T zGf0l=NEz0`Zqv?!D}^x?4nOSZu5B%+2#;`&f{Qc{xGxRByewY3Ruya_#X^02%Bfn zPNZ>iM-B|@G_{f<#U(9>FmKnPkNgg?Ih- z1p-hY4!eW5rg}jUc1P1RhyAYYx~f^XXxNTdB&^&N=TdgzoCG5oCS16?!xIKrzp(>^ znEASxnz}j*-H#7HR5|xTj3U`u2&R7M+YbFK&P=MnO@29K8ov4VTQr;!t=vWb(GvnG z`8&XSHhE5tC(*?{)Nuorh0cCkelvM`g+k?YzH?ZiKA{9Uw+ll;`PHa1qNIdAjS5T5 z6Tzh9Dcj^vzrg%>5uV^88nE7JAGcLNu$ojLgkzMjeGW_-=zy6TQ3XplBdEID)8LyLy46Z6Dg)K$Owhi zD>lG?EN7)nT$P50S@lDgZc^UwUn9!3O!##^_oSdZ@?!zt=0w#P@`U`#ul!2x>3{eD z>i6l(5Rg1Q&Re<_?Kv)U^AruTsm!z8P#jzXB3Epo$UOe5LzC&z)fJ~UtrrDRrq&F< zPENbF;XC&VYM${-C`!L7I5t682CgxXFVW(3{;C`_5F*}+3YgyQXBkSPld5qT#WZ4$ z)Kpmm<~Y(F5bdxwD<@w~$&2_G79KSyytdfg3T1_@Fk*FC843#(#|0lvgoq0rPyyca zqKd^9vKz5585)tT@?GqamguB;12+s6Y<&{LRx}p#?0V;MHzmE75|E2R{=fQL|2QfT z2ne!+`w-uzgUcvM^je9V?on`&<^lH=18A0bAv-86jU{Zus;ZlM^OEi=^0My^Jks$z zGT!JiI*6$BmYtL^c>engkAj)>&hAIsZy76Y7nwXU@05y6*{)!$V(cTCVpxjx!Ie}s z4c7$%^v?X43A_dYaCbtwbNf&?7|UDnTeQcs&q2d2)Bans)Nog_p?x{QIy=ah9-ryfXqo$o%TU z%`p3y;mQA2H^-bkOP2xxC6fC;(7hOXWm!!_7ZaIqLa*WlZ)tdb_ubDk_+-954u{cQ z+V#G`~|m4Kxmm>(}Z{HBES;~WB5oA3b1pB``ffs`ac%4Y^5q>wo< zc~Zg?xOa$QLCYiDTPS$0>*}ySI03#|ee>c4SU^=~SPhsfLmL^-nVunK%kPCpjg&zu z{eWZg9J~yp#>U7z+s#0nV$BopU}YS<=8lXe^|#;r=;h0Ai9-Z3=*FS0*G*Gv(s<=V z7bb&O`l1h$tg6Zk3GX(usqpii6NVOY#EoZ2_OT;jvwh*FGb|ibFZV#gO#D6!L9h!) zLM+;nEm}V|QqthF!YNFKjys_U$tFw)!$Zxpeo8krZPFk8;G?>CPz~ua4ZS!<68eOT z8)P%8*b}BB{gAt)<52TibHy#1SaD(X@WvvDp~*qk$6}}|4Hhd526_dk)%I?+C^kQi zo`&}ziV6<3#Xh4Fc13)$lgI!s#tEXQi?ym~GOt4WF-K40tlWGi)75~nVq${C8ZA-O z8O{SE0COpQz6YRJGjeG(%CwbS7r>6Fmnp0FZ)QGG=tX7Pvs+JGi?* zHXsi~DS_r4wftism+&=)a}3>K82-UO`7Snj`U?Cjk8TXZ4*<)WKFNJf0Pcb>Pf9-3Y;^rgr9)ucNTi_2qe!%Y+IWS5Ua6rgl0q4eDllwZNl`B_rj?=~>OutLm^4VrnE>~m zc?SN3jR;q}Pwfc#NG_R5xS^(`2K;ex&!SgZPmbh1o_5&~E_Nb*lBRKVBy1m$82W#a zDpv3mW`i}xY&sG#%qa^XTGTYo+h{n*03h%x9XW}!B($1Xydg2x0>zL;w8GNKnv7DV zmSBl95WT7zen|paUE7X+q8-$fu&A@o6c3uiHVgcPk!L7xgA$|A*?onuZ2ptn+W{;m zq!U}s&E-Q!((VA~AFx%N16a9xlFsU4#ofr!`KoTI{|HJ}`R4^Qo}TLNg^PGkbBsDh z3BXX1Eh=>!Q_ix8;=nZnS&f;O4ro#_N}OZ61#j|wy8Ghc+wC_nO+4aLUL|h>u^~G| z0DtHXL>=@`r>xf-G47{S(oB-0CZ;yw&Qo+6z2+ImFAkP;@xk3^0;#5tt}oXA?euTgzD+tUua0^PyKlO$N%*E zlOjaS^qU}g((l7O_~I=|W;X+IQnc9vEEA^47panePMBbsY6%z7fD1n^G6YMQAnxIr zhCEkS3!T9w|GL=_5BBK2&6soJR5e%NE-^`ypEJ1usi5==%yE(@`R9;1aCQl1{~Uf1 zvh)CRNP^^<^G~q!5PlONC#a%sJeJ*dtxI!Nr6TK&ow)oek_RIy`o5RsK6`*=VEVmq36f_Sl3>}KW#ldy76pM&jYy=LI9Hyc72<{-Rj{Q9D_r)G^fK^T}xkTCraCW(EBpqYq?WZ7K3*le&T zec$zc~E5y!53o5 zhN54`6tu03p`kZfDr(^_2=JI|(WJVY!)e`$fu>o-JL)ZvmKOvt3dv4wG3oCnrdL9c zMm$fyC{gJ!#bxM;8ciWPUHsvpSmk}z7x_R9qoo+#r>a8PGw!!TvUw7=cUMILlPUJorWr9wlmx`RK{uDY~V9#z!01CX$bMy_X7{n zcl4m~MXXALJv4brLi0scAkTjN*FMP`ir*JR?&I7?1vg4UtYzsQP)d`2r!mK@pH{6q zk;bwtLR6H0=Eq(TNt|))!-y;;V~I}+{ZD@FUq5B}!@~u_9_b!}i>Qxu?*&5~(~~Lo zUFD7ub7YhRmo$HXC@{6EEdM`$?e7qP8ZFz9pWLVi?in5inU4$cXC?%D4cdn95CopV z;dI{{K+!6N8HyEvvco1M}l; zw|qPMZ+qrPSX}x|j~8$m2w}J%rz+#iZV-w$cY2Gc_W}ib-EOa{x=;@K+aKNU`%le=D$+zN(Ocwdv4Jw|_{5fra*^^~u?$qDUur4_8D^q2vjJ0|O`^73MLaHqtMs7+(Y0 zv=Cz~{E#OFQwA@j+4y`)M8|?~a~Vd6hv=!#vGkMmyyLAr)WXTa+MhN&fCSq(VhquK z%m`8`j7!&TFb~N%Xe3s3Xs5Hir&i2x!%)HG8^#_3mtF?zSR9l=ccF+?Rq@yV?jJNT zU(G6+4jjiGgAq-<$6)eHo)8F?q>p1venwy)J;Hq=Kv%|P4kFoIuF%)eVd-L<^i`Q} zwi~)RFG}>W>jMU4?#g9A=59`m-5_j9^+R;i1(F`=9)gRgk8~dxE^@!qVfncgJCB=! z7+wwvUhu|&mj_?*33llvC%gpfUNvswXLvFMzl;f#cnloF6Xk;%A+dHM#xYuu7QrR? zqaX;6f1OLo^L68vWO^w^z~eiEm><@C=|B_hlXF{_ExyyAA_KE0!F>r65VA}-M_7i> zaFIM7!#+{2L0u>iRo!@mX|+PzxVqY69RK|1KX=mA?^2@!b&5sRmjXe#ZZ?ELRENV3 zRR*C6XO8Q?j+?ayGc4l5rE*hMmo9}TxQ&7PBHR}fNdDO^;dc1all*hYoxs~=(dtzw z2Sb|Vj@AvMMxe7Zem^1h!FF9&eEZL}AUPWugh-`R@(%^eI^S1+!f(I2FY;R~NY!3cM z9AhaqbVJm^dTA@H)K;TQOcd5~wsw-@h}a;KCX5=C9~Immc6eeI%)oSoG~HUq702uK z=IZK&+{1gfX_;r$s16Y}n@v-1@Q*|KdVMrazCi{snaTP6jNVTM1sQ~33u1}78fZv# zy_wqT2zRnBE91yL0Vf+n1jxgPV4jVm>FZl4$uc~PTmy5 zGa!y%`P{V12fwU>O+i=MUMh)XHCdlS(i#F(Bqw{qFo>cKnjDP!Q}*N)#?NuJ^&>*A zDUpMQ8CC_6ZHY0d>#2@sNC-IreM#rSg<;dgr6(dZMwrH>Hk{8k3qaz{rX@0w^An0@JJ{SFQ8J{`;_#SkoOB?~g)!uab!|oZL0w4^mFEN)G4!^fO_hEM#C;iSL4`iEQ_RKNy%+c_l z!hMF8oNz_Z++BAJ0Q0G$p{;g?}8=K7+cdhPFpmcYWONBw@;8 zcl$9c;hR8dbA6UD*A{qFx0~Thm?PYo+lPXOGhe)AFo6=(H3nbiDU<>}7i;*6BlRpJ zpeao2s{Z!n)pgrWaWCyD{pSmJqe#sN&T|~)GhR!jmAj95lGwu!lul?Tg?s2buZQ}m zUHJn6jc}AOdQeX2aJ2lk#|XiAaQ?`f@sOL~1`D9bH&@?40!>~2;=22jKfU%y?OP$n zSZM9;tW)JQL&WiMHTKPVJ-F-4G>!2mESv{c7JGut0j6;C_{^8~xmhpTL*U6)dZC(;b-2;B1<0p%nLm$v2{Dp=VnqB3O-MKKRuca#xcp#x7KEA`mxXp^lS7o5PP@h9@-|51M8$0(?E(`iW4eXHz4#;Eel$qMz14l0(3ESi%NT^R zG2x@7($RXoMkmIOuS#i?amgE$qoke z3&*S|D`d`ky-B(+VKPBCg~-ohfZ=28YqTVE^9R3gp5YSae!Ig#?r;`)ar}Z`5gSt^ z!I5NqAw*#78Dk-I0k|osmLI{V5I=c==43Q%cn5?cz$DM`(E&6kOyB7NlGI4QGhD<; zkI6s79C)9bryivTPTfGj`9GpKu*7lf`B>XvJR)emvCSBCk6-|P-es9P;#@r+OD_L zfCA?XFiL9D9np&zeTM^zd)d;q&!lxvgy2W2n$k0RDm+Ua7jEQ)?}V0z>EfI}M5cNd z>X)o6CI5`U{zV(cg3sIpB-FQ6N`2nk&MwuH7zl z%IJO|Ciql_Dry;+RaKXaK9Yh@${PA{v)(q9M-X|>c%}>5!|OOT6a`Q@Z$l=sOFayC z&xr7R#AsCSowT@n{wb%iGrJzOHgu>^i1Pw-sA=l1A21>`mX0nkxH#hlU$j@HJBc1r zd9vTGl`8ge-IbGCsKW35r$|!VOS7`v8HxOPE&u(_eZ}zMjY`J z=p~g^r3bV=s2R>>mjN@!Ax;!4(ob$nQ%utgsw$C_1xAFK4{k-H&MqiX^C323kxG_n z(KoJ{z4)s-EuZdWR4;4C#-@zw$SJhpM`_Ok2<}YHzctvK&d{DYf$H zU;&SCUkzNe`|Ln7#x962%j&S-S7ps8J&r?%{OsE6SFg*mp^Kmc7eL*6twApt(&frx zshR$xA1|}M+kJSrDBv;f69hOBtTuSb6gL}+UC`^eU<~jlG-<|1vfdepaF+DKKKKIR zqSK!09)b*!6dSvPzev0w5}ZQgAQ$1Glmd#vxs#I&QzFHcffeRG~q9pk%dl^pd-Z`Ibs@5G zZau?n=;8a6s0eoFRgWD*1oTu)fJ+I z3iFuM++lx^)Uy%uquE?hgSzt8FFYEY8X*`F3ZOD}08l}w9nnKYkvR=wiW;L^W08bZ z!aC5-$i{xa9PIW>NDC775N-{Y!Ae%hN9I^Rnj2oj;rbj z()=_I|H2HRuC#&ow3Zf=8Ti98vD#^ch?bDI->P^27c~zf<%V4%+z2Qe&`l-W!F3*bJ|KmNKdFYUvK>E@XqL6Qqwz7 z){~N;f)r&{R%+Fb>tegv)@AZ&K&r=cJWlm}m;Cf9ur($&F-E`@EbUToJJ&1qK# zoPtN!r!YEkoYGP%2G0Sx;{Habg6-qY20lUkQ)Wk)1BDQcwgM>Ch$3+lW`c?}W`KhO zq%a0Rim|jE(Adbs^`3+Z52GXt#9Z`eaTYwjp_qaUqng?$#3Hgwbs7~t_2+Rs4u118 zBSlUnFe<=LsOb~0F^$9{wpyyImCPw0>mh2vpmrUGMAa(Sn2rlhiz(~*XIrgrksssM zW7V9ntE@-NC~A3S3g@W$Q7Z^P$kn#<-Lc`l*8`}MLLy*1+VKW%@KKskRCQ6Ui~6cr zZ!i-*B1{gBGmxxyRW~wo>5TX$@z;XSIl#3eSIU{)7Y)mXKHwgLi>PmR<1iKVTGh)g zE0LXL<|?EUwEF+fJvv;VBH)8_0h@Sa!%eyH6eBJffTiEJDK|U*4-6fvk^7 zT)4`6oc`>e`w6TQJpiFn#QtJ6()Vwqfx3VB<}SL9|t}I%EC-hM#C^+ ziP$OxKwsOd7iHC;>+}PLN0ODEZo*d;V7O}H0Wo#ltIovn`jajESa@el zT@2wzdXoRbGy4-P1AzxT0-6Z{YNBzr7*(hnI#>03ZxCVNVec{e_+?ynZR>pV36>eo zZiWm0ZI|HoV}d(9vwHyUj59yp>A4ZP2s2FPA75mTV+5;V#DXn6k)pclo+G)_YC@?^ z{Oz32>=HceS-KRGU>Pz80_kxMnIGrC`FH7Ax;b$6;AnvMSkM5^Zj4OoIPEmd{ZYNP zz0%VOrB%Io`Qtx#==y*0&;R7-zu2A9-Ed3r5f%^kE@0vw5Al1Bp<~C&)hb8wuIqaL z`g;`ZL+c%n3{B6SpfMvQK>1l;1)RV2yC3S|D&K{s6mokQT$UkWzrR+j_!Y>5-+Lix zk#`=`&$gSj{Ti!z;XyHGW2n9eK zP=WRk2vne1OOi)a9o0^_AU%&Xm=GtcHBT@$NFWF7&}4$abuW6o^jBS+I18XuF|3QWTnxC^pNw83%KzlZN&#QEtjssDjhY*&~mlP zNbIc$wn1e#$c{GgYwyz0?y-;!!KNwp*Z=*$kh1+;LCihE-3u4-9&k?vaPG3HpA&*$ z+?jxZ*$*9Ksamh;00!`;@m9pp&}4^npA#FgF7FVCAwq=rs>fFkn{o}$1+fL%OxM+L zqL@2m6fh|Jw9?3U@KZ@SdNVjDMEo;WP9O1D_P{&4uL9x>q_&FETn;NQ!VU>ODPs=I z6R7xb{e^#WC&eV}O8!NxybMiLU}PkL`V3z;q}jL;-7%zP<~^jC8KW`|y<@*SlO%!Lwi7t z^C+DFc0vnYW+o7}1YTbhcbQ<3WEpwsS%w44$BW`-xG($ytPgpqZpNk`6z8GqJ%bUS zM|AkehP^z1GRvw&d%bGv{q=XSYEVJ)MM<{{HE;g#KM?EHPv%xsp~Gu~@XpPf!?3-2 z;W-n(QR4;BtK-n_Uzb%?uPM;&n&x922t%?TVY#kxGt6o~_L=?dzyHsw z=tk|97>TlIYw2be{`%yZ8#)-1E9qgMBm|D%;_iiuc=x#v1{e9_z`K1<-#948s;U79$aLoy+{2L8-{RK9uKV_zZ&9x|@$GP^ zuJ3wO5!h;6yowOaVWik3yoCCAbP>q9Gz|*@hRlFM z8+9x0G^WN*nO}a(ss>4Uh>g<=m>b{u-}8Pl=( zH!r_QJHenwq8#>nl5Vyy=#U0C=qX2ZyEs1Zk_#6^sdZV{QZ)Vzpw2})@Fcp`0?JOpBv7U#?g zNr)e1l;*Oc{o^?f2~jENx~nTg^f6`mW@7Z--g&iidi2@l319o+}kG&G>YCT7UR zitX6-N7ymwQ3LiW4+h~dct>NPN?cGK8?CooXr&<9ON(k#eXy9`L)7~=67(B zJ~ka8ju{9%Il|+eR-}gH5u=e1$BPpb@M~7uq=G|dm2Zc_KVX$h@h^Fyy=&*^Vu_F?= zZQH}xA6BDA1%1L}=uau96*dp`qKfXxzi zA^-aIb(VRtB%R1Fs#KzCdXbLCP;v<+bOY=hdiv0ISnX}QJH)pgDI1H$uZn<`DP6`T z58)Nn1|Jdx?+mo2B95x^1UE9&Gc3HaC6XB-@!n{Gnd7lvt!g)Dsh3br$UxJ0jz2~y zKP0!DbYU-l^wT8eEE!q=NyDh4Z>(WCP~BjME|Fje9JNy1&ILI~{U+=r`zNYBgNA_> z)QE~kf*w>RFr?XHF@*ApOd~68ng!V88DPDs$x(?oKQxUbsA`u}+P1Cg8WWbhLL19z zE2grxlZ(2f)MyLD(^hJKv#9q3s`VImFI>dC)4eZTWOt|gAmCiG_|{xusLJaxR5oiY z+`b>yO;Z)wVRzjRJ+X+~JHHTXn$j?1$7yK+qWf~x6(lfZ|EnpK)HNe_j zjP{}%Mv)B6v)eUe5Jvf5{I`EsD@DZ9gCBH&dp$m;$3L%@>s-r0Q~@YcG{Z}H4y0Z;PJzw@gl zEHl3-<>K)IQo_69sPrfhoyWE01JLK^?Pwgj3dV_&$xSq%6y%`#5r=TbuXF9` zLW?g)0Nn)J=I*9(JobH8sY9pc3wU5Y{DFbRH@8&K2)>UjJ}*dZv~}nxXtk4{ar0_# zH-Jnq-ny=%y;EMG)1$#_B_zx4tr&0^ny;UGCo;k{>7(8GTP<3?*=pZ*KmPVd+bi!x zE~>S|v}&k8QPgoVKIOF{G3t~T3XA)+66k@JqO7u`AATjfbcgh2z4iRR;=@YPX_Uqo zHzzp-={2*$mo@A4;m{)gNlp^RK#nyu4NbFUgv8)RV3CADk#%ZxMN-sx6&9X0gapCQ ztfZ5E39XS|#IKvUTs#SmIMR>#ScWZ1jgBkY65pvePrJ&b@&$L4g?}nWzt>0vRiKsM zg(0i#=#9zeCK2yqDBLhqNEyp>s$TF9h$j>;b+5kbF@3MEuMzKH{{Q>ee@D_VNlL?y zi{0m3N=)g5&e7)IPGx&x%eST6lKK$#cFJ|Z9q*PC_RWTnp0^_`P~nb&_IA(J5Ix2kKn z07Qkms{FHn`Qb+B7XzcJO(}-ud(Mnv&LN}}S;q;@vTYA2sSM!l)r(=uo3)?r{vvS^ zdRKYiBtjs;jZIwckhZT8Em z@trFgjsRuIynM0!qn~|;qH@aOet>BY$5m5$Z}@hz-M0rcP)e&R43HcG`}RlQk|oG* z53PDpQTgTjsYj)C9kh-Mh=T`EtwY-?0kQ(oLVeMXtoG{YMbcT(?p|TLFy>?OqAxP* zkDM91Jr%K&rE&hcRHO+*%auBGCq|Bw7cqs=$MzR>Gv|oIw$W;jgyd2>1EPTv!pxy! z9mt!U$al1|NHr@Lb+%C!%zH&3F|#o@OkG2%cf#Vijjj|+jPA7}j(;*UjmemhlJOF2 z!JJFt!pFuP(h>vCFWVDEQCoNsg4eeZ(4TZjd)d0WqYO8Ju2mlBwK!`>c_=)z3+z!v zvU1;?LtsTeq6S$vkl+~|8-Sn4CmDZ+8)UJLrLkCZf%nR&X~&`MreSIiJ2I=WMN3Q| zckWRU;z(FD4n)bON#Gib)BJ`!qQ}w zkJ4Ix7^ddx3cIx%-0mgKN>GVcN|utSA*y%=eneOz@{#T{1MNQ;8R(5gmBzj4&ZT*w zOW>U0hxvH$SRq`|*(3S@p7xji&;Ogq;29nTFE=j-?{x{f#R+n&GsQR`rhh~wYUv0} zni3bEe;x-fi5|j;gu6u^bWWVwI_VJiyJvV8xIp5COA%(6LzZuo2be?dgJsP7qAlZG zf>O0>L&B?JA==u`n=nvo5aZXct|2%yPBf3*?z(R3qO2#s)~EhJ64dc#-MDXLI=0;b zZHCZ(IHDLOru&sZk;TQ|F;k8*QLnF11X0J)UR2spa{LbFG4_2O!+yCqmhc6ER?659 z9pon0LMs9@sBNC-EoD1-y4c3O*^nP*a0rVp0UfjfTOBn6kA2PHiU}rAAbE^qQ?KzG z(}`5i3Lq01RO*~5iw6JS6%y>PHd6kMRA~CQLzy8DAv|l zU4Iz;Qi=exh*KK8A`7=2(aq}O`H(fg=rr7{0?Ax(Ldy?@KGgcO5liXBFIj~8A0bxQ z{a~;tjfyCTKc*}(hYcOky-jIM)c;rs;fZeWQ=`7IPcV*5?TPkd>@BdU$UJN%5lm;9 z`;ePqX#{iQI#l{@z^Qa^d3@O2?^F9&<&)E@X z(72XGl_HI;(PUAXvQCl58EzOV0AmT{Vm#Vx)2=q#O8FdeY!X6ZCEOLK%<8K|>WeVM=si7%_=9E>nR>!OFUT-&>uJ2dQ2lvy^SIv66{YHv% zIImE^*QGbyF!tMa4-@#8{`?j-DQNbq08rsa3`W18f|8?F*Xd$D5|<2Pf<{>kS;-+cJCfTbQzokS;5o^0*s#J5H{U%lTES8Kedlo?Y|`IR-EG z;if5n`qQ5}^kVBfW?B#W%6l^b~ccUn1tkj0MH$I4gao3AQA z6se(d&7(sj}aPEtB zrP5t_X*|0qM-qBA^lI{Oh!oZO7TWG}&u|~u7mk6jJz#l&p*+dNPLGUpIfMW@fwwT_ zF9enp^dxr|z=mN@3E|FYpXgXqBv_@aP-;h1r0=OslXtKjV{f!8>`F4a$+Ym?__ol0 z`CtF7$m1Cj-rvW4#t@P^u0}-Q#Ss)VY(}q)HOn{Kb?Y}7#`U_1g_Oxd!Z8Dh8;1dv zOD&y2?ae&L&1U^aKlQfE&mAp~2h_4WPKbfD(i>n`oAovXlw7gQi^G1$cz`$ z=&!afuU}oObe=N!q5v}m%8C6cGO2Lj##vo>6)2Ia()Av4A2p}JPa;O*&m3UC*aLV% z1Ekr`xucvgK(JH22ktoS{d}tNyb4VI?BY_e^S8i|yGTwoRAFc-r(rFJn>l~#2|M>T<2UWLKq;Kl&xJB$h>xo=dm z#wyRh{n3w_y6U@@e9SIim3f6tg91zd{abq%RZ}Q5Kkr<=b7a^H z=x3U#p(B7g*^sotPqJ6g=!_l3BibM<{_<$S5K%0Q1l`m$!5ky4`nxT__;Klgvb6@? zHAix2P~C8JsRyef?Nz5J?26J1$)`qhBMeW6ar&g=YD*I}9jK59(yK0Gtk{7@ zln4j^njC&SNX0ZX(_yl{ap{wMA#h-3s;Io+m3ECeEa8TsVmj%`I7oCm#T&i4{eGyh zDaV##T-lAYszd_oR>#`9=|Y8AjZCETLt_49xa4`rU={_jrWmT@Xzz76LzGW?39hMfxdJb>DM4EJ@z&_N_hwyf}WQ>bXYg`|-7aal%%IEQ`+ zz=mnN0nO68{`{bd%Xh@Dp)M6p%wGiJxbMb4{&}mq^tFIG5(4^)~Gq(~=L#MQLjU~-uht_na3z(3sNzM36Km^U2 z)JM|Zo6vw!Kl<*dN{OIEGGbJ$^{+f@PV3ROV@C*M2l@CA@V55&G;G^FV~`esXzS@0 z#txPzq(oV+n})L5!*zuxJJxEgZWp2)qD5vVpljLffpmlka$qP2>XE$9QMR$6b4rc^8r`T9uM0vCQ(bs z2Rp>NnwbZLh(ow5A=*vm+Jymx%Hr3MJr{c1WLPZT&=m#x*|G!AQ80}bP10~w(JQz# z1R^XV*nU5gPYA=oMtQB8R+;s|R*A~UJvCB1hp&VtUdrMOM0*3ijh%(?R{DU{^y0cM z>#A61U!9Fgi(UNseS)zARM{uRm0e4q7J;UPx-U{c6QQ!~)pA1wO zgOpQq-`0#L=vI+Gd4}%uS zo9*@WE03cfR9D&6`i0Wk9U9P926u(<+Rn|`3cb84lU82WuU}vP`Clpj`9HgL;pM9V z)hP5H%sHn|3paPU{Io((1+#7IC&Qbj0hx7Ofwq77U--$PJDqI)@&Fw5h~NMi&dz3+#0ueMhjU|yevWkNTru+m0|AkGmCGV=^& z#sh@V%v%K_$=kpcB9_PV-XATakg89!Md(f^$lwFMvbsr zj>D=%TKQ$qIIMyKW<`l^`H;SCR>ik%PII35I~EIZ3Jn15hX@mOXx^G7$gj}_10jqM z|Aq>V{tot4$APdA=C|6-MAn#bHb%oD(%4^^2@qx&P6QDlW*ovM)DAf|5c5>3ZkD|Z zGUjOQqXZj-hC@`axhne95EI%rntqj8Vg*IX`B;>Ftoc|I|7fHbpIBynYx?|#p%P%v z#UYfVx5m*=m{XGrEbEOIy~S07nkj$vZ~Sh;1?A9Tq-3xK4Uh&?((0=QpR1DG*9sSC zxX(Qa?Ae&HfQ+C(M;JqguGwAtRU;QMwf+0H^%g&dokVpS%(!E0XHt<5e!LgAsPu0@+?^+lDuYOSoQsZtcb~@;b|;A3YOS>XZH-BBE;#+ zkq$cSckwB4rC{If(NfSbAS5i_Zs;&YhGDEr>8jbRQNbZ1VkRv|#tQwR_mpMV_8q+U zv846c0}U@qcU<(HACLhFh@;REVrzU+BCNV@aPY2tb=Y73(?9vHsmd>POrZa$YX%DP zvZ9spEtx)ag7KTa37Embh#dP^zXbk3+gz9^-OzMs53!Pf-VaHrd1zp`6xr8a+{|AD zCu|vo+&S{3_C5sSNWrR-90M+iU66@jXb%UhzOt&x37qc_dn}CO*d7rMRXq&-{%}A% z@QR$s;rehWC>C<`dw{C4?!I%D(F=FS!TXBBwsg~2RbaRD9-tq)-SsOn@MdJXFTUwX zw;|xZ{0wkiz>D* z`obPrxf$$@GKB9`r4!uHlRD==>@WutHV{4IUD06k=$4_lZzq=#nMzzxPkI+*550k6 zAE1>k{L#6WrG3UgL*B=IcmVSdB!YqN3Zy$82u~9>P=fR-Nv!(5g&5G_tcV>X$KQ?dq}WzVN5tUrf2>=^LAl4fuO48=F~<`e z&biwP6(A6!v9PAO^n3qj={qs%3z(ii5_@wk- z6o3Spg%Z~fexILa!t>P+L5sp$LBQLH>v;zCIJ9IFefEHAfx?Y`Sk+b9ou%wsX)fR; z5Jk1BD$N6}HU^aVfn~$gA7--BO7MQwOEb;+DXS7BYxS+NX5C=@knK-@_NNK$Z9g`f zE7ezIvet;A0Z>m3qp`>o*$i;wACINR3E2)o21+xOAl>)q@1x>U-m{97Ww*BI#x z1p+Jw|EfV2J`lnDH|fCAu9QoMxL=m_Koq^X;R>edefxVn1zO*=3W*J*zDi-h3$=z8jO zu_-9N-4uKZApxS$&f#641uoRJ?$B0JeE#HO8y!wEWVdF?qDw6NFe+3Q(GK_RVL;^$ z22K*%9vcFurg76?%I9t%d2Cr1bv_zlc_Q~@#dycDbUw%VQC_QD>^bG8_ zMJB)4yimmsm{!xes=_dk3dxXH+C5rKfcYG>J;-i_^+KNO5)-S3JCxPhBS%^Qn3wK7 zrrH5A4s7*dqIGToJ!x2qD{4FTM;e$<1&YDR0&!SquBelxa`V_9BN~mPCP9UGXAW2) zG01%Oe`s_^Prai8YW+r>q}W?4v(u0mwQYYk5foeaylh(jt2V?a*Y7&jR>ZC zgFWVRGBP0O7m=A zWI=ecS%|)bQzaAZ4{b`CWp1h?gr2*)ayQ=3zWW~ePGA~H^c<{*hcLYeaM=m|PQQ&= zLocpGj7zxG4g;@*@Z78qmAMiB_#40Z zJMPNHBh1)*lH+F@`p(&!-0xaIzWl@B<|Swc(l-hS-EO?yWgMoq?ZGocfM8i^ zeBqQh;~2+$83OcE;%m?F)dB?IigbJ|WthsM(Oz+7Wa_$oQ&(-*B83kqSYA1dcpJO6 zLkaQ#LS3&n8)PNiI8I5qxC){92?eF`R|9HX4l$1KT@Kr2rxeLk%`$7p{I4E?4m4=qDJ3>YUbCUWAB zTPDif4WIU{f}ZL?+a!sdU!-Q}^|zL& zWmpSIqaY6T)u7;A?7k@6yrnKrp*E2~sz`(pafrQu9l+2VVx7q*q5qE*iHfMEhv@S& zM({a#RbYZ5h}LDE*1!&NI@|RSbi%QcIK#Zr4D)Eq5Sh9<;|DjXsdcf*ncEXkGp%)0 z%)*Ep`R_fHB3kp+Sb`Z#HMSU_2vj0@1*kza)xrcdGzc5W`kf(gaE7k$#7+=O!YQnp@XSe}g9TA&8 z@iPp992sv7xQE!L0Snf>`+xf%{)s&qai9?YpOOc~FGlZ|084s#jC*fz6fhL?e7D=f zkKjfLy>aJ+3t29C!;9P@Hr27Q)u}eeTEzS8;R2zLaqkG=Fczj`+C}a-`RS>lgNWVt zs&I;_E_wv5(G?Xub^&6AcU6O))DJ`FG#DAkvSVO8!&eR=_>gXh@$HKjecK`KkZJJC z4MxZ`Kv#V~pn?-u)$^E>00pIL)_GBC2D=@_OW`Fb^oMCW^y7Ms%JB7&S9ToK3RFPk zik{s;nL!%?Q7YnyBz2qo)%QQ`+waG=I}AjN6ki@VhYeTKY^zF7FF*+B({dv3o|^Ki zX}f;x+gJyX5qZ4+v<>w$2b5jEQH5q0U*(Te16Tw>7OQp$8zXn|qy!o2w5Tx|L+_iH zYjxFoKr7&M?!a%DXymBg(P6nX61z|c_zjEXA-@u}mLC?1evh7}X`1_qLibFx}fVFR-(a_l`)LbO;z zaJTkY^e%v?9+I^mx*=CoeP}n!4r6a>T`DZJ9{yo#8Q(Vw>f^DdO@w3KAge?9RD5S+ zRt}}6nLsm1EpD-wJi&JA=oxN2fa}CniS5QpJ5}~=^~i-G6v|euH9Qq@eY4R~1jsvw zBTJ5SnY&%EK(%qD!PJ_Cdxj4N^g4Y3s3^NdaYPMc=yt=9m+M}lWsEqjz}Ra$m9wxd z=~8K(KSl0>Udrtgg(dAg#{J*`Mj01)oYZ%~obbtCItBb2N>{)gVqBibRQmhrzy%&3 z?|wLN0?g^^*kbEKwGK2w%GAT*5FBh^AjGAy3BeFN@}rIm`(wQ~Q)BRE$<^Qd?SGmE z!ZUo;5L$IzmCzMr^x~WCIAQ%}by;7(`hFY`olZdzap>B9zel5ZarL5Y2UM7@+owDj z{!QI5+C%z{2<%ZQud z56VA&SWrjw)gveXAY1*Vh`Cz|I%0t zFt|4KK+DnQVa@5z(qPPJxQx7^t{OsLy?XdM@Ays#0}aYi@xe;b(xtdy|B700*LB^C zm)|z)^)ZVNuoiXMtR0fmIjmhHfYKd#nmFj~x9@mnwb87uxHrS*~jC0~01X?Wr!>*!KaMM)&yD~dBD-gJFF4t-;T!ufBN@R`m}Y%huZw0A_W)x0 zJ2hBH7cqYzHP~795P#02_Xh$>ev4@rB|t8afi#7@^S(9gLC6h&;w|}K8c+*o_|PF zx~ryH|LTvnFe_EO$H83IoZzu39}TxNoC|#bYy*n$s47UO+r;Aln}r%^Y~9p!I*JO~ zX6T3-Dns9|>*`^3k#eCN{x|>~fu2;+N(s#vR=;lQ((m8*WVhe#Vk1+rx%yFFZ7E^- zsYi-Q7gZHy-D5SyBrjCDU3+(S*S3uJ{h@2>hM1mYJfiA$#cB+>f)Mhqsq}dqdK7`A zjpV-i9DcEic@2yk=Z$-Jt09q4naLA^|GmCOt9E%ob`v_gmPS@-I?zb-SD%kK8nY^I zsn!G~^GQ@_=fnwlD(AK~|Cm|44J#q67rTNGUw~ap;J=#>JaewwMFTWJ2Z{gVaflCcJ2+qlUek{D ze4coKMo%A(fS@2{11045fgs2yxGxT-$-4_3D{>77Hkd#8omqh^FpL@C{BvF$AG#J>_t3V7{eFG* zE#kV5-5vY=o>9HuT_@@qr?zRZfj=-^rR?TRAI^RG_TB1n4}(fzmNz26-xyDfFI5v- zF5e>pqcYYIRJf+8zkdDd)h~X=>wo2c@~dqhXKjU4L4NP_-1&H)o1-l~z|v#&l0SLk zH@OH+e*#hsmo2@~>VP>;@{k-NN!UP6*CmRb%9)xW7Mk}GhOTBY9%k5G@2JQh{Expt zUN4J&0OqpNFA$zbrhXJ%&NCQ0&z_o@krzOnKH_rU`8C?ukNtvc#c6m1dkzCgaEeCy)ysaSry`z{KO?ihFBx1k` z2G6lBL;@L=wq7fQdbZ~iEJPsCt#S*vVMS0W`okiQ$6o7$$7Dyj5QUL~*@IPjeZ2?W zVGt7+Y^XZK{HYP60095=NkljBl1zPKSHF>@fCdGpJu~Q)yg3 z;W13))#OQ)4TMywPh`B^Y}Uv>L?jy92Pdl}P-n6FuzX#?$9+p{z@aJG32Rvh?EP zaeH-D*6USX9pGhqJ0S#{x<1qDZ_aFmi)6<$YFUnjun`_DYaK zAc3d~K#0;@$uH{X5gbAyV*{d={${H#Y7U=st$o~3^DrXNuxj7F1zJF=q2M7tix zI@gM@6#Id4$kopWzdRd8nL6-G>7@A1;ajmxkbrDh^3VzkKnMUvx}J3k}9oyl7x~w%_d;-+kwI z2++F*ci2jD?O{)Hf>fzgMoFD2`^YLA?E$vHtMv8(Px2?vZHQO1i-5ZylRv@ixqvx5 zhL7z92~*>BuG4z^g2(M<)v}lWJ%hviL8!sqq-8=nVWMCOt6f9r)T+iN_wuCzsZxrdELf7 z0_}7)>R8HNF(g9gHqA*=R6BOay9>!h6_Pm1 zfVxX$YKs`*t;g5~XM|P6&A?6splt%F%Kp${#31F}TqLZD(lsBEwA1<-hp9PWeB>2Llj}zE#%rJmZHa zMIv+*hKF~SJ5Zt9!UcC#wAG;Y5D1Rfvj?u9Du=YSi!%MRj? zWv)Wb;2$?q8nbDK+}!8J+?%7O*Ma#HC~oh09Gr{QwsR(No_^=6-K( zgWV3sd6}r5g!mPZ1>Dm-$RYUPb4PqExO4ENz=y`wEtq@AP7e>sZ{;ut%q1*6vs;Fw zCyzMCGD+2s{h@Wc2=WcO*w>)2>vi9EsMy2M5wfYPx?UGW*}2c%+fkK?Wev|& zM_&2Y}j(r$)?|3+ZE zIK4Xm8j{=`o;-JkFJCP~!jt~+n0}Y;O~^Ug4HqC=L4ES04l9Q3usiQF@)&W)Q)fy|%k5tIq9JE+JsuyW$8IhQ3}m>dcYzxdJCo z$`xMSB|eX*YZ;zdiIqZ(fssgw+jUJfzWe?cWz`Tmb}cHd7XpJ%it%&}@L4rUJZ$qO4YE|C$oWF1G|s0Czx$ zzvYydw9;fz7P)GIpnHUop|yg=mU%IVrU+&dxuJ#-YyzX5`VOlp*%uy1NoEpOgsUXu zvC}fiJNGbJC4Qnw{L>0QP8Ho4`Pk{i$4^rNKY2rh<8&BSwYiB0_cqup3yKWmWKS$x9K0-Q_+z@7HkGdvO`_(!+^Y}cdrU%Ou)M$p3%9R0_RE*#=M&CWg61TwVv zr8oZ5zxrEApcpeeLUiKE|JlP5O>cD{4&oCE@?2LD^YL`{!KuPz|AS=ZvBOy06XM8- zl=1rvPXH-e?gO(EdyqGHMpx{SfxX|fE$VgOF&sR=+O~(mIWX~0S1>>bN04}zOTB?} z$|0Y7;p0FWV+-WI8FVS|e!Rbl`2aAYlDIuX^7A`;F5sfr)b(l_M=V!YI}5B7{MaBd zLZ`#NMgO>ZadkKxV64y(`aoG#P)}GaBfoKQcHAQq74+^iIqY}j{y+a0e$(gWLSd-FND?3hf*2`Mq-DvH+;&U0 zm+bZ+iNPkY(YYI&`8w`GyEC|R~O*cxow?v`ay7DFh z=FEKWMcg@}zi;2jsFwo@nN;=-__=uRZ8OKkwN~+`U_;idP1A7=- z?hxy%OcB%REXnh{>w4EN;y|-Gw8%A~F{z!|_FPyNR_;x~=rH=pXU65sm~lbP;~Urd}UokEY{XdbF2vJjzRhe9Ma)JE2F~ouuULpvZOA9{RFZ z7fiA&Nuvu+_)~9@Q5wb4a4mOjbr*VQ3I4hFWo@@8qwrWr1pn_S9Pa2gGdHd&5@HXF z?`YH#!<37K4v&ABmLEKg{=zz*U!f>u6_}#so)HPWnAlske0wp0ZIa{3kX9UrSBBF< zW75G2#4nXms>p#n$AJ{-BpOxeG>KhM&?p0lN0Cr}piM_g|08x5jv8IMFXz&U-e9j*%lL1$x3?t%dBzv1UL0b)SJ zwM~a*j8z{iXvi3HHxakN1%AhJY`JR9oZBdk2TBt8kmNU1CAqTFUw7$nLv}Dxv;y)wMRyuyR594Q_ zW}ouLpZo9#W6X`8vyaY(Z~ECM7@MT6*4ALN?>ZO}7p|_VzHbkfWwoxV^?EbS)A_k> zlJuKCE6RAs{|-NAOFJ6?&IZPcu6xAyt$)tK*~tC(O#?Umj8Dh!F>n% z?9=Q9`uc!>qwxa1ryLdbB%*fc+bo;mS5O2@VOoT8=G#(r+9sRDe2(2o;<2o;kbRAU zdx7LKvSVT5UU=nzhp4+eE5;;k+bqgrv6#2eSi!z6+C_8f+xg$=J96 zg6q8Tpf2fY3XDzO%)26UukLekzDvIN&(NNFi#7u{qyE2Ay zfEQYPssWEkm1O(*#@o?3*$uM)X^OVLQ_ovp*vKO+Ya{&LH#&AlQN(UX7IdO@A07fv zvt-DVZSOkJ#!KcXITRrcB(eTacsoSId@1==n#9oBAsMJvr({GC?!wMB(3p2mmP-f7T)3?EW4N^SY;Lk64FAh zzYDhk5VW6B^n^$!4#Z*s1f~06l<#2`q~X8osD!-ai4RPrIptwr*&rhC!pjJwP8~TT zWX%{J!SI;iY_i>h8$M^^pU)D*wTm8O_Te*;yEJ2hG3ILI_{YH2luk6%)f&PGPgYGn z3|K<(-sSDKEIl#LGYloX7qYbYF>z=(B=FQTxN1rDol}V0?cqTgs^4nj?=Gs6aG8a+oaAVnawMM@$0e3%? z;+URdBeq`orA!tX(esg~0|<@fjZyJ4R!)i>9|s2H2jAe3?1=ZKZu6p8tyeH69EazD zvq6)xR5ymk%*zGUMPW9TG)b0)q1$?$SFDN*zUL&=gq^|U_qFC#RTHx;e2L0-nz?_? z=Nn&tp7PUaeah^e-zS%fXY^jM!Ju(%0Au%GijYy`&|Ut(D*Q2u82y!KruD}+ansmY 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

Hv6y0)K}1*jd|G486n_G`%A%*#by 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ⅅaEo4 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�S*q2S?Kc?tE!7nU;(L20<5ew%qnGhX7N*EFH3i#?PH_xM6C~@d^?hQ$lCySsl z^o_c(9K40don*2Ms-wpOyp8Oe7M>{2K(@pQsq3pc%Rkn{Q4 zKTd@D43&Dj?n3qj{7IpC!++F@nDUY|k8BpJ;EEms8RnEsWt++jyo1(~P|-9CwTK|2 zte9t|yKR$6gd(XfB(%xsUc17Iw;b?Iz?rBW9Q_r7lynLwat4L)?b?rMNh(8j*G#*{ zyHHw(k^(pwQuc1QH$^Ssr$6(J2R{C#fA8OqIxjlNrhAvjiCl5)n`($>^d-yZddJwO z<>Ao*Jf?*TXn(V8Rc5=*r3?Hp;9=J`?a|S}#o7J4n`r?#NxVhDZb##6ak5+-Abc`q zd}LWf&p#IZiBCrI+j026{hz+Hns2cxI=C$16GY0j*)(xI0EYe$bmAXrU*KVwJN?ml z5uM-aO2DIc;pxuZ6z4{DzQTzRg{wUuTt)}plpEK|Dn4C$=K|jHwFl$tXUVnI^ypD^ zZ64oW_ht6t%~^OCWa6fY4p_bcz;_oegDMuE4zAd25EfDOMb?rg~UC)9-oF+^29kl9v;S8jA|eSC6ded*4sV>0`mXGx>u zx6Ah?2d5|LE<>qNdUxwCBPn?1)Awcm%VPYq-w}VyyP_v=U@%A#_X(niI#U#Sn;#=0 zjPI+jufO`*z0ZH`)q9tB>*~rc|HflS@TNFoxyLG;Akv0PfFwD-8EA0bkm zBDn9~!Eq=UA!dge!T`FyzjpNs#)>j96|50U+igimJORV@d|BF9lq8E&U2luh&E;s@ zVzC53oCeh4Tx<-wi00@FHwb`4IsCi+$P>kUiSg19Y#5xpee2e4{Q&;amQSE>;;&C0 z#R)YD*CWwF5kxq%Ol?F9nc-NV2HsM73kU9pG0h%-keL;=WUFCcWwYtnXP4M(0H42d_KKAG}Fl8|>YnPXEbhfJv9blf& zD~R+VPm3=u+^84ZtFTu$Hteq+CD)G08R$*4-ENRgSdUHwK=#%5u&*F^!*~WCgfLyE5PqhV#LFpnTaN&~-R0pKxollhWEV)?k!s=hb zcFada2Y@f>Nac2*1gzis>Kb+IHs@fwmg!KqBf5wO{3L%M8Uk{>$7USa!Oq6%k;AxY z`&AjCT+DVghjF>~AsT9okdpgmi|vscQY`K2+ui)|6goxk4zE3m+{u3FZn;>Yps`tF z6!XPOEbVs!k%>pejO9 zC`vEgf2-f93fpU(g+9e_qx~0Hx1(0H#>tzYt#A=)1}{evyzm__yhR7{5>pc11mtzK zSRjYi&g+|Xj|wkf+R;}B;-Y!jeS9{{@dp4D6s&gM--(*TLWcZ3PhXP(B+(~6__VGn z&%g&U5m)aK{t*yC6DOgZzwvkfaBlonoIV0ZVd5AZSS%E(sGvxA9A+v)Y6%8O4tg76 zs(()-Sd1~aPC=S4{`O%?k<+mv3>X9)&mCkW#mUkb5%+B1e z*W1O67|>E+Lx4ZL^1$d;&N1lfQ2@o}#e_6Z4-+XW+xGQ{6p9oIb z0*Y7A{ya0E!2&f8>ZP~1iBqEXkRKqJeg&p%Ke?FW^I|by%sF?n-G1gLf4;73d^^Oc z$l+GIrbST{{d)0ZAGeIY#_XrGR7$+`BKk{j%Xpd zAV?5L5Hr;Auf(y4MYcNGufvg!(B?cfZBBVPVh+ie&y48bE!aoRWf&&C;sY`(?Dn&iIX>DYhm+ z?KVM%q1wHR(k1gcYU3&{@;MJ26YDHIqLaJbjy!~1gkoq{@D9YQIl6a4Q9!Dqz6+^| zoC8@49N=bChjCNKJE+UWCfb6vI!bH?lg&v%Vg%v>ch_xWaGK5F%}8O&zi~yo+J;DR z4(0pcYR;SGZZiO%vwQTBDtyQdN}#EA+aNdu^-uosH=d*N9pCe>7d}xTI)n-IO_MBE zxsUxu?&~e&W~y4O(bNag0CO`+3eN7jQ(H)yI#|@Un|PCi8=FaNRtL)= zSwd%V+mprc!589}-WNS`IvuYP(yF)Ku72o0`*JbcOfj%V@PTU{C2dYHA`Eya7Z!3TMEov;Q;oRjtI{ZlpMO&^_>?d^5+ z>u)%s@rkG7gTfudZmqMYua0#eS8cq==yEd8qkf!z_ofR+pFSR*I!SiJ>`UvzsT-ev z>aif0g!=`G`(`=m@iQU;Dpfek=@p(<2gtdvXs4gj6~AV zILF>3qbbfwUPn~~k02U}mbC;Y+m57H1X+|E z@UC~z+7XQ;CtPm5DR9`VF>4i1r_Nr{48cH4(Y#tmio7ik1*y1EIjjeVuWQ&t62`-lV#%RzdG}vqfjpi%d}9}fBsWniuwV7 z3ypM5BzETbn1MZTcAD)_8Qch45Zmc1O_r;gni75>{kE5D0p*zk+JXf>14jWl2Vkt1 z(8FLT;io?HlZe7rxJJOYfO{})2RJ>#yZs;kjxT=flh_Y$H0? z8G+%q2lL`GT7@A}-Uu z?r6CJa`?v$-Mw=iP@)A|7VTpt=sO*EnaV(v@3*xIf@O0#bs%is(X+6=z-YK)MRlug(Ul@{E7?e`Q{kOh|o58(V@5}Jwq z_n2gWW{p>#cmc}5B@!jRl&>5+XTUoT@en&Wx(42ohRF3BDV-(T0 z9f@njf)v7>S_2M9;+O98)HiS}&eOPkdXJe#Z4lQp-6Ab^F?HfA$?634ugj zKho(*r?W)0&tvuz+AyNGyUfymCL~ET**HmOuSZ zPfKseH&HpiT%!x+7Dg&fV)r2SaI_a?n5H10ASwUyCE*Rk;(V~9+HG-&N3LIk@lX*b zfXBsLYE(h?)u};%$En4 zCFes2ySBPKzkhlEw(L1Sy@YN@L8Oj%Xo0j?NI_jfgrUlaIgP6Y`M^}V{hnhNiz4w6 zNTex+qY4mq^L}22R$H?-45A_KO&bl8v;m$Zo*1AJ6Hq(A`g;edoOqe~Fqsy)M`bb# z_v_WpZuV}^YjBB^2WRIH_iB4NB(SG4abb~9jz=yIDFshQI5dLR%-jMaEl5Zf<#O(0 zhgYj&x$t&*qz#$?F7zEI0Sa+cSq8#)epZKAUy>BKv*oP^+xuthgM+1>>Ql#N>up1& z-|}=J))NSZakIGy>WD#mumakO@F6_MM+bFNV-@s*kmq=G9N)crf3+yyI4_Cy2ouF4 zXR6$b4m9wy_J9nvJ2U`?0iZYzBocM`2cbM&x?dhwfOy@QF6RNLUEG`8Ijdj2RVCzH zqIBknSQO0{OOgaMn&l;ci9lVTWh8W?T637NsRKdZ-`Xp0TF%*4z%q8?T0t5>SX%fn zD+}ii--3ar=d2hJe1`<%d(g z*e#D6)#yqycPQYiult`J+F!3{LNbI*zo7Wkp+wz}?*hgnN{um{)}_yZlKko}lbuu+E}IFqZ?X&8mlrS%cr%=^x{ z+-a3hm&PDqVpb{rfe_8^it{i&b)c?IgrA#`nA8fQ@a zuUWW(te-yev5(nZq#e$Xck14b9daPF8uy%zmos>|t9Z$iU+n#w;aCFC~-0m+KtN0Nmh=&dOIDl?ZLdF5C%|3&G(I*#_M! zFo}ja7V+EV(KKIxpnF7UG(cKDjZND$RRu3cMx#TJl7r)uQ*G7T@hh|CzP-HrHW!8( z11`g;hCWQ$6F&-s6Kt7p_DQU*3XwV*-CKr}8}HmSG2>ikY_2`|PPzm71W8AAy=itk zq>_~dC7_6|Y4dVU>#2ftM1eVyYKDbF`DZ`G1@+M7P^(iC=7f-oS>eDn0>IIHIHOw< z2Nle^hI%$$PA|-~?UJ5Px-QZ2J|?m~K-}{wmsd2kb%PFxj=k%tH0o3wKnzHX>d2u9 z^koh;P(66&-hvP!A%doLS?q2Mgmcp^S*dw4W`cq{a%ysL)Z~*=ooOkEP82K^psV9{ z+jvvypldvAtxuJ8P~zu*_8ZSp`SXA2KPbWj6X*;5bKPT6 z^6wG5&n{oTe(llU{Oodj)`~+0$Y?eu&Dhi9$y|j*g*IyL{(0z08fs{JFy-KI{@3PV zgwg$N{Py|y$Z>paF>N=OZ>8^DPuaUq7st`%?&7O)vXI1vE4dVhCxFvCimiHp%LSTM z)4%c{f7c@t9rX3q^3yjfpe zCP{I8W79cD)>gZwPv`mguYaofwhwO(4|+7>{ACQSMB5q7$BAZ1bG{zG`o`IpzIK0I zRj6MCOJ_|B1ev{pSy?*VKU@BzU%ry(4t7*^^Wc1~DF!)Wy$F|+X`xsKRyb9TDev)l z{|aK3RymxD$+les0$e@4imBjMMi3gLd!~@f0KU_nO}0&qFCyf;T+kk=SV-@w9~cQV zP6cPPoM=cZNRm%ZZ!8Z_Fe|+!Ng&hr-hLh3MM>Vo%RX_%5K%^z(B^;%dxQ_r4S12R zlaobdk&+%f=VYOXal5OrB0Az`&|Ik`9ARs+piHmTEx2u;nd!n$|6CJdR38z5kG zCeUTNqbr(-7UVzIkBVn*q+t;(%IJ614#dW(QBxS*0DB#YJy%Tp^=WuJ>)|mta2@ z&g)Q)(YdRgyR)Az+Zpo08DL})bmur1`4!)}c#U)s98?**d_2IP3K#|%gz(@+U~Lwf^U@-9e;GrTH{^H{*cGi8+KLX_*9VB6*= z;T}}q_naOQp-^Ke&X*@sEFi#_syw>=O1O);%Jn@{q02&qrowBlZ){rK z5i8Ic5QB|Pa9z&v%l#{A@fq~0EveHiY|BU3x0xNx~YFC-T@>^EM*n(aU=z@*c6QdkAd7yN(m zrH?^H`Rrwr)cmkWq1)XMd(#`lHy)RZl1|Z=txp^oK)RfINC0rfqeqP2jzn-dSzA9( zP(o6<>3BfliDKNyl7px=V5{v3`k-lA{~jO1kH3Oz>bapOq4D9Rri^5MZ3*GefBj|r z_K`L2v|@^6V1uMn3$je}r5NMpU09aP_2T@Jw>YU$*oBuSAmHZGyGRDHm)`b$Rd)m> zr;q41NsJ?lPJ;8gLS}h1BxLlgS4rO^X5@q`3#eN;I{c;5io%S8_VU?1&Hs0 zsIb32-=96Wj~Br5X^i6TyXyS5&e2bundDx{c5D`tKXkJ!8?__~-s{^sltyR_Dx+jh zQwQiR&Ygj<0G*5;@5@uH+K(sAk4}@AT%vNJx(Q@lmP?AFO&F&xO5RLU@Ud@icdX$rT+ zI*MxIO7VP6pKBBx;RU2SR$7K_rBPfMS28;d6T9u;4ZQu&{oFT}etr7W-ns;Y?%fg<9TBb4+kI zr0;rc`p%bSy6N8egWGp*uQzuHHF4b1@hQ3e%I{vi{iW3`Z=n{5P-8*7 zZtORcOd^SCJsHu_&_hdlc-mg%zSX6Tq@d#z|K8ifD;Lq}B6{iR;%r;Kkw2NX8_43v zA73WxFJbRo6(HC^h7EO577ev~>t&0!y;J0;ZPUGeS-x-z*V7uCHoCJGQLDkp#szF7 ziQaL57Tkp6t?0?SUH0bduYdBnE01Llw2KB7o*+|9?r?RuiIzY9t1nBlwB3m}5XWq4 zi>yn_)2gm-oE|;>$jQO7IB!>X)_`8$RF~VkFF*aBkN*1C&i@bp-ux3EnjXLIJpiOA zVTU&t#f@V<%fQ&|V$*)u2L7|g_dX^B%uW)ka9m61h>0AVwRt+Qo5Wk!Wh=z zjTY4oH<2IP0L_T2F}A>ird%lSKN`f5y4F>coS>_R=Q;!|O1g&BzF~Qzl zMc^4pQXFl>QVb{7;+`8mhos4h!kg_4CsI)q22IXWoHLO{%?No>nY*$pn}J73xjKMj zXLMd0H~yF(I$A1Xy0(Jg3`lO>*wm5RJP#+e9p(IrgOsBIx+}6TBfRQVFxC$SFsqv9kA$%}4OrD6wm=w+! z22fneD*|wG7}TWY_D|GsV#+6n#sg^>a)u_wAssh6685Q`T8NCJdtnC!8Q=*u@FnAg zxxLviy$*@Dm+&iyo8JJ%EJC)?=@PV;A)GA1!G2k0QMbiMuRQ*)pZJyA%aX)N28F*9 zyX22J24_>03j!-zRnC{lnKsyO@jd7W%PtT5^7tZMG_(qgEn81CLW`Ot^4add5@GEd;lkVXfs2EqY-jH>Mhp5wl# z!6&gQz-4!Khd)}3XS&4JtK3qlqWt+h>}~;n>ihRRa~)rxmQQ`~8I|aer@J)nV-IZX zZ~vp$X-|jW>NJjFP$mIQ8Uw3zh9|fZ2ZApUm`GKPOmtAT1=zY17YKq4tQPaa8*PWn z?G_YYS5@HK$>hzEE<@6!!)k|p{7`gp^h^Kj*S705m`9kUb|k3?jINT=@DWL*&PDYT zy&#6=jvrq##nt0kxs<&z!;q6 z9LZG>T zwgYZ-EiCwUV6P#Aajg!;60&+Yk2(RuQ5=PFwNHA{HiUjG0@v1~4X2a%Ai9g}SP5-3 z_$^n50yqNGLcD`~_u#EPhvr<FzR~~srdkCxT2gfo^JrND^L@KyP-;*M` z`h(Nk?Ilv;QrQEixu(39OHd#mRTH(DOk`#0jVlN^pAGQYmKcwC@C(3F9@ceLv#aqt zE#BmfDuY|j+;eOe9(xb>g0tvl1`0Hbz48XE;h%G`^g$u-ebE62VR@G3A~G@~mx#wa z+yO@+1VlVyy#vlcaqY~cG{^zq!Hh}5@x-uE0p%d2>F@pU|63`A*Z2I1KU1Xccs_PJ ztiS5euP{IyrQWj0p*2UVc{{{KQHV=JOMG4Le8dM#WqolO4>hRu*cEpNy4@XPT+iN^ zzU77ZTR%LW9H_Ip8Q#8q=X`zn;QTExiQdG~Y#RHzJzp(Pma}*M#y`C{ySYwgkjS7! z{Xs>+CAeP0nmG(fA7sjQF(`#|_I&`yg8$I#Sv=K!{FS@oc|E@LG=%l)uWU{h?SmWd zc%iuaIb$q?gd*QT2$xnC#C)$01yE6MuH^bUKQ`nXuE;;Ek>TtDlI&-~g~faAT} zBV0IN7LRqaIXZcSEcC(oda*jLqT=!Er&Cv--F>}kM&k0}={x?TzdZZbK3$*@QhQoz zYA2%^b0hO`x#_<8`uQtwUfjP7dnlzz+sXntgVUTlbdoF!poI-jO*{Lq{?QErKFEQs z-MxQKtQj3hqL&&O!qqMVIOB}g1q2Y?5bZ;O?ZZK~+g`fZ291pI(aE7RvcdUOLkMcs zZi6;4IwMXrW<`nD)ZU_I_DKnJhfE$gDsXNeD(|e|EGtj0o*tcC;i}o#4zTN3kTQ+1@Z zQ2c%hd>7f#A5aCc(WYS1yzpvKEbL_yw5b^b<2W@~QM!wlvs=q~d9awfuL5yP>aq4?q|>)jOX$ zx^|3WOPaL4JSX9tFBSv{&m-a!US04O`xJzxL5biYMorbUJ)YW&UGk;38?XeQ4BZJC zmvIdn@wYwpkD+3pV1P}q0DH}EKA0NNoxz23O~vJTS)h|WFT$2-D_242kv7Glku*HO zn!jWzgbyoFs^TH%fGJR}+*{<0UzAbRJ9LlXQ41R3LOP30(@lVo$cUQ5G)rh3jSn$` zxR?bk&~#9APK;7VdBY_0BHJPDu}HI#1b22Pp#ZJ^ubF) z?R*)1v!0`}e{JDBU3<7Xz>(_o4>ohyz~YRaZ>?=i6zdK&he4YzyC5_8BO=)Az`)H{v*DG z&jqR+4c=GGz?fwfP$h$jq5My8vrq^*XG?c2*FQ^AC*!PHEp==w(Yaci* z%Qv>ss%=T4FUH;wTYvQV&=5;@AH8nFbGg2(pcjRpgM zqJ-onyftiuhR`acU`_iM#h61odEW6cD@jeMz@3tLGM#YMQIO-%Kjq_qv?D8s@6S=Hwc!Phvbbt;fU4| z#JB&7Gr`(#oWLtz9)_I&=*GioWMV^@Y*7t{OtYgr!b$c-K9SBzaY=Yq!7U1qwhOyfCl;U66>%v#!{0PQV=a~Z<*Sc zR!V-zl}&>Jf=YoSm#3*s$Gi;0vy2o7RdJ!$B3jf2P#IE(Y3Dsms0LMe8vuv9A^!QF z`^Ixr{*CYZpHXww4oCCZB6U!xBJo)`@V8na=9QC2RqJjbDEb(XpX zR_6dtymo)J@u}13Pk(Flu4kfy1<7G@`)qsf{)4(%-@X5}S>g->Jwrm6<(1<{SBuqi z?|5RhoPFVo=YQw_`8l_i48DO2;oktR&y+X*LSn9u?HRB)?H{YC%^;!BU0%Wto_HR*;!N|a zSFS#2v!D32FWcnMVC|;Bb8*I+zJsOtPg(6DWcK8G2{~9FMvfyF$t{U;($GBD5LxO}RY0cH_|^mkPzR0$ty` z{bsefl!7LUE;+!`T!bu_C0<~bJzhv@^8%9B&*#N*Q5Ho;!hZzKFUyL4W%=>ZYV`Iq zI;wN)p&cGEkkg8IQw&t4VFyTZu_Qe54|U>koDC+_ZFqvd&Pb-$;cDxxYv+;tdfTEg zd~Lhawb7rLxQA$g*BOis8*@E z5<@%{*YPNwNwek(Rf|jdnZp99bR3at?t81y8lcO<8UMjMtTt2^>_C(9v{~*j6QE1GFi$%rpdm$XsRy za4YxVZ+y)gd3Oh(yS+W9f9oedN*S;^0v@Mu8t*O7VjWBiVyBT0y%ER=l2#IYMwkxp z1gR(ltMRpOa?lr71f0e$CUQ<{p*kcY7&udk4JFx>z75h&%not)2jZ4DJ z!w}_~85IYBz;sjK{m%wY=H?Z#d{FaQB z`XtNzG56pdRG&AY`@H;SmKJ*H=Wvr~wAMzG7$2@=f3uoiwpHW!8T^K#q#*k}Hs zMTOV*{P8~>cbmy&23Y6v6Ho2;E%W-tDJbC_^r>~tHhb?@@n|Gu8 zXUSjx!OJVnmy^-wNXUD1C%Og(;zk7TeDTPKhU%idd?gyTjys4|!5MW~1ZeZbrw~;A z7;d~IEbXSFJHzVEwtwz)dD_1Gb+Y#GkD11hzSSIkc?YK$R(byMr;qA;U%LIEKD~A_ z8`qo{7EfZX7zcHRM-2cdGt9%|0i*O%B^^7Ui|OMXQJlwVad7hJkgfiU&;CZxo+%xF zk>{hwao<&!?R>tvnv8Xpmv6uIN?3OqKXH9|aCG(f^wD=e{`!CTmsgmNqcJeHekjUC zRn>3a-hSoP`xn~{4Zv}npZ6{ZZP>*o0d*XwK{MlwsBKP#ZfDcB%75k;ueUw+;d+X* z%gvsvP)>04eQ$h+5%ygZR?CP{`{iLx+s@p}K~|cDCl>I+QkTnx`H3o~S~Lgi>H!B# zlp-PF@|!r>Z}wGfA(69uKF4jSJ;}T&pVLnted^?BNkyXr2(CAKaQ9ZVIj4hTc4f*| zb>k`~Ln&4`gl7sB*msmq?aATcV(H9Ana|4Q6-c)|;yM^vsIYzVcL_j@!TYva8T+q$c2QnPrst0Z=$U?qaW zkiZ*QFWgrh_&bS_LzWYItc3;)VZ;H7kJV)P>~JOardf7;aB#Ftox7vuBmhRaX}#X! zxy!|(?^=?sqmwJtiy3S-TM`M}dgs34j;(<3Rn=miKnfo?6_z8hF=6%WJ@*~WP+KF^rZ8_Lzs!9MJ{K6M7c3(@DCxErqf5>WJGrFmT|p0 z34Gt|2*ijqrMs!v;gUj=a}&Z4KEw(y%yPu#NRq)Wi+%y#r zf^uqw@5xDMEW$|xwcsq?*Cg8-jkoiKDttHrc?C$Q1lkS(S_nSage^7^x=`*yGS@oF z3_HRCv6Y4tK2zVBAF2)pce6kAZY@y#2(tJcm*VwV(XZJ7^-3 zg~-sCJO`n)6?HkwQ#TQh{@`x>Ra|=vJz_l=Q?3(AvNRT_P<6gwe)^b(fbsxhKE<)y zL;4h0=bGf_VuNRLUgr%PF4@w+zV5*29&5mFAapK}d3Z0om_QH&_A;DF;~FmSzJW+N z4FsR%S>L${29;J-V@1}1$vNXm#`3`zZmhT6pZ>P@I)Lqby+-B|AY#@q{MDcQVpx3c z3N5${8AcfTaVs0_m3O!lt->Vil3$sea3(zwUhPG?bRuR?}OiI!h28THZ zx5W63CPZXj{lMm8y;R> zDENl&eD51SN~OX_nudhKU5QG~Yb;O^nDcmD5T-9r=9;fFU|WcZ`G)Xp!H6-O$9s7P z2^B5$BL!QQ#fS2(I23J;ijJ|JrmmyFP_Ya99V)?W2)%W+LzI{(F?Z@3^Bb4cai((4 zT)|!LD+b*C8x)FD3!wyH7bW$eeP{yRqHksroU=MOB3e)t+IMM3y)6pq$o;opCMwuY zUnc1^knn?HIy@OvO!(RZUdUxaqr#Y93W%GlAT@1U3m3t`Sbpy%`B2$rM59|2(4>qI zD~|0?<2Q{868gF=@!QMfT>)r=d^992G|&{8o`0`C`8*QEVC}qpR^Po^LFkssms z{`GJF#qZ;EBKNi>@NXbHSO{FnUd|Q^S4L=`(QIiS#%{hk0u^vg^mnnYX~M_eHGTS} zM=Z}d+W9qR5b&O#8bm zw1v=hd=0@kg%>Bgu}HE-HnmBA1`kp5WOl(i_NH)mxc)|Tb$$1(-aBY(J z?DZJ#qgUZl=S_Tdx7)&j9)I$AQiHeOeD&z?5ZZHp6CWL&eAh?P|MWk)cDV3So~bur z&EB}P`Hj!tT5oGg%Vu%Q@q|ZNi;D#|Bk%D)1-67#!4Zb!$_O=DZz%K~B65Cb{ z+MbsMN3jMuZWQ-IBF>sM^%kZ)0t60ViDjfHd6uk*qmH|GM`R((Qu$91Dp+{1s=kG! zatPgd=gDS$e7K;}%gZgn8uMBE)S|w^Zb(kViW!ZfwKz(i6%YvQ7H79C8V9ixV|bw$ zr&w$iM6}v?&pp9HfskhFv6^nrZ?(*YscZGgTChXJ zftz3@yy1MM!)aWOmeO!HZ%CqOEto098jp~sn+%xjriqWJcP;L$3VbF+6eUc1NL*cF zZM}vpxbYrF_7O^u3Po{d$eassAL9bn^_{RFu-aVG!MvDbJAJ#upYz4RS$SofuM8E) z&=Nyslx5l04Xbwya(`Td2n~WkIEfM%nONx)8ywj}K!Zy;X{6&EWH^Y3({a|lzPZ@N zXrgN-3|bSBJ)cpr|?2?miIm|HE_@9lhnfBNwDOx{yC~JdVHcyPv;(_s(v;k?H=cuYJW2s}HuroqRJ3w%Afl9fjCV_>2uA*z za27ti^vO?$0)-HZwl?}_G7(xuRNi-wZ|q?(6W>XbDB|mO^L15+wxfA!3n&(NPFKd= zBS$XA-L?IB)jKXYn_Yw<7=-QPP&f@~I{V|UdZMklpCwbS#9(k=e8f*nwL8;Deup?| zQyfJTomB!D*Sc?(<=8UJxvJJ*e?|&+>tL)VBx|186%#B%1L?*bwPMJA))*|#>VwR~ zJ?NcWJ#1xu0K3vqY+sg@`_pt0$CHMxsrMdn`<358wJJX3IkO~8g;Ai<(L3dguZg3H zgb?^PIk|rFC7hQ%*R6J@`+9S!Zs|V>k^3lSAN@jwt9*D~3h|2yr zaAr4^QeR^L8jNcu{cbhS;5MK6$)C0Z;q|@$+Fw}Bq0kPLK=`PR-ZxD<^bN^)w>=-z zVzIh98y?)R%QRc)MZt3FX83o%XZGTI<0l`9!jN(N#@m}$-gxWgoiEJN`QDZ!0Jvn5 zT|Ij2A*~R72@o8D+Z@odH zazK`|tEa0s9&Os>&we`k-~WeK%N%1Hf9>=4Z{FH&@o1wA(CQ-?boLPghK;mXwQxnB zR8R*EKrTzTC9c=DxVIxHcpujG5GBJ})rlC4bx{A~`->`W*H$FESKO+dx-|@1|;2 zi(Mjiq!#w8FJ|4_6~ES(o_64{wsy7Kd&USW{dW9Zj%!-tV?sH%IB*-=gn_s)2YQQNvvjww4W61F&EL^51cpgRD3gZT5zlu*nRh|Ac9F_(C_T#|V`pQiiPS6T--g#aSPi6$ zCPj!*6vyepjEd!eGfp0wwDO5oI9bBi+M#O==r6yitnZuPk#tJeq*F$T5FcHSSRRkX z!M);8N8>arr)>1}u)QNOAsptT91`|JTN1(so*?w`rTR30I#6yj3Rjz+D1oR8>&Ga- zXr-Wo81}!zOVY!6Ac%4Fo1Lhb8seMnaj<9M@Pd+&jXq!U{oe&?eI z>l5Gd6au89s@7g+*dDE;v-k$uCByuKxbfHg+T*H8XXcM;Q4bGwt-o;&$3tHorPc=m z|HYcrpq3}u!_Y%Up`C?NxYT4Fi~y8%9UWryJ}S$^ef$>rA$H7yulW}K2MR+h;LsTI z#qHOWcc=n@DtzX5a(S^?t`@ujP)S`-2W7ta?#I#5r#|!yP4Y>u)P;|#Qw{S6e(q~~ zW5eZWQQ$2hv`gL0R^-U3EWdtx;wF}5Z zKM#Xf!B5>Og}?jmz52zk+`M%wJYw$~3=+@{Z1DGw1i^D0W#Ec4Cr`kXFdI(n=|CRH zC@0~%22PxBpQr!>z?rvd&xUaF*Gsgoyd2}PbDp{X%8=us{U>Q|IF(<%ed$x*VPkN9 z!@p|e2W|1rHa`5!yMN^shS`F)Odu_4nzE_R(rMu{*QR-%K6~RD40m1Cw;x<$5|3V4 zRgO;$+j`v8=C#y^Gtp;@7MBrGSV#qordddN%n3Cag-e}IH1SCphn&_R;2<_>qzstP zd;y>3WWoRp50rmenp!(-Y*>$ubcr>NeoIfWXj7j&kPLtT;hX2tyM)+;EkpL)1`G`JnUm$+tf!r*8a z5!55$UC2Q$+BRcM+PTe~Z#IX{fI?eDW!f#J9I=)MX3LbSTA_r2J*-%n$L2neX7 zUbDYs|HsbtB;*#QO?tySr~4JfC&jfVp7RR|f{x(EMjnkmyeu8D;wp}APEmmoOx|sP zbkOq9kqnULxjY6B;7tD6pirE=iooceV|*~RqF!hf#^I2rf!N_sQJ2u-TuwM8c1@>O zWPuk+U*arsjs+QU?P2cy>3{u)ew=gnukZctKUR03T-XjfN%Hw&>C|{$%;I7e)gA04 zJVM)GD4l{QEsOC(&&S{Tq3C@t#H1*=#QA0Y%9}Uey7@-k?uh0gJJ1H`)9J~L=bwK1 z^yCoS>e}(OThXumUi8~vj$XS%;7_n^nmu$?Q~9(I5~jtXXohjQI;h+B>LNca$9h*6 z;Ns>1_SNt1Pu(T*OS3r^hkfD$!4n-1!fn))EP3>4Y8Yk@dzi;=;X{+)8G8&Cg%KRT zkie9*B!6vk?Lk*U0Ainh_p^L)H5)GlN@S~WJr`)_JpxzTSfgKvqDq$^3zP~# zH3m7n-$SQicm;4Jc4o(_kL`2N`@UR=F;SUfo2T&ycdcwJQ(%Oh8P zod4HuMNd9{dh4S9d;gcy_q~w(=9lii^2P=6Vn54>%*i6j(5(=RSJIdz?##PjtRbJG zlbA_7`K(pg@Vd%=?iU}0@{$s_UH{4H$qF;2gQ!!xf&@(9s&^` z5T@*u6rIzSi#ZZ%>k2_QxB%dEEg~>4zsS30w>-KwUma(8ZmLKf zNnCBtcNcf)O|iP3FODvnxI{614TpR3(d(Pb`v)%ZS`rV)R_5i*$9bhm*N+R>OOiVA z;Q%o)(dAH!vUK^9BY-5s=-&K8KJ025wdgiY*t9O>tr2{SzmN#7FE_*3?P^n$rt6(i zaY|WEr_s=PE)Ik7-hmU`Iar7T^E$r{nAGi`+65}2|Je_fF@iDUvK*HcsYCK@#E7g%f@HDNaV=)HoL74nTX>eaXaJrg2X_J z+OI4`N3cZlmiy<^%ePzbK}vu`OhkZCMM$Qq(G7i=2B|S5eLIcd z0BDoW(HF21_W=g#9lZ#(QP@xhYFby@FyJxG7YoWvv#{x~qhL_ekoh>Zf~H-&McPRa zqOGI_w2SKLwJEV!zJ~~>j5thtySDNvli0B9gE)aN`-~JfL1(*-aOTnAIY(42nS50Fno zRkwk&&RPdVlNaCnB#Qs!ho3edrI`o=8=W~XzsD>L5$nWOzW)!}qg0lUJ{fwWNtyKP zLiEQ`4OD6j=M4&ya~l-ZF#mXC<{jdg#$wpRusqih1bf!R~;T6R@IKnUwZE& zp!RpX_&mpB=Wl1ERxlckF^d1@&wiQq?Y+evZXIf(D=|TrlHJD0j;T$Dr~}_p{WcSO z7H%s1Z$B)7G`JFY3ah7ezBJsz6&!-Ul#J~l4~$HtAb{}S1w!$!{Ih?)USHxTP;>Yw z@}X8~;x`hNfDDF0a~ND2!3MtU30$R~sNEwrQ=|wd^WuDtgw0)y=V4vP{u^FiMZuCy zd9B6nb?R!mP+O>$*TahV9X|3)oZ|J-Z~e}F?ZE(o(eT;n3k^WCL3zd!s6@9aQsNvD z$EVIqWTN9mzH5o1)2eXY0yy|!U7c@TwTvAVnd=q+kip?$j!|MT8SjmzE=q>;%mZwY zs32Q>j%ytR1#@Y(dUqRW!dgep<`9tk^4U3qs4BzQ#NYFKH^WDmxla}dnFuC`upvl?_pX?|AU~rCjSrq>w1#lQ4ss_| zgH36E>0}Oz5PK)ceQ+e9$y%_7VJZqt#U0+{XDBO)VI-gV@xN>P!|Q+jzxa26eL9QG zvCPZe&{o@aD#@u9$Jg=q<#GvR)g&t#PxM_Mj{o>4r$9tMOM}i~hkc4)-tG#XMgwe7pv!SG_sgRhR*? zHno;Dp$MQg%SZA?gq6>iWVM+O(py9=#&O!c9rvUwAaJK-gXtJPwWBbOm_}!p4aT`R zdSs~XjYCDFARYT{j?^Qkdc%WaMtGRQ-rT2XPRUp@X;M)f#$yGU z4|13@f~taQDW`8oNLXGhPM>=JuB!gafAX8Vrq9y&Kn)C+HCk&PzcRbM&a!g3S#PSw z+aMQZvDsC}hx0s{KKz05XMVYR{PD;B%(qn^e&4P0b-pZ%^^Qt86J400Vb0V`DuOAP zXIeXU%P(04MuV@BGQ?^VeuEd!e&Vx_au$Y%72kVs!KsD`amz(1E_Cssz>bz+eiVY@ zur*xF{NIws+S5%QNk5?zx zu+=nk8@-|L)(>vgyYm7?udc`QE2xM}lN@`wDi@&TEIm9tSS=Ulm*)qEN2X~suJ0h& zqy)r90&0<^Rc(SuqTrOEPmy;#1;c{iAQIZF-;tN(IeGq~@Sc#``{T(Cd~qB&zkTb~K4jzHz6!v&MVeUI0r#w7*4QatK4tU>-|*cm-S9I}PnJrbqz7w7H%`u#;`l zumFp`G^&WmZni2fM;H&_#D2Zq99B@BtFoL0_8_x6psSA$jBSQI-b598omZ^DFJ*%5F9XZgDnK3R4l&5K+zk*dpUCMfCwI-sfSUjg^{^Y9jX z-lbvnha;|rmhhzq92`}h#!3%T6o;qy<~MJ9VqUdEM2nEcFi7vrl-p%sKFWo}d-Q>2 z^TiS>TolE7pS=dB{1hZO+^(Pm$r|&L0?=G=8v=kU^(os z$-Fmux{)Q{V*tn78vhwrjjL=*q)2Hr62&IP zAhJn{q1l0C2Q)i38V#TkZvMgvJDzj=Jnudi0OirRB#|w81NVG8tnlWr*4k@hPk}B9 zs2EjQO{Y`X7m3PFS-4l@dmg<|R?T1hz+8UDB*s{RQAC#Xes=(xdM^Gk<7wvn;&ez%v}{0CNAotqqyYoPpCgeYa))7l+=?1&x%hIUf~Px$5h zgCF?;NQrH=AtFoXS$RNlPP9Ti&D@e_wKwuxT_-uL*RhpYX$n%tn$SnO0~;|bH(fX? z=TIojfury3&^uJd&qi_rC-KyNs}at0Nk~vaB!_rmRdH%|jkVRj`3Pu{b0sxqxAXat zu7EQ@!zMvsOc6{Zy@jvY5Xai>$M|S?IBj20+JfTih-te@fd*-uM-qkn*xmPI8i0S8 z20NvWutN=wszh4L#p!Jv;i{>^yCUg{)A^wSGNKF3aRRt+gP|hN>-n+HFG$24$?N#& z009CRB+zVA7Zk~)ZC1K8Q;Wzt=#VggGA@IJ*L3KcGA&`qQg~i> z`lI$E$HvjLq5y;-`1Ts*U9}PY(yV`?fCmuq`{Yy4LY{7XJy1WQ%k+kv=Xc}h4g?)% z1p&g;II2ZE+L(1!qwkCEs#(^U3qM^CR0s*UIEw1TCP9KkH@w?4?t&+eR85NEHDMQt zO`)117oA`KjZX%29zXn}KTZ@fNlj)Zql8qsxADbO6~ZO;gLz&fXOl_xV;|~&;OXv} z{mn4z)~oKDHx^%e@q0(d--Tryt|wI}!EbQx@bW#EFI_&jhjQ1=`s?5AzVOZNr@q`- zmH@jMrjW5-7DXg_$#_V*hFEqp#~ecGR?F_XI+{0~74c>ST;S6m5|7sD?m<$o2jer% z(QrLWnqxy8QVylUq$6nRpwG8%O)@kLZ zFnrHAK_j6Qv_y3O- zL9DfYAOtwUZak@xUsF`gVm=SER+F)lH>f_&!C`(|V++aGnO~I&qY+DSls%PHe~niG zUpF0d3ydClIG0$1(Qr6)Xt6UHPoOLgdbupPPg5jm$PaNRhPW!1ls}ox=%*@+H?CcC z8-^A5y3`prJCAXXMp z7rHIV=F&k1e;&Si2uX8;lgWBr_hCR$QYB4CWJ0Gr8|HQJ5A1M-QF3vkM;4O9WcmWEA>4OEwrd#^GDtI4g_%Ts22!gu&wY1?08Y4c|iRu{e?w|3} zFOCv#P}WK6h*3~^032#QRIx*(-#rcZEsFfTPu`cM1JaidK7CbCHHv1aK@8$N7n4Lv z&5hnq{{O?I%JkwD=!}g3^$3>4MAeXYDxlo(2$vL35kjrLS+%iqS>%*Ogf)h36X>Md zmMc?EonXhw71oU_>l_q8EOh|xnxzce2x5&d$E(q2RaGha(sK+uv;r~cEsx&B3_)EC z7M&y91o5;)74(Z_5;3;#b^Y{X_Yk>$`~#1p{6tPvZUAQ6kbd+Z|IW+&0w!PpFNeeE zVi~AfMzzP*y0iO0f#v_QT9YQZIo5W5iQEISaYeJBYJd`j&C|5$)bWdcw5;Ch*|g(r zzBc&E7r%IXe2mqzI}o4=DxfD$Jw=!ji9vg=6Yy4B28#9x9%Ycb?(|G`ggOvtbk;DC zbC^S&2x_A(eu6+Owz`EjTdacpJeY=kNQ# z^U6?Kl%f_wdCbI;YoHd6kzqMGUKE4DN1I7wTZ;@r!Xf zNDx=rrkqY!ACK6B|6+cMJA}#hymk(Uz?%aYnzFzlfJ8PN(_y|?P@-2ZYVt#uZ8UNl z=oGPb+?S)pf(Hmpp9sWjXbE;sxv6Go34sbhDu;Jn$nLp_hx`E5)`6{2WO9j=-tX3p z*;FJznRm&V2MFGsmZ9Ic1s7yodlTt>&(lu>D}De?@_8?2_5iHg&rff1tp-xd?|LS1 zC*{Ws-{28)`B3th?=>}vqPueO6gxv-l)y?rH`N~{9`q45^g~MyLaHr2t$+UKKVjE# zeEj1-h(2KUHp!$zF4G^^H5Py?Nu> z&1iAb$jDvQFq69l732#0@x1o_cmowCB5e4bPfRMEuBr1ZdxyPs_Xqt zwE}NHV}ONO;m?x6;MLp9S5G!)56|xnmf7ybH*cQw2ECKx*Xe(p4U=@dGpI=FCdqn$ z2k%b@3YumdOrT(q6CNFO*2^|gL2@E$&=7>v_ZHWPIrhye{HM9o_;G?dq=~&;y#JXx zKl%H=`GvDPj=bzlcP^gUJ$o>%R_W2Q`<+)_|IVw|(m}U2JR=^hCuwtdFey*#|NZ~` z@DE?w{PaIApZ`ebfAXI=IhL2bS&j`dwU(EJT$?Ut1J&TtVG4okhu|xF1nmF<;1~ep z6maLu^pl@DPZWefz+&7vnHelf&_GK|qnimS<2FgHUPOzA@3WK8K`8RcW3YQ4X^ z4+Lln-b4dMOBxmfNrEoPq>(1#Yest)v&k;@imqTfT5xjvTDdqO2ALjQJiK@h&TyD? zcE+PL?d^_ynRqgpbXJu{GI7`Ab#F)BBBgUrlLI=e!TiR+I>83Wx!f22CJV5H{ALoc| z3nymRHW6@ejV#59b$frtOTRb@f~dw_!%Z1uUMW>BwMwa5B1$GS6r+E1H2dJQ4`EHJ z)#{_qJ_wc)G~h+-P~+55jTTJp^-un{f8IC#O3z-FNc!gt6)FPbYfD^~3t5`XTM`)gU<2XH6S)Hz!wc+-CAvNLus3NTA~esbt7f)XE{ZA}#rrT3 z4@orhS;KbhSBO9KLv%q7w`WoUzz~gqH8cjmjR5o~z1~+o|H9GnQCSu3TAeuXfcQMx z`xA0b!!&6a+JQs_A3TjA!w4cFr3Kz`#H0$(^lKUKxb0ZWB?EBw#aI5m|C8i3HTI)PS!fW-qDEA+EYvdzyE_Dl1Mzj z?m%sOriwinfWiw(V)RXHwIMnf#ECFSHqnDO;Bi#3002=+Yl?F-UdL%+-eWA&CvE(s zuz@9lha_XKq04f1+#6)BOG&cUB10?lOi*&~$Xfmy+WMkA?prrWm+m(orj64+zBKLQ zw+YHIdj;M944aQl1Trjf`Bln)XEUxGjwAi4zRrV>Q6For@s zH?S(vLAu4`;m1~G(dlJ?v|5o%r$8>u4hcI*0qBD2%sYX#?2GmUogc2$N_(bPr6@G( z?j(1nNlVc;Ea&?&X#=Xtx`EC%sZ&<{U;XWiB04bd%9& z_iR5MZ3Sj9sPg6V_|1HAa(H<5;+6Xe1kay2=&y@vvl$Ij*C4^@NF9;RwN@g@^9p0r z_nlz$sH#^DMhp_sVJ}IL{h@1`P3kuNWVR?HR;!nJF&<9{W7Gs!I;> z)5#pIy1Enjlp%}shCkkf`aSm?!&D{)XszLbbU`Hz7d=9JtSGoSajoS7E7Y{FT8ltTN5Y`>5I@&Y1yoj;jlxXgMx1(3%xL+ z1>20~4ZfiPe%6iP0DIMx$son$2qQ|}N*zQAg-SEi(cx&SKvJOV5L@4kSSjul7{wqK z5Yj$%z>JcJ7Wmm^_(85>o(kikT7*Hy${XmzAnrg8ln%H!g@HmH<#2-#l!I$u)Do%S(=2tkW! zA_zuG12_nqL*Y~grIiaxSAYja5EEbcQ3iZ1r!K*KzI$bpjkQ@Dr@XXT;mqT{`~i0M#Y`u{0M87#{Q~3 zNbj6zmTgT8f;Spwv(wY3AHBRY9TT>G{QVCH+(@}Y+Kvj7h8lc0v`NxT#lQbg|6DKq z8eO;-i)t(zEJH*WyhN;HvP(WfsE9i>AHiZrwop^b69Xa# zzL!EcVfI3*tyMwXWijOq5N&1cmKwO^KKd0gJ!0NO{I?xSr};Z?zMLe3qEI^h0tU@> zJA{BKx|OB`b0`To&>a@xJ&#@_kon-#_u%Z&M*ZgjL@I*Q*bFA#u}qeC6H;@IpZ7|Eh^28qUZe3|0|( zoY&fqayAGAPess}Q>}w`P6n$L38aU3`Z&oR=E*HynoOVww-h28Bt*+eX?vYt`{dh8 zzy9Kn{*`Pr8o6CL3fm)wTXhB>ekObBk={q118aepFPkra^Yz8z=yd)jJrm9`+ z)$b*bJn+!@GiT198Nr+ER6+yZNXF&fQEV$bl&C_%cQ`K5EySFgi~beS(k$>@LlW1jD;qWHGk;Uu-9tQ(ycxs-wO8OZ&ASQ&EaIq{LR#|Um^LbqtdD#p{yG2=_&X&Z4 z*iIXgYACr)(KX&nThr~9RmllA`E^24v(_!+V;Xcb9u4VkJaRGfFiUpGp*4Gh)I7&^ zXfV-b3973xF%m+*_-{;WS)d4O;#nf?2(35Arwiy-R%$EZFI?@!rfu{EElCDIhZE=@ z6o%1Av_fgnPD1E5(1z~_0e$&vU}N-;U7!bWkoZO*W{rbE7WPJ`h(99XY*UP~+3bvy z-LaQivn+~B%!32Jn#gQZhMNXP-y&GFGy zbq>7YBLqUZjnQKm3*!k4v`;S~u@Ky;G3}LHfLzUJx=T}w(>pi_001%Ir?n&-Q3R(| z77I>5dNlT1E?8|m5!G!@|3cf?b)1MYN&Dy~Pbe8Av7eA@z=lg9p{>V(;*JG*DEa0% z3IiQQ+n}yPRKQd)Na(gkDjVIN|Vh)iL_7>zYcyTWAr0SpXJ1QI~eo#PL0HTO}?3_b@ zrMN%}s9L(RD9Q8u*~jm}jX|i7KmTyuxQH1dI(jZXSUU1i6FyWGviyU8_6Kbje-4k` z%U6*UP*caCtg(PX75`~==@sQ<>59>oaz%(%fgj^^ffePYB$EYe;07*qXB_iANs|bP{qXlotvXz}kfli?c zD*fggfBf~?>2dCNzDq1@3x)@@`PUAq1a_D_etGP5TyjV=ZX#@4kO##K20i!;!orML z1s2MsVYfU%lDFc9DF`P#(5ggR(Ubuw{MD@+j&Zk5RQM-K#G;O*-|aVoSX>}&5$F+{ z;(&asqn9*pnCr*9uv#`7O~B!n?!qcocdvZWtN+}y&%`1JANQ*o}Mc}gw*DM zDji8k0(7pUb&ErXtjfBIa2n9sXv(0~rja5l23>pyJ8To^8VC3eRq6IT=D7Hi5P7PE zWIJm`Y?x$_LI(Ex@u~Hsop_1ajG%xpk(0Yc&Z;i_d`&n|Dj)_xfs1w6D{>EK100&P z2U9Fb4~hh${xqeYOfcYwG=y5cH1&-ah@t*xLSms7^>_9UbpX;+*Fqq` zdFIyp+p%H#p$|V9Q0A+$>aH>4Jdr7|MkBz5hfWO;yo(^|Ttg)I?e(1Gbi-@s-K0P) zDzlt$ArT@BD+~_-7xyC(z2CB*r?d_|vow43vB$+F`p4Yh!He0=Y|!&H60}Wk4c##+ zVvjJwo7PsNWJF%zULtB$l_l#o=d2pc3&r|wq1sCvXmvQVmnAUG1z-?4_uH{1W54+G zKPT96{Kr53SNrKOTOS?QQ-tjOPbPoqW8H_Zc83X6=+BqM@of3>Yj4~>dL1Zfv~f#Z zc;1~14|b>1hwr~~?r;jOq>z8~mF`zwT>s7sxIx`26p&kvF9Miu-7nHL`RStWuNGy~ z$FsUX6nReIl&gud$mutePnsSo^m?(s8SZRqT38=W5=~ov(92?=$H4iWRUe%smskmQrznji>Lx9Dj{I4se?P125e7VVUf_d^wNXR zHTnFX{^7Upxp4UUt-M;?UagBWlkAxbql<)lN%oaD?z}wje&9;;^_$6WeCztN7kb0t z+2`MN4_g28pZeZnQM~7g^WVDm^@j!z{NQ`fKKSf9-z=);c+fX(t8i8dm_ryf!l>a@ z1NF!z9>Qig?)$);Zi=G+Tc5eGSau2G>uPoT&M{1as;HOnhXxVhwd z_r8(ymxi;D)Uhqi8Wdj5=Ckqm09uw+acAblG%^t$$8aAH)W?jkiB;4$qzmVZnLu5# z{^rK*TUnAUOXGfg7MTgR^RhlUT{I^Au3{jzYX=fEc%rT&kWS(5rHeQjBtg)ktkSff z_Pdku2&+S=G#}^}a$Nk%<^~s_hAagMlFTvzD z2F#Us#DHcOYm!^XG@}>djg{exfDN$MFdFk^z(B4TIRo)M9so}#ru>UTH)6)3+Z#@H z!aFz%K~)o>Zihrg|LeJ%&9s&_Scj~d>PQ}r)}Slf9_erbk7xlZC23mGEHq3+loEh# zqyhnvFkK8}-tO&&`=BpakF+Fdd`igZiyB7u5E5&Q$uhQdzQ}dU0Hq87ldi$RB-y9w zuofq}Vz6pfNVX@jrxE9|rq^eXfTlr$ez|tAR_OHd4j2SZoZwZFD6m-7E~2LU`rz_r zdT{DOP)MOQK+@ZIgHyo9R1vYcb`%~tapGEdfJLJu1yHw<7rt&g6%~C^Sxew$pU~G= z-MZKQtY=hmSJau@&1Q@7WP;qHZ}@ZiM(Qzha1kll+ds$`v!bk@ef%=C_{ekjLmSW& zcXvWn^y9{7G(@6YEqACvtva6ijZB9BlZ4Qp#*<2jZZ`V)x}+!I?tn2@sIj>bk2 z(-mP*&@Q4sN}?v%D5#($!8|nFub5+b2n-z6CSXKZqj7wOnE)5aETVw{)x=>g+L%%S z782dVx-l9Q0-ld4bGk* zig1Aj9r1L#7_9^SaE?|`r$s6fUMkb6GYKRr^Z9(aT$W`Gj+7CKkZ>qOc-}^J?>O40 z{BpD>?PJRsX>b`qb8q{-dG2a1`{dPk8}gkQ&fUg@=K|Iqu3+0#mLm5XxQf7SrbO0b zThety`YL|uPAYj5f6T#U>zuTc5*7W$BsoUXg6B||x&iV!Rw-k5`Z2B`zmvaI^AbT&p|qlY5CKdnIm}{q+=Ss z^Tx~4-OdR5@P*J5t(F~*xC&qVnuFj-@S5(k8dW^>>mf#9D#%*Gw_jG_QV~McSL^q@ z?}Les)z_pDv>3oeF3BOF7oud+*v>#fIt)$7ropcUEBrG04gm~Cc_hs+O8R5JwbFq9 zir9aY43L6FiMyqahD`B`pduY_O4$)_Lu)Upn%!4k924)#@Rhz_oc zn#io1;Y5A4m?&X9h8_fM!(oaX!)%yA1VAiwI$!#Nq%+DmAVr5XIjmNHFx=^Gs$M#A zZx0NrpN;Zkd8?}A>FJ_?2E?iTUQ!ZF#7nvayVJ#M1V+Qo;?BwG=dZ(67nY3Wd2e^j zufq*HBb{_)sjh-nfLFcK(-q3pELVdxxih;B?mNR2u3wxc$)+R%-OY+5857WSHwCaL z734kyJ;jj$i65jNm?gNOcXIiGXY2W`-~alnUwpH~9FMd9C|&Q3va?t2ON--o?Uo0l z&6nS(-+hH}ef|62ymho0KKbOm|MIi1-kIk_4b8gy=)JoqM|X;HGu|YZ&!&I(uRmU< z$G4mBlE|(%M0f-ih)KUBoHS}B5;E2@BS0e~tWi7lR79M;Pk!p+Vu{Y;vevim93!R3 z4LSsYqO>syQo<>C5v_%c7$qZL;HLZVrX(Z2r17RKrhZ37bD|t>%x2f_HM2>_2kGR% zT8-~_;V&3=5Y0hzbu9!<-Ogw~Vq!s<5r2WP{S ziVqA7JuOy+C2bB{q5A+TN0hLLWEYY4$QK}jpFD*r z0lArp5inAi;=>oG8tmPPLpTqiK;ZxI^4kJIN-@xERwB1A6Hcdnff|bCAga zK_{|cQ-u)->KbuT7=nj%#JW2V-#-C>po8keiRu6>Eq@I&5Gx(DuF^(eoWe>Az=H4K zru3(8UlmTbGl3*RgN$}h5AR?1(j1I**ma!gyp|Eg|r~<<* zsUno~uR}2=HiFmgT@0w#fAP=w9hHT2)CSe1^Nhea^i%&3riLgayF}-}D92p(P`>x^ zE71MJ&t4T%_ymg70c1$+XQj|h>fuQGKlx8TyY1u8%`v`s1%-k9!4Jb8Z7wWVSg|$o z8J_EhO{#eais}!-;3>Q`RS1I~{OCJwh(Ayn&~{7q-HBM4w`ISA!cEHJvn7&}=KwRY z2sH){t^-UT;V?7-p!nJ@biFA42N5a4e`*VC{>wf!%4Y3&4y!) zXjvAkDu3@&57X(#-usBd29ZG_dDy;z@7DKz`q#dur3>nQfz72tnrV=9(maop8fs#nh0(?Zk22BY`J0+-$z` z<*%L|A1&q!oAm$~Ns8c=bx90nuA=5RS!cFrwAYrQe0$EZtx`9bLT1LmAT@H$%lS5I-2nq zR*B+HdeC^`Tk~9?L@{X8>$ESXD;N=Z#yy*dP{8BsJ@nM;XY^68%0WLin~&qc$DcwD z^e%+{U5C&#n0F^?D2CHel2K{`K4sE*WQ3%FtrC@jk^#G~4(K#`m~ZE-V@GWA08N2D z2NN#i=q@HAOj^pKzzNA!QFVkf><1jv=Y+HqhH|Y2fh?+kJb)&p5CeQE-~m0p61j?2 zz#Q-i`iH0nX8>nJuysA+hH4^;exk)B#G%M|VvNB-JE&5Ujc~5?48F($u((jMF6o}4 zQm@bhDeD=$oZ@sozRxU4fosIrD={Iv^_+(a0`^CZD8ySQ?_kJmzI1zNtx`b*flQ%9{dCe_<(q78cW2sb76c7>y%}|z z**dGct_kecbL?5Maat*@7vH;f%Z|*J|Sqq6Ac;?m}Pf(sdMYnUiZ?$nd74T$}4yN_K!Zlc;%1p zf9$F3+{K^&+{>T%)K|}p)00K#+=M)GHS7(p9`>rw-Tp|5@f0< z9m5XIR=^Bz5fxKpexWM(sVha5{KoH}i}80#ZEoE;g1g3=;h`vl2tgVT1zLJRFkFO< zqe8GiB65MzN4!=9WJnirHnOk5kh0Wl`orDP&Y514p|(ltr#Z^yd{fS5C%1NX_8xrr zp$8v&C`|$|ckZf3!wfMcqK!U(;Z}t+w@~+(un_czyJmWF^RiDmWG)N zE61Y@|(V7?b8pP z(JZ(l^Lb@zN4N=2hk#amSL!Au4=iv@$U#{_sor20#!{BRg zod|N<02GYiVLX}uG%dS-SIh#n0)%C6)*Bbd8A{Q-Zm|W14kd{Zv+MwNng>-b(DAh! z4h^efo(?CJ$Kpc-N+LWo5>A^`fh@(KE7lr7E_Q|s1`tk>yiikF}_J2Gq_BqYyt zg$1#IKCIU-@+BT1%Z6^YmLz39sN`;FMF^YkJqg{?XIdctyI37z#5px@aK<*(SF?kT{+*;EP5;B&Gt*+5z3aHBk z#rkAvLudR5(wvj_wf?f+xm*u-PP*x;+Yq18iW8s00YLn-)wdEA=Tzt#Wok$Pkt81n zviOwV${81mO18Lg0PNbIb)v$NM`%N$L^?QfiD-rui14L@$0$pL%(oJ;4posqdSyD< zJNJ=i?~_sv`1v{wsz_mj55u=$mAj5MKmCc%Z~ORja7-^;iD*N?H9qz)0hDNHmz)c7 zxh1d@h;kcVs)ZrS*ai5s{WaNeJp(K?l2N~Sq&y*@ln5b)c}RnIv1*e0GXxBHZb4TIHzj zC{42Ye70%I=bwEH8~5?|K17)21cDyfSxec{AN<_!zGNH=cH2B{l>vbPqC%RNn041p zHSX_@1?*!RaT35Qp&~wY(4Uw|L@>xCW@hwCCo*m%p_QjKfQ2|pc01D|@lX$Bee=q< zU%Gzd+Wd4TBkmqRLNjI^nw&PglO%D%p)Of!8wtiwTyl8fJM;1kT7`>^TfQP9Tcei$ zK|qBzsz^+LTVvugx_rB{DEfBGl)QVw(b@<^o!Zepx42O$Qi9V@Mi@{jWl6wU;m>0B zqur2^6r%ft(-` zOz^#lz)4A`nJPyDxTHIbI~I-H!|{7^9s$=2p(Acug%GDh%$#62xJ6^|6npN9?xv~S z_tCjlKQW1=GiVQJAWMrwywB)I$3SY}S3lB}U}e*Ek%X-a?36BlD9y!vr4=jhxSVq7 z3k(h^=$R40AWKI28sl%RR8*vAMvZk!Xcn0(qg^bZVipT_{S_(wZp`qO`fAO@9M6Q@e9AU3Z7V0`N z4nLZvtb=I(>8GFA^n8^+adG#`B z6;JxtZXchV&fd6weKx-(y4ogM*R>t&UF>%^4?pnGnLR>~&B<)@m2a)T`r`UuyGxxp}nM-^s3@G>s7e?mV`RO~$~94{H2K zsT(c$x`N{!2~5M-RQb}OMI^Oa`jR#5b7j2#HSKSP-MXI)$jE&qpqK4s&7$f}2gNsU ztiSs5jUho%2ogMjTuLCZg`pJBy$Vwq8Y85 za$W#db2K|S$X5H~-N~SPaCl);&DZrJJ$L`%;kl#Nzusx){lP@ich`f>dNdlHE~|g} z_2cEbH|%%DSr-nT$yN{UE{CK2Rek+d{mA`S2mj?i{J;L4kNwZiOcqa{tCzjq7hWs= z@U`-dWpgPj2I=G=X-w!Go@i$M8_$eC@a_*z%F*?FbBp|fP)ir0l*Zry=&dUR0P^In zECON2LaVy-Tfcw7m$xIxxqWnsutNllg0e1Q+A({|tK6KtlHx~f#VUh)qWw0AlB;)2fgvwjA@wlr;`yiu_E+dth&A}PeN2w zqv5deqx04A&1>^=bLHw)oF|avMV&iyf`K7X!A7lWNR4#%~RI)Q)%vJQ*FA}kU1hAVRfrsWOG!a~b4%AFD zcb(=|3^GF7)|{DyV5-dz(;*H34np!ld}M;U5DXiudBQ_sSCt0DL1#`6X*QxY9#J@+ z_y^!gC)}J(=kAii0k%~8Azg`B2ovxI#P=a8S}tnFPR%JQ1`7mC0gIvgXc-!itjU<02T@HNQ#GTL2#2X z+R+gKOc)u26HtIh&x_#3ervQ%gY+wLR5*#uz3^i6Xxhwy|C3}eCARRoJ4_5T!dBPr zx!hvvO*?UZ3|KX7t7&z3JYw=rh*b&YGAwh z&c(%OuP|oVfS8F&P|yI+6p_*DbA(qVn;%-!igH-F%%o&Y%`x& zxD~1;(pG78&|mdTfWS7aA%%@QI%Ynf`Q6_>B~jT9jlX>WWw=eu8l-?AoJ)cB4hPB; zCdGedB*y=o^DstzIRQ7{AaGQISSiB7wy;#NZY(OXQ6gpqfc`84TkI?vfq;7AXvWG> z{q6~&x`_(*jGF}S$dY~r5_dWT_jo~&4VbW+vR~%7}LTwalZ(;Y% zNN6tzj%njQo%;E9Gblflj`iuqKpNF)j9!A*lHK1<=e|dt!Z{n40~nUIDu~4ggN#l@ z2GbKq$F*ddB4P0T(cD)}FsX#~6cYel9Rx!);33yY2W}!>zrly;6QLS^tGZBJ*LlME+PAQvYHd>yHaLp+)tDj_m}L0;67F?=n;WfFpiz|Zj)w#g;U zeITq`;y>*-2;kQYgSPyOpBNBG(@Bz&FcQhf$G{-3;u@_u5Kzg-)oQYTj<~beO(>@{ z(+|b?PYjzLkcT8GFi05B9EmDrb6_2OmAsIHQox0T#Z7d!)FwD}5}A{(F&YMRG?oKT z2=MX6M(9Q%x*}$RM!uS1H4_&Ha9u>~3M2H2mR!wNJM@T=p=-rjy$!wW^hN}Z+}f=B zn~KEg6F>L%u3tC4_&3WM=JsB_cJqa=Il2^8sYUw^qr$m+uRQj^J?BY0kcH!u4f)CM zexdV)7wKn3G(IJsTNh1!dS+)pqEnT{syBimc?F1ao*qdM<7-`=G|9L(#Hnk5A&!n_ z)g8bXS$H*j0IVRdfgAO`ZG6hYf-4YCkQi zRsY_-=Jvcd%{EuhL%-yU-#Yrtw@*o$!=`nJ2_ve260lrz6-p-3GAu5~gATDheA^uw z;11L7!Ni4jM8pKmh{rJP6*W2MU|w~0I{9AO9~7?)CJ!DQo*D1#9$kOg#Wqcz4tDyk zjH}j-Z`b_Ri?tty!C7>%hsjA^J+!-d*OhTPSbz0+=XO0fzq5Cye0_gX-N-jT`ta_> zX?iPP|C2A=`QnY{%4Bu#PJ`^%b#u5gc;j?%D|_vK`{Dn=$DX!?OUsu?EWx_5)k&r_yff=LmdB>Gt1yWV5eC-Uswdu|ZV?i^P zqhq85tp!x{3cx`}sg46x!-ilKY0ow5j&eDe1(r}4{18rG>l($2{iNUVO?}jmI7dUh zDlej*s$UH5?8XvmEB$Imycvnv7^8->cDh2hx*}Xl7#$M4E3l`Er4d zI*7BfF(7n89}5G1A(rTL*iHj$i65_{WElx_wOlSCodH`MM9d(nJ&CtoG0;dF4QhB%5wKTl=HtHd8 z04xGJRz$vrf>Z__plf^)1&6M~!eU~TP$WbPLyVUxiyS&Av!vodlc1%oQ5_#jG3-6h zDI={-gxpIW(}bK*y628SGU%;u^8+nqxaq5PBr1-tc~fP?)kYUmo)hr$JFOJ7DZ`v~ zU8U1iGG0#3E?n^y`(X+g>V=^WV;BjG=&qbp!R6u-ZA|9Tq|`B;!T3=T5MbBp`!b_4 zLpJMXG<2g+Wdm}!R4F7QmU^I~Zefv-aIYV|cV{};Bgy{oGY>>qu)*KMV2DZ3X+r5? zXjN9_&;E-qZTt8SdW_CqP!5cbja0%EOL`ds&Pa9KK~ST(krQGHZc)n688nen&JhXq zEtDAgxNHdhHxHqhk)gQCbC5y6tWh^2Y&llOwz$DeoPpO&$qjLh^(2WhARwRHl{-+8sASZsgGKTFP z&%>y23jPX!>t4dd7;bYLMIugyy4<4uKm5#RPfm`tJAdLpTlBXaQpcaD8h7H@p2ky; zw;plF*SHPw(cZpmjPO5V;R3w21~H0uz*(25BRi!@WJjhYz9?^(kjl}iV2c%1^=+3X z5_LOpZec^yy9W()TnFZUQ2u8JG}*qs61j(P@I-%zcL4}nhlw!6`jKoHnMMU}OY==b zU~2PkK@#VjpoWpRw8&VKM>d6&fmD3apH{3T3%d6__6+C5A$TslSJaZf0(bYv)c|oo zj=vXGuB^zpby>Pl4HW7oHrxrv4{N2HUe&n=h1T%M_X~+8DM9N<2n~S0gXnhUD7&zc zafA}-l*7WBbtpPX0EA2_UFO}Ud^GDUrLX*#dMV`GGTVcCVYZ$6f#zB-AZTIK*3wbz zIOG@hg2GKsN*^TXy3mnFgrB~7OF)#6w)D~zdqxSp4j>0v(DxcNI>Q63dDq+kp=bw> zO$O*GEQx#x$S8#Ucd!Wvc2oTOdg<`0FBkcGnyCgY^@zR$WwHP_5Hopk)<^xh%M||+ zvDM`ge?b}-MoY>I9=zUk3)cv9F5*}u+oxLvgdf3-s${Ub`W z$MrA$r*w#9!RFIBkd`R#tFQa*ro)4CgpcFVaDR6fbzUr+uY7y+xi7C@cuDGK-MW+Q zY>LyeT&75$OQHwEKJjaBRV~88bxuxZ(2B5soRGI*OFKn(ob)<=W@+fBOF-KNHipF; zzn$V3F;rU3mj%FMm1uJ?+|PCn@k*2-0nm7o)8cn9O#qE;QK!3;%`ESX&JC-ix4HLB zZ#Xjb{mm~OfAfvmFhP~JEK(itwGdj1=4c}KH>-B}OBtjjbhRk+nzR<*oibbX@JHPwc z3&%5TgV^3VK0$86PAcrUEq@zk+6I)OGqTwdpIiC!t z9VGw*rdbNdGe6l=-?;=t!tYtT(SS5;vyNC_}OIHJ&=ZaRR% z4dm))Bzy)+h2K^JstVeYkWh&NOOk@sX6~ZCZ>1voZ`@+*F{5V#KsL^h1_?6-~`Zzja9cbDH<8~ zYauykK}X1Gj{8WL#Vo9mfPjcKD5L%$YnBoTC*c{4Ce)^SVxnb#?cr* z0uu=TMZmzrQC&om01z|~W^BIqou4sQx8<1JQ%6FqkuklMqh~~14GF3~&b6cDmh-UE zD2Ho?=^b)Gd>#=L$qoRwHDK7FW)KB)ij9&z@lIxtT;M5|lGSiPF2a|)YDF>nRUc_p zP3V=yl1EAA>)()t4qz34iIi&F!VPU9o*@>OK|PmrN)4P8=r}^Hc90z|rWcD|x}=8F zGJ=FXRN8>5XW+2`UI>8Bk*H7yY;brn!(<4TR{GQ&vKQk&ERhnh_e3@oLoF#}s zx$XuAtG88A%<~GRMa19t?t9VDo$>fRk6%LMCA1qmIP?phsSB-<1I$O74u0kr{{tr~ z+1X1D#Y8-SsnIs#AbqKTB-@v*lL5Y21Ar51B?8@U?GG}7_91dga1RSlIMTK%FCh!H zVu>v>*4|h0D9;J;27+fV4zh>1@f&6dIYT?OarWpCwW?&Chi!zMh&uMG=L@Au3NBhv zq`>0j4%QI6xme`GEW@s}iWH;E-725=Q>>~>P@Z|@a#^pkwEv-}t`1U%Ein?UBx-;* zo1gvlZ_qs`qYHxpnT&5N&udLT9bupeII(Re6-sX)0lD0HP{RQ>a2_gvBT&aEX_!%T z#Je2!rwuS1(K^w2((fW?+`~+w4*{v=B_>$PZi(9-GSa&$ z$lZ!A<&vioI!X+>AVMP?D~IZdL}egNwUMF00%*y|_R2;)MLD$qdXb>5h6#$rqxKLk zOd4mP>T4~wWIc^JJw)0G8DOW&$+&@dh&2sI1GIo;)S^a00;gS^TDyys=tw|rMO$%D zQ?DZ=fQ)7aGsLOw+0xs?OPaFhwiOBxDhZ?pjOX5uV~cyQci$t=NMhwn#*tJ2IkHJ? zMeL5S0>4AZP05|IED)tYk=`iIGp8dQj*3Hpg1tzdD+x8_jy9RrJ5SP%AO=l*d4$e% zsH-OF`rQi2V%M^&T`H!+#$*my(<;tJcaO<&DX@daF&gE%H5yhY!vPq=0!0i`QU?NC zSIOitmfFGW_PlOlvmJ6y}FpDKK!2L|u}rhcrg= zfC9st8f7E)+6-2%ns&5!h{T5$!$wDr!4=Z23PS^9Dd`zqOZk>jP|jSFivIC-@Ygb~ z#59yElTA&y-}qWrhjdu74ma9D&hTf zJov5=j}XY80V>62$|{`!r_w%}nE`M+_@#qUW)c_f5U_Pn=Z3HXN`C5d z7q8t$su9EW@#(Rmy**mQb$=kqHUCUe25D8{bS6-X!~42OgDUWgdzFkdK3(+036zZv=pN$+&wBpYG~gqRw!!W}1>d^SFbmq6q> zG5v}Pqs({oviPj(I8)ovCemN-11kjxa3Klir=J+=Nfu0ECK`yEY3LM$ z+~_m1u&~lCCQym&3F5chfu~{9Z5R?N`J6gRpaViV0cSLx9NoN@FHg>%zmLE#*a(8# z@D~f^tQFX_FkuT!Rsbh*JoAmMlcFePfQnMV&$x<6qs0uMFj%nFc7VyROqSA#)H5A{ zHBZ3;{{vE1q{<2vCV_GJ7Fh~r6n;|+B4Cl}#I0qKk}@AncVGb$k|w0n=34X_sw$Sc z3)jzgk&BgsV?FMKgVqK&bu}7~30JUJ*!h+h$Rmj~X=F=AiBDVZ*jAex)Rpqk>CzXM zE@X9~verGKo(>Tgt#i&z(M2E8pfn@3rWb?BEm^|1B3-e7Kg&izOM8?J5r5L76H=th`shx%m?wi2 z2ItEe^5iR|-R^UbUn=YM$KU@bX))yUotD_aQ;9-KdVYEa6q{c1GoSck+r@v7V|e}w zKq+ecF%$5;05ldBv_WQs7RH!ng%MrHyEdG1=Hx~z64G7cDzo7|I1)S5gmg-VvO&vH z4AD`1C}kNbLDMebMS5&y8%m*&7!2Q9rV55K+7t}kJJFC|MuYwdssKDLw+F1!*wK|&{9ugY zuA>(^9&QF~9uNaIYnY;l79_HtfYO8LIR1Lr2E95hLu20QXV~

y!3?LbO^X$;gqD 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 KwCCJGR)kRlzZA4AaCMn?aOQ; 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%0DjO>@dU3PL&?AOnCO)_E$X@_BlH8U-1fa?mk6 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

3XepPv5D&A0aFQkbDymFc$+()#a>h%Vp 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⩔~DSn`LHp_0mGe?zI^`GFV9~* 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%0DR&>5uYf=T+r^ob8c`}W5mNk@N}-1lb; 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

7$IWE~gOELpkj64Kq6I4&y^vlW|o}^ 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⋘ zhSqjR+$;)0j;6`cpKrbENyHS&1>-u8)iEA|j0wQ9DqcXJX`3i)CP!)RDcZWBUPyrCX)~vM4_K~wI!ZV>2N}C)QijnMhu37n1Pd3 zxW!8_`Nl5f2HoxYwZnAcR;iO9B z1*SsFwhF~3-Dn&+M8kS=0XowvI?&!^$>8M3?s#njvr(bLP6R?lxW&=taV>_u0!HU86tQi zf6O6vU}*Edj%Zkn0NM*bph$-aP{<4!6@dNPLjr=aDO#bvLeYblqGrA@rf8ThWoAy; zi@e}2Go0h3>OCF$(XS{*9?k@1nNxviLb-|-F-k)?^_5Ot4TUEthUySh_(i0Uk*^Y&i7z`$dI=;A#mH8+zXe*jUviks%BG=$)#xiS@DAQ2if z_r4M8M8tSEpSq!Z0WQD-8mD4d%#5i{l=Mr}=|!I}F~Te>A%00jP;)>Rtcn&nVRIdA zzF<1e_+9P>q%j0yDba@jYJ==iKpT01IA&3>`uK54*+aw?6v#Ti^cZ+n+rA=)r@} ze*B{X8S0Xn)0KX9@cR(WKwdA2J`w!p&6c2pErN+0^QT;23W5Y6wA)?x z+x_n5`t0mXMR<)MxI7y*RE& zb{EUTj8U9koR;*5S-YCmi*a%6y{fgGpVvMB-4ik5M8*lPoUmiaM7?JM!V_~$P2kgA#$zylEJkACV{+PsDo3^UL2OW);r8UH8jL|sF5!Ep`sxnj6EIA7$ zLbN|pHf43hWV1<`B&3_{%OKR!7EZhvPC2L5dubqjME5&?#6F*Lltp>Uwcf-uIX^JH z9-HZ!snHZ6R6&Ms6Di>XAfD+#sGY}%x4JUvhux3d00OivoUmM zDj4_H6wE&nNT%TjExh>rXGL%}X?(H$UtOAxHyy5c3ECPHd>*W()>lH%?l?xzF0CLwru5oz6%tUx| zkt&x+%*Rbdkj~{flQ+mg7~Mwq$`#9i^#hbRA{!I{vkN#eM4%c(ciI1N^m_2B!i2T_ z3?edZu6jDT6j0YSi2}0VAOuMEGgS%-vR~QMXDr-${LSa>W^?CjA4GlXmsTp#35k{D$34!f6zcT( zMg_q)SV+^H#~#yRmO#|?Bmn?YQ@`~ryYW1o4-$BkC90mzXeE^FaSCd&7>?p4Fl49v z#`7GQQ6V*xPw1lw|fBV1u&(wip zfKm!nkag$TVtvnIjiR{i?SCz}Y_Kz_{It(^dSECXWLj{$VF`SSihL_Rn+!V(F__Y8 z9E6E)%%4#Wn;=G&MS> zSTQ}TEwfHjZXH=}ub)vw1(ZIkH?pDftK-41Obzdh_q|-q7Qg!G|0RKJ?|twc(nG=` zS;aVZ8V+N`abWB=054mp`}8A|b|)mV6NZk9qi>UrB)YS@bGT0$}4>@=v64UB1_ihbf==R-ix6`4DKt@R6=FdnN zfAMoRR&6tkhs%pQo3o9sYkDN=dV~t6p%Fk5p#>B-Y;Rs&-gyi9N%oG0*TrnV+rNJG z9K&$75tUL3Fz!|ckE4GeyK`RMZ1}k?xe5Zd8^zKI~hK#4AO{izhBQ zQ#GR{Qv!_NTpX{&CEbZd8mMHO4I$8gnMfhdf6S?w4{y5h4 zngF5-_B+Z}v1-jAx3L*yVsAkV=1roIT?&PUpNd}K6%79A?=h;>;MeiaGvy&GxCp;F zOYjz896C87Hju&5A`Zg>jGN$=kZvOkLLyoh9}@>!qSav%Zux;zjyR8kz!XIk9unQb zRWpEyoW%Gg70;pz>&Sraa6EbEo2M9{(`T^?HUf1Zl9qT9#$7X|3sb5>5dPz>GUMDh z(=eF9N)`Wj><3>~fz#21@tb($H6J~Ntyg+S_`IWh=If3~;Se{L=C3jZ*dF+UH7+uua&inrMS!!NF>L4d$uu@(>i1`E+b9{TLX1g4at``Uocx z9--)o`Y5TyJb*!`Vmbv6+I?ZOZXR=v*r+A=+*C$Ac#*MU8*({c>~}Xb*{s|6i>D-G z?%|y;@O}j3yedPQ@eHO6*1){Iu72wJj_--tknDkvm`_g$ySY|(}yNrTq5<+Wz0feQD9y%rGB%zNd=NfI)?)UHmNL1 zr#9B2$?{7C3AF`NOQ>tk9I8HqGN)QrxFl@%z99;94>LomOhOOKV2`8|Yq9ePXvSdT zLR*+kO&e9_aB9ViASp*F)#A}I~h-Toxm*z^~u0VS+C(p4tU)RrbLeQBt=}gq61-#oY z6FN@WIgOM7cW?<*3pO;dUq*MT`L*+#b!=_D&J{=sqd`!7_ z5*zN&Ie>wB)z+)NA9sWUDCjLds!nepv(MK&d2^3O>hR+Wx5-M$6us25%uTo6#sDBL ztAVZ0j{0o|Z=OF#zxD%L2$K!G>B1nO-c(H~FPlQ65JLtQ=>en5`r^uS3QYg1i&I7* zTdQS$(J%VZ-liH35;W0{V!0tL9!|)MP4LH#+EyyGV^}lOg@C?Ki_%anCbZEdW|EF% z&$_V$+L<`WiLEhxKqi5s5r|L-FkZrQb(ttjv08<&?47)^Gbs5=10jYN>e(Y5y9VYH z0!;tL!#nT3_s)Co=l8uwzxoA8qDmSZ@Ylm$W6xz4wK(pTP7&g1{8E{~>QF^G(DFN9 zu8+%le$2Y2KI6uYQbl$lN?F7gM!183JpzHrE#jw!aS70-2HAodzYaVn!XOl6Y#@b} zs{*p=ZwQx`Wyu3zX$Y#$mRC+ksA5NsPk1HOU6P=Y@5|7bBn4o>1%-n_oKe#7tC`Gri1niPyP&XUp>?TT$-gw1AIZ>nZ9TeZW@ zbLJ;ZHV)&>X-Sf8B0_avNv1!~uD^0c9KnUD3--k9 z1@kd9lJil)Mhh2vGPS($EI2yGR3Orq$4P5 z*%xUHmkub%M0%Jph=F!_c7BO=_WLc3bJkEo1S07{BY`Cd00ACDr&%wk8y}y8;$rMZ zGK$l1RxjSWbFuHc0c~sRg4uPw9?GT*u-UsRp{Xtkhsjv9*&Nr=EyFW8GGl&vODvKwand z#n5Z^$p>g9eZ|Ah8A)|oxxr6OJ&j5kd5CO|8?roQAl|NnTXRfxG7yKYNhyv^1~Bt| zh8y2KV;AaW(T#`Qu6yTPF#;lRyBKD}CDK`7nVK~)#p%NV3@;bsY4u|3LKtUY(f&*d zQ&0MoyCH9m5RJ6WXL7uAL0}K(XX~o=yM(!9H|d$6Y7W;4qvp}0uoEO7K@ zMXQzJAw7**{+DPz;D95O3Th;JdYQs_l!45ZbrsEqWPnC1&n5seRzN(GvDr05L4^Jz z4a`z8ujEd8E?gW!N;7sIsCl?~SpdT@%xg&aW81FJFtp&~n_>JSX3f|UiUR1%Qe;3{ zA{WxiTEb{_0095=NklBt=W73QhKlxzas!Obk=gz{iFP zwcF&Q;O{~h+|sX~`i+vpgkEEKnKqPXFoYD=f)uz<=2O2@?Vrp1`Jcu zmEk4h2ema-Em?^)1uWx9dWEZL55?)fZGNvff)bC?6t!D4 z=8y*K0pIAZEE(s_RK(FVf*ygG#xMci2uKE^n1Z2tlJ*D|!a8+uNIdzS!SObtGMFPu zaAXR_;Du1?ESaA7Qj`cqCuq%|V8=co4R57}7rMQ800^cR6STP@!IU|IXhRx@w;}PX zQp*>-Wcuh-iP1xJt*33d4j~TOF6L$I5OR;1Xi7}9uuq%l!PdqD0up#R=Ec|fg#xBx z@p0_vP4=}1?Mv5$E&iw(Wr#7{DJu3sUU0k8R6qb+tru42y>EH)<-w#2XjvR;Z?bA~ z=Q<)m0z+iSxAw*wGk2gET_;y75WtVTDk^<2Il_II)kYGTA{e>`&!i>*x$YtCGk$Yb z$H9bO2$0NnOc+kH+$ZKp8NirEArrfcb)8p{wzcG3A7$#{18Pnr{Ca7x3ZrlV04;T- zu6<#`Y}WYhAio0+L85Dd%bZyXrWE^uWumGECK_O2#&slgy?p+R+**rGmIsm%L}3Mk zE#qr8PEyMV0wSqtD>U4+O?`2t7Hp=qLlHN3(MstJbhd#Nkxo+%lY=G3dTcO}1k~*i zsptfS11Jm$5Zi>O$6+(UEX+s*u(UTTV|BH6+vUa{LxDrAa-i8{93g~ltX#>#;_a5t zP>#Ixn;x-bqc?2offT@x=FxZEE>qS-G%N{g@Jg08%#Z9W=3o2zx88gIy|2Ij{^w6V zhnO=K0m6>D!?@*h*__i8z|34;KxqavE!Y?jJ4>em)GyPO%5PZ6fLlfm7)@*~5PHaO zknm9iad1*0Vj{g-_`-hY6CoWjr$73@HZ=aslcT8vHiIMxo9cpVCw`}A#%u=_c|};U z((sH-p5i1Kx$c35VBMu*$e1aHyw7-K{V4m;mkm`3uVK+oYnIUtegY0eTrHr7=xITBUNyhe_zL@QI+!Yypf+_&d_{<$PS#G(UOe_19lde13IL zK2(aV;f>Bld=w4yS{_&H<+AqG8M9+`8uA^E$VrX}E5vjYm;SII6f?$nIeptjB!XI(S{73ouOx{bGm!smy%pvT%f&|H+y4iAS7JuedBNl2{@ad z(Vs=pr^&?IZ@+zUap@4IqC|-^a&0wTdupgx5k^k0U%g=Z2YvKI4MZUv4gLP~r@V<}vy|d+6yV~yiM~|P* z55sQt*3qwhmmvbL!+$P})7-gnvXGb*y*tE!1ETpbJzU1E>4V{>NMYAXY74;nLTG+b ztDARy)5I9j?XUa&4FVEqXlQd4Bt`|I@~6iMyQ$>v<`tIw&SmAzf1Zc3HzUxwAmi8G z`mc=`m9THeh)~pOpuhV3joF?^=1&^NWZ;4&LCaa~IR#nyPw{|DpnMmLym>Ex@!#2t6w?8X8So3kl*0!A(gS@^!3lsYr5`kyD2Msc zH4w001jCAGC|BAF1Y{cPFkOf?%4;gx2N^Q+#+~$hK#c~O>7$yJU1C>$DfvT_< zTUAJ(0;pktu@ONX!ea1r8yVAmu|+?pq?bcHXj=MA;w4IEONv5k-)CUP2o?Hv)9+M- zSh-o<=JZmqN|1s#X~%_i0X7;UCJf|WB*w=GYz0+ocSM0X9nD?R=$i9&{a?Y zTPrGRCW|3xk<^hwsb}Uh@xTZ!jy;L5nWCH^1u}>nCs4nuS@h}hE#TXnax10}6+mJk zSRBEah=GJ+`(|;CK+g!aKDsYM4sc><8R$?yYwe;>Z6H+wDngJhl>$G3C1-9>w^20U zhsJ<2cuMU?BHht(QH)Ty3=!JY4Jog;v~qx&wYAtM@dGXjaOKa!sKb-|$ztxlft;fb zyJ4L+fF$KKizf&{q+@);+75YAg{&1_a*mF?cZm!M4Wyn|!y=^k8Es%{K}z9N!6+%T zxC-MRS|UfnAV}9~2?yOUoV>c^SJ08@pf6`xx&E-w=Y^7L_U!BG3p%;?Mu#Eu#Bb*& z#Xjz1<^n&+9qj!nfr!e2Q`D}*;9al^GR{b}qM36lOo&)p)+cBTY#A9)gx>_m1be|W zX@x}z5tFrQxjJVCwr-tedZQ|ADlBK6V}Y5wDi7ObHgmq-kZ+M*T9>_~OCd+}RA%IyP7Np9Fzvl1@=vMJR@-2R zbQYx5Vb83(o8S8IgKt0h@WYQk{`OCQ`ZJiJo(_K4&By-GZ=o?;)-W@=$MESSq0|>#N^_)EM2oP3O0+WsRT(?asY-tXz0=ty6CG#bwpa>rLCjLygqv z3xJjt@sP7R*SsQ(49D`VaL#ki&d&k=RVNQ@J}WyT;a-d|vhs!6h)M=Ztl3nHcIlHN z7+d@X_VLClYwDnYdFZ`S(_z2gpKZ>w)V13b;PaA>4tH$p zC8_*dmzS?@wl8jW4lw$@nE;J2U_zxRmM!Yj5{zt;zr4J7_ucpE#;=x3*=@+MuAirc zJg5dXudt0ToGs{zr%%8ZC&$PY*A&Dn>cl`fl^N5o-EIqX9%{w3 zkFRNrhl@M+5&Zu8)Ob(%*sgqG$%_}yx7}%M-??Fw$jv9{T@1XM3f=&0RFyqLN#=RN zZ80hj^Q5`*Q@GNv6LE4Z(o+b^f1F+DWefpLIe?^mAwA+WYYf0!{0+vWl1-aBNdhb= z7kN&ji&5Cd>h5~EZhUPQr~(2GMHPFSF$G44NxT?0;ACP^elV}LtscE0yG>cxfp|8G z3?P=|05Swk1|G+C()X+yAR?AVy`gvQz6cJ7)G@=rZm63lSoHNQ{^mEZ2&H|NHf*Jw z4aBId$WS4(G%?dhlRLRl%PFDZ8TLYfk2DNLiP3PP3$~eYsgHyzKCvarAxodINE-ue z_zW9t0RT?StPOyAVpcporqL{N{=Dey5(oXH1FXj6*VbL`LQNi9Kn{2wg?-9caUCD{s zf{uxG8Q+~Y;}K|WaAXWG;eqTD?Q^W#NLQFmZ`el{mX&&iiZ-A>QlHJ4rt4zJq2945 zEBZw8$_yJ$vAhS2Udmb80zExN|LMe6V#sX~Uk89y zJCEUkgSiSbG$>fXNs*1Q6JIp)H(pWso$r0$PW>Y*#pccYLY8sVC)MaO`3L6fR26f@ zDtx=fY?dXLr0BT6+|$;#C?FitFurNt$`d|FOy$(77(U64UkFliaQhT?jm$|OhUYVR zY+fZpASbm9;XdXcn7mB|>j5Zx^|DE<+a#OK+?QI&&p%4&Hv+14&%?!>pyq`>gbKFi z=Oz|-EKGQFywRD-VC|xvFCIaG0E|tNOl5i98_f#qN}#!$8mJB7gHiAB0)Xyt*!N>U zblwo@b#sNp^GCck>(rVA?cf-w2}usPx-sW4S4I`)3rLedBXa>T8lgXflTH))mHt14*S|=}cm2ri^L(?I#D$^L(Qw`Z*E4*?G`wX0JVce*adA{1VFGDYC^P+IJITZPAawl$X z>vVR92?cF~C)Nm8ppLBj{SEyBg%e?7msYVvM1FM=pP@vpj&S0^G|V@LFbqI|Ojk`v z&}C5L=twB2wvUcwe?a7b$FHF|#ONON=Et`Ul=@if|W3(#!op4Q=0 zdJH(wwsI)bNxT*88DolR$r!&bboifc`2ZQ}*%N3FzLKA8hUVk}lBR-}OI)Y*Cb8Tn zmVPibgEI3IoWWE?K{A%-SFZ4yWY>qf^?@%wTBt?mL^0=s}#p z*5QXP*zv_yVy-$liOcDv&Q18FibJEA8GM1%X&GH{8nq^#aNfaR=zXNY)j=A9oi6O6 zl9>ln3S$=O7H=Wkg zOhGgn#{m5io)7p`-))Ee&CqR$Zc2r0Su3@wzOEn87N_e5Z@XWDcGY4#Sl{(4A80At z<>ejY?b7gQBQ4A60ht`K)u*w}m!-g*H^hno;(Is9Y3Y|&l8{&PVRjtqYC(B7&CZrU z1WRWP!F<;Gr6b5`FacNZ_?piMu;izsb0Vvvd_?uauitmaxYjd}`Q1)lvl_Si0jxKh z&9bVx-3|jwFX}Wwk8IenL)9d3we1=J@d&M&snl}b^+Q|NZ^({Kr%bZZfcG$#+vRBv z&6u=d?KIo0oA=&*`_7#^>hCb3Xm~g2wj=dG-?W$*ai8X=S1(`Qz4tb%2QCV#g`mBB z`QqmKwH17=mJ5G+XNH6I+Gj|#5M#m;FHA?8PQ_f^4ee`=%T>KOzhny6x;$q@0+MGh zUf%4xs=eABtJQHJPC7mjTSK2PXpo61v!Px6TK-+nHawLR9O%VL!d^NR^I&UqjHf%u z+S;{9XgGkFyAbnp>kh5!x~b|lk&V7p2h>)K#^h4e#By{P0?}X^v8Q8l!%a0itBpQ* zp?xsoEL@x4Y(q>nc^_ z%>v0U=>m~$ngb`pU}0*g%S$cjk;vx1hLI4czBu9|sIXxp#e+E&wy4dJty6jXh6}&C zvk(Usvm%bz!T`Oi_d?6|20%%KFZSszLS=jaWHQz$G=bBkUqyizk%2sgUY$13J`ftB zry)4TJ@=`S0-U`lKLAXfiXavF;kV41@|W(kAcl4-bCQOzLo3KKqEiA%^jn_R2*0r? zFXIeC5=zwJlO}LQeuzYl?_xp-g6Ev&R~WDy!?~4nDUbFr?foIH5QSrdS8^a(LpSCX z`em-?$O49AMJ*XUx{t>>d9#xGF>euJHZe3tWk14(XGc|8hjf`cd7#kzVOkRcE~04Dvz8J9xDuZoeh;E6Ep*wxtZI|*Yca6lDNhuCuAB%jed z#!qE>nG9G-%$F#)>y+wW=z>D;(qz9}h&zG)_4S+m&FgNzCG2oYZQ`rZKJldR4!@XM z2QEQ{LC)1r~*2;Hk(Qh#iEgxsFF= z#xZrUl-FY3h$vjZD#-B6hY$!TQJmzfJi@_|`MEe&F5ZJO*)GaP5^s5DLp&gcFiMMh z{3SUfu954Aa=8NONq`bwie*e!bBE-{L|N&TOqJQ8s)cW3Df-Y5o#`@XGUpmdpr)@= zOwQnX)xJN$HPMf*80OkWLr%Vt`;?N&p!p0^2A85`7{d5r2RqmBojI80Ft9O%Xa>&s zDL_h@R1&y}ze#`|VSF4RPXv#x$N>CvlKAXMP2)Mt2Zbn~PYm5eGA|G$!=M!5<<+ZM z>18rt)m+e`z%W%;)B=ZY?{{5kxF{xbEqWnnpvfqWUY6_idX*hPEL-nY3cjR<~JVL6D1AdBPzAW+UZ$UjCK07-*TQp}FAqXI9 z(THbn>w`a~oRTx$#GoIG)u;H#c|PINNQ3(ni%0-UC0Ovo|8k~^3S&-R!NM}<7|@YF zO}F?ZlBraiSucN~Lk;-Md>-eL0;S;Wy@6nn$J}saz1Er5Fy@=Jyyn4yZmH=SE7%ZK z=SV^eVg8liLy#fT=rSg_&Y4h9Cavgn{s-NX5+_L@4%?ucQ;a1#?p`f_d( z98oyypo=L9CRWCe$|5{E{c&hF$u2Z}=eARQ0}jmlwXw=3QBV6TvnU!)630aY69H^_ z%Q#Ip-rU9{L^f192P)4Va}8WAvN_|@_o63G2ZxE(tix+;870`KTrN*TKN~?~H(zb& z!ZZlVM+TVv*zJ#p9;jTQaG_=E@OM03zkXqF+0M__hNtcZCh>kWD^WNsw}LEJ$YCNm zpN+7A-ue6QzRhe0qi~jDeADIPqPUZ89nT6< z{OP&#r%xW=y?Y< zUU+fbwTWRLRM1C4it!Ep^l%zTz(I*|mVWeWn`4DW+^T8MPz>eNNq0!(JluXcn9X+E zYeLJ=?ICX4T!kLT+5I!(RaWc5nLv1lp=Q9orTCQ0B9RQx6dp*Gx5IKbYO(MLo_51I zs<_b<=8O!eFJqKrEESq|6WHY(6LAF$Sv*9l5TQ6+V^_L!)B~6ar?`$;1x41=Y^E)J z8fsD=W_{4#X%A-!uo=h@-FtEhuQXRyIxE=X^SMG|4CkL!*l`M<0B%Sy5|K69uWO*0 z4+OI`V5i&P^xckmAdOwgCCuq|A_jfx7`am#iexN~l5$pHQ~kvZj7ApA7rQbe!(MCg zD^Q>*Acao>U=tHKWaLFKt=v?_l+%!Fc*JHjF6~#9=@BxHWUAHloob23^vt zuvi<-q(=oCJME;u!U-hd+s}lB{eA}_8>HXhR5BAsPiW)}Ch9VQ+K>}B#DJnun0;ju zE*p9Th3Ur1kSHC|j2YEqp#CYyfybhrch@VNfA~bjvei%Bbl=fZ>hJq05yl)1`;||AjnW23Z}u)iDNs$u|beiYV|n6!}CXf#bMbd5X<%-{3m~+pE>djaa}J# zV13L}Ry7g7@T6ZwSZ66B4^%Fb7;*tkC>_$WY>GQ>b2E_wjYA?D3#;}01L zNe@YCN0edUn-p8(Clp8TOE#|CKcPtdWD`X#Rmw3J z4a+iv2Ik^uZLE(O>DyTDv6owoFa&_w%%`xVULkArIzLcPbJSdS4 znTVm_w(-^e5Jx~En9blJHjvA15z^`EfpTDgfSwO2I%ZzXX?zoU1TbtnZ+Q0GHt7~i z@r=m<(F98Lt+~NzKVnEQJOAZRe(b`43?u{_{oQ0dTY!lS`79f96fF5e0q=b92|IL3 z-%w?)K^=|3n;A}|^B4=pi25bBwSIuAM-@AKU0=DsU!wQflG?%l1^=l95lLI7}s|#ST z>~>SxzPl!!&KG!(1d9m}d5D*})-wk-h<*vo_Bj+!GtBPHy7xz z@l8zn8$yI+aL^Rci&*HMG#c|2$4wZAMAJH_Rvt~9N zSG#UpF*97O#^Z`GPq@_Bj0t{yA-pBg!4+Jd)FZqkdn5y6|IO=H``wo0I8NahtjPob zMy+POUV&z=LN8|hrd`9*v)!p#V!I^M2t-B4ftN3@*}(zME;g5Eo2$#qwn0`cNHsWn zCmxzO>v68<#8qjDcV?r6UYA*$S$R3yvTL4SW_S zgFE^9q9jv`K&4aoFB=MFGwf!j9nqMa%9R5_QZAY&d^sddGG{{^_=(qhWwm#4H}9dL z!NCPim0~rM+UywCh0c_*B0V^-=fCmpqW8-*CRBauY3^6f)BT)4uX&3+{fIuGn>n2} zZS&}+`sM3m<16E|jl!6`<|hM)FOyC98%V$S2U}oI9q`xCfIXvXSh58OgGOxgc_%%ML2tC@f5s(uZiTNmuIgS&VAVQrCM${(RWAHLprx6OE zj4lZ&b`m@(aTM3&1VUi*nCqBtzfl8AZ=fygamWx)Qv_F`)JRl;er!YS|GR(tFC?2y zcE8yELBcy8Ne6JiJOLI2 z=w#d;py>sNz8`Yefr%-+z%$0D1R)nkGjUgQjtdfv5B|zCS z5W_)r0EL(g*u(&AAJM5#8(6L8%fVF~ms({BM3xLp(H~@jAX-+iqZgVvw1>nkFex(I zGeU|ok>Bw$*i#q~U;?n+SDYx#876IYR%h=^#se`toR~ABNE(xIdJ~*Opcn;Z8tdZF zM;UboMuQ!B*pK8uOZhlfFIunH#wOeh$vlpKw^fC1Y8K@NWGtZi6R{YH>7rr+-H zM|Oa}u}|jOkxEaeod9QtL81ms>jD?9jcHj!qU(+Ry%I4D*7M88-4z`4X5X`ZgHkMp>jAdOQg?r_IGZ znznVFugcMN5DhvQ?rL#BHa*yH3}=jCmeq0W>_mP@2Yo0XzrS1~f(qOF{mZkn);M?5 zah@z5n`b;Xsl`a3j{WrB;a@R5r5sH_Ok?%CSxo7CmEU4<{rXwA+j0i0rqH$;voHQ) z0^?e{rBL-XIPB$?rwo3wFyDKdEA+)9wtOJ=B(}|=DIqom9y)P zTv77$@srCtcbJztaHup~WU=4xUOaz7A6Qh!9L{g0fclgH5RPb1zw+q2YD^C3(U$1V zB!_$&A6;BrzC{e9M+c4hY27r}H#ht1=k@x0tnM3cPseTN>+<-OEEZxOoIEC9z<12s zqUhFnoeXO;Fb`y!o3#Wtg$m7a&Sz8II<~ctjA<@q0g#PTeYY=5_!ebV6eb}$kxCs- zqRrK+o`~g}?CH864}be>%Tu?FCrBa@EtJG}(o{-r&k%}ytIBGeumAj!FP(!NeIdN0 zL!OG@!0<=5T2)QA_eHKvy9WG7Y8WI_lx7IpHDgv)tBVpAVLk)4%xeqeQKotXG=Ahk zD*`)70K2@RSpo1)ilm95k>zJPNVP?o9B5FQq2E_XW$KlCm1PMwl0Y-qLCtXxxv>Kh z^YP&0ra^Ud(&ekY#4OwzY|4Un$-n8<;Yi46$tg|MMFNmfMgdlGyJsKoMf4Dduxv}@ z7V)xiAb^p#bgGyI9}hXfKi_;k?6(qS^OnCkdZ$9XGu|O3fudcchxeWlol_igV2FaA zmC=P}j277C2O6TCM^<3i4)SmXoibEK9umKXsVQ}Gds|ulSEuwRMu9>p9Bva>(4wMw zPYOg~kFLp5=wTRiD=>}wC=yj62nE$0!zdoDTyZ7TN*7p|wvok3^9`TDMi4l+N8~2J zQn_qIg>XVemuTq@YNRq$HnO2XpD!)$&s4C31uK;=)5LL<0iJ1#@%XlG7(_MV?NDfP z?10ML!>BC#(6~v-V1U4Y&3PX{F?4C$12Pp47Y!Ry>_4a58jA9jjwsjN40f)d z!d2)iLLmy1REjTz7xqu(F6seaLAsE%S6wVD8*}W522)IrGSw3;INNsv+`F)AASFE| zhj1+?8W3~Dx-|fKb;54Yrwc+eyV#G1nn6M^brQ1O=nTTBmFPiVXa@n}yeI}g@ovDv zb2JuxFu6bEF@v}o1<Tl+NM@SHe4t3B8au5x637y7O2S==5;*u zuHli9*cX4kf!ha;ZTjNrlNZmQa~4800WDa~f3|%3ar=i{`TWXekYg;l|BVlkMMnwP zNL{G0(Py6EP{2TIi6=Cu=+cS-K-G8$7-DJ=27=h38Ob7^hr_LJ%@P!XiU5j*aBR+&T}+ z{pbJ1e=TS_i1P$KDMi-VVgpkZfif{cA3n{Qz$>_qLbc2iU=dQH>RjS=@-8cSs1%dd zCF&qYOt%wE8Ist}(s^~XmhWta|7q^ThMd$74N`b&VR9HSH38aEi8^Z1Lm0MinlVv1 z4hL`a^H%H?gO4i_(6f`@(+b7t z{dq_)m)d>xFxqc?;1P~i9pW#m^Octl2-jYm5)$p%3$#8E$h^9vuwicoe(U%}4fFNp z{Nmz*{8^lHfZuAPoF$0 ziSp~4o-~C{=^7gi9Wx24LBfa&a~N9!o%>`2%Zj-xg`4xY2tFplrf5F#GVX8QymCbsggAckgsgpRejBO9hy`JsN$~6V^Mf$X|vSGdv}qRD8cbY8MP6#!5~7Ub%jM0k`#axg z>RI3UJp^hXChJrH@^Vk-nS4eg>Mo)=>>d{9fAPq4O3gSm5?Dl#Jig=N3`2xNXJVyF zv0-kZA>cTHR1hz|BPf9g@JlCj;G`u+D7!&`!!Y0|OcOpdLB$D%NQvi!OXQ;5F7q5z zS`{Yw2S8@tDUzl$0vCoDFufrXve+)95yaY>G~5DuIw3bB{+Pe%=rX0<%8KL)gVyqK z_QEj3J(V;w?SaSynAYK=fSKWwlQA+`Xu9q|n2d#JmZ<`@v7f?#tpW`q z!W+GLI!P=Iu|4Z2nL&v#<9QuO|pIpWvZf-!(GhB4H)6g_`rTubzOJ; z>KRb9>+`eAJ75cP-hh`*Q8cE@j+2=Qqp? zWDnYiP9t~O;ds>YV%Dlq4Y@XkLg)3$Cu$G@?ZiEenbEfhIl!ZSsl>oghsl${hyh71 zL|KNRh7ZX%gp}C|I3L1^IXo0B%TgG}_72k=7G%7y$!vhGLYFQ1xs)s+O|TmPJmty; zn}(_=#JikDXWFOu!ii#Nyfg_R8h|P3V35fPvco060KY7`H)#4yfc3rp&IYp%%AjJd z?+vMPCNU#Ey-FwQq9$BZPIv6BNFQ^C0w}sWU$!gLtkLz!yh|9k*%%0D3sb&7%^tm} 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в@OAO7g-o8NhGi%^1& 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 JR3o4l@IS?81YiIF