diff --git a/OptiScaler/framegen/ffx/FSRFG_Dx12.cpp b/OptiScaler/framegen/ffx/FSRFG_Dx12.cpp index a8322836..29567c58 100644 --- a/OptiScaler/framegen/ffx/FSRFG_Dx12.cpp +++ b/OptiScaler/framegen/ffx/FSRFG_Dx12.cpp @@ -817,8 +817,12 @@ bool FSRFG_Dx12::ReleaseSwapchain(HWND hwnd) if (_swapChainContext != nullptr) { - auto result = FfxApiProxy::D3D12_DestroyContext(&_swapChainContext, nullptr); - LOG_INFO("Destroy Ffx Swapchain Result: {}({})", result, FfxApiProxy::ReturnCodeToString(result)); + // Don't call D3D12_DestroyContext for swapchain context + // Most probably we are calling it from wrapped swapchain's Release which means that the swapchain is already + // destroyed and calling D3D12_DestroyContext will cause an error + + // auto result = FfxApiProxy::D3D12_DestroyContext(&_swapChainContext, nullptr); + // LOG_INFO("Destroy Ffx Swapchain Result: {}({})", result, FfxApiProxy::ReturnCodeToString(result)); _swapChainContext = nullptr; State::Instance().currentFGSwapchain = nullptr; diff --git a/OptiScaler/framegen/xefg/XeFG_Dx12.cpp b/OptiScaler/framegen/xefg/XeFG_Dx12.cpp index ff30c886..e3f369eb 100644 --- a/OptiScaler/framegen/xefg/XeFG_Dx12.cpp +++ b/OptiScaler/framegen/xefg/XeFG_Dx12.cpp @@ -1434,8 +1434,12 @@ bool XeFG_Dx12::ReleaseSwapchain(HWND hwnd) if (_fgContext != nullptr) DestroyFGContext(); - if (State::Instance().isShuttingDown && _swapChainContext != nullptr) - DestroySwapchainContext(); + // Don't call D3D12_DestroyContext for swapchain context + // Most probably we are calling it from wrapped swapchain's Release which means that the swapchain is already + // destroyed and calling D3D12_DestroyContext will cause an error + // + // if (State::Instance().isShuttingDown && _swapChainContext != nullptr) + // DestroySwapchainContext(); ReleaseObjects(); diff --git a/OptiScaler/hooks/FG_Hooks.cpp b/OptiScaler/hooks/FG_Hooks.cpp index 77c4bca3..70517d39 100644 --- a/OptiScaler/hooks/FG_Hooks.cpp +++ b/OptiScaler/hooks/FG_Hooks.cpp @@ -975,10 +975,28 @@ HRESULT FGHooks::hkFGRelease(IDXGISwapChain* This) return o_FGRelease(This); This->AddRef(); - if (o_FGRelease(This) == 1) + + if (State::Instance().gameQuirks & GameQuirk::DoNotPreserveFGSwapChain) { - LOG_INFO("Preserving FG Swapchain from release"); - return 0; + if (o_FGRelease(This) == 1) + { + if (State::Instance().currentFG != nullptr) + { + LOG_DEBUG("FG Swapchain released, deactivating FG"); + State::Instance().currentFG->Deactivate(); + } + + LOG_DEBUG("FG Swapchain released, clearing currentFGSwapchain"); + State::Instance().currentFGSwapchain = nullptr; + } + } + else + { + if (o_FGRelease(This) == 1) + { + LOG_INFO("Preserving FG Swapchain from release"); + return 0; + } } return o_FGRelease(This); diff --git a/OptiScaler/misc/Quirks.h b/OptiScaler/misc/Quirks.h index c583066c..134e0fec 100644 --- a/OptiScaler/misc/Quirks.h +++ b/OptiScaler/misc/Quirks.h @@ -56,6 +56,7 @@ enum class GameQuirk : uint64_t CreateD3D12DeviceForLuma, ForceCreateD3D12Device, ForceDepthD32S8, + DoNotPreserveFGSwapChain, // Don't forget to add the new entry to printQuirks _ }; @@ -118,7 +119,7 @@ static const QuirkEntry quirkTable[] = { QUIRK_ENTRY("sora_1st.exe", GameQuirk::UseFsr2Dx11Inputs, GameQuirk::DisableDxgiSpoofing), // Ninja Gaiden 4 (Steam) - QUIRK_ENTRY("ninjagaiden4-steam.exe", GameQuirk::DisableResizeSkip), + QUIRK_ENTRY("ninjagaiden4-steam.exe", GameQuirk::DisableResizeSkip, GameQuirk::DoNotPreserveFGSwapChain), // The Last of Us Part I QUIRK_ENTRY("tlou-i.exe", GameQuirk::AllowedFrameAhead2), diff --git a/OptiScaler/wrapped/wrapped_swapchain.cpp b/OptiScaler/wrapped/wrapped_swapchain.cpp index b37947b2..9fb7d1eb 100644 --- a/OptiScaler/wrapped/wrapped_swapchain.cpp +++ b/OptiScaler/wrapped/wrapped_swapchain.cpp @@ -431,6 +431,7 @@ ULONG STDMETHODCALLTYPE WrappedIDXGISwapChain4::Release() auto fg = State::Instance().currentFG; if (fg != nullptr && fg->Mutex.getOwner() != 1 && fg->SwapchainContext() != nullptr) { + fg->Deactivate(); fg->ReleaseSwapchain(_handle); if (State::Instance().currentFGSwapchain != nullptr)