Skip to content

Commit

Permalink
Handle Final Fantasy XVI's NVIDIA Streamline Interposer trying to cre…
Browse files Browse the repository at this point in the history
…ate a new SwapChain while the game is exiting and has no window to create a SwapChain on in the first place... wtf?!
  • Loading branch information
Kaldaien committed Sep 19, 2024
1 parent ae1be31 commit 2c59eab
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 28 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -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\<Game Name> directory.
Expand Down
4 changes: 2 additions & 2 deletions include/SpecialK/DLL_VERSION.H
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/ffxvi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
84 changes: 65 additions & 19 deletions src/render/dxgi/dxgi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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 <IUnknown> pStreamlineFactory;
This->QueryInterface (IID_IStreamlineDXGIFactory, (void **)&pStreamlineFactory.p);

std::wstring iname = SK_UTF8ToWideChar (
SK_GetDXGIFactoryInterface (This)
Expand All @@ -6732,19 +6738,32 @@ _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,
CreateSwapChainForHwnd_Original ( This, pDevice, hWnd,
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);

Expand All @@ -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
Expand Down Expand Up @@ -6896,16 +6912,25 @@ _In_opt_ IDXGIOutput *pRestrictToOutput,
//
if (pCmdQueue != nullptr && pDev12 != nullptr)
{
SK_ComQIPtr <IDXGISwapChain3> pSwap3 (pTemp);
SK_D3D12_HotSwapChainHook ( pSwap3, pDev12.p);
SK_ComPtr <IDXGISwapChain3> pNativeSwap3;
SK_ComPtr <ID3D12Device> pNativeDev12;

if ( pDev12.p != nullptr &&
SK_slGetNativeInterface (pDev12.p, (void **)&pNativeDev12.p) == sl::Result::eOk)
pDev12 = pNativeDev12;
SK_ComQIPtr<IDXGISwapChain3> 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 ?
config.render.framerate.pre_render_limit : 2);
}

SK_DXGI_CreateSwapChain1_PostInit (pDevice, hWnd, pOrigDesc, pOrigFullscreenDesc, &pTemp);
//*ppSwapChain = pTemp;
SK_DXGI_WrapSwapChain1 (pDevice, pTemp,
ppSwapChain, orig_desc1.Format);

Expand Down Expand Up @@ -8412,8 +8437,14 @@ IDXGISwapChain4_SetHDRMetaData ( IDXGISwapChain4* This,

if (display.gamut.maxLocalY == 0.0f)
{
SK_ComPtr <IDXGIOutput> pOutput;
This->GetContainingOutput (&pOutput.p);
This->SetFullscreenState (FALSE, nullptr);

// Make sure we're not screwed over by NVIDIA Streamline
SK_ComPtr <IDXGIOutput> pOutput;
SK_ComPtr <IDXGISwapChain4> pNativeSwap4;
if (SK_slGetNativeInterface (This, (void **)&pNativeSwap4.p) == sl::Result::eOk)
pNativeSwap4->GetContainingOutput (&pOutput.p);
else This->GetContainingOutput (&pOutput.p);

SK_ComQIPtr <IDXGIOutput6>
pOutput6 ( pOutput);
Expand Down Expand Up @@ -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 <ID3D12Debug> pDebugD3D12;
if (SUCCEEDED (_D3D12GetDebugInterface (IID_PPV_ARGS (&pDebugD3D12.p))))
pDebugD3D12->EnableDebugLayer ();
}
}

SK_ComPtr <IDXGIFactory> pFactory;
CreateDXGIFactory2_Import ( factory_flags,
__uuidof (IDXGIFactory), (void **)&pFactory.p);
Expand Down
12 changes: 8 additions & 4 deletions src/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 2c59eab

Please sign in to comment.