[dxvk] Integrate frame pacing

This commit is contained in:
netborg 2025-02-18 21:39:01 +01:00
parent 01ccd1e776
commit a8bd6f069e
8 changed files with 44 additions and 12 deletions

View file

@ -3,6 +3,7 @@
#include "d3d11_swapchain.h" #include "d3d11_swapchain.h"
#include "../dxvk/dxvk_latency_builtin.h" #include "../dxvk/dxvk_latency_builtin.h"
#include "../dxvk/framepacer/dxvk_framepacer.h"
#include "../util/util_win32_compat.h" #include "../util/util_win32_compat.h"
@ -354,6 +355,10 @@ namespace dxvk {
if (m_presenter != nullptr) if (m_presenter != nullptr)
m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency()); m_presenter->setFrameRateLimit(m_targetFrameRate, GetActualFrameLatency());
FramePacer* framePacer = dynamic_cast<FramePacer*>(m_latency.ptr());
if (framePacer != nullptr)
framePacer->setTargetFrameRate(FrameRate);
} }

View file

@ -5,6 +5,8 @@
#include "d3d9_hud.h" #include "d3d9_hud.h"
#include "d3d9_window.h" #include "d3d9_window.h"
#include "../dxvk/framepacer/dxvk_framepacer.h"
namespace dxvk { namespace dxvk {
static uint16_t MapGammaControlPoint(float x) { static uint16_t MapGammaControlPoint(float x) {
@ -1112,6 +1114,9 @@ namespace dxvk {
} }
m_wctx->presenter->setFrameRateLimit(frameRate, GetActualFrameLatency()); m_wctx->presenter->setFrameRateLimit(frameRate, GetActualFrameLatency());
FramePacer* framePacer = dynamic_cast<FramePacer*>(m_latencyTracker.ptr());
if (framePacer != nullptr)
framePacer->setTargetFrameRate(frameRate);
m_targetFrameRate = frameRate; m_targetFrameRate = frameRate;
} }

View file

@ -113,7 +113,7 @@ namespace dxvk {
void DxvkContext::beginLatencyTracking( void DxvkContext::beginLatencyTracking(
const Rc<DxvkLatencyTracker>& tracker, const Rc<DxvkLatencyTracker>& tracker,
uint64_t frameId) { uint64_t frameId) {
if (tracker && (!m_latencyTracker || m_latencyTracker == tracker)) { if (tracker && m_latencyTracker != tracker) {
tracker->notifyCsRenderBegin(frameId); tracker->notifyCsRenderBegin(frameId);
m_latencyTracker = tracker; m_latencyTracker = tracker;

View file

@ -2,6 +2,7 @@
#include "dxvk_instance.h" #include "dxvk_instance.h"
#include "dxvk_latency_builtin.h" #include "dxvk_latency_builtin.h"
#include "dxvk_latency_reflex.h" #include "dxvk_latency_reflex.h"
#include "framepacer/dxvk_framepacer.h"
namespace dxvk { namespace dxvk {
@ -310,13 +311,13 @@ namespace dxvk {
Rc<DxvkLatencyTracker> DxvkDevice::createLatencyTracker( Rc<DxvkLatencyTracker> DxvkDevice::createLatencyTracker(
const Rc<Presenter>& presenter) { const Rc<Presenter>& presenter) {
if (m_options.latencySleep == Tristate::False) if (m_options.latencySleep == Tristate::False)
return nullptr; return new FramePacer(m_options);
if (m_options.latencySleep == Tristate::Auto) { if (m_options.latencySleep == Tristate::Auto) {
if (m_features.nvLowLatency2) if (m_features.nvLowLatency2)
return new DxvkReflexLatencyTrackerNv(presenter); return new DxvkReflexLatencyTrackerNv(presenter);
else else
return nullptr; return new FramePacer(m_options);
} }
return new DxvkBuiltInLatencyTracker(presenter, return new DxvkBuiltInLatencyTracker(presenter,

View file

@ -270,7 +270,7 @@ namespace dxvk {
if (canSignal) if (canSignal)
m_signal->signal(frameId); m_signal->signal(frameId);
} else { } else {
m_fpsLimiter.delay(); m_fpsLimiter.delay(tracker);
m_signal->signal(frameId); m_signal->signal(frameId);
if (tracker) if (tracker)
@ -1243,15 +1243,14 @@ namespace dxvk {
// Signal latency tracker right away to get more accurate // Signal latency tracker right away to get more accurate
// measurements if the frame rate limiter is enabled. // measurements if the frame rate limiter is enabled.
if (frame.tracker) { if (frame.tracker)
frame.tracker->notifyGpuPresentEnd(frame.frameId); frame.tracker->notifyGpuPresentEnd(frame.frameId);
frame.tracker = nullptr;
}
// Apply FPS limiter here to align it as closely with scanout as we can, // 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 // and delay signaling the frame latency event to emulate behaviour of a
// low refresh rate display as closely as we can. // 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 // Wake up any thread that may be waiting for the queue to become empty
bool canSignal = false; bool canSignal = false;

View file

@ -1,5 +1,6 @@
#include "dxvk_device.h" #include "dxvk_device.h"
#include "dxvk_queue.h" #include "dxvk_queue.h"
#include "framepacer/dxvk_framepacer.h"
namespace dxvk { namespace dxvk {
@ -46,6 +47,8 @@ namespace dxvk {
DxvkSubmitInfo submitInfo, DxvkSubmitInfo submitInfo,
DxvkLatencyInfo latencyInfo, DxvkLatencyInfo latencyInfo,
DxvkSubmitStatus* status) { DxvkSubmitStatus* status) {
if (latencyInfo.tracker)
latencyInfo.tracker->notifySubmit();
std::unique_lock<dxvk::mutex> lock(m_mutex); std::unique_lock<dxvk::mutex> lock(m_mutex);
m_finishCond.wait(lock, [this] { m_finishCond.wait(lock, [this] {
@ -66,6 +69,8 @@ namespace dxvk {
DxvkPresentInfo presentInfo, DxvkPresentInfo presentInfo,
DxvkLatencyInfo latencyInfo, DxvkLatencyInfo latencyInfo,
DxvkSubmitStatus* status) { DxvkSubmitStatus* status) {
if (latencyInfo.tracker)
latencyInfo.tracker->notifyPresent(presentInfo.frameId);
std::unique_lock<dxvk::mutex> lock(m_mutex); std::unique_lock<dxvk::mutex> lock(m_mutex);
DxvkSubmitEntry entry = { }; DxvkSubmitEntry entry = { };
@ -274,7 +279,9 @@ namespace dxvk {
} else if (entry.present.presenter != nullptr) { } else if (entry.present.presenter != nullptr) {
// Signal the frame and then immediately destroy the reference. // Signal the frame and then immediately destroy the reference.
// This is necessary since the front-end may want to explicitly // 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->signalFrame(entry.present.frameId, entry.latency.tracker);
entry.present.presenter = nullptr; entry.present.presenter = nullptr;
} }

View file

@ -5,12 +5,15 @@
#include "util_fps_limiter.h" #include "util_fps_limiter.h"
#include "util_sleep.h" #include "util_sleep.h"
#include "util_string.h" #include "util_string.h"
#include "../dxvk/framepacer/dxvk_framepacer.h"
#include "./log/log.h" #include "./log/log.h"
using namespace std::chrono_literals; using namespace std::chrono_literals;
namespace dxvk { namespace dxvk {
std::atomic<bool> FpsLimiter::m_isActive = { false };
FpsLimiter::FpsLimiter() { FpsLimiter::FpsLimiter() {
auto override = getEnvironmentOverride(); auto override = getEnvironmentOverride();
@ -48,7 +51,12 @@ namespace dxvk {
} }
void FpsLimiter::delay() { void FpsLimiter::delay(const Rc<DxvkLatencyTracker>& tracker) {
FramePacer* framePacer = dynamic_cast<FramePacer*>(tracker.ptr());
if (framePacer && framePacer->getMode()) {
return;
}
std::unique_lock<dxvk::mutex> lock(m_mutex); std::unique_lock<dxvk::mutex> lock(m_mutex);
auto interval = m_targetInterval; auto interval = m_targetInterval;
auto latency = m_maxLatency; auto latency = m_maxLatency;
@ -71,8 +79,11 @@ namespace dxvk {
// that can be written by setTargetFrameRate // that can be written by setTargetFrameRate
lock.unlock(); lock.unlock();
if (t1 < m_nextFrame) m_isActive.store(false);
if (t1 < m_nextFrame) {
m_isActive.store(true);
Sleep::sleepUntil(t1, m_nextFrame); Sleep::sleepUntil(t1, m_nextFrame);
}
m_nextFrame = (t1 < m_nextFrame + interval) m_nextFrame = (t1 < m_nextFrame + interval)
? m_nextFrame + interval ? m_nextFrame + interval

View file

@ -7,6 +7,8 @@
#include "util_time.h" #include "util_time.h"
namespace dxvk { namespace dxvk {
class DxvkLatencyTracker;
/** /**
* \brief Frame rate limiter * \brief Frame rate limiter
@ -38,7 +40,7 @@ namespace dxvk {
* and the time since the last call to \ref delay is * and the time since the last call to \ref delay is
* shorter than the target interval. * shorter than the target interval.
*/ */
void delay(); void delay(const Rc<DxvkLatencyTracker>& tracker);
/** /**
* \brief Queries environment override * \brief Queries environment override
@ -46,6 +48,8 @@ namespace dxvk {
*/ */
static std::optional<double> getEnvironmentOverride(); static std::optional<double> getEnvironmentOverride();
static std::atomic<bool> m_isActive;
private: private:
using TimePoint = dxvk::high_resolution_clock::time_point; using TimePoint = dxvk::high_resolution_clock::time_point;