From ec8bf73b9f2cd5be71b664ae54c4c9f38fc9d564 Mon Sep 17 00:00:00 2001 From: cdozdil Date: Tue, 12 Aug 2025 23:07:02 +0300 Subject: [PATCH] Added PoC V-Sync control --- OptiScaler/State.h | 2 ++ OptiScaler/hooks/HooksDx.cpp | 24 ++++++++++++++++ OptiScaler/menu/menu_common.cpp | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/OptiScaler/State.h b/OptiScaler/State.h index 082871ad..b80a4627 100644 --- a/OptiScaler/State.h +++ b/OptiScaler/State.h @@ -95,6 +95,8 @@ class State bool FGchanged = false; bool SCchanged = false; bool skipHeapCapture = false; + std::optional forceVsync; + UINT vsyncInterval = 0; bool FGcaptureResources = false; int FGcapturedResourceCount = false; diff --git a/OptiScaler/hooks/HooksDx.cpp b/OptiScaler/hooks/HooksDx.cpp index ba9adfce..ee6c25c0 100644 --- a/OptiScaler/hooks/HooksDx.cpp +++ b/OptiScaler/hooks/HooksDx.cpp @@ -297,6 +297,21 @@ static HRESULT hkFGPresent(void* This, UINT SyncInterval, UINT Flags) LOG_WARN("Couldn't copy hudless into the backbuffer"); } + if (willPresent && State::Instance().forceVsync.has_value()) + { + if (!State::Instance().forceVsync.value()) + { + SyncInterval = 0; + Flags |= DXGI_PRESENT_ALLOW_TEARING; + } + else + { + // Remove allow tearing + SyncInterval = State::Instance().vsyncInterval; + Flags &= 0xFDFF; + } + } + HRESULT result; result = o_FGSCPresent(This, SyncInterval, Flags); LOG_DEBUG("Result: {:X}", result); @@ -681,6 +696,9 @@ static HRESULT hkCreateSwapChainForCoreWindow(IDXGIFactory2* pFactory, IUnknown* pDesc->Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; } + // For vsync override + pDesc->Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + ID3D12CommandQueue* realQ = nullptr; if (!CheckForRealObject(__FUNCTION__, pDevice, (IUnknown**) &realQ)) realQ = (ID3D12CommandQueue*) pDevice; @@ -821,6 +839,9 @@ static HRESULT hkCreateSwapChain(IDXGIFactory* pFactory, IUnknown* pDevice, DXGI pDesc->BufferDesc.Width, pDesc->BufferDesc.Height, (UINT) pDesc->BufferDesc.Format, pDesc->BufferCount, (UINT) pDesc->OutputWindow, pDesc->Windowed, _skipFGSwapChainCreation); + // For vsync override + pDesc->Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + // Crude implementation of EndlesslyFlowering's AutoHDR-ReShade // https://github.com/EndlesslyFlowering/AutoHDR-ReShade if (Config::Instance()->ForceHDR.value_or_default() && !_skipFGSwapChainCreation) @@ -1147,6 +1168,9 @@ static HRESULT hkCreateSwapChainForHwnd(IDXGIFactory* This, IUnknown* pDevice, H pDesc->Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; } + // For vsync override + pDesc->Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + // Disable FSR FG if amd dll is not found if (State::Instance().activeFgOutput == FGOutput::FSRFG && !FfxApiProxy::InitFfxDx12()) { diff --git a/OptiScaler/menu/menu_common.cpp b/OptiScaler/menu/menu_common.cpp index 345ae766..6ae79ed4 100644 --- a/OptiScaler/menu/menu_common.cpp +++ b/OptiScaler/menu/menu_common.cpp @@ -2634,6 +2634,57 @@ bool MenuCommon::RenderMenu() "UI elements and ONLY UI elements should have a pink tint!"); } + auto forceVsyncOn = State::Instance().forceVsync.has_value() && State::Instance().forceVsync.value(); + auto forceVsyncOff = State::Instance().forceVsync.has_value() && !State::Instance().forceVsync.value(); + + if (ImGui::Checkbox("Force: V-Sync On", &forceVsyncOn)) + { + if (forceVsyncOn) + State::Instance().forceVsync = true; + else + State::Instance().forceVsync.reset(); + } + + ImGui::SameLine(0.0f, 16.0f); + + if (ImGui::Checkbox("V-Sync Off", &forceVsyncOff)) + { + if (forceVsyncOff) + State::Instance().forceVsync = false; + else + State::Instance().forceVsync.reset(); + } + + ImGui::SameLine(0.0f, 16.0f); + + ImGui::BeginDisabled(forceVsyncOff); + + ImGui::PushItemWidth(50.0f * Config::Instance()->MenuScale.value_or_default()); + if (ImGui::BeginCombo("Sync Int.", std::format("{}", State::Instance().vsyncInterval).c_str())) + { + if (ImGui::Selectable("0", State::Instance().vsyncInterval == 0)) + State::Instance().vsyncInterval = 0; + + if (ImGui::Selectable("1", State::Instance().vsyncInterval == 1)) + State::Instance().vsyncInterval = 1; + + if (ImGui::Selectable("2", State::Instance().vsyncInterval == 2)) + State::Instance().vsyncInterval = 2; + + if (ImGui::Selectable("3", State::Instance().vsyncInterval == 3)) + State::Instance().vsyncInterval = 3; + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + ImGui::EndDisabled(); + + ImGui::SameLine(0.0f, 16.0f); + + if (ImGui::Button("Reset##10")) + State::Instance().forceVsync.reset(); + // if (State::Instance().activeFgInput != Config::Instance()->FGInput.value_or_default()) //{ // State::Instance().activeFgInput = Config::Instance()->FGInput.value_or_default();