[dxvk] Rework frame rate limiter

This commit is contained in:
Philip Rebohle 2023-08-07 12:46:32 +02:00
parent b0b46fd075
commit 87483ed483
3 changed files with 19 additions and 41 deletions

View File

@ -672,10 +672,6 @@ namespace dxvk {
if (!frame.frameId)
return;
// Apply the FPS limiter before signaling the frame event in
// order to reduce latency if the app uses it for frame pacing.
applyFrameRateLimit(frame.mode);
// If the present operation has succeeded, actually wait for it to complete.
// Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would
// restrict us to the display refresh rate on some platforms (XWayland).
@ -687,6 +683,10 @@ namespace dxvk {
Logger::err(str::format("Presenter: vkWaitForPresentKHR failed: ", vr));
}
// Apply the FPS limiter before signaling the frame event in
// order to reduce latency if the app uses it for frame pacing.
applyFrameRateLimit(frame.mode);
// Always signal even on error, since failures here
// are transparent to the front-end.
m_signal->signal(frame.frameId);

View File

@ -35,12 +35,12 @@ namespace dxvk {
std::lock_guard<dxvk::mutex> lock(m_mutex);
if (!m_envOverride) {
m_targetInterval = frameRate > 0.0
? TimerDuration(int64_t(double(TimerDuration::period::den) / frameRate))
: TimerDuration::zero();
if (isEnabled() && !m_initialized)
initialize();
if (frameRate > 0.0) {
m_targetInterval = TimerDuration(int64_t(double(TimerDuration::period::den) / frameRate));
} else {
m_targetInterval = TimerDuration::zero();
m_nextDeadline = TimePoint();
}
}
}
@ -51,36 +51,18 @@ namespace dxvk {
if (!isEnabled())
return;
auto t0 = m_lastFrame;
auto t1 = dxvk::high_resolution_clock::now();
TimePoint now = dxvk::high_resolution_clock::now();
auto frameTime = std::chrono::duration_cast<TimerDuration>(t1 - t0);
if (m_nextDeadline == TimePoint())
m_nextDeadline = now;
if (frameTime * 100 > m_targetInterval * 103 - m_deviation * 100) {
// If we have a slow frame, reset the deviation since we
// do not want to compensate for low performance later on
m_deviation = TimerDuration::zero();
if (m_nextDeadline > now) {
Sleep::sleepUntil(now, m_nextDeadline);
m_nextDeadline += m_targetInterval;
} else {
// Don't call sleep if the amount of time to sleep is shorter
// than the time the function calls are likely going to take
TimerDuration sleepDuration = m_targetInterval - m_deviation - frameTime;
t1 = Sleep::sleepFor(t1, sleepDuration);
// Compensate for any sleep inaccuracies in the next frame, and
// limit cumulative deviation in order to avoid stutter in case we
// have a number of slow frames immediately followed by a fast one.
frameTime = std::chrono::duration_cast<TimerDuration>(t1 - t0);
m_deviation += frameTime - m_targetInterval;
m_deviation = std::min(m_deviation, m_targetInterval / 16);
uint64_t nFrames = TimerDuration(now - m_nextDeadline).count() / m_targetInterval.count();
m_nextDeadline += (1 + nFrames) * m_targetInterval;
}
m_lastFrame = t1;
}
void FpsLimiter::initialize() {
m_lastFrame = dxvk::high_resolution_clock::now();
m_initialized = true;
}
}

View File

@ -54,14 +54,10 @@ namespace dxvk {
dxvk::mutex m_mutex;
TimerDuration m_targetInterval = TimerDuration::zero();
TimerDuration m_deviation = TimerDuration::zero();
TimePoint m_lastFrame;
TimePoint m_nextDeadline = TimePoint();
bool m_initialized = false;
bool m_envOverride = false;
void initialize();
};
}