dxvk/src/dxvk/dxvk_latency_builtin.h
Philip Rebohle 356873e2fb [dxvk] Add latency tracker
Implements a basic latency sleep solution that is intended to work
without requiring games to support any related vendor features.

This alone is not enough to expose the Reflex API to applications via
dxvk-nvapi, but since that relies on NV_low_latency2 specifics anyway,
we are going to add an implementation based on that extension later
with an extended interface.
2025-01-20 16:25:15 +01:00

135 lines
3.6 KiB
C++

#pragma once
#include <array>
#include "dxvk_latency.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 Timings for a single tracked frame
*/
struct DxvkLatencyFrameData {
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 cpuPresentEnd = time_point();
time_point queueSubmit = time_point();
time_point queuePresent = time_point();
time_point gpuExecStart = time_point();
time_point gpuExecEnd = time_point();
time_point gpuIdleStart = time_point();
time_point gpuIdleEnd = time_point();
duration gpuIdleTime = duration(0u);
time_point gpuPresent = time_point();
duration sleepDuration = duration(0u);
VkResult presentStatus = VK_NOT_READY;
};
/**
* \brief Built-in latency tracker
*
* Implements a simple latency reduction algorithm
* based on CPU timestamps received from the backend.
*/
class DxvkBuiltInLatencyTracker : public DxvkLatencyTracker {
using time_point = typename DxvkLatencyFrameData::time_point;
using duration = typename DxvkLatencyFrameData::duration;
constexpr static size_t FrameCount = 8u;
public:
DxvkBuiltInLatencyTracker(
int32_t toleranceUs);
~DxvkBuiltInLatencyTracker();
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:
dxvk::mutex m_mutex;
dxvk::condition_variable m_cond;
duration m_tolerance;
double m_envFpsLimit = 0.0;
std::array<DxvkLatencyFrameData, FrameCount> m_frames = { };
uint64_t m_validRangeBegin = 0u;
uint64_t m_validRangeEnd = 0u;
duration sleep(
uint64_t frameId,
double maxFrameRate);
DxvkLatencyFrameData* initFrame(
uint64_t frameId);
DxvkLatencyFrameData* findFrame(
uint64_t frameId);
duration computeFrameInterval(
double maxFrameRate);
static duration computeIntervalFromRate(
double frameRate);
static duration estimateTime(
const duration* frames,
size_t frameCount);
};
}