Skip to content

Commit b57ceef

Browse files
mrsaturnsanFaless
authored andcommitted
[Windows] Improve frame pacing by busy waiting as needed
1 parent 0eadbdb commit b57ceef

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

platform/windows/os_windows.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,15 @@ void OS_Windows::initialize() {
265265

266266
// set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as
267267
// long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1)
268-
timeBeginPeriod(1);
268+
TIMECAPS time_caps;
269+
if (timeGetDevCaps(&time_caps, sizeof(time_caps)) == MMSYSERR_NOERROR) {
270+
delay_resolution = time_caps.wPeriodMin * 1000;
271+
timeBeginPeriod(time_caps.wPeriodMin);
272+
} else {
273+
ERR_PRINT("Unable to detect sleep timer resolution.");
274+
delay_resolution = 1000;
275+
timeBeginPeriod(1);
276+
}
269277

270278
process_map = memnew((HashMap<ProcessID, ProcessInfo>));
271279

@@ -2223,6 +2231,46 @@ String OS_Windows::get_system_ca_certificates() {
22232231
return certs;
22242232
}
22252233

2234+
void OS_Windows::add_frame_delay(bool p_can_draw) {
2235+
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
2236+
if (frame_delay) {
2237+
// Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
2238+
// the actual frame time into account.
2239+
// Due to the high fluctuation of the actual sleep duration, it's not recommended
2240+
// to use this as a FPS limiter.
2241+
delay_usec(frame_delay * 1000);
2242+
}
2243+
2244+
// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
2245+
// previous frame time into account for a smoother result.
2246+
uint64_t dynamic_delay = 0;
2247+
if (is_in_low_processor_usage_mode() || !p_can_draw) {
2248+
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
2249+
}
2250+
const int max_fps = Engine::get_singleton()->get_max_fps();
2251+
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
2252+
// Override the low processor usage mode sleep delay if the target FPS is lower.
2253+
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
2254+
}
2255+
2256+
if (dynamic_delay > 0) {
2257+
target_ticks += dynamic_delay;
2258+
uint64_t current_ticks = get_ticks_usec();
2259+
2260+
// The minimum sleep resolution on windows is 1 ms on most systems.
2261+
if (current_ticks < (target_ticks - delay_resolution)) {
2262+
delay_usec((target_ticks - delay_resolution) - current_ticks);
2263+
}
2264+
// Busy wait for the remainder of time.
2265+
while (get_ticks_usec() < target_ticks) {
2266+
YieldProcessor();
2267+
}
2268+
2269+
current_ticks = get_ticks_usec();
2270+
target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);
2271+
}
2272+
}
2273+
22262274
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
22272275
hInstance = _hInstance;
22282276

platform/windows/os_windows.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ class ComAutoreleaseRef {
9494
class JoypadWindows;
9595

9696
class OS_Windows : public OS {
97+
uint64_t target_ticks = 0;
9798
uint64_t ticks_start = 0;
9899
uint64_t ticks_per_second = 0;
100+
uint64_t delay_resolution = 1000;
99101

100102
HINSTANCE hInstance;
101103
MainLoop *main_loop = nullptr;
@@ -188,6 +190,7 @@ class OS_Windows : public OS {
188190

189191
virtual Error set_cwd(const String &p_cwd) override;
190192

193+
virtual void add_frame_delay(bool p_can_draw) override;
191194
virtual void delay_usec(uint32_t p_usec) const override;
192195
virtual uint64_t get_ticks_usec() const override;
193196

0 commit comments

Comments
 (0)