From f174ab5014c1c31b8e3597e9fa5b7767be24a0ad Mon Sep 17 00:00:00 2001 From: 117il3 <864826684@qq.com> Date: Sat, 12 Sep 2026 01:49:01 +0800 Subject: [PATCH] feat(DLSSNR): add layerCount parameter, allow stacking up to 10 layers --- src/Effects/DLSSNR/DLSSNR_AI_Filter.hlsl | 9 ++ src/Magpie.Core/DLSSNRFilter.cpp | 161 ++++++++++++++------- src/Magpie.Core/DLSSNRFilter.h | 2 + src/Magpie/Resources.language-en-US.resw | 1 + src/Magpie/Resources.language-zh-Hans.resw | 1 + src/Magpie/Resources.language-zh-Hant.resw | 1 + 6 files changed, 125 insertions(+), 50 deletions(-) diff --git a/src/Effects/DLSSNR/DLSSNR_AI_Filter.hlsl b/src/Effects/DLSSNR/DLSSNR_AI_Filter.hlsl index 60ca961b6..7bd956b3e 100644 --- a/src/Effects/DLSSNR/DLSSNR_AI_Filter.hlsl +++ b/src/Effects/DLSSNR/DLSSNR_AI_Filter.hlsl @@ -141,6 +141,15 @@ float localStructureStrength; //!STEP 0.05 float skinStructureStrength; +//!PARAMETER +//!GROUP DLSSNR +//!LABEL Layer Count +//!DEFAULT 1 +//!MIN 1 +//!MAX 10 +//!STEP 1 +int layerCount; + //!PARAMETER //!GROUP DLSSNR //!LABEL Automatic Mask diff --git a/src/Magpie.Core/DLSSNRFilter.cpp b/src/Magpie.Core/DLSSNRFilter.cpp index 272bc7043..52ba3516a 100644 --- a/src/Magpie.Core/DLSSNRFilter.cpp +++ b/src/Magpie.Core/DLSSNRFilter.cpp @@ -45,6 +45,8 @@ DLSSNRSettings ParseDLSSNRSettings(const EffectOption& option, bool hdrEnabled) "localStructureStrength", 1.0f, 0.0f, 2.0f), .skinStructureStrength = getClamped( "skinStructureStrength", 0.0f, 0.0f, 2.0f), + .layerCount = std::clamp(static_cast(std::lround( + getParameter("layerCount", 1.0f))), 1, 10), .useAutoMask = getParameter("useAutoMask", 0.0f) >= 0.5f, .uiCorrection = getParameter("uiCorrection", 0.0f) >= 0.5f, .motionRequest = ParseDlssOpticalFlowRequest(option), @@ -605,9 +607,11 @@ struct DLSSNRFilter::Impl { uint32_t nextCommandSlot = 0; winrt::com_ptr sharedInput11; winrt::com_ptr sharedOutput11; + winrt::com_ptr sharedPing11; winrt::com_ptr inputSrv11; winrt::com_ptr sharedInputSrv11; winrt::com_ptr sharedOutputSrv11; + winrt::com_ptr sharedPingSrv11; winrt::com_ptr sharedInputUav11; winrt::com_ptr colorConvertShader11; winrt::com_ptr colorDownsampleVerticalShader11; @@ -639,6 +643,7 @@ struct DLSSNRFilter::Impl { winrt::com_ptr compositeOutputUav11; winrt::com_ptr sharedInput12; winrt::com_ptr sharedOutput12; + winrt::com_ptr sharedPing12; std::unique_ptr guidanceInterop; winrt::com_ptr fence11; winrt::com_ptr fence12; @@ -1429,10 +1434,12 @@ static void SetEvaluateParametersUnsafe( DLSSNRFilter::Impl& impl, const DLSSNRSettings& settings, const FrameGuidanceView& guidance, - bool guidanceReset + bool reset, + ID3D12Resource* color, + ID3D12Resource* output ) { - impl.parameters->Set(PARAM_COLOR, impl.sharedInput12.get()); - impl.parameters->Set(PARAM_OUTPUT, impl.sharedOutput12.get()); + impl.parameters->Set(PARAM_COLOR, color); + impl.parameters->Set(PARAM_OUTPUT, output); impl.parameters->Set(PARAM_MVEC, impl.guidanceInterop->Motion()); impl.parameters->Set(PARAM_DEPTH, impl.guidanceInterop->Depth()); const FrameGuidanceRegion full = FrameGuidanceRegion::Full( @@ -1449,8 +1456,7 @@ static void SetEvaluateParametersUnsafe( impl.parameters->Set(PARAM_INDICATOR_INVERT_X, 0); impl.parameters->Set(PARAM_INDICATOR_INVERT_Y, 0); impl.parameters->Set(PARAM_ENABLED, 1); - impl.parameters->Set( - PARAM_RESET, impl.resetHistory || guidanceReset ? 1 : 0); + impl.parameters->Set(PARAM_RESET, reset ? 1 : 0); impl.parameters->Set(PARAM_STYLE, settings.style); impl.parameters->Set(PARAM_INTENSITY, settings.intensity); impl.parameters->Set(PARAM_LOCAL_TONE, settings.localToneStrength); @@ -1464,11 +1470,14 @@ static bool SetEvaluateParametersSafely( DLSSNRFilter::Impl& impl, const DLSSNRSettings& settings, const FrameGuidanceView& guidance, - bool guidanceReset, + bool reset, + ID3D12Resource* color, + ID3D12Resource* output, DWORD* sehCode ) noexcept { return NgxRuntimeGuard::Invoke([&]() { - SetEvaluateParametersUnsafe(impl, settings, guidance, guidanceReset); + SetEvaluateParametersUnsafe( + impl, settings, guidance, reset, color, output); return true; }, false, sehCode); } @@ -1781,6 +1790,7 @@ EffectParameterApplyMode DLSSNRFilter::GetParameterApplyMode( parameterName == "localToneStrength" || parameterName == "localStructureStrength" || parameterName == "skinStructureStrength" || + parameterName == "layerCount" || parameterName == "useAutoMask" || parameterName == "uiCorrection") { return EffectParameterApplyMode::Live; } @@ -1844,6 +1854,7 @@ bool DLSSNRFilter::ApplyLiveParameters( _settings.localToneStrength = candidate.localToneStrength; _settings.localStructureStrength = candidate.localStructureStrength; _settings.skinStructureStrength = candidate.skinStructureStrength; + _settings.layerCount = candidate.layerCount; _settings.useAutoMask = candidate.useAutoMask; _settings.uiCorrection = candidate.uiCorrection; _settings.residualMultiplier = candidate.residualMultiplier; @@ -1886,6 +1897,7 @@ bool DLSSNRFilter::Initialize( _settings.localToneStrength, 0.0f, 2.0f, 1.0f); _settings.localStructureStrength = ClampFinite( _settings.localStructureStrength, 0.0f, 2.0f, 1.0f); + _settings.layerCount = std::clamp(_settings.layerCount, 1, 10); _ngxCore = &ngxCore; _impl.reset(); FrameGuidancePerformance::ResetDlssnrGpuTiming(); @@ -1967,7 +1979,17 @@ bool DLSSNRFilter::Initialize( if (!CreateSharedTexture(*impl, sharedDesc, true, impl->sharedInput11, impl->sharedInput12) || !CreateSharedTexture(*impl, sharedDesc, true, - impl->sharedOutput11, impl->sharedOutput12)) { + impl->sharedOutput11, impl->sharedOutput12) || + !CreateSharedTexture(*impl, sharedDesc, true, + impl->sharedPing11, impl->sharedPing12)) { + return false; + } + // Chained layers ping-pong between the output and ping buffers, so the + // ping buffer always needs an SRV for the final residual composite. + hr = impl->device11->CreateShaderResourceView( + impl->sharedPing11.get(), nullptr, impl->sharedPingSrv11.put()); + if (FAILED(hr)) { + Logger::Get().ComError("Create DLSSNR ping SRV failed", hr); return false; } if (impl->useResolutionScaling) { @@ -2141,6 +2163,7 @@ bool DLSSNRFilter::Initialize( "residualSaturation={} residualLightness={} shadowStructureMultiplier={} " "reflectionGlowMultiplier={} preset=fixed-0 " "style={} intensity={} localTone={} localStructure={} skinStructure={} " + "layerCount={} " "opticalFlowMethod={} opticalFlowQuality={} autoMask={} uiCorrection={} depth=zero-contract disabled=false " "experimentalHdrPath={} experimentalHdrScale={}", ENABLE_CORE_FEATURE18_DIAGNOSTIC ? "core-diagnostic" : "signed-snippet", @@ -2153,6 +2176,7 @@ bool DLSSNRFilter::Initialize( _settings.style, _settings.intensity, _settings.localToneStrength, _settings.localStructureStrength, _settings.skinStructureStrength, + _settings.layerCount, static_cast(_settings.motionRequest.method), static_cast(_settings.motionRequest.quality), _settings.useAutoMask, _settings.uiCorrection, @@ -2191,6 +2215,11 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { Impl& impl = *_impl; ID3D11Texture2D* input = context.input; ID3D11Texture2D* output = context.output; + // Layers alternate destinations starting at sharedOutput, so an even layer + // count leaves the final result in the ping buffer. + const uint32_t layerCount = static_cast( + std::clamp(_settings.layerCount, 1, 10)); + const bool finalLayerIsPing = (layerCount % 2) == 0; if (impl.lastEvaluatedFrameId == context.frameId && impl.lastEvaluatedParameterRevision == impl.evaluateParameterRevision && impl.lastEvaluatedInputRevision == context.inputRevision) { @@ -2198,7 +2227,8 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { const bool composited = CompositeResidual( impl, output, impl.disabled ? impl.sharedInputSrv11.get() : - impl.sharedOutputSrv11.get(), + (finalLayerIsPing ? impl.sharedPingSrv11.get() : + impl.sharedOutputSrv11.get()), _settings); if (composited) { impl.residualParametersDirty = false; @@ -2296,33 +2326,34 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { } ID3D12GraphicsCommandList* commandList = commandSlot.commandList.get(); - D3D12_RESOURCE_BARRIER barriers[2]{}; - barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barriers[0].Transition = { - impl.sharedInput12.get(), D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - D3D12_RESOURCE_STATE_COMMON, - D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE - }; - barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barriers[1].Transition = { - impl.sharedOutput12.get(), D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - D3D12_RESOURCE_STATE_COMMON, - D3D12_RESOURCE_STATE_UNORDERED_ACCESS + // NGX requires the color input readable as a shader resource and the output + // writable as a UAV. Chained layers alternate both, so each layer boundary + // re-transitions the resources; the whole chain returns to COMMON before + // D3D11 regains ownership. + D3D12_RESOURCE_STATES inputState12 = D3D12_RESOURCE_STATE_COMMON; + D3D12_RESOURCE_STATES outputState12 = D3D12_RESOURCE_STATE_COMMON; + D3D12_RESOURCE_STATES pingState12 = D3D12_RESOURCE_STATE_COMMON; + const auto transitionLayerResource = + [&](ID3D12Resource* resource, D3D12_RESOURCE_STATES after) { + D3D12_RESOURCE_STATES& before = + resource == impl.sharedInput12.get() ? inputState12 : + resource == impl.sharedOutput12.get() ? outputState12 : pingState12; + if (before == after) return; + D3D12_RESOURCE_BARRIER barrier{}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition = { + resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, before, after + }; + commandList->ResourceBarrier(1, &barrier); + before = after; }; - commandList->ResourceBarrier(ARRAYSIZE(barriers), barriers); impl.guidanceInterop->Transition( commandList, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); const bool guidanceReset = evaluateGuidance->requiresHistoryReset && impl.lastGuidanceResetFrameId != context.frameId; + const bool historyReset = impl.resetHistory || guidanceReset; DWORD sehCode = 0; - if (!SetEvaluateParametersSafely( - impl, _settings, *evaluateGuidance, guidanceReset, &sehCode)) { - commandList->Close(); - Logger::Get().Error(fmt::format( - "DLSSNR evaluation parameter setup raised SEH {:#x}", sehCode)); - return fail("ngx-parameters-seh"); - } if (impl.timestampQueryHeap) { commandList->EndQuery( impl.timestampQueryHeap.get(), D3D12_QUERY_TYPE_TIMESTAMP, @@ -2332,9 +2363,47 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { const Impl::EvaluateFeatureFn evaluateFeature = impl.useSignedSnippet ? impl.snippetEvaluateFeature : static_cast(&NVSDK_NGX_D3D12_EvaluateFeature); - const NVSDK_NGX_Result result = CallEvaluateFeatureSafely( - evaluateFeature, commandList, impl.feature, - impl.parameters, &sehCode); + // Physical chaining: each layer samples the previous layer's output and + // only the first layer may reset history. + NVSDK_NGX_Result result = NVSDK_NGX_Result_Success; + bool evaluateSucceeded = true; + ID3D12Resource* layerColor = impl.sharedInput12.get(); + for (uint32_t layer = 0; layer < layerCount; ++layer) { + ID3D12Resource* const layerOutput = (layer % 2) == 0 ? + impl.sharedOutput12.get() : impl.sharedPing12.get(); + // The transition away from the previous layer's UAV write also orders + // it before this layer's shader read, so no UAV barrier is required. + transitionLayerResource( + layerColor, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + transitionLayerResource( + layerOutput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + if (!SetEvaluateParametersSafely( + impl, _settings, *evaluateGuidance, + layer == 0 && historyReset, layerColor, layerOutput, &sehCode)) { + commandList->Close(); + Logger::Get().Error(fmt::format( + "DLSSNR evaluation parameter setup raised SEH {:#x}", sehCode)); + return fail("ngx-parameters-seh"); + } + result = CallEvaluateFeatureSafely( + evaluateFeature, commandList, impl.feature, + impl.parameters, &sehCode); + ++impl.evaluateCount; + evaluateSucceeded = !sehCode && NGXSucceeded(result); + if (evaluateSucceeded) { + ++impl.evaluateSuccessCount; + } else { + ++impl.evaluateFailureCount; + impl.disabled = true; + if (sehCode) { + Logger::Get().Error(fmt::format( + "DLSSNR EvaluateFeature raised SEH {:#x}", sehCode)); + } + break; + } + if (layer + 1 == layerCount) break; + layerColor = layerOutput; + } const double evaluateCpuMs = NativeBackendTiming::ElapsedMilliseconds(evaluateStart); if (impl.timestampQueryHeap) { commandList->EndQuery( @@ -2345,18 +2414,6 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { commandSlot.timestampQuery, 2, impl.timestampReadback.get(), uint64_t(commandSlot.timestampQuery) * sizeof(uint64_t)); } - ++impl.evaluateCount; - const bool evaluateSucceeded = !sehCode && NGXSucceeded(result); - if (evaluateSucceeded) { - ++impl.evaluateSuccessCount; - } else { - ++impl.evaluateFailureCount; - impl.disabled = true; - if (sehCode) { - Logger::Get().Error(fmt::format( - "DLSSNR EvaluateFeature raised SEH {:#x}", sehCode)); - } - } if (!evaluateSucceeded || impl.evaluateCount == 1 || (NativeBackendTiming::Enabled && (impl.evaluateCount <= 8 || impl.evaluateCount % 120 == 0))) { @@ -2371,13 +2428,15 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { impl.disabled), !evaluateSucceeded); } const auto submitStart = NativeBackendTiming::Now(); - for (D3D12_RESOURCE_BARRIER& barrier : barriers) { - std::swap(barrier.Transition.StateBefore, barrier.Transition.StateAfter); - } + transitionLayerResource( + impl.sharedInput12.get(), D3D12_RESOURCE_STATE_COMMON); + transitionLayerResource( + impl.sharedOutput12.get(), D3D12_RESOURCE_STATE_COMMON); + transitionLayerResource( + impl.sharedPing12.get(), D3D12_RESOURCE_STATE_COMMON); impl.guidanceInterop->Transition( commandList, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COMMON); - commandList->ResourceBarrier(ARRAYSIZE(barriers), barriers); hr = commandList->Close(); if (FAILED(hr)) { return fail("command-list-close"); @@ -2400,14 +2459,16 @@ bool DLSSNRFilter::Draw(const NativeEffectDrawContext& context) noexcept { if (!CompositeResidual( impl, output, impl.disabled ? impl.sharedInputSrv11.get() : - impl.sharedOutputSrv11.get(), + (finalLayerIsPing ? impl.sharedPingSrv11.get() : + impl.sharedOutputSrv11.get()), _settings)) { return fail("residual-composite"); } } else { impl.context11->CopyResource( output, impl.disabled ? impl.sharedInput11.get() : - impl.sharedOutput11.get()); + (finalLayerIsPing ? impl.sharedPing11.get() : + impl.sharedOutput11.get())); } const double submitMs = NativeBackendTiming::ElapsedMilliseconds(submitStart); if constexpr (NativeBackendTiming::Enabled) { diff --git a/src/Magpie.Core/DLSSNRFilter.h b/src/Magpie.Core/DLSSNRFilter.h index de8d60e2e..19602d8d2 100644 --- a/src/Magpie.Core/DLSSNRFilter.h +++ b/src/Magpie.Core/DLSSNRFilter.h @@ -20,6 +20,8 @@ struct DLSSNRSettings { float localToneStrength = 1.0f; float localStructureStrength = 1.0f; float skinStructureStrength = 0.0f; + // Number of chained DLSSNR evaluations per frame. Layer N reads layer N-1. + int layerCount = 1; bool useAutoMask = false; bool uiCorrection = false; MotionVectorRequest motionRequest{}; diff --git a/src/Magpie/Resources.language-en-US.resw b/src/Magpie/Resources.language-en-US.resw index 09c9105c5..821374866 100644 --- a/src/Magpie/Resources.language-en-US.resw +++ b/src/Magpie/Resources.language-en-US.resw @@ -1221,6 +1221,7 @@ (Reduces DLSSNR Quality) Input Resolution (%) NR Intensity + Layer Count Local Structure Strength Local Tone Strength Reflection / Glow Control diff --git a/src/Magpie/Resources.language-zh-Hans.resw b/src/Magpie/Resources.language-zh-Hans.resw index 6ce77c394..73507055e 100644 --- a/src/Magpie/Resources.language-zh-Hans.resw +++ b/src/Magpie/Resources.language-zh-Hans.resw @@ -1221,6 +1221,7 @@ (会降低 DLSSNR 质量) 输入分辨率(%) NR 强度 + 叠加层数 局部结构强度 局部色调强度 反射/辉光控制 diff --git a/src/Magpie/Resources.language-zh-Hant.resw b/src/Magpie/Resources.language-zh-Hant.resw index a6048595c..99b6282de 100644 --- a/src/Magpie/Resources.language-zh-Hant.resw +++ b/src/Magpie/Resources.language-zh-Hant.resw @@ -1188,6 +1188,7 @@ (會降低 DLSSNR 品質) 輸入解析度(%) NR 強度 + 疊加層數 局部結構強度 局部色調強度 反射/輝光控制