fix(videoout): wait for flips with the high-resolution condition on Windows
The flip queue's condition variable went through winpthreads, whose timed waits end on GetTickCount64 steps: the present thread's 10 ms poll for SDL events and the pad, taken while no flip is queued, ended 11 to 24 ms later on Windows, and WaitForFlipRoom's 60 s bound had the same granularity. Use TimedWait::Condition, which waits on an event and a high-resolution timer, so the poll ends at 10 ms and a submitted flip still wakes the present thread at once. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
0490ca18dd
commit
96890dee37
|
|
@ -20,6 +20,7 @@
|
|||
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
|
||||
#include "prx/libSceVideoOut/include/BufferReuseTracker.hpp"
|
||||
#include "prx/libc/include/Shutdown.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SceTypes.hpp"
|
||||
|
|
@ -190,7 +191,7 @@ struct FlipRequest final : AgcDriver::IFlipRequest, std::enable_shared_from_this
|
|||
|
||||
struct FlipQueue {
|
||||
std::mutex mutex;
|
||||
std::condition_variable changed;
|
||||
TimedWait::Condition changed;
|
||||
std::list<std::shared_ptr<FlipRequest>> requests;
|
||||
std::atomic<std::size_t> reservations = 0;
|
||||
std::exception_ptr failure;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr std::uint64_t FLIP_ROOM_WAIT_MICROS = 60000000;
|
||||
constexpr std::uint64_t PRESENT_POLL_MICROS = 10000;
|
||||
|
||||
void require(bool condition, const char* reason) {
|
||||
if (!condition) throw std::runtime_error(std::string("VideoOut: ") + reason);
|
||||
}
|
||||
|
|
@ -119,7 +122,7 @@ public:
|
|||
|
||||
void WaitForFlipRoom() override {
|
||||
std::unique_lock queueLock(queue->mutex);
|
||||
const bool room = queue->changed.wait_for(queueLock, std::chrono::seconds(60), [&] {
|
||||
const bool room = queue->changed.WaitUntil(queueLock, TimedWait::DeadlineNanos(FLIP_ROOM_WAIT_MICROS), [&] {
|
||||
return queue->failure || queue->stopping || cfg->shutdownToken.stop_requested() || queue->reservations.load() < VIDEO_OUT_FLIP_QUEUE_CAPACITY;
|
||||
});
|
||||
if (queue->failure) std::rethrow_exception(queue->failure);
|
||||
|
|
@ -164,7 +167,7 @@ public:
|
|||
if (!cfg->failure) cfg->failure = error;
|
||||
cfg->vblankCond.notify_all();
|
||||
}
|
||||
queue->changed.notify_all();
|
||||
queue->changed.NotifyAll();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -222,7 +225,7 @@ void FlipRequest::GpuReady(const std::shared_ptr<AgcDriver::FrameTiming>& frameT
|
|||
queue->requests.push_back(shared_from_this());
|
||||
ready = true;
|
||||
}
|
||||
queue->changed.notify_all();
|
||||
queue->changed.NotifyAll();
|
||||
// The worker is done once the request is queued: hardware does not stall the command processor
|
||||
// on a flip, and the title observes completion through flipPendingNum, which processFlip drops
|
||||
// once the frame's GPU work and its presentation have completed (see Driver::Present). A
|
||||
|
|
@ -241,7 +244,7 @@ void FlipRequest::Fail(std::exception_ptr error) noexcept {
|
|||
std::lock_guard lock(cfg->mutex);
|
||||
if (!cfg->failure) cfg->failure = error;
|
||||
ReleaseLocked();
|
||||
queue->changed.notify_all();
|
||||
queue->changed.NotifyAll();
|
||||
}
|
||||
|
||||
VideoOutDriver& VideoOutDriver::Get() {
|
||||
|
|
@ -273,12 +276,12 @@ VideoOutDriver::VideoOutDriver() {
|
|||
} catch (...) {
|
||||
if (vblankThread.joinable()) {
|
||||
vblankThread.request_stop();
|
||||
flipQueue->changed.notify_all();
|
||||
flipQueue->changed.NotifyAll();
|
||||
vblankThread.join();
|
||||
}
|
||||
if (presentThread.joinable()) {
|
||||
presentThread.request_stop();
|
||||
flipQueue->changed.notify_all();
|
||||
flipQueue->changed.NotifyAll();
|
||||
presentThread.join();
|
||||
}
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
|
||||
|
|
@ -301,7 +304,7 @@ void VideoOutDriver::Shutdown() {
|
|||
}
|
||||
presentThread.request_stop();
|
||||
vblankThread.request_stop();
|
||||
flipQueue->changed.notify_all();
|
||||
flipQueue->changed.NotifyAll();
|
||||
presentThread.join();
|
||||
vblankThread.join();
|
||||
{
|
||||
|
|
@ -377,7 +380,7 @@ bool VideoOutDriver::close(int handle) {
|
|||
cfg->outputModeEvents.clear();
|
||||
cfg->vrrStatusEvents.clear();
|
||||
cfg->vblankCond.notify_all();
|
||||
flipQueue->changed.notify_all();
|
||||
flipQueue->changed.NotifyAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -570,7 +573,7 @@ void VideoOutDriver::presentLoop(std::stop_token token, std::promise<void>& star
|
|||
while (!token.stop_requested()) {
|
||||
{
|
||||
std::unique_lock lock(flipQueue->mutex);
|
||||
flipQueue->changed.wait_for(lock, std::chrono::milliseconds(10), [&] { return token.stop_requested() || flipQueue->failure || !flipQueue->requests.empty(); });
|
||||
flipQueue->changed.WaitUntil(lock, TimedWait::DeadlineNanos(PRESENT_POLL_MICROS), [&] { return token.stop_requested() || flipQueue->failure || !flipQueue->requests.empty(); });
|
||||
if (flipQueue->failure) std::rethrow_exception(flipQueue->failure);
|
||||
if (token.stop_requested()) break;
|
||||
if (!flipQueue->requests.empty()) {
|
||||
|
|
@ -611,7 +614,7 @@ void VideoOutDriver::presentLoop(std::stop_token token, std::promise<void>& star
|
|||
}
|
||||
if (current) {
|
||||
current.reset();
|
||||
flipQueue->changed.notify_all();
|
||||
flipQueue->changed.NotifyAll();
|
||||
}
|
||||
}
|
||||
} catch (const ProcessShutdown&) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue