From da20361af41b7c5a51bd49ef450710c7e9068b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lewandowski?= <49685661+FakeMichau@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:08:52 +0200 Subject: [PATCH] AnitLag 2 support for Opti's FG (#94) * AnitLag 2 support for Opti's FG * Move the code around --- OptiScaler/NVNGX_Proxy.h | 8 ++- OptiScaler/OptiScaler.vcxproj | 3 +- OptiScaler/OptiScaler.vcxproj.filters | 41 ++++++++++--- OptiScaler/fakenvapi.h | 84 +++++++++++++++++++++++++++ OptiScaler/hooks/HooksDx.cpp | 7 ++- OptiScaler/hooks/HooksDx.h | 1 + OptiScaler/pch.h | 17 ++++++ 7 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 OptiScaler/fakenvapi.h diff --git a/OptiScaler/NVNGX_Proxy.h b/OptiScaler/NVNGX_Proxy.h index 4784ded5..e8689666 100644 --- a/OptiScaler/NVNGX_Proxy.h +++ b/OptiScaler/NVNGX_Proxy.h @@ -7,6 +7,7 @@ #include #include "nvapi/nvapi.h" +#include "fakenvapi.h" #include #include "detours/detours.h" @@ -77,11 +78,12 @@ inline static uint32_t __stdcall HookedNvAPI_GPU_GetArchInfo(void* GPUHandle, NV inline static void* __stdcall HookedNvAPI_QueryInterface(NV_INTERFACE InterfaceId) { + LOG_FUNC(); const auto result = OriginalNvAPI_QueryInterface(InterfaceId); if (result) { - if (InterfaceId == NV_INTERFACE::GPU_GetArchInfo) + if (InterfaceId == NV_INTERFACE::GPU_GetArchInfo && !Config::Instance()->DE_Available) { OriginalNvAPI_GPU_GetArchInfo = static_cast(result); return &HookedNvAPI_GPU_GetArchInfo; @@ -155,6 +157,7 @@ inline static void HookNvApi() if (OriginalNvAPI_QueryInterface != nullptr) { LOG_INFO("NvAPI_QueryInterface found, hooking!"); + fakenvapi::Init((fakenvapi::PFN_Fake_QueryInterface&)OriginalNvAPI_QueryInterface); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); @@ -378,6 +381,8 @@ public: if (_dll != nullptr) return; + HookNvApi(); + LOG_INFO(""); Config::Instance()->upscalerDisableHook = true; @@ -490,7 +495,6 @@ public: { if (!Config::Instance()->DE_Available) { - HookNvApi(); HookNgxApi(_dll); } diff --git a/OptiScaler/OptiScaler.vcxproj b/OptiScaler/OptiScaler.vcxproj index f6f7685b..d7cd1d55 100644 --- a/OptiScaler/OptiScaler.vcxproj +++ b/OptiScaler/OptiScaler.vcxproj @@ -82,7 +82,7 @@ $(SolutionDir)external\vulkan\include;$(SolutionDir)external\nvngx_dlss_sdk;$(SolutionDir)external\xess\inc\xess;$(SolutionDir)external\FidelityFX-SDK\ffx-api\include\ffx_api;$(SolutionDir)external\simpleini;$(SolutionDir)external\unordered_dense\include;$(SolutionDir)external\spdlog\include;$(IncludePath) $(ProjectDir)fsr2\lib;$(ProjectDir)fsr2_212\lib;$(ProjectDir)fsr31\lib;$(ProjectDir)vulkan;$(ProjectDir)d3dx;$(ProjectDir)detours;$(SolutionDir)external\xess\lib;$(LibraryPath) dxgi - D:\Folders\Games\SteamLibrary\steamapps\common\DEATH STRANDING DIRECTORS CUT\ + F:\SteamLibrary\steamapps\common\Cyberpunk 2077\bin\x64\ .\x64\Debug .dll @@ -207,6 +207,7 @@ copy $(SolutionDir)nvngx.ini $(SolutionDir)x64\Release\a\ + diff --git a/OptiScaler/OptiScaler.vcxproj.filters b/OptiScaler/OptiScaler.vcxproj.filters index 95a6a020..ee86cebd 100644 --- a/OptiScaler/OptiScaler.vcxproj.filters +++ b/OptiScaler/OptiScaler.vcxproj.filters @@ -170,9 +170,6 @@ Header Files - - Header Files - Header Files @@ -248,7 +245,31 @@ Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -392,9 +413,6 @@ Source Files - - Source Files - Source Files @@ -449,6 +467,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/OptiScaler/fakenvapi.h b/OptiScaler/fakenvapi.h new file mode 100644 index 00000000..dcd4d018 --- /dev/null +++ b/OptiScaler/fakenvapi.h @@ -0,0 +1,84 @@ +#pragma once + +#include "NVNGX_Proxy.h" +#include "FfxApi_Proxy.h" +#include + +enum class FN_INTERFACE : uint32_t +{ + Fake_InformFGState = 0x21382138, + Fake_InformPresentFG = 0x21392139, + Fake_GetAntiLagCtx = 0x21402140 +}; + +using PFN_Fake_InformFGState = uint32_t(__stdcall*)(bool fg_state); +using PFN_Fake_InformPresentFG = uint32_t(__stdcall*)(bool frame_interpolated, uint64_t reflex_frame_id); +using PFN_Fake_GetAntiLagCtx = uint32_t(__stdcall*)(void* al2_context); + +class fakenvapi +{ +private: + inline static struct AntiLag2Data + { + void* context; + bool enabled; + } antilag2_data; + + inline static PFN_Fake_InformFGState Fake_InformFGState = nullptr; + inline static PFN_Fake_InformPresentFG Fake_InformPresentFG = nullptr; + inline static PFN_Fake_GetAntiLagCtx Fake_GetAntiLagCtx = nullptr; + + inline static bool _inited = false; +public: + typedef void* (__stdcall* PFN_Fake_QueryInterface)(FN_INTERFACE InterfaceId); + + inline static const GUID IID_IFfxAntiLag2Data = { 0x5083ae5b, 0x8070, 0x4fca, {0x8e, 0xe5, 0x35, 0x82, 0xdd, 0x36, 0x7d, 0x13} }; + + inline static void Init(PFN_Fake_QueryInterface &queryInterface) { + if (_inited) return; + + LOG_INFO("Getting fakenvapi-specific functions"); + + Fake_InformFGState = static_cast(queryInterface(FN_INTERFACE::Fake_InformFGState)); + Fake_InformPresentFG = static_cast(queryInterface(FN_INTERFACE::Fake_InformPresentFG)); + Fake_GetAntiLagCtx = static_cast(queryInterface(FN_INTERFACE::Fake_GetAntiLagCtx)); + + _inited = true; + } + + // Inform AntiLag 2 when present of interpolated frames starts + inline static void reportFGPresent(IDXGISwapChain* pSwapChain, bool fg_state, bool frame_interpolated) { + if (Fake_InformFGState == nullptr || Fake_InformPresentFG == nullptr || Fake_GetAntiLagCtx == nullptr) return; + + // Lets fakenvapi log and reset correctly + Fake_InformFGState(fg_state); + + if (fg_state) { + // Starting with FSR 3.1.1 we can provide an AntiLag 2 context to FSR FG + // and it will call SetFrameGenFrameType for us + auto static ffxApiVersion = FfxApiProxy::VersionDx12(); + constexpr feature_version requiredVersion = { 3, 1, 1 }; + if (isVersionOrBetter(ffxApiVersion, requiredVersion)) { + void* antilag2Context = nullptr; + Fake_GetAntiLagCtx(&antilag2Context); + + if (antilag2Context != nullptr) { + antilag2_data.context = antilag2Context; + antilag2_data.enabled = true; + + pSwapChain->SetPrivateData(IID_IFfxAntiLag2Data, sizeof(antilag2_data), &antilag2_data); + } + } + else + { + // Tell fakenvapi to call SetFrameGenFrameType by itself + // Reflex frame id might get used in the future + Fake_InformPresentFG(frame_interpolated, 0); + } + } + else { + // Remove it or other FG mods won't work with AL2 + pSwapChain->SetPrivateData(IID_IFfxAntiLag2Data, 0, nullptr); + } + } +}; \ No newline at end of file diff --git a/OptiScaler/hooks/HooksDx.cpp b/OptiScaler/hooks/HooksDx.cpp index 40f548f6..f27a2486 100644 --- a/OptiScaler/hooks/HooksDx.cpp +++ b/OptiScaler/hooks/HooksDx.cpp @@ -1561,9 +1561,12 @@ static HRESULT Present(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags HooksDx::currentFrameIndex = (HooksDx::currentFrameIndex + 1) % HooksDx::QUERY_BUFFER_COUNT; } - ImGuiOverlayDx::Present(pSwapChain, SyncInterval, Flags, pPresentParameters, pDevice, hWnd); - + + if (Config::Instance()->FGUseFGSwapChain.value_or(true)) { + fakenvapi::reportFGPresent(pSwapChain, FrameGen_Dx12::fgIsActive, frameCounter % 2); + } + frameCounter++; // swapchain present diff --git a/OptiScaler/hooks/HooksDx.h b/OptiScaler/hooks/HooksDx.h index 2d0a45c8..2efaa462 100644 --- a/OptiScaler/hooks/HooksDx.h +++ b/OptiScaler/hooks/HooksDx.h @@ -9,6 +9,7 @@ #include #include "../FfxApi_Proxy.h" +#include "../NVNGX_Proxy.h" #include #include diff --git a/OptiScaler/pch.h b/OptiScaler/pch.h index 9cba73a0..657531b2 100644 --- a/OptiScaler/pch.h +++ b/OptiScaler/pch.h @@ -74,6 +74,23 @@ typedef struct _feature_version unsigned int patch; } feature_version; +inline static bool isVersionOrBetter(const feature_version& current, const feature_version& required) { + if (current.major > required.major) { + return true; + } + if (current.major == required.major) { + if (current.minor > required.minor) { + return true; + } + if (current.minor == required.minor) { + if (current.patch >= required.patch) { + return true; + } + } + } + return false; +} + inline static std::string wstring_to_string(const std::wstring& wide_str) { std::string str(wide_str.length(), 0);