[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 "../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<FramePacer*>(m_latency.ptr());
if (framePacer != nullptr)
framePacer->setTargetFrameRate(FrameRate);
}

View file

@ -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<FramePacer*>(m_latencyTracker.ptr());
if (framePacer != nullptr)
framePacer->setTargetFrameRate(frameRate);
m_targetFrameRate = frameRate;
}

View file

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

View file

@ -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<DxvkLatencyTracker> DxvkDevice::createLatencyTracker(
const Rc<Presenter>& 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,

View file

@ -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;

View file

@ -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<dxvk::mutex> 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<dxvk::mutex> 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;
}

View file

@ -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<bool> FpsLimiter::m_isActive = { false };
FpsLimiter::FpsLimiter() {
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);
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

View file

@ -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<DxvkLatencyTracker>& tracker);
/**
* \brief Queries environment override
@ -46,6 +48,8 @@ namespace dxvk {
*/
static std::optional<double> getEnvironmentOverride();
static std::atomic<bool> m_isActive;
private:
using TimePoint = dxvk::high_resolution_clock::time_point;