mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-21 22:54:16 +01:00
[dxvk] Add latency tracker based on NV_low_latency2
This commit is contained in:
parent
5dbfb66d99
commit
ad536cf157
@ -1,6 +1,7 @@
|
||||
#include "dxvk_device.h"
|
||||
#include "dxvk_instance.h"
|
||||
#include "dxvk_latency_builtin.h"
|
||||
#include "dxvk_latency_builtin_nv.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
@ -311,6 +312,9 @@ namespace dxvk {
|
||||
if (m_options.latencySleep != Tristate::True)
|
||||
return nullptr;
|
||||
|
||||
if (m_features.nvLowLatency2)
|
||||
return new DxvkBuiltInLatencyTrackerNv(presenter);
|
||||
|
||||
return new DxvkBuiltInLatencyTracker(
|
||||
m_options.latencyTolerance);
|
||||
}
|
||||
|
213
src/dxvk/dxvk_latency_builtin_nv.cpp
Normal file
213
src/dxvk/dxvk_latency_builtin_nv.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include "dxvk_latency_builtin_nv.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
DxvkBuiltInLatencyTrackerNv::DxvkBuiltInLatencyTrackerNv(
|
||||
const Rc<Presenter>& presenter)
|
||||
: m_presenter(presenter) {
|
||||
Logger::info("Latency control enabled, using VK_NV_low_latency2");
|
||||
auto limit = FpsLimiter::getEnvironmentOverride();
|
||||
|
||||
if (limit)
|
||||
m_envFpsLimit = *limit;
|
||||
}
|
||||
|
||||
|
||||
DxvkBuiltInLatencyTrackerNv::~DxvkBuiltInLatencyTrackerNv() {
|
||||
VkLatencySleepModeInfoNV latencyMode = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV };
|
||||
latencyMode.lowLatencyMode = VK_FALSE;
|
||||
latencyMode.lowLatencyBoost = VK_FALSE;
|
||||
latencyMode.minimumIntervalUs = 0;
|
||||
|
||||
m_presenter->setLatencySleepModeNv(latencyMode);
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyCpuPresentBegin(
|
||||
uint64_t frameId) {
|
||||
// Not interesting here
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyCpuPresentEnd(
|
||||
uint64_t frameId) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
auto frame = getFrame(frameId);
|
||||
|
||||
if (frame)
|
||||
frame->presentPending = VK_TRUE;
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyCsRenderBegin(
|
||||
uint64_t frameId) {
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_SIMULATION_END_NV);
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_RENDERSUBMIT_START_NV);
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyCsRenderEnd(
|
||||
uint64_t frameId) {
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_RENDERSUBMIT_END_NV);
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyQueueSubmit(
|
||||
uint64_t frameId) {
|
||||
// Handled by driver
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyQueuePresentBegin(
|
||||
uint64_t frameId) {
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_PRESENT_START_NV);
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyQueuePresentEnd(
|
||||
uint64_t frameId,
|
||||
VkResult status) {
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_PRESENT_END_NV);
|
||||
|
||||
std::unique_lock lock(m_mutex);
|
||||
auto frame = getFrame(frameId);
|
||||
|
||||
if (frame)
|
||||
frame->presentResult = status;
|
||||
|
||||
m_cond.notify_one();
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyGpuExecutionBegin(
|
||||
uint64_t frameId) {
|
||||
// Handled by driver
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyGpuExecutionEnd(
|
||||
uint64_t frameId) {
|
||||
// Handled by driver
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::notifyGpuPresentEnd(
|
||||
uint64_t frameId) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
auto frame = getFrame(frameId);
|
||||
|
||||
if (frame)
|
||||
frame->frameEnd = dxvk::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::sleepAndBeginFrame(
|
||||
uint64_t frameId,
|
||||
double maxFrameRate) {
|
||||
bool presentSuccessful = false;
|
||||
|
||||
duration sleepDuration(0u);
|
||||
|
||||
{ std::unique_lock lock(m_mutex);
|
||||
|
||||
// Don't try to sleep if we haven't set up
|
||||
// low latency mode for the swapchain yet
|
||||
if (m_lowLatencyEnabled) {
|
||||
auto curr = getFrame(frameId - 1u);
|
||||
|
||||
if (curr && curr->presentPending) {
|
||||
m_cond.wait(lock, [curr] {
|
||||
return curr->presentResult != VK_NOT_READY;
|
||||
});
|
||||
|
||||
presentSuccessful = curr->presentResult >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (presentSuccessful) {
|
||||
auto t0 = dxvk::high_resolution_clock::now();
|
||||
m_presenter->latencySleepNv(frameId - 1u);
|
||||
|
||||
sleepDuration += dxvk::high_resolution_clock::now() - t0;
|
||||
}
|
||||
|
||||
{ std::unique_lock lock(m_mutex);
|
||||
// Set up low latency mode for subsequent frames
|
||||
VkLatencySleepModeInfoNV latencyMode = { VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV };
|
||||
latencyMode.lowLatencyMode = VK_TRUE;
|
||||
latencyMode.lowLatencyBoost = VK_TRUE;
|
||||
latencyMode.minimumIntervalUs = 0;
|
||||
|
||||
if (m_envFpsLimit > 0.0)
|
||||
maxFrameRate = m_envFpsLimit;
|
||||
|
||||
if (maxFrameRate > 0.0)
|
||||
latencyMode.minimumIntervalUs = uint64_t(1'000'000.0 / maxFrameRate);
|
||||
|
||||
m_presenter->setLatencySleepModeNv(latencyMode);
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_INPUT_SAMPLE_NV);
|
||||
m_presenter->setLatencyMarkerNv(frameId,
|
||||
VK_LATENCY_MARKER_SIMULATION_START_NV);
|
||||
|
||||
auto next = initFrame(frameId);
|
||||
next->frameStart = dxvk::high_resolution_clock::now();
|
||||
next->sleepDuration = sleepDuration;
|
||||
|
||||
m_lowLatencyEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxvkBuiltInLatencyTrackerNv::discardTimings() {
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_lastDiscard = m_lastFrameId;
|
||||
}
|
||||
|
||||
|
||||
DxvkLatencyStats DxvkBuiltInLatencyTrackerNv::getStatistics(
|
||||
uint64_t frameId) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
|
||||
auto frame = getFrame(frameId);
|
||||
|
||||
while (frame && frame->frameEnd == time_point())
|
||||
frame = getFrame(--frameId);
|
||||
|
||||
if (!frame)
|
||||
return DxvkLatencyStats();
|
||||
|
||||
DxvkLatencyStats stats = { };
|
||||
stats.frameLatency = std::chrono::duration_cast<std::chrono::microseconds>(frame->frameEnd - frame->frameStart);
|
||||
stats.sleepDuration = std::chrono::duration_cast<std::chrono::microseconds>(frame->sleepDuration);
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
DxvkLatencyFrameDataNv* DxvkBuiltInLatencyTrackerNv::initFrame(uint64_t frameId) {
|
||||
auto& frame = m_frames[frameId % FrameCount];
|
||||
|
||||
frame = DxvkLatencyFrameDataNv();
|
||||
frame.frameId = frameId;
|
||||
|
||||
m_lastFrameId = frameId;
|
||||
return &m_frames[frameId % FrameCount];
|
||||
}
|
||||
|
||||
|
||||
DxvkLatencyFrameDataNv* DxvkBuiltInLatencyTrackerNv::getFrame(uint64_t frameId) {
|
||||
auto& frame = m_frames[frameId % FrameCount];
|
||||
|
||||
if (frameId <= m_lastDiscard || frame.frameId != frameId)
|
||||
return nullptr;
|
||||
|
||||
return &frame;
|
||||
}
|
||||
|
||||
}
|
114
src/dxvk/dxvk_latency_builtin_nv.h
Normal file
114
src/dxvk/dxvk_latency_builtin_nv.h
Normal file
@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "dxvk_latency.h"
|
||||
#include "dxvk_presenter.h"
|
||||
|
||||
#include "../util/thread.h"
|
||||
|
||||
#include "../util/util_sleep.h"
|
||||
#include "../util/util_time.h"
|
||||
|
||||
#include "../util/config/config.h"
|
||||
|
||||
#include "../util/sync/sync_spinlock.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
/**
|
||||
* \brief Internal timers for LL2 timing
|
||||
*/
|
||||
struct DxvkLatencyFrameDataNv {
|
||||
using time_point = dxvk::high_resolution_clock::time_point;
|
||||
using duration = dxvk::high_resolution_clock::duration;
|
||||
|
||||
uint64_t frameId = 0u;
|
||||
time_point frameStart = time_point();
|
||||
time_point frameEnd = time_point();
|
||||
duration sleepDuration = duration(0u);
|
||||
VkResult presentResult = VK_NOT_READY;
|
||||
VkBool32 presentPending = VK_FALSE;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Built-in latency tracker based on VK_NV_low_latency2
|
||||
*
|
||||
* Implements a simple latency reduction algorithm
|
||||
* based on CPU timestamps received from the backend.
|
||||
*/
|
||||
class DxvkBuiltInLatencyTrackerNv : public DxvkLatencyTracker {
|
||||
using time_point = typename DxvkLatencyFrameDataNv::time_point;
|
||||
using duration = typename DxvkLatencyFrameDataNv::duration;
|
||||
|
||||
constexpr static size_t FrameCount = 8u;
|
||||
public:
|
||||
|
||||
DxvkBuiltInLatencyTrackerNv(
|
||||
const Rc<Presenter>& presenter);
|
||||
|
||||
~DxvkBuiltInLatencyTrackerNv();
|
||||
|
||||
void notifyCpuPresentBegin(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyCpuPresentEnd(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyCsRenderBegin(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyCsRenderEnd(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyQueueSubmit(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyQueuePresentBegin(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyQueuePresentEnd(
|
||||
uint64_t frameId,
|
||||
VkResult status);
|
||||
|
||||
void notifyGpuExecutionBegin(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyGpuExecutionEnd(
|
||||
uint64_t frameId);
|
||||
|
||||
void notifyGpuPresentEnd(
|
||||
uint64_t frameId);
|
||||
|
||||
void sleepAndBeginFrame(
|
||||
uint64_t frameId,
|
||||
double maxFrameRate);
|
||||
|
||||
void discardTimings();
|
||||
|
||||
DxvkLatencyStats getStatistics(
|
||||
uint64_t frameId);
|
||||
|
||||
private:
|
||||
|
||||
Rc<Presenter> m_presenter;
|
||||
double m_envFpsLimit = 0.0;
|
||||
|
||||
dxvk::mutex m_mutex;
|
||||
dxvk::condition_variable m_cond;
|
||||
|
||||
uint64_t m_lastFrameId = 0u;
|
||||
uint64_t m_lastDiscard = 0u;
|
||||
|
||||
bool m_lowLatencyEnabled = false;
|
||||
|
||||
std::array<DxvkLatencyFrameDataNv, FrameCount> m_frames = { };
|
||||
|
||||
DxvkLatencyFrameDataNv* initFrame(uint64_t frameId);
|
||||
|
||||
DxvkLatencyFrameDataNv* getFrame(uint64_t frameId);
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -91,6 +91,7 @@ dxvk_src = [
|
||||
'dxvk_image.cpp',
|
||||
'dxvk_instance.cpp',
|
||||
'dxvk_latency_builtin.cpp',
|
||||
'dxvk_latency_builtin_nv.cpp',
|
||||
'dxvk_memory.cpp',
|
||||
'dxvk_meta_blit.cpp',
|
||||
'dxvk_meta_clear.cpp',
|
||||
|
Loading…
x
Reference in New Issue
Block a user