[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:
@@ -164,13 +164,20 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
|||||||
normalized_framerate_limit))
|
normalized_framerate_limit))
|
||||||
: 1.0;
|
: 1.0;
|
||||||
uint64_t last_frame_time = Clock::QueryGuestTickCount();
|
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;
|
constexpr double duration_scalar = 0.90;
|
||||||
|
#endif
|
||||||
|
#if XE_PLATFORM_LINUX
|
||||||
|
constexpr double duration_scalar = 1.0;
|
||||||
|
#endif
|
||||||
|
|
||||||
while (frame_limiter_worker_running_) {
|
while (frame_limiter_worker_running_) {
|
||||||
register_file()->values[XE_GPU_REG_D1MODE_V_COUNTER] +=
|
register_file()->values[XE_GPU_REG_D1MODE_V_COUNTER] +=
|
||||||
GetInternalDisplayResolution().second;
|
GetInternalDisplayResolution().second;
|
||||||
|
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
if (cvars::vsync) {
|
if (cvars::vsync) {
|
||||||
const uint64_t current_time = Clock::QueryGuestTickCount();
|
const uint64_t current_time = Clock::QueryGuestTickCount();
|
||||||
const uint64_t tick_freq = Clock::guest_tick_frequency();
|
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) {
|
if (elapsed_d >= vsync_duration_d) {
|
||||||
last_frame_time = current_time;
|
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();
|
MarkVblank();
|
||||||
if (cvars::vsync) {
|
const uint64_t estimated_nanoseconds = static_cast<uint64_t>(
|
||||||
const uint64_t estimated_nanoseconds =
|
(vsync_duration_d * 1000000.0) *
|
||||||
static_cast<uint64_t>(
|
duration_scalar); // 1000 microseconds = 1 ms
|
||||||
(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));
|
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;
|
return 0;
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user