From 2c59eab266de912e6f4ff3ccf0d77abd1f8c53c5 Mon Sep 17 00:00:00 2001 From: Kaldaien Date: Thu, 19 Sep 2024 05:55:44 -0400 Subject: [PATCH] Handle Final Fantasy XVI's NVIDIA Streamline Interposer trying to create a new SwapChain while the game is exiting and has no window to create a SwapChain on in the first place... wtf?! --- CHANGELOG.txt | 16 ++++++- include/SpecialK/DLL_VERSION.H | 4 +- src/plugins/ffxvi.cpp | 4 +- src/render/dxgi/dxgi.cpp | 84 ++++++++++++++++++++++++++-------- src/scheduler.cpp | 12 +++-- 5 files changed, 92 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 48ef5f480..d5b5ad3da 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,18 @@ -24.9.19.1 +24.9.19.3 +========= + + Handle Final Fantasy XVI's NVIDIA Streamline Interposer trying to + create a new SwapChain while the game is exiting and has no window + to create a SwapChain on in the first place... wtf?! + + Make sure all HDR metadata API calls bypass NVIDIA Streamline's fake + interposer SwapChain and are directed to the real SwapChain with a + real display attached. + +24.9.19.2 +========= + + Added aggressive anti-stutter mode to FFXVI; trades increased idle + CPU load for decreased traversal stutter. + +24.9.19.1 ========= + Added auto-loading of .asi files from a game's directory or from any subdirectory of Special K's Profiles\ directory. diff --git a/include/SpecialK/DLL_VERSION.H b/include/SpecialK/DLL_VERSION.H index d1a57999c..ca00642f2 100644 --- a/include/SpecialK/DLL_VERSION.H +++ b/include/SpecialK/DLL_VERSION.H @@ -3,8 +3,8 @@ #define SK_YEAR 24 #define SK_MONTH 9 #define SK_DATE 19 -#define SK_REV_N 2 -#define SK_REV 2 +#define SK_REV_N 3 +#define SK_REV 3 #ifndef _A2 #define _A2(a) #a diff --git a/src/plugins/ffxvi.cpp b/src/plugins/ffxvi.cpp index d41e4f268..6c1daf779 100644 --- a/src/plugins/ffxvi.cpp +++ b/src/plugins/ffxvi.cpp @@ -348,7 +348,7 @@ SK_FFXVI_PresentFirstFrame (IUnknown* pSwapChain, UINT SyncInterval, UINT Flags) if (SK_FFXVI_ActiveAntiStutter) { - config.render.framerate.max_delta_time = 50; + config.render.framerate.max_delta_time = 15; config.render.framerate.sleepless_render = true; config.render.framerate.sleepless_window = true; } @@ -670,7 +670,7 @@ SK_FFXVI_PlugInCfg (void) { if (SK_FFXVI_ActiveAntiStutter) { - config.render.framerate.max_delta_time = 50; + config.render.framerate.max_delta_time = 15; config.render.framerate.sleepless_render = true; config.render.framerate.sleepless_window = true; } diff --git a/src/render/dxgi/dxgi.cpp b/src/render/dxgi/dxgi.cpp index defb31498..bb5032172 100644 --- a/src/render/dxgi/dxgi.cpp +++ b/src/render/dxgi/dxgi.cpp @@ -6701,10 +6701,17 @@ _In_opt_ DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, _In_opt_ IDXGIOutput *pRestrictToOutput, _Out_ IDXGISwapChain1 **ppSwapChain ) { - auto& rb = - SK_GetCurrentRenderBackend (); + SK_ReleaseAssert (pDesc != nullptr); + SK_ReleaseAssert (pDevice != nullptr); - SK_ReleaseAssert (pDesc != nullptr); + if (! IsWindow (hWnd)) + { + SK_LOGi0 ( + L"IDXGIFactory2::CreateSwapChainForHwnd (pDevice=%p, {hWnd=%x}, ...)" + L" was passed an invalid window!", pDevice, hWnd); + + return E_INVALIDARG; + } if (! config.render.dxgi.hooks.create_swapchain4hwnd) { @@ -6714,11 +6721,10 @@ _In_opt_ IDXGIOutput *pRestrictToOutput, pRestrictToOutput, ppSwapChain ); } - auto *pOrigDesc = - (DXGI_SWAP_CHAIN_DESC1 *)pDesc; - - auto *pOrigFullscreenDesc = - (DXGI_SWAP_CHAIN_FULLSCREEN_DESC *)pFullscreenDesc; + IID IID_IStreamlineDXGIFactory; + IIDFromString (L"{ADEC44E2-61F0-45C3-AD9F-1B37379284FF}", &IID_IStreamlineDXGIFactory); + SK_ComPtr pStreamlineFactory; + This->QueryInterface (IID_IStreamlineDXGIFactory, (void **)&pStreamlineFactory.p); std::wstring iname = SK_UTF8ToWideChar ( SK_GetDXGIFactoryInterface (This) @@ -6732,9 +6738,10 @@ _In_opt_ IDXGIOutput *pRestrictToOutput, (uintptr_t)pDevice, (uintptr_t)hWnd, (uintptr_t)pDesc ); // This makes no sense, so ignore it... - if ( pDevice == nullptr || - pDesc == nullptr || - ppSwapChain == nullptr || (! SK_DXGI_IsSwapChainReal1 (*pDesc, hWnd)) + if ( pStreamlineFactory != nullptr || + pDevice == nullptr || + pDesc == nullptr || + ppSwapChain == nullptr || (! SK_DXGI_IsSwapChainReal1 (*pDesc, hWnd)) ) { DXGI_CALL ( ret, @@ -6742,9 +6749,21 @@ _In_opt_ IDXGIOutput *pRestrictToOutput, pDesc, pFullscreenDesc, pRestrictToOutput, ppSwapChain ) ); + if (pStreamlineFactory) + SK_LOGi0 (L"Ignoring call because it came from a Streamline proxy factory..."); + return ret; } + auto& rb = + SK_GetCurrentRenderBackend (); + + auto *pOrigDesc = + (DXGI_SWAP_CHAIN_DESC1 *)pDesc; + + auto *pOrigFullscreenDesc = + (DXGI_SWAP_CHAIN_FULLSCREEN_DESC *)pFullscreenDesc; + // if (iname == L"{Invalid-Factory-UUID}") // return CreateSwapChainForHwnd_Original (This, pDevice, hWnd, pDesc, pFullscreenDesc, pRestrictToOutput, ppSwapChain); @@ -6757,9 +6776,6 @@ _In_opt_ IDXGIOutput *pRestrictToOutput, DXGI_SWAP_CHAIN_FULLSCREEN_DESC new_fullscreen_desc = pFullscreenDesc ? *pFullscreenDesc : DXGI_SWAP_CHAIN_FULLSCREEN_DESC { }; - ///bool bFlipOriginal = - /// SK_DXGI_IsFlipModelSwapEffect (pDesc->SwapEffect); - pDesc = &new_desc1; pFullscreenDesc = pFullscreenDesc != nullptr ? &new_fullscreen_desc @@ -6896,8 +6912,18 @@ _In_opt_ IDXGIOutput *pRestrictToOutput, // if (pCmdQueue != nullptr && pDev12 != nullptr) { - SK_ComQIPtr pSwap3 (pTemp); - SK_D3D12_HotSwapChainHook ( pSwap3, pDev12.p); + SK_ComPtr pNativeSwap3; + SK_ComPtr pNativeDev12; + + if ( pDev12.p != nullptr && + SK_slGetNativeInterface (pDev12.p, (void **)&pNativeDev12.p) == sl::Result::eOk) + pDev12 = pNativeDev12; + SK_ComQIPtr pSwap3 (pTemp); + if ( pSwap3.p != nullptr && + SK_slGetNativeInterface (pSwap3.p, (void **)&pNativeSwap3.p) == sl::Result::eOk) + pSwap3 = pNativeSwap3; + + SK_D3D12_HotSwapChainHook (pSwap3, pDev12); if (rb.active_traits.bImplicitlyWaitable) pSwap3->SetMaximumFrameLatency (config.render.framerate.pre_render_limit > 0 ? @@ -6905,7 +6931,6 @@ _In_opt_ IDXGIOutput *pRestrictToOutput, } SK_DXGI_CreateSwapChain1_PostInit (pDevice, hWnd, pOrigDesc, pOrigFullscreenDesc, &pTemp); - //*ppSwapChain = pTemp; SK_DXGI_WrapSwapChain1 (pDevice, pTemp, ppSwapChain, orig_desc1.Format); @@ -8412,8 +8437,14 @@ IDXGISwapChain4_SetHDRMetaData ( IDXGISwapChain4* This, if (display.gamut.maxLocalY == 0.0f) { - SK_ComPtr pOutput; - This->GetContainingOutput (&pOutput.p); + This->SetFullscreenState (FALSE, nullptr); + + // Make sure we're not screwed over by NVIDIA Streamline + SK_ComPtr pOutput; + SK_ComPtr pNativeSwap4; + if (SK_slGetNativeInterface (This, (void **)&pNativeSwap4.p) == sl::Result::eOk) + pNativeSwap4->GetContainingOutput (&pOutput.p); + else This->GetContainingOutput (&pOutput.p); SK_ComQIPtr pOutput6 ( pOutput); @@ -9665,6 +9696,21 @@ HookDXGI (LPVOID user) config.render.dxgi.debug_layer ? DXGI_CREATE_FACTORY_DEBUG : 0x0; + if (config.render.dxgi.debug_layer && SK_IsModuleLoaded (L"d3d12.dll")) + { + D3D12GetDebugInterface_pfn + _D3D12GetDebugInterface = + (D3D12GetDebugInterface_pfn)SK_GetProcAddress (L"d3d12.dll", + "D3D12GetDebugInterface"); + + if (_D3D12GetDebugInterface != nullptr) + { + SK_ComPtr pDebugD3D12; + if (SUCCEEDED (_D3D12GetDebugInterface (IID_PPV_ARGS (&pDebugD3D12.p)))) + pDebugD3D12->EnableDebugLayer (); + } + } + SK_ComPtr pFactory; CreateDXGIFactory2_Import ( factory_flags, __uuidof (IDXGIFactory), (void **)&pFactory.p); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 92b9c4a47..78ab2b9af 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -1295,11 +1295,15 @@ SleepEx_Detour (DWORD dwMilliseconds, BOOL bAlertable) return WAIT_IO_COMPLETION; } - if (SK_GetCurrentGameID () == SK_GAME_ID::FinalFantasyXVI) - { - static uint64_t sleeps_skipped = 0; + static const bool bFFXVI = + (SK_GetCurrentGameID () == SK_GAME_ID::FinalFantasyXVI); - if (sleeps_skipped++ % (config.priority.available_cpu_cores / 2) == 0) + if (bFFXVI) + { + static thread_local uint64_t sleeps_skipped = 0; + if ( sleeps_skipped++ % + (config.priority.available_cpu_cores * + config.priority.available_cpu_cores * 2) == 0 ) { return SK_SleepEx (dwMilliseconds, bAlertable);