diff --git a/OptiScaler/dllmain.cpp b/OptiScaler/dllmain.cpp index 0a5aba18..8f812fdd 100644 --- a/OptiScaler/dllmain.cpp +++ b/OptiScaler/dllmain.cpp @@ -1584,6 +1584,8 @@ void CheckMemoryForProxies() XeFGProxy::InitXeFG(); XeLLProxy::InitXeLL(); + XellHooks::Hook(); + NVNGXProxy::InitNVNGX(); } diff --git a/OptiScaler/hooks/FG_Hooks.cpp b/OptiScaler/hooks/FG_Hooks.cpp index b5bf938b..5d53e706 100644 --- a/OptiScaler/hooks/FG_Hooks.cpp +++ b/OptiScaler/hooks/FG_Hooks.cpp @@ -1175,7 +1175,7 @@ HRESULT FGHooks::FGPresent(IDXGISwapChain* This, UINT SyncInterval, UINT Flags, Hudfix_Dx12::PresentEnd(); if (willPresent && !State::Instance().reflexLimitsFps && State::Instance().activeFgOutput != FGOutput::NoFG && - !IdentifyGpu::getPrimaryGpu().usesDxvk) + !IdentifyGpu::getPrimaryGpu().usesDxvk && !XellHooks::canLimit()) { FrameLimit::sleep(fg != nullptr ? fg->IsActive() && !fg->IsPaused() : false); } diff --git a/OptiScaler/hooks/Xell_Hooks.cpp b/OptiScaler/hooks/Xell_Hooks.cpp index 52de73cb..d407a2c0 100644 --- a/OptiScaler/hooks/Xell_Hooks.cpp +++ b/OptiScaler/hooks/Xell_Hooks.cpp @@ -52,12 +52,44 @@ void XellHooks::blockExternalContexts(bool state) { blockExternal = state; } // Use context to determine blocking, otherwise just block calls from exe bool XellHooks::shouldBlock(xell_context_handle_t context) { + if (context && ourContext != context && gamesContext != context) + gamesContext = context; + + if (!blockExternal) + return false; + if (context) - return blockExternal && ourContext != context; + return ourContext != context; else return GetModuleHandle(nullptr) == Util::GetCallerModule(_ReturnAddress()); } +bool XellHooks::canLimit() { return gamesContextCanLimitFps; } + +// only DX12 +bool XellHooks::update() +{ + if (!o_xellGetSleepMode || !o_xellSetSleepMode || blockExternal || gamesContext == nullptr) + return false; + + xell_sleep_params_t currentParams; + o_xellGetSleepMode(gamesContext, ¤tParams); + + if (!currentParams.bLowLatencyMode) + return false; + + gamesContextCanLimitFps = true; + + float fpsLimit = Config::Instance()->FramerateLimit.value_or_default(); + + if (fpsLimit < 0.0f) + fpsLimit = 0.0f; + + currentParams.minimumIntervalUs = static_cast(std::round(1'000'000 / fpsLimit)); + + return o_xellSetSleepMode(gamesContext, ¤tParams) == XELL_RESULT_SUCCESS; +} + xell_result_t XellHooks::hkxellDestroyContext(xell_context_handle_t context) { if (shouldBlock(context)) diff --git a/OptiScaler/hooks/Xell_Hooks.h b/OptiScaler/hooks/Xell_Hooks.h index 43003841..1ef50255 100644 --- a/OptiScaler/hooks/Xell_Hooks.h +++ b/OptiScaler/hooks/Xell_Hooks.h @@ -21,6 +21,8 @@ typedef decltype(&xellD3D12CreateContext) PFN_xellD3D12CreateContext; class XellHooks { inline static xell_context_handle_t ourContext = nullptr; + inline static xell_context_handle_t gamesContext = nullptr; + inline static bool gamesContextCanLimitFps = false; inline static bool blockExternal = false; inline static PFN_xellDestroyContext o_xellDestroyContext = nullptr; @@ -39,6 +41,9 @@ class XellHooks static void blockExternalContexts(bool state); static bool shouldBlock(xell_context_handle_t context); + static bool canLimit(); + static bool update(); + static xell_result_t hkxellDestroyContext(xell_context_handle_t context); static xell_result_t hkxellSetSleepMode(xell_context_handle_t context, const xell_sleep_params_t* param); static xell_result_t hkxellGetSleepMode(xell_context_handle_t context, xell_sleep_params_t* param); diff --git a/OptiScaler/menu/menu_common.cpp b/OptiScaler/menu/menu_common.cpp index c587b425..6449cbf1 100644 --- a/OptiScaler/menu/menu_common.cpp +++ b/OptiScaler/menu/menu_common.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #define MARK_ALL_BACKENDS_CHANGED() \ for (auto& singleChangeBackend : State::Instance().changeBackend) \ @@ -4621,7 +4622,10 @@ bool MenuCommon::RenderMenu() } else { - currentMethod = "Fallback"; + if (XellHooks::canLimit()) + currentMethod = "Game's XeLL"; + else + currentMethod = "Fallback"; } if (state.rtssReflexInjection) diff --git a/OptiScaler/wrapped/wrapped_swapchain.cpp b/OptiScaler/wrapped/wrapped_swapchain.cpp index 0762ec5c..40a351dc 100644 --- a/OptiScaler/wrapped/wrapped_swapchain.cpp +++ b/OptiScaler/wrapped/wrapped_swapchain.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #pragma intrinsic(_ReturnAddress) @@ -123,6 +124,8 @@ static HRESULT LocalPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT else ReflexHooks::update(false, false); + XellHooks::update(); + // Upscaler GPU time computation if (willPresent && (fg == nullptr || !fg->IsActive() || fg->IsPaused())) { @@ -470,7 +473,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDXGISwapChain4::Present(UINT SyncInterval, UIN // When Reflex can't be used to limit, sleep in present if (!State::Instance().reflexLimitsFps && State::Instance().activeFgOutput == FGOutput::NoFG && - !IdentifyGpu::getPrimaryGpu().usesDxvk) + !IdentifyGpu::getPrimaryGpu().usesDxvk && !XellHooks::canLimit()) FrameLimit::sleep(false); } else @@ -760,7 +763,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDXGISwapChain4::Present1(UINT SyncInterval, UI // When Reflex can't be used to limit, sleep in present if (!State::Instance().reflexLimitsFps && State::Instance().activeFgOutput == FGOutput::NoFG && - !IdentifyGpu::getPrimaryGpu().usesDxvk) + !IdentifyGpu::getPrimaryGpu().usesDxvk && !XellHooks::canLimit()) FrameLimit::sleep(false); } else