mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-06 20:58:37 +01:00
[dxvk] Integrate frame pacing
This commit is contained in:
parent
01ccd1e776
commit
a8bd6f069e
8 changed files with 44 additions and 12 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue