mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-23 19:54:16 +01:00
[dxvk] Fix lack of forward progress guarantee in presenter
Turns out that relying on two threads to unblock another was a bad idea. Fixes a potential deadlock w.r.t. swapchain recreation with Reflex.
This commit is contained in:
parent
ac17d5f9b1
commit
efeb15edbd
@ -259,9 +259,16 @@ namespace dxvk {
|
||||
return;
|
||||
|
||||
if (m_device->features().khrPresentWait.presentWait) {
|
||||
std::lock_guard lock(m_frameMutex);
|
||||
bool canSignal = false;
|
||||
|
||||
{ std::unique_lock lock(m_frameMutex);
|
||||
|
||||
m_lastSignaled = frameId;
|
||||
m_frameCond.notify_one();
|
||||
canSignal = m_lastCompleted >= frameId;
|
||||
}
|
||||
|
||||
if (canSignal)
|
||||
m_signal->signal(frameId);
|
||||
} else {
|
||||
m_fpsLimiter.delay();
|
||||
m_signal->signal(frameId);
|
||||
@ -1203,24 +1210,25 @@ namespace dxvk {
|
||||
void Presenter::runFrameThread() {
|
||||
env::setThreadName("dxvk-frame");
|
||||
|
||||
std::unique_lock lock(m_frameMutex);
|
||||
|
||||
while (true) {
|
||||
PresenterFrame frame = { };
|
||||
|
||||
// Wait for all GPU work for this frame to complete in order to maintain
|
||||
// ordering guarantees of the frame signal w.r.t. objects being released
|
||||
{ std::unique_lock lock(m_frameMutex);
|
||||
|
||||
m_frameCond.wait(lock, [this] {
|
||||
return !m_frameQueue.empty() && m_frameQueue.front().frameId <= m_lastSignaled;
|
||||
return !m_frameQueue.empty();
|
||||
});
|
||||
|
||||
// Use a frame ID of 0 as an exit condition
|
||||
PresenterFrame frame = m_frameQueue.front();
|
||||
frame = m_frameQueue.front();
|
||||
|
||||
if (!frame.frameId) {
|
||||
m_frameQueue.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -1240,20 +1248,27 @@ namespace dxvk {
|
||||
frame.tracker = nullptr;
|
||||
}
|
||||
|
||||
// Apply FPS limtier 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
|
||||
// low refresh rate display as closely as we can.
|
||||
m_fpsLimiter.delay();
|
||||
|
||||
// Always signal even on error, since failures here
|
||||
// are transparent to the front-end.
|
||||
m_signal->signal(frame.frameId);
|
||||
|
||||
// Wake up any thread that may be waiting for the queue to become empty
|
||||
lock.lock();
|
||||
bool canSignal = false;
|
||||
|
||||
{ std::unique_lock lock(m_frameMutex);
|
||||
|
||||
m_frameQueue.pop();
|
||||
m_frameDrain.notify_one();
|
||||
|
||||
m_lastCompleted = frame.frameId;
|
||||
canSignal = m_lastSignaled >= frame.frameId;
|
||||
}
|
||||
|
||||
// Always signal even on error, since failures here
|
||||
// are transparent to the front-end.
|
||||
if (canSignal)
|
||||
m_signal->signal(frame.frameId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,6 +315,7 @@ namespace dxvk {
|
||||
std::queue<PresenterFrame> m_frameQueue;
|
||||
|
||||
uint64_t m_lastSignaled = 0u;
|
||||
uint64_t m_lastCompleted = 0u;
|
||||
|
||||
alignas(CACHE_LINE_SIZE)
|
||||
FpsLimiter m_fpsLimiter;
|
||||
|
Loading…
x
Reference in New Issue
Block a user