diff --git a/Assets/Resources/FSR2/ffx_fsr2_tcr_autogen_pass.compute b/Assets/Resources/FSR2/ffx_fsr2_tcr_autogen_pass.compute new file mode 100644 index 0000000..9c71006 --- /dev/null +++ b/Assets/Resources/FSR2/ffx_fsr2_tcr_autogen_pass.compute @@ -0,0 +1,33 @@ +// 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_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 + +#include "ffx_fsr2_unity_common.cginc" + +#include "shaders/ffx_fsr2_tcr_autogen_pass.hlsl" diff --git a/Assets/Resources/FSR2/ffx_fsr2_tcr_autogen_pass.compute.meta b/Assets/Resources/FSR2/ffx_fsr2_tcr_autogen_pass.compute.meta new file mode 100644 index 0000000..0002090 --- /dev/null +++ b/Assets/Resources/FSR2/ffx_fsr2_tcr_autogen_pass.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b478fba0a6a87b44b8be7c35deb5f0dc +ComputeShaderImporter: + externalObjects: {} + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen.h b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen.h new file mode 100644 index 0000000..101b75d --- /dev/null +++ b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen.h @@ -0,0 +1,250 @@ +// 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 < getTcThreshold()) ? 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/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen.h.meta b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen.h.meta new file mode 100644 index 0000000..c135d50 --- /dev/null +++ b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen.h.meta @@ -0,0 +1,27 @@ +fileFormatVersion: 2 +guid: 18b8590c99b171a4e9af68dfd2c3ff02 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen_pass.hlsl b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen_pass.hlsl new file mode 100644 index 0000000..8e635d1 --- /dev/null +++ b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen_pass.hlsl @@ -0,0 +1,114 @@ +// 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 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 "ffx_fsr2_callbacks_hlsl.h" +#include "ffx_fsr2_common.h" + +#if defined(FSR2_BIND_CB_AUTOREACTIVE) + cbuffer cbGenerateReactive : FFX_FSR2_DECLARE_CB(FSR2_BIND_CB_AUTOREACTIVE) + { + float fTcThreshold; // 0.1 is a good starting value, lower will result in more TC pixels + float fTcScale; + float fReactiveScale; + float fReactiveMax; + }; + float getTcThreshold() + { + return fTcThreshold; + } +#else + #define fTcThreshold 0.05f + #define fTcScale 1.00f + #define fReactiveScale 10.0f + #define fReactiveMax 0.90f + float getTcThreshold() + { + return fTcThreshold; + } +#endif + +#include "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_FSR2_NUM_THREADS +FFX_FSR2_EMBED_ROOTSIG_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(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/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta new file mode 100644 index 0000000..d133259 --- /dev/null +++ b/Assets/Resources/FSR2/shaders/ffx_fsr2_tcr_autogen_pass.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9feb1fa4d6cff5a4799298dc69b12a8e +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 8c9d5e8..a77b13b 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -329,6 +329,12 @@ MonoBehaviour: cutoffThreshold: 0.2 binaryValue: 0.9 flags: 13 + autoGenerateTransparencyAndComposition: 0 + generateTransparencyAndCompositionParameters: + autoTcThreshold: 0.05 + autoTcScale: 1 + autoReactiveScale: 5 + autoReactiveMax: 0.9 outputMotionVectors: {fileID: 0} --- !u!1001 &1313173313 PrefabInstance: diff --git a/Assets/Scripts/Core/Fsr2.cs b/Assets/Scripts/Core/Fsr2.cs index ffa6d27..772cfc3 100644 --- a/Assets/Scripts/Core/Fsr2.cs +++ b/Assets/Scripts/Core/Fsr2.cs @@ -182,6 +182,14 @@ namespace FidelityFX public float CameraFar; public float CameraFovAngleVertical; public float ViewSpaceToMetersFactor; + + // EXPERIMENTAL reactive mask generation parameters + public bool EnableAutoReactive; + public RenderTargetIdentifier? ColorOpaqueOnly; + public float AutoTcThreshold = 0.05f; + public float AutoTcScale = 1.0f; + public float AutoReactiveScale = 5.0f; + public float AutoReactiveMax = 0.9f; } /// @@ -252,6 +260,15 @@ namespace FidelityFX 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 diff --git a/Assets/Scripts/Core/Fsr2Context.cs b/Assets/Scripts/Core/Fsr2Context.cs index 4b5ce0e..890285d 100644 --- a/Assets/Scripts/Core/Fsr2Context.cs +++ b/Assets/Scripts/Core/Fsr2Context.cs @@ -47,6 +47,7 @@ namespace FidelityFX private Fsr2Pipeline _rcasPipeline; private Fsr2Pipeline _computeLuminancePyramidPipeline; private Fsr2Pipeline _generateReactivePipeline; + private Fsr2Pipeline _tcrAutogeneratePipeline; private readonly Fsr2Resources _resources = new Fsr2Resources(); @@ -66,6 +67,10 @@ namespace FidelityFX 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; @@ -79,6 +84,7 @@ namespace FidelityFX _spdConstantsBuffer = CreateConstantBuffer(); _rcasConstantsBuffer = CreateConstantBuffer(); _generateReactiveConstantsBuffer = CreateConstantBuffer(); + _tcrAutogenerateConstantsBuffer = CreateConstantBuffer(); // Set defaults _firstExecution = true; @@ -100,10 +106,12 @@ namespace FidelityFX _accumulateSharpenPipeline = new Fsr2AccumulateSharpenPipeline(_contextDescription, _resources, _fsr2ConstantsBuffer); _rcasPipeline = new Fsr2RcasPipeline(_contextDescription, _resources, _fsr2ConstantsBuffer, _rcasConstantsBuffer); _generateReactivePipeline = new Fsr2GenerateReactivePipeline(_contextDescription, _resources, _generateReactiveConstantsBuffer); + _tcrAutogeneratePipeline = new Fsr2TcrAutogeneratePipeline(_contextDescription, _resources, _fsr2ConstantsBuffer, _tcrAutogenerateConstantsBuffer); } public void Destroy() { + DestroyPipeline(ref _tcrAutogeneratePipeline); DestroyPipeline(ref _generateReactivePipeline); DestroyPipeline(ref _computeLuminancePyramidPipeline); DestroyPipeline(ref _rcasPipeline); @@ -115,6 +123,7 @@ namespace FidelityFX _resources.Destroy(); + DestroyConstantBuffer(ref _tcrAutogenerateConstantsBuffer); DestroyConstantBuffer(ref _generateReactiveConstantsBuffer); DestroyConstantBuffer(ref _rcasConstantsBuffer); DestroyConstantBuffer(ref _spdConstantsBuffer); @@ -155,6 +164,21 @@ namespace FidelityFX dispatchParams.Exposure = _resources.AutoExposure; else if (dispatchParams.Exposure == null) dispatchParams.Exposure = _resources.DefaultExposure; + + if (dispatchParams.EnableAutoReactive) + { + // Create the auto-TCR resources only when we need them + if (_resources.AutoReactive == null) + _resources.CreateTcrAutogenResources(_contextDescription); + + if (resetAccumulation) + commandBuffer.Blit(_resources.PrevPreAlpha[frameIndex ^ 1], dispatchParams.ColorOpaqueOnly ?? Fsr2ShaderIDs.SrvOpaqueOnly); + } + else if (_resources.AutoReactive != null) + { + // Destroy the auto-TCR resources if we don't use the feature + _resources.DestroyTcrAutogenResources(); + } if (dispatchParams.Reactive == null) dispatchParams.Reactive = _resources.DefaultReactive; if (dispatchParams.TransparencyAndComposition == null) dispatchParams.TransparencyAndComposition = _resources.DefaultReactive; @@ -192,6 +216,14 @@ namespace FidelityFX // Initialize constant buffers data _fsr2ConstantsBuffer.SetData(_fsr2ConstantsArray); _spdConstantsBuffer.SetData(_spdConstantsArray); + + // Auto reactive + if (dispatchParams.EnableAutoReactive) + { + GenerateTransparencyCompositionReactive(dispatchParams, commandBuffer, frameIndex); + dispatchParams.Reactive = _resources.AutoReactive; + dispatchParams.TransparencyAndComposition = _resources.AutoComposition; + } // Compute luminance pyramid _computeLuminancePyramidPipeline.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchThreadGroupCount.x, dispatchThreadGroupCount.y); @@ -251,6 +283,21 @@ namespace FidelityFX ((Fsr2GenerateReactivePipeline)_generateReactivePipeline).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; + _tcrAutogenerateConstantsBuffer.SetData(_tcrAutogenerateConstantsArray); + + _tcrAutogeneratePipeline.ScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchSrcX, dispatchSrcY); + } + private void SetupConstants(Fsr2.DispatchDescription dispatchParams, bool resetAccumulation) { ref Fsr2.Fsr2Constants constants = ref Constants; diff --git a/Assets/Scripts/Core/Fsr2Pipeline.cs b/Assets/Scripts/Core/Fsr2Pipeline.cs index f6b9686..6e3d083 100644 --- a/Assets/Scripts/Core/Fsr2Pipeline.cs +++ b/Assets/Scripts/Core/Fsr2Pipeline.cs @@ -404,4 +404,48 @@ namespace FidelityFX commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); } } + + internal class Fsr2TcrAutogeneratePipeline : Fsr2Pipeline + { + private readonly ComputeBuffer _tcrAutogenerateConstants; + + public Fsr2TcrAutogeneratePipeline(Fsr2.ContextDescription contextDescription, Fsr2Resources resources, ComputeBuffer constants, ComputeBuffer tcrAutogenerateConstants) + : base(contextDescription, resources, constants) + { + _tcrAutogenerateConstants = tcrAutogenerateConstants; + + LoadComputeShader("FSR2/ffx_fsr2_tcr_autogen_pass"); + } + + public override void ScheduleDispatch(CommandBuffer commandBuffer, Fsr2.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + if (dispatchParams.ColorOpaqueOnly.HasValue) + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvOpaqueOnly, dispatchParams.ColorOpaqueOnly.Value, 0, RenderTextureSubElement.Color); + + if (dispatchParams.Color.HasValue) + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputColor, dispatchParams.Color.Value, 0, RenderTextureSubElement.Color); + + if (dispatchParams.MotionVectors.HasValue) + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvInputMotionVectors, dispatchParams.MotionVectors.Value); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevColorPreAlpha, Resources.PrevPreAlpha[frameIndex ^ 1]); + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvPrevColorPostAlpha, Resources.PrevPostAlpha[frameIndex ^ 1]); + + if (dispatchParams.Reactive.HasValue) + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvReactiveMask, dispatchParams.Reactive.Value); + + if (dispatchParams.TransparencyAndComposition.HasValue) + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, Fsr2ShaderIDs.SrvTransparencyAndCompositionMask, dispatchParams.TransparencyAndComposition.Value); + + 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/Fsr2Resources.cs b/Assets/Scripts/Core/Fsr2Resources.cs index 62ea55d..2ef9e79 100644 --- a/Assets/Scripts/Core/Fsr2Resources.cs +++ b/Assets/Scripts/Core/Fsr2Resources.cs @@ -36,11 +36,15 @@ namespace FidelityFX public Texture2D MaximumBiasLut; 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 @@ -104,6 +108,23 @@ namespace FidelityFX 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); + } + private static void CreateDoubleBufferedResource(RenderTexture[] resource, string name, Vector2Int size, GraphicsFormat format) { for (int i = 0; i < 2; ++i) @@ -115,6 +136,8 @@ namespace FidelityFX public void Destroy() { + DestroyTcrAutogenResources(); + DestroyResource(LumaHistory); DestroyResource(InternalUpscaled); DestroyResource(LockStatus); @@ -126,6 +149,14 @@ namespace FidelityFX 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) { diff --git a/Assets/Scripts/Core/Fsr2ShaderIDs.cs b/Assets/Scripts/Core/Fsr2ShaderIDs.cs index db9624b..b07c967 100644 --- a/Assets/Scripts/Core/Fsr2ShaderIDs.cs +++ b/Assets/Scripts/Core/Fsr2ShaderIDs.cs @@ -47,6 +47,8 @@ namespace FidelityFX 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"); @@ -65,6 +67,9 @@ namespace FidelityFX 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 CbFsr2 = Shader.PropertyToID("cbFSR2"); diff --git a/Assets/Scripts/Fsr2ImageEffect.cs b/Assets/Scripts/Fsr2ImageEffect.cs index dd85783..7ee9ca3 100644 --- a/Assets/Scripts/Fsr2ImageEffect.cs +++ b/Assets/Scripts/Fsr2ImageEffect.cs @@ -75,6 +75,25 @@ namespace FidelityFX public Fsr2.GenerateReactiveFlags flags = Fsr2.GenerateReactiveFlags.ApplyTonemap | Fsr2.GenerateReactiveFlags.ApplyThreshold | Fsr2.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.")] + [SerializeField] private GenerateTcrParameters generateTransparencyAndCompositionParameters = new GenerateTcrParameters(); + public GenerateTcrParameters GenerateTcrParams => generateTransparencyAndCompositionParameters; + + [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; + } + [Header("Output resources")] [Tooltip("Optional render texture to copy motion vector data to, for additional post-processing after upscaling.")] public RenderTexture outputMotionVectors; @@ -97,6 +116,7 @@ namespace FidelityFX private Fsr2.QualityMode _prevQualityMode; private Vector2Int _prevDisplaySize; private bool _prevGenReactiveMask; + private bool _prevGenTcrMasks; private CommandBuffer _dispatchCommandBuffer; private CommandBuffer _opaqueInputCommandBuffer; @@ -155,7 +175,7 @@ namespace FidelityFX _opaqueInputCommandBuffer.GetTemporaryRT(Fsr2ShaderIDs.SrvOpaqueOnly, _renderSize.x, _renderSize.y, 0, default, GetDefaultFormat()); _opaqueInputCommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, Fsr2ShaderIDs.SrvOpaqueOnly); - if (autoGenerateReactiveMask) + if (autoGenerateReactiveMask || autoGenerateTransparencyAndComposition) { _renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); } @@ -165,6 +185,7 @@ namespace FidelityFX _prevDisplaySize = _displaySize; _prevQualityMode = qualityMode; _prevGenReactiveMask = autoGenerateReactiveMask; + _prevGenTcrMasks = autoGenerateTransparencyAndComposition; } private void OnDisable() @@ -216,14 +237,15 @@ namespace FidelityFX OnEnable(); } - if (autoGenerateReactiveMask != _prevGenReactiveMask) + if ((autoGenerateReactiveMask || autoGenerateTransparencyAndComposition) != (_prevGenReactiveMask || _prevGenTcrMasks)) { - if (autoGenerateReactiveMask) + if (autoGenerateReactiveMask || autoGenerateTransparencyAndComposition) _renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); else _renderCamera.RemoveCommandBuffer(CameraEvent.BeforeForwardAlpha, _opaqueInputCommandBuffer); _prevGenReactiveMask = autoGenerateReactiveMask; + _prevGenTcrMasks = autoGenerateTransparencyAndComposition; } } @@ -288,6 +310,15 @@ namespace FidelityFX _dispatchDescription.Reset = _reset; _reset = false; + _dispatchDescription.EnableAutoReactive = autoGenerateTransparencyAndComposition; + if (autoGenerateTransparencyAndComposition) + { + _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 FSR2 expects this when using inverted depth @@ -359,6 +390,11 @@ namespace FidelityFX _dispatchCommandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.UavAutoReactive); } + if (autoGenerateTransparencyAndComposition) + { + _dispatchCommandBuffer.ReleaseTemporaryRT(Fsr2ShaderIDs.SrvOpaqueOnly); + } + Graphics.ExecuteCommandBuffer(_dispatchCommandBuffer); // Shut up the Unity warning about not writing to the destination texture