1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

VBlank: If previous frame is still in progress, wait until next frame to trigger a new vblank

Previously, the vblank's thread loop would block on each iteration until
the current async callback had finished, at which point a new async
callback would be immediately triggered.

The new implementation only waits on the vblank event. If a vblank
callback is still in progress the next time the vblank event is
signalled, we assume the last frame is overrunning and avoid sending a
new async update.

The intention of this change is to avoid saturating the message thread
with expensive vblank callbacks. It's also nice to remove a lock,
although that's just an incidental change.
This commit is contained in:
reuk 2024-12-12 15:29:07 +00:00
parent f887979ec0
commit d3d7d4c89c

View file

@ -56,12 +56,7 @@ public:
{
cancelPendingUpdate();
{
const std::scoped_lock lock { mutex };
threadState = ThreadState::exit;
}
condvar.notify_one();
state |= flagExit;
stopThread (-1);
}
@ -121,24 +116,24 @@ private:
{
if (output->WaitForVBlank() == S_OK)
{
if (const auto now = Time::getMillisecondCounterHiRes();
now - lastVBlankEvent.exchange (now) < 1.0)
{
Thread::sleep (1);
}
const auto now = Time::getMillisecondCounterHiRes();
std::unique_lock lock { mutex };
condvar.wait (lock, [this] { return threadState != ThreadState::sleep; });
if (now - lastVBlankEvent.exchange (now) < 1.0)
sleep (1);
if (threadState == ThreadState::exit)
const auto stateToRead = state.fetch_or (flagPaintPending);
if ((stateToRead & flagExit) != 0)
return;
threadState = ThreadState::sleep;
if ((stateToRead & flagPaintPending) != 0)
continue;
triggerAsyncUpdate();
}
else
{
Thread::sleep (1);
sleep (1);
}
}
}
@ -150,32 +145,22 @@ private:
for (auto& listener : listeners)
listener.get().onVBlank (timestampSec);
{
const std::scoped_lock lock { mutex };
if (threadState == ThreadState::sleep)
threadState = ThreadState::paint;
}
condvar.notify_one();
state &= ~flagPaintPending;
}
enum Flags
{
flagExit = 1 << 0,
flagPaintPending = 1 << 1,
};
//==============================================================================
ComSmartPtr<IDXGIOutput> output;
HMONITOR monitor = nullptr;
std::vector<std::reference_wrapper<VBlankListener>> listeners;
enum class ThreadState
{
sleep,
paint,
exit,
};
std::atomic<double> lastVBlankEvent{};
ThreadState threadState = ThreadState::paint;
std::condition_variable condvar;
std::mutex mutex;
std::atomic<int> state{};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VBlankThread)
JUCE_DECLARE_NON_MOVEABLE (VBlankThread)