[GPU] Ensure vsync works correctly on Linux.

Linux scheduler quantum is 4-10ms so requesting 15ms sleep ends
up sleeping 16-19ms and overshoots our target causing games to run
at 57fps
This commit is contained in:
Herman S.
2025-09-27 18:09:50 +09:00
parent 9a704bff1e
commit 37fabb9346

View File

@@ -164,13 +164,20 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
normalized_framerate_limit))
: 1.0;
uint64_t last_frame_time = Clock::QueryGuestTickCount();
// Sleep for 90% of the vblank duration, spin for 10%
// Sleep for 90% of the vblank duration on Windows, spin for 10%
// Linux uses full sleep duration due to scheduler quantum issues
#if XE_PLATFORM_WIN32
constexpr double duration_scalar = 0.90;
#endif
#if XE_PLATFORM_LINUX
constexpr double duration_scalar = 1.0;
#endif
while (frame_limiter_worker_running_) {
register_file()->values[XE_GPU_REG_D1MODE_V_COUNTER] +=
GetInternalDisplayResolution().second;
#if XE_PLATFORM_WIN32
if (cvars::vsync) {
const uint64_t current_time = Clock::QueryGuestTickCount();
const uint64_t tick_freq = Clock::guest_tick_frequency();
@@ -181,18 +188,12 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
if (elapsed_d >= vsync_duration_d) {
last_frame_time = current_time;
// TODO(disjtqz): should recalculate the remaining time to a
// vblank after MarkVblank, no idea how long the guest code
// normally takes
MarkVblank();
if (cvars::vsync) {
const uint64_t estimated_nanoseconds =
static_cast<uint64_t>(
(vsync_duration_d * 1000000.0) *
duration_scalar); // 1000 microseconds = 1 ms
const uint64_t estimated_nanoseconds = static_cast<uint64_t>(
(vsync_duration_d * 1000000.0) *
duration_scalar); // 1000 microseconds = 1 ms
threading::NanoSleep(estimated_nanoseconds);
}
threading::NanoSleep(estimated_nanoseconds);
}
}
@@ -210,6 +211,22 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
xe::threading::Sleep(std::chrono::milliseconds(1));
}
}
#endif
#if XE_PLATFORM_LINUX
// Linux: Use simplified timing logic to avoid oversleeping
MarkVblank();
if (cvars::vsync || normalized_framerate_limit > 0) {
uint64_t sleep_duration_ns =
static_cast<uint64_t>(vsync_duration_d * 1000000.0);
if (!cvars::vsync && normalized_framerate_limit > 0) {
sleep_duration_ns = 1000000000 / normalized_framerate_limit;
}
threading::NanoSleep(sleep_duration_ns);
} else {
xe::threading::Sleep(std::chrono::milliseconds(1));
}
#endif
}
return 0;
},