Skip to content

Commit

Permalink
[util,dxvk] Limit frame rate based on deadline
Browse files Browse the repository at this point in the history
This should work better now that present_wait is universally supported.
  • Loading branch information
doitsujin committed Jun 10, 2024
1 parent ed95853 commit 2263485
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 40 deletions.
7 changes: 3 additions & 4 deletions src/dxvk/dxvk_presenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,10 +667,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.
m_fpsLimiter.delay();

// 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).
Expand All @@ -685,6 +681,9 @@ namespace dxvk {
// Always signal even on error, since failures here
// are transparent to the front-end.
m_signal->signal(frame.frameId);

// Apply FPS limtier here to align it as closely with scanout as we can.
m_fpsLimiter.delay();
}
}

Expand Down
39 changes: 8 additions & 31 deletions src/util/util_fps_limiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ namespace dxvk {

m_heuristicFrameCount = 0;
m_heuristicEnable = false;

if (m_targetInterval != TimerDuration::zero() && !m_initialized)
initialize();
}
}
}
Expand All @@ -56,10 +53,11 @@ namespace dxvk {
std::unique_lock<dxvk::mutex> lock(m_mutex);
auto interval = m_targetInterval;

if (interval == TimerDuration::zero())
if (interval == TimerDuration::zero()) {
m_nextFrame = TimePoint();
return;
}

auto t0 = m_lastFrame;
auto t1 = dxvk::high_resolution_clock::now();

if (interval < TimerDuration::zero()) {
Expand All @@ -73,27 +71,12 @@ namespace dxvk {
// that can be written by setTargetFrameRate
lock.unlock();

auto frameTime = std::chrono::duration_cast<TimerDuration>(t1 - t0);

if (frameTime * 100 > interval * 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();
} 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 = interval - 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 - interval;
m_deviation = std::min(m_deviation, interval / 16);
}
if (t1 < m_nextFrame)
Sleep::sleepUntil(t1, m_nextFrame);

m_lastFrame = t1;
m_nextFrame = (t1 < m_nextFrame + interval)
? m_nextFrame + interval
: t1 + interval;
}


Expand Down Expand Up @@ -131,10 +114,4 @@ namespace dxvk {
return m_heuristicEnable;
}


void FpsLimiter::initialize() {
m_lastFrame = dxvk::high_resolution_clock::now();
m_initialized = true;
}

}
6 changes: 1 addition & 5 deletions src/util/util_fps_limiter.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ namespace dxvk {
dxvk::mutex m_mutex;

TimerDuration m_targetInterval = TimerDuration::zero();
TimerDuration m_deviation = TimerDuration::zero();
TimePoint m_lastFrame = TimePoint();
TimePoint m_nextFrame = TimePoint();

bool m_initialized = false;
bool m_envOverride = false;

uint32_t m_heuristicFrameCount = 0;
Expand All @@ -59,8 +57,6 @@ namespace dxvk {

bool testRefreshHeuristic(TimerDuration interval, TimePoint now);

void initialize();

};

}

0 comments on commit 2263485

Please sign in to comment.