diff --git a/src/d3d11/d3d11_swapchain.cpp b/src/d3d11/d3d11_swapchain.cpp index a2d356067..cb5679854 100644 --- a/src/d3d11/d3d11_swapchain.cpp +++ b/src/d3d11/d3d11_swapchain.cpp @@ -3,6 +3,7 @@ #include "d3d11_swapchain.h" #include "../dxvk/dxvk_latency_builtin.h" +#include "../dxvk/framepacer/dxvk_framepacer.h" #include "../util/util_win32_compat.h" @@ -354,6 +355,10 @@ namespace dxvk { if (m_presenter != nullptr) m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency()); + + FramePacer* framePacer = dynamic_cast(m_latency.ptr()); + if (framePacer != nullptr) + framePacer->setTargetFrameRate(FrameRate); } diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 73218c516..05466eca5 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -5,6 +5,8 @@ #include "d3d9_hud.h" #include "d3d9_window.h" +#include "../dxvk/framepacer/dxvk_framepacer.h" + namespace dxvk { static uint16_t MapGammaControlPoint(float x) { @@ -1112,6 +1114,9 @@ namespace dxvk { } m_wctx->presenter->setFrameRateLimit(frameRate, GetActualFrameLatency()); + FramePacer* framePacer = dynamic_cast(m_latencyTracker.ptr()); + if (framePacer != nullptr) + framePacer->setTargetFrameRate(frameRate); m_targetFrameRate = frameRate; } diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index ec9fa0761..d33938dda 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -113,7 +113,7 @@ namespace dxvk { void DxvkContext::beginLatencyTracking( const Rc& tracker, uint64_t frameId) { - if (tracker && (!m_latencyTracker || m_latencyTracker == tracker)) { + if (tracker && m_latencyTracker != tracker) { tracker->notifyCsRenderBegin(frameId); m_latencyTracker = tracker; diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index eed3eb3cf..cdb59e6c7 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -2,6 +2,7 @@ #include "dxvk_instance.h" #include "dxvk_latency_builtin.h" #include "dxvk_latency_reflex.h" +#include "framepacer/dxvk_framepacer.h" namespace dxvk { @@ -310,13 +311,13 @@ namespace dxvk { Rc DxvkDevice::createLatencyTracker( const Rc& presenter) { if (m_options.latencySleep == Tristate::False) - return nullptr; + return new FramePacer(m_options); if (m_options.latencySleep == Tristate::Auto) { if (m_features.nvLowLatency2) return new DxvkReflexLatencyTrackerNv(presenter); else - return nullptr; + return new FramePacer(m_options); } return new DxvkBuiltInLatencyTracker(presenter, diff --git a/src/dxvk/dxvk_presenter.cpp b/src/dxvk/dxvk_presenter.cpp index 0e3c87762..79e10ad66 100644 --- a/src/dxvk/dxvk_presenter.cpp +++ b/src/dxvk/dxvk_presenter.cpp @@ -270,7 +270,7 @@ namespace dxvk { if (canSignal) m_signal->signal(frameId); } else { - m_fpsLimiter.delay(); + m_fpsLimiter.delay(tracker); m_signal->signal(frameId); if (tracker) @@ -1243,15 +1243,14 @@ namespace dxvk { // Signal latency tracker right away to get more accurate // measurements if the frame rate limiter is enabled. - if (frame.tracker) { + if (frame.tracker) frame.tracker->notifyGpuPresentEnd(frame.frameId); - frame.tracker = nullptr; - } // Apply FPS limiter here to align it as closely with scanout as we can, // and delay signaling the frame latency event to emulate behaviour of a // low refresh rate display as closely as we can. - m_fpsLimiter.delay(); + m_fpsLimiter.delay(frame.tracker); + frame.tracker = nullptr; // Wake up any thread that may be waiting for the queue to become empty bool canSignal = false; diff --git a/src/dxvk/dxvk_queue.cpp b/src/dxvk/dxvk_queue.cpp index 6d2d153b6..0c74428a0 100644 --- a/src/dxvk/dxvk_queue.cpp +++ b/src/dxvk/dxvk_queue.cpp @@ -1,5 +1,6 @@ #include "dxvk_device.h" #include "dxvk_queue.h" +#include "framepacer/dxvk_framepacer.h" namespace dxvk { @@ -46,6 +47,8 @@ namespace dxvk { DxvkSubmitInfo submitInfo, DxvkLatencyInfo latencyInfo, DxvkSubmitStatus* status) { + if (latencyInfo.tracker) + latencyInfo.tracker->notifySubmit(); std::unique_lock lock(m_mutex); m_finishCond.wait(lock, [this] { @@ -66,6 +69,8 @@ namespace dxvk { DxvkPresentInfo presentInfo, DxvkLatencyInfo latencyInfo, DxvkSubmitStatus* status) { + if (latencyInfo.tracker) + latencyInfo.tracker->notifyPresent(presentInfo.frameId); std::unique_lock lock(m_mutex); DxvkSubmitEntry entry = { }; @@ -274,7 +279,9 @@ namespace dxvk { } else if (entry.present.presenter != nullptr) { // Signal the frame and then immediately destroy the reference. // This is necessary since the front-end may want to explicitly - // destroy the presenter object. + // destroy the presenter object. + if (entry.latency.tracker) + entry.latency.tracker->notifyGpuPresentBegin(entry.present.frameId); entry.present.presenter->signalFrame(entry.present.frameId, entry.latency.tracker); entry.present.presenter = nullptr; } diff --git a/src/util/util_fps_limiter.cpp b/src/util/util_fps_limiter.cpp index 621e9a453..95fb79e7e 100644 --- a/src/util/util_fps_limiter.cpp +++ b/src/util/util_fps_limiter.cpp @@ -5,12 +5,15 @@ #include "util_fps_limiter.h" #include "util_sleep.h" #include "util_string.h" +#include "../dxvk/framepacer/dxvk_framepacer.h" #include "./log/log.h" using namespace std::chrono_literals; namespace dxvk { + + std::atomic FpsLimiter::m_isActive = { false }; FpsLimiter::FpsLimiter() { auto override = getEnvironmentOverride(); @@ -48,7 +51,12 @@ namespace dxvk { } - void FpsLimiter::delay() { + void FpsLimiter::delay(const Rc& tracker) { + FramePacer* framePacer = dynamic_cast(tracker.ptr()); + if (framePacer && framePacer->getMode()) { + return; + } + std::unique_lock lock(m_mutex); auto interval = m_targetInterval; auto latency = m_maxLatency; @@ -71,8 +79,11 @@ namespace dxvk { // that can be written by setTargetFrameRate lock.unlock(); - if (t1 < m_nextFrame) + m_isActive.store(false); + if (t1 < m_nextFrame) { + m_isActive.store(true); Sleep::sleepUntil(t1, m_nextFrame); + } m_nextFrame = (t1 < m_nextFrame + interval) ? m_nextFrame + interval diff --git a/src/util/util_fps_limiter.h b/src/util/util_fps_limiter.h index 7c33a559f..d5610afd4 100644 --- a/src/util/util_fps_limiter.h +++ b/src/util/util_fps_limiter.h @@ -7,6 +7,8 @@ #include "util_time.h" namespace dxvk { + + class DxvkLatencyTracker; /** * \brief Frame rate limiter @@ -38,7 +40,7 @@ namespace dxvk { * and the time since the last call to \ref delay is * shorter than the target interval. */ - void delay(); + void delay(const Rc& tracker); /** * \brief Queries environment override @@ -46,6 +48,8 @@ namespace dxvk { */ static std::optional getEnvironmentOverride(); + static std::atomic m_isActive; + private: using TimePoint = dxvk::high_resolution_clock::time_point;