mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-24 04:54:14 +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;
|
return;
|
||||||
|
|
||||||
if (m_device->features().khrPresentWait.presentWait) {
|
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_lastSignaled = frameId;
|
||||||
m_frameCond.notify_one();
|
canSignal = m_lastCompleted >= frameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canSignal)
|
||||||
|
m_signal->signal(frameId);
|
||||||
} else {
|
} else {
|
||||||
m_fpsLimiter.delay();
|
m_fpsLimiter.delay();
|
||||||
m_signal->signal(frameId);
|
m_signal->signal(frameId);
|
||||||
@ -1203,24 +1210,25 @@ namespace dxvk {
|
|||||||
void Presenter::runFrameThread() {
|
void Presenter::runFrameThread() {
|
||||||
env::setThreadName("dxvk-frame");
|
env::setThreadName("dxvk-frame");
|
||||||
|
|
||||||
std::unique_lock lock(m_frameMutex);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
PresenterFrame frame = { };
|
||||||
|
|
||||||
// Wait for all GPU work for this frame to complete in order to maintain
|
// 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
|
// ordering guarantees of the frame signal w.r.t. objects being released
|
||||||
|
{ std::unique_lock lock(m_frameMutex);
|
||||||
|
|
||||||
m_frameCond.wait(lock, [this] {
|
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
|
// Use a frame ID of 0 as an exit condition
|
||||||
PresenterFrame frame = m_frameQueue.front();
|
frame = m_frameQueue.front();
|
||||||
|
|
||||||
if (!frame.frameId) {
|
if (!frame.frameId) {
|
||||||
m_frameQueue.pop();
|
m_frameQueue.pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
// If the present operation has succeeded, actually wait for it to complete.
|
// 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
|
// Don't bother with it on MAILBOX / IMMEDIATE modes since doing so would
|
||||||
@ -1240,20 +1248,27 @@ namespace dxvk {
|
|||||||
frame.tracker = nullptr;
|
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
|
// and delay signaling the frame latency event to emulate behaviour of a
|
||||||
// low refresh rate display as closely as we can.
|
// low refresh rate display as closely as we can.
|
||||||
m_fpsLimiter.delay();
|
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
|
// 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_frameQueue.pop();
|
||||||
m_frameDrain.notify_one();
|
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;
|
std::queue<PresenterFrame> m_frameQueue;
|
||||||
|
|
||||||
uint64_t m_lastSignaled = 0u;
|
uint64_t m_lastSignaled = 0u;
|
||||||
|
uint64_t m_lastCompleted = 0u;
|
||||||
|
|
||||||
alignas(CACHE_LINE_SIZE)
|
alignas(CACHE_LINE_SIZE)
|
||||||
FpsLimiter m_fpsLimiter;
|
FpsLimiter m_fpsLimiter;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user