From 36f66e41599c4dc0628c7ed58d4ac167963c4380 Mon Sep 17 00:00:00 2001 From: cdozdil Date: Wed, 1 Apr 2026 02:15:26 +0300 Subject: [PATCH 1/2] Added FGInput -> DLSSG -> Nukem --- OptiScaler/Config.cpp | 5 ++++ OptiScaler/NVNGX_Parameter.h | 9 +++--- OptiScaler/Util.cpp | 8 +++-- OptiScaler/framegen/dlssg/DLSSG_Dx12.h | 6 ++++ OptiScaler/hooks/D3D12_Hooks.cpp | 6 +++- OptiScaler/hooks/Dxgi_Hooks.cpp | 9 ++++-- OptiScaler/hooks/FG_Hooks.cpp | 33 +++++++++++++------- OptiScaler/hooks/Reflex_Hooks.cpp | 8 +++-- OptiScaler/inputs/FG/DLSSG_Mod.h | 5 +++- OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp | 38 +++++++++++++++++------- OptiScaler/menu/menu_common.cpp | 23 ++++++++++---- OptiScaler/proxies/Streamline_Proxy.h | 15 ++++++++-- OptiScaler/wrapped/wrapped_swapchain.cpp | 18 +++++++++-- 13 files changed, 136 insertions(+), 47 deletions(-) diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp index eb7962b1..fd58272d 100644 --- a/OptiScaler/Config.cpp +++ b/OptiScaler/Config.cpp @@ -97,6 +97,8 @@ bool Config::Reload(std::filesystem::path iniPath) FGOutput.set_from_config(FGOutput::FSRFG); else if (lstrcmpiA(FGOutputString.value().c_str(), "nukems") == 0) FGOutput.set_from_config(FGOutput::Nukems); + else if (lstrcmpiA(FGOutputString.value().c_str(), "dlssgwithnukems") == 0) + FGOutput.set_from_config(FGOutput::DLSSGWithNukems); else if (lstrcmpiA(FGOutputString.value().c_str(), "xefg") == 0) FGOutput.set_from_config(FGOutput::XeFG); else if (lstrcmpiA(FGOutputString.value().c_str(), "dlssg") == 0) @@ -632,6 +634,7 @@ bool Config::Reload(std::filesystem::path iniPath) if (!SpoofHAGS.has_value()) { SpoofHAGS.set_volatile_value(FGInput.value_or_default() == FGInput::Nukems || + FGOutput.value_or_default() == FGOutput::DLSSGWithNukems || FGInput.value_or_default() == FGInput::DLSSG); } } @@ -826,6 +829,8 @@ bool Config::SaveIni() FGOutputString = "FSRFG"; else if (FGOutputHeld.value() == FGOutput::Nukems) FGOutputString = "Nukems"; + else if (FGOutputHeld.value() == FGOutput::DLSSGWithNukems) + FGOutputString = "DLSSGWithNukems"; else if (FGOutputHeld.value() == FGOutput::XeFG) FGOutputString = "XeFG"; else if (FGOutputHeld.value() == FGOutput::DLSSG) diff --git a/OptiScaler/NVNGX_Parameter.h b/OptiScaler/NVNGX_Parameter.h index 5155db0e..4312a999 100644 --- a/OptiScaler/NVNGX_Parameter.h +++ b/OptiScaler/NVNGX_Parameter.h @@ -522,7 +522,8 @@ inline static void InitNGXParameters(NVSDK_NGX_Parameter* InParams) } // not ideal as it doesn't take different APIs into account - if (State::Instance().activeFgInput == FGInput::Nukems || State::Instance().activeFgInput == FGInput::DLSSG) + if (State::Instance().activeFgInput == FGInput::Nukems || State::Instance().activeFgInput == FGInput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) { InParams->Set("FrameGeneration.Available", 1); InParams->Set("FrameGeneration.NeedsUpdatedDriver", 0); @@ -531,10 +532,8 @@ inline static void InitNGXParameters(NVSDK_NGX_Parameter* InParams) InParams->Set(NVSDK_NGX_Parameter_FrameInterpolation_NeedsUpdatedDriver, 0); InParams->Set(NVSDK_NGX_Parameter_FrameInterpolation_FeatureInitResult, 1); - feature_version moreCopeVersion { 2, 11, 0 }; - auto maxCopeCount = State::Instance().streamlineVersion >= moreCopeVersion ? 5 : 3; - - InParams->Set("DLSSG.MultiFrameCountMax", State::Instance().NukemsMFG ? maxCopeCount : 1); + // Streamline handle the max interpolated frame count + InParams->Set("DLSSG.MultiFrameCountMax", State::Instance().NukemsMFG ? 5 : 1); if (State::Instance().NVNGX_Engine == NVSDK_NGX_ENGINE_TYPE_UNREAL || State::Instance().gameQuirks & GameQuirk::ForceUnrealEngine) diff --git a/OptiScaler/Util.cpp b/OptiScaler/Util.cpp index 48b7a133..f687c1bc 100644 --- a/OptiScaler/Util.cpp +++ b/OptiScaler/Util.cpp @@ -381,7 +381,9 @@ std::optional Util::FindFilePath(const std::filesystem::p { auto normalizedPath = entry.path().lexically_normal().wstring(); to_lower_in_place(normalizedPath); - if (State::Instance().activeFgOutput == FGOutput::DLSSG || !normalizedPath.contains(L"\\opti_dlls")) + if (State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems || + !normalizedPath.contains(L"\\opti_dlls")) { LOG_INFO(L"{} found at {}", fileName.wstring(), entry.path().parent_path().wstring()); return entry.path(); @@ -410,7 +412,9 @@ std::optional Util::FindFilePath(const std::filesystem::p { auto normalizedPath = entry.path().lexically_normal().wstring(); to_lower_in_place(normalizedPath); - if (State::Instance().activeFgOutput == FGOutput::DLSSG || !normalizedPath.contains(L"\\opti_dlls")) + if (State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems || + !normalizedPath.contains(L"\\opti_dlls")) { LOG_INFO(L"{} found at {}", fileName.wstring(), entry.path().parent_path().wstring()); return entry.path(); diff --git a/OptiScaler/framegen/dlssg/DLSSG_Dx12.h b/OptiScaler/framegen/dlssg/DLSSG_Dx12.h index d182bf29..b3da273c 100644 --- a/OptiScaler/framegen/dlssg/DLSSG_Dx12.h +++ b/OptiScaler/framegen/dlssg/DLSSG_Dx12.h @@ -59,6 +59,12 @@ class DLSSG_Dx12 : public virtual IFGFeature_Dx12 { if (StreamlineProxy::Module() == nullptr) StreamlineProxy::LoadStreamline(); + + if (StreamlineProxy::Module() != nullptr && !StreamlineProxy::IsD3D12Inited() && + State::Instance().currentD3D12Device != nullptr) + { + StreamlineProxy::InitWithD3D12(State::Instance().currentD3D12Device); + } } ~DLSSG_Dx12(); diff --git a/OptiScaler/hooks/D3D12_Hooks.cpp b/OptiScaler/hooks/D3D12_Hooks.cpp index df5ba647..b9410173 100644 --- a/OptiScaler/hooks/D3D12_Hooks.cpp +++ b/OptiScaler/hooks/D3D12_Hooks.cpp @@ -1112,8 +1112,12 @@ static void HookToDevice(ID3D12Device* InDevice) if (State::Instance().activeFgInput == FGInput::Upscaler && !Config::Instance()->FGDisableHUDFix.value_or_default()) ResTrack_Dx12::HookDevice(InDevice); - if (State::Instance().activeFgOutput == FGOutput::DLSSG && StreamlineProxy::LoadStreamline()) + if ((State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && + StreamlineProxy::LoadStreamline()) + { StreamlineProxy::InitWithD3D12(InDevice); + } } static void UnhookDevice() diff --git a/OptiScaler/hooks/Dxgi_Hooks.cpp b/OptiScaler/hooks/Dxgi_Hooks.cpp index 565dc4df..c3eb70f2 100644 --- a/OptiScaler/hooks/Dxgi_Hooks.cpp +++ b/OptiScaler/hooks/Dxgi_Hooks.cpp @@ -127,7 +127,8 @@ inline static HRESULT hkCreateDXGIFactory(REFIID riid, IDXGIFactory** ppFactory) { IDXGIFactory* real = nullptr; - if (State::Instance().activeFgOutput != FGOutput::DLSSG && + if ((State::Instance().activeFgOutput != FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && Util::CheckForRealObject(__FUNCTION__, *ppFactory, (IUnknown**) &real)) { *ppFactory = real; @@ -185,7 +186,8 @@ inline static HRESULT hkCreateDXGIFactory1(REFIID riid, IDXGIFactory1** ppFactor { IDXGIFactory1* real = nullptr; - if (State::Instance().activeFgOutput != FGOutput::DLSSG && + if ((State::Instance().activeFgOutput != FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && Util::CheckForRealObject(__FUNCTION__, *ppFactory, (IUnknown**) &real)) { *ppFactory = real; @@ -245,7 +247,8 @@ inline static HRESULT hkCreateDXGIFactory2(UINT Flags, REFIID riid, IDXGIFactory { IDXGIFactory2* real = nullptr; - if (State::Instance().activeFgOutput != FGOutput::DLSSG && + if ((State::Instance().activeFgOutput != FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && Util::CheckForRealObject(__FUNCTION__, *ppFactory, (IUnknown**) &real)) { *ppFactory = real; diff --git a/OptiScaler/hooks/FG_Hooks.cpp b/OptiScaler/hooks/FG_Hooks.cpp index 1e930d3a..e5084b78 100644 --- a/OptiScaler/hooks/FG_Hooks.cpp +++ b/OptiScaler/hooks/FG_Hooks.cpp @@ -58,7 +58,9 @@ static bool CheckForFGStatus() Config::Instance()->FGOutput.set_volatile_value(FGOutput::NoFG); State::Instance().activeFgOutput = Config::Instance()->FGOutput.value_or_default(); } - else if (State::Instance().activeFgOutput == FGOutput::DLSSG && !StreamlineProxy::LoadStreamline()) + else if ((State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && + !StreamlineProxy::LoadStreamline()) { LOG_DEBUG("Can't init StreamlineProxy, disabling FGOutput"); Config::Instance()->FGOutput.set_volatile_value(FGOutput::NoFG); @@ -66,7 +68,8 @@ static bool CheckForFGStatus() } if (State::Instance().activeFgOutput != FGOutput::FSRFG && State::Instance().activeFgOutput != FGOutput::XeFG && - State::Instance().activeFgOutput != FGOutput::DLSSG) + State::Instance().activeFgOutput != FGOutput::DLSSG && + State::Instance().activeFgOutput != FGOutput::DLSSGWithNukems) { LOG_WARN("FGOutput is not set to FSR-FG or XeFG"); return false; @@ -105,7 +108,8 @@ HRESULT FGHooks::CreateSwapChain(IDXGIFactory* pFactory, IUnknown* pDevice, DXGI { State::Instance().currentFG = new XeFG_Dx12(); } - else if (State::Instance().activeFgOutput == FGOutput::DLSSG) + else if (State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) { State::Instance().currentFG = new DLSSG_Dx12(); } @@ -232,7 +236,8 @@ HRESULT FGHooks::CreateSwapChainForHwnd(IDXGIFactory* pFactory, IUnknown* pDevic { State::Instance().currentFG = new XeFG_Dx12(); } - else if (State::Instance().activeFgOutput == FGOutput::DLSSG) + else if (State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) { State::Instance().currentFG = new DLSSG_Dx12(); } @@ -1063,17 +1068,21 @@ HRESULT FGHooks::FGPresent(IDXGISwapChain* This, UINT SyncInterval, UINT Flags, sl::FrameToken* localToken = nullptr; sl::Result tokenResult = sl::Result::eErrorReflexAPI; - if (willPresent && State::Instance().activeFgOutput == FGOutput::DLSSG && fg->IsActive() && !fg->IsPaused()) + if (willPresent && (State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems)) { ((IDXGISwapChain4*) This)->GetCurrentBackBufferIndex(); - const uint32_t frameId = (uint32_t) State::Instance().currentFG->FrameCount(); - tokenResult = StreamlineProxy::GetNewFrameToken()(localToken, &frameId); - - if (tokenResult == sl::Result::eOk && !ReflexHooks::gameIsSendingMarkers() || + if (!ReflexHooks::gameIsSendingMarkers() || !Config::Instance()->FGDLSSGUseGamesReflexMarkers.value_or_default()) { - StreamlineProxy::PCLSetMarker()(sl::PCLMarker::ePresentStart, *localToken); + const uint32_t frameId = (uint32_t) State::Instance().currentFG->FrameCount(); + tokenResult = StreamlineProxy::GetNewFrameToken()(localToken, &frameId); + + if (tokenResult == sl::Result::eOk) + { + StreamlineProxy::PCLSetMarker()(sl::PCLMarker::ePresentStart, *localToken); + } } } @@ -1150,7 +1159,9 @@ HRESULT FGHooks::FGPresent(IDXGISwapChain* This, UINT SyncInterval, UINT Flags, if (tokenResult == sl::Result::eOk && localToken != nullptr && (!ReflexHooks::gameIsSendingMarkers() || !Config::Instance()->FGDLSSGUseGamesReflexMarkers.value_or_default()) && - willPresent && State::Instance().activeFgOutput == FGOutput::DLSSG && fg->IsActive() && !fg->IsPaused()) + willPresent && + (State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems)) { StreamlineProxy::PCLSetMarker()(sl::PCLMarker::ePresentEnd, *localToken); StreamlineProxy::ReflexSleep()(*localToken); diff --git a/OptiScaler/hooks/Reflex_Hooks.cpp b/OptiScaler/hooks/Reflex_Hooks.cpp index fd74c794..db68ae24 100644 --- a/OptiScaler/hooks/Reflex_Hooks.cpp +++ b/OptiScaler/hooks/Reflex_Hooks.cpp @@ -46,7 +46,8 @@ NvAPI_Status ReflexHooks::hkNvAPI_D3D_Sleep(IUnknown* pDev) #endif static bool skip = false; - if (State::Instance().activeFgOutput == FGOutput::DLSSG && StreamlineProxy::IsD3D12Inited() && + if ((State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && Config::Instance()->FGDLSSGUseGamesReflexMarkers.value_or_default() && State::Instance().currentFG->IsActive() && !State::Instance().currentFG->IsPaused()) { @@ -124,8 +125,9 @@ NvAPI_Status ReflexHooks::hkNvAPI_D3D_SetLatencyMarker(IUnknown* pDev, if (pSetLatencyMarkerParams->markerType == SIMULATION_START) _lastMarkerFrame = State::Instance().FGLastFrame; - if (State::Instance().activeFgOutput == FGOutput::DLSSG && StreamlineProxy::IsD3D12Inited() && - Config::Instance()->FGDLSSGUseGamesReflexMarkers.value_or_default() && + if ((State::Instance().activeFgOutput == FGOutput::DLSSG || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && + StreamlineProxy::IsD3D12Inited() && Config::Instance()->FGDLSSGUseGamesReflexMarkers.value_or_default() && State::Instance().currentFG->IsActive() && !State::Instance().currentFG->IsPaused()) { sl::PCLMarker marker {}; diff --git a/OptiScaler/inputs/FG/DLSSG_Mod.h b/OptiScaler/inputs/FG/DLSSG_Mod.h index 53c8ffcd..f7bdfa6b 100644 --- a/OptiScaler/inputs/FG/DLSSG_Mod.h +++ b/OptiScaler/inputs/FG/DLSSG_Mod.h @@ -134,8 +134,11 @@ class DLSSGMod { LOG_FUNC(); - if (_dx12_inited || Config::Instance()->FGInput.value_or_default() != FGInput::Nukems) + if (_dx12_inited || (Config::Instance()->FGInput.value_or_default() != FGInput::Nukems && + Config::Instance()->FGOutput.value_or_default() != FGOutput::DLSSGWithNukems)) + { return; + } _dll = TryInitMFG(); diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp index 04a824f5..5fbb2810 100644 --- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp +++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp @@ -92,7 +92,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_Ext(unsigned long long InApp return NVSDK_NGX_Result_Success; } - if (State::Instance().activeFgInput == FGInput::Nukems) + if (State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) { DLSSGMod::InitDLSSGMod_Dx12(); DLSSGMod::D3D12_Init_Ext(InApplicationId, InApplicationDataPath, InDevice, InSDKVersion, InFeatureInfo); @@ -298,8 +299,11 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Shutdown(void) shutdown = false; - if (State::Instance().activeFgInput == FGInput::Nukems) + if (State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) + { DLSSGMod::D3D12_Shutdown(); + } State::Instance().NvngxDx12Inited = false; @@ -311,8 +315,11 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Shutdown1(ID3D12Device* InDevice) shutdown = true; State::Instance().NvngxDx12Inited = false; - if (State::Instance().activeFgInput == FGInput::Nukems) + if (State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) + { DLSSGMod::D3D12_Shutdown1(InDevice); + } // Added `&& !State::Instance().isShuttingDown` hack for crash on exit if (Config::Instance()->DLSSEnabled.value_or_default() && NVNGXProxy::IsDx12Inited() && @@ -454,8 +461,11 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_PopulateParameters_Impl(NVSDK_NGX InitNGXParameters(InParameters); - if (State::Instance().activeFgInput == FGInput::Nukems) + if (State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) + { DLSSGMod::D3D12_PopulateParameters_Impl(InParameters); + } return NVSDK_NGX_Result_Success; } @@ -647,8 +657,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_CreateFeature(ID3D12GraphicsComma const Config& cfg = *Config::Instance(); // Nukem's DLSSG mod passthrough - if (state.activeFgInput == FGInput::Nukems && DLSSGMod::isDx12Available() && - InFeatureID == NVSDK_NGX_Feature_FrameGeneration) + if ((state.activeFgInput == FGInput::Nukems || state.activeFgOutput == FGOutput::DLSSGWithNukems) && + DLSSGMod::isDx12Available() && InFeatureID == NVSDK_NGX_Feature_FrameGeneration) { LOG_INFO("Passthrough to Nukem's DLSSG CreateFeature for FrameGeneration"); @@ -732,7 +742,9 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_ReleaseFeature(NVSDK_NGX_Handle* } } // Clean up OptiScaler feature with framegen - else if (State::Instance().activeFgInput == FGInput::Nukems && handleId >= DLSSG_MOD_ID_OFFSET) + else if ((State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && + handleId >= DLSSG_MOD_ID_OFFSET) { LOG_INFO("D3D12_ReleaseFeature modded DLSSG with HandleId: {0}", handleId); return DLSSGMod::D3D12_ReleaseFeature(InHandle); @@ -775,12 +787,14 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_GetFeatureRequirements( { LOG_DEBUG("for ({0})", (int) FeatureDiscoveryInfo->FeatureID); - if (State::Instance().activeFgInput == FGInput::Nukems) + if (State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) DLSSGMod::InitDLSSGMod_Dx12(); if (FeatureDiscoveryInfo->FeatureID == NVSDK_NGX_Feature_SuperSampling || (FeatureDiscoveryInfo->FeatureID == NVSDK_NGX_Feature_FrameGeneration && - ((DLSSGMod::isDx12Available() && Config::Instance()->FGInput == FGInput::Nukems) || + ((DLSSGMod::isDx12Available() && (Config::Instance()->FGInput == FGInput::Nukems || + Config::Instance()->FGOutput == FGOutput::DLSSGWithNukems)) || Config::Instance()->FGInput == FGInput::DLSSG))) { if (OutSupported == nullptr) @@ -992,7 +1006,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom } // Nukem's DLSSG mod passthrough - if (state.activeFgInput == FGInput::Nukems && handleId >= DLSSG_MOD_ID_OFFSET) + if ((state.activeFgInput == FGInput::Nukems || state.activeFgOutput == FGOutput::DLSSGWithNukems) && + handleId >= DLSSG_MOD_ID_OFFSET) { LOG_DEBUG("Passthrough to Nukem's DLSSG EvaluateFeature for handle {}", handleId); return DLSSGMod::D3D12_EvaluateFeature(InCmdList, InFeatureHandle, InParameters, InCallback); @@ -1013,7 +1028,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_GetScratchBufferSize(NVSDK_NGX_Fe if (OutSizeInBytes == nullptr) return NVSDK_NGX_Result_FAIL_InvalidParameter; - if (State::Instance().activeFgInput == FGInput::Nukems && DLSSGMod::isDx12Available() && + if ((State::Instance().activeFgInput == FGInput::Nukems || + State::Instance().activeFgOutput == FGOutput::DLSSGWithNukems) && InFeatureId == NVSDK_NGX_Feature_FrameGeneration) { return DLSSGMod::D3D12_GetScratchBufferSize(InFeatureId, InParameters, OutSizeInBytes); diff --git a/OptiScaler/menu/menu_common.cpp b/OptiScaler/menu/menu_common.cpp index 15b5d96d..ca8457b3 100644 --- a/OptiScaler/menu/menu_common.cpp +++ b/OptiScaler/menu/menu_common.cpp @@ -2975,7 +2975,8 @@ bool MenuCommon::RenderMenu() { FGOutput::Nukems, "FSR3-FG via Nukem's", "Enable DLSS-FG in-game" }, { FGOutput::FSRFG, "FSR FG", "FSR3/4 FG" }, { FGOutput::DLSSG, "DLSSG", "For 40xx and above" }, - { FGOutput::XeFG, "XeFG", "XeFG" } + { FGOutput::XeFG, "XeFG", "XeFG" }, + { FGOutput::DLSSGWithNukems, "DLSSG with Nukem's", "DLSSG with Nukem's" } }; // clang-format on @@ -2999,6 +3000,15 @@ bool MenuCommon::RenderMenu() inputOptions[nukemsInputIndex].set_disabled(state.swapchainApi == API::DX11, "Unsupported API"); outputOptions[nukemsOutputIndex].set_disabled(state.swapchainApi == API::DX11, "Unsupported API"); + auto constexpr dlssgWithNukemsOutputIndex = (uint32_t) FGOutput::DLSSGWithNukems; + if (!state.NukemsFilesAvailable) + { + outputOptions[dlssgWithNukemsOutputIndex].set_disabled( + true, "Missing the dlssg_to_fsr3_amd_is_better.dll file"); + } + outputOptions[dlssgWithNukemsOutputIndex].set_disabled(state.swapchainApi != API::DX12, + "Unsupported API"); + // FSR FG output requirements auto constexpr fsrfgOutputIndex = (uint32_t) FGOutput::FSRFG; outputOptions[fsrfgOutputIndex].set_disabled(state.swapchainApi != API::DX12, "Unsupported API"); @@ -3125,7 +3135,8 @@ bool MenuCommon::RenderMenu() auto fgOutput = reinterpret_cast(state.currentFG); if (((state.activeFgOutput == FGOutput::FSRFG || state.activeFgOutput == FGOutput::XeFG || - state.activeFgOutput == FGOutput::DLSSG) && + state.activeFgOutput == FGOutput::DLSSG || + state.activeFgOutput == FGOutput::DLSSGWithNukems) && state.activeFgInput != FGInput::NoFG && state.activeFgInput != FGInput::Nukems) && fgOutput) { @@ -3765,8 +3776,8 @@ bool MenuCommon::RenderMenu() } // DLSSG controls - if (state.activeFgOutput == FGOutput::DLSSG && state.activeFgInput != FGInput::NoFG && - state.currentFGSwapchain != nullptr) + if ((state.activeFgOutput == FGOutput::DLSSG || state.activeFgOutput == FGOutput::DLSSGWithNukems) && + state.activeFgInput != FGInput::NoFG && state.currentFGSwapchain != nullptr) { if (StreamlineProxy::LoadStreamline() && currentFeature != nullptr && !currentFeature->IsFrozen()) { @@ -3837,7 +3848,9 @@ bool MenuCommon::RenderMenu() if (currentFeature != nullptr && !currentFeature->IsFrozen() && ((state.activeFgOutput == FGOutput::FSRFG && FfxApiProxy::IsFGReady()) || (state.activeFgOutput == FGOutput::XeFG && XeFGProxy::Module() != nullptr) || - (state.activeFgOutput == FGOutput::DLSSG && StreamlineProxy::Module() != nullptr))) + ((state.activeFgOutput == FGOutput::DLSSG || + state.activeFgOutput == FGOutput::DLSSGWithNukems) && + StreamlineProxy::Module() != nullptr))) { if (!Config::Instance()->FGDisableHUDFix.value_or_default()) { diff --git a/OptiScaler/proxies/Streamline_Proxy.h b/OptiScaler/proxies/Streamline_Proxy.h index 93434ab2..2112b2ac 100644 --- a/OptiScaler/proxies/Streamline_Proxy.h +++ b/OptiScaler/proxies/Streamline_Proxy.h @@ -69,10 +69,15 @@ class StreamlineProxy return true; auto owner = State::GetOwner(); - State::DisableChecks(owner); + if (State::Instance().activeFgOutput != FGOutput::DLSSGWithNukems) + State::DisableChecks(owner); + else + State::DisableChecks(owner, "sl."); + std::filesystem::path slPath = Util::DllPath().parent_path() / L"opti_dlls\\streamline\\sl.interposer.dll"; LOG_INFO(L"Trying to load sl.interposer.dll from dll path: {}", slPath.wstring()); _dll = NtdllProxy::LoadLibraryExW_Ldr(slPath.c_str(), NULL, NULL); + State::EnableChecks(owner); if (_dll != nullptr) @@ -332,8 +337,14 @@ class StreamlineProxy pref.numPathsToPlugins = (uint32_t) paths.size(); auto owner = State::GetOwner(); - State::DisableChecks(owner); + + if (State::Instance().activeFgOutput != FGOutput::DLSSGWithNukems) + State::DisableChecks(owner); + else + State::DisableChecks(owner, "sl."); + auto initResult = StreamlineProxy::Init()(pref, sl::kSDKVersion); + State::EnableChecks(owner); if (initResult == sl::Result::eOk) diff --git a/OptiScaler/wrapped/wrapped_swapchain.cpp b/OptiScaler/wrapped/wrapped_swapchain.cpp index 9ece8fc0..b9804370 100644 --- a/OptiScaler/wrapped/wrapped_swapchain.cpp +++ b/OptiScaler/wrapped/wrapped_swapchain.cpp @@ -500,8 +500,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDXGISwapChain4::SetFullscreenState(BOOL Fullsc #ifdef USE_LOCAL_MUTEX // dlssg calls this from present it seems // don't try to get a mutex when present owns it while dlssg mod is enabled - if (!(_localMutex.getOwner() == 4 && Config::Instance()->FGInput.value_or_default() == FGInput::Nukems)) + if (!(_localMutex.getOwner() == 4 && + (Config::Instance()->FGInput.value_or_default() == FGInput::Nukems || + Config::Instance()->FGOutput.value_or_default() == FGOutput::DLSSGWithNukems))) + { OwnedLockGuard lock(_localMutex, 3); + } #endif if (Config::Instance()->FGUseMutexForSwapchain.value_or_default()) { @@ -554,8 +558,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDXGISwapChain4::ResizeBuffers(UINT BufferCount #ifdef USE_LOCAL_MUTEX // dlssg calls this from present it seems // don't try to get a mutex when present owns it while dlssg mod is enabled - if (!(_localMutex.getOwner() == 4 && Config::Instance()->FGInput.value_or_default() == FGInput::Nukems)) + if (!(_localMutex.getOwner() == 4 && + (Config::Instance()->FGInput.value_or_default() == FGInput::Nukems || + Config::Instance()->FGOutput.value_or_default() == FGOutput::DLSSGWithNukems))) + { OwnedLockGuard lock(_localMutex, 1); + } #endif if (State::Instance().currentFG != nullptr && Config::Instance()->FGUseMutexForSwapchain.value_or_default()) @@ -863,8 +871,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDXGISwapChain4::ResizeBuffers1(UINT BufferCoun #ifdef USE_LOCAL_MUTEX // dlssg calls this from present it seems // don't try to get a mutex when present owns it while dlssg mod is enabled - if (!(_localMutex.getOwner() == 4 && Config::Instance()->FGInput.value_or_default() == FGInput::Nukems)) + if (!(_localMutex.getOwner() == 4 && + (Config::Instance()->FGInput.value_or_default() == FGInput::Nukems || + Config::Instance()->FGOutput.value_or_default() == FGOutput::DLSSGWithNukems))) + { OwnedLockGuard lock(_localMutex, 2); + } #endif if (State::Instance().activeFgOutput == FGOutput::FSRFG && From 18dd28742f229c092020599f2879e946e142406f Mon Sep 17 00:00:00 2001 From: cdozdil Date: Wed, 1 Apr 2026 02:15:53 +0300 Subject: [PATCH 2/2] Fixed DisableChecks logic --- OptiScaler/State.h | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/OptiScaler/State.h b/OptiScaler/State.h index 1e5efd69..cb27df39 100644 --- a/OptiScaler/State.h +++ b/OptiScaler/State.h @@ -43,7 +43,8 @@ enum class FGOutput : uint32_t Nukems, FSRFG, DLSSG, - XeFG + XeFG, + DLSSGWithNukems, }; enum class WorkingMode : uint32_t @@ -307,26 +308,32 @@ class State /// Lower case dll name without `.dll` at the end. Leave blank for skipping all dll's static void DisableChecks(UINT owner, std::string dllName = "") { - if (_skipOwner == 0) - { - _skipOwner = owner; - _skipChecks = true; - _skipDllName = dllName; - } - else - { - _skipDllName = ""; // Hack for multiple skip calls - } + // if (_skipOwner == 0 || _skipOwner < owner) + //{ + _skipOwner = owner; + _skipChecks = true; + _skipDllName[_skipOwner] = dllName; + //} }; static void EnableChecks(UINT owner) { - if (_skipOwner == 0 || _skipOwner == owner) + _skipDllName.erase(_skipOwner); + + // if (_skipOwner == owner) + //{ + if (_skipDllName.size() > 0) + { + // loop in reverse to get the last added owner + _skipOwner = _skipDllName.rbegin()->first; + } + else { - _skipChecks = false; - _skipDllName = ""; _skipOwner = 0; } + + _skipChecks = (_skipOwner != 0); + //} }; static void DisableServeOriginal(UINT owner) @@ -348,12 +355,15 @@ class State }; static bool SkipDllChecks() { return _skipChecks; } - static std::string SkipDllName() { return _skipDllName; } + static std::string SkipDllName() + { + return _skipOwner == 0 ? "" : (_skipDllName.contains(_skipOwner) ? _skipDllName[_skipOwner] : ""); + } static bool ServeOriginal() { return _serveOriginal; } private: inline static bool _skipChecks = false; - inline static std::string _skipDllName = ""; + inline static std::map _skipDllName; inline static UINT _skipOwner = 0; inline static UINT _lastOwner = 0;