Skip to content

Commit

Permalink
[Windows] Improve frame pacing by busy waiting as needed
Browse files Browse the repository at this point in the history
  • Loading branch information
Faless committed Nov 28, 2024
1 parent 9aed9ec commit 2f49423
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
50 changes: 49 additions & 1 deletion platform/windows/os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,15 @@ void OS_Windows::initialize() {

// set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as
// long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1)
timeBeginPeriod(1);
TIMECAPS time_caps;
if (timeGetDevCaps(&time_caps, sizeof(time_caps)) == MMSYSERR_NOERROR) {
ticks_min_sleep = time_caps.wPeriodMin * 1000;

Check failure on line 270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'ticks_min_sleep': undeclared identifier

Check failure on line 270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'ticks_min_sleep': undeclared identifier

Check failure on line 270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'ticks_min_sleep' was not declared in this scope
timeBeginPeriod(time_caps.wPeriodMin);
} else {
ERR_PRINT("Unable to detect sleep timer resolution.");
ticks_min_sleep = 1000;

Check failure on line 274 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'ticks_min_sleep': undeclared identifier

Check failure on line 274 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'ticks_min_sleep': undeclared identifier

Check failure on line 274 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'ticks_min_sleep' was not declared in this scope
timeBeginPeriod(1);
}

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

Expand Down Expand Up @@ -2223,6 +2231,46 @@ String OS_Windows::get_system_ca_certificates() {
return certs;
}

void OS_Windows::add_frame_delay(bool p_can_draw) {
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
if (frame_delay) {
// Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
// the actual frame time into account.
// Due to the high fluctuation of the actual sleep duration, it's not recommended
// to use this as a FPS limiter.
delay_usec(frame_delay * 1000);
}

// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
// previous frame time into account for a smoother result.
uint64_t dynamic_delay = 0;
if (is_in_low_processor_usage_mode() || !p_can_draw) {
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
}
const int max_fps = Engine::get_singleton()->get_max_fps();
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
// Override the low processor usage mode sleep delay if the target FPS is lower.
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
}

if (dynamic_delay > 0) {
target_ticks += dynamic_delay;

Check failure on line 2257 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2257 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2257 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context

Check failure on line 2257 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context
uint64_t current_ticks = get_ticks_usec();

// The minimum sleep resolution on windows is 1 ms on most systems.
if (current_ticks < (target_ticks - ticks_sleep_min)) {

Check failure on line 2261 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2261 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2261 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context

Check failure on line 2261 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context
delay_usec((target_ticks - ticks_sleep_min) - current_ticks);

Check failure on line 2262 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2262 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2262 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context

Check failure on line 2262 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context
}
// Busy wait for the remainder of time.
while (get_ticks_usec() < target_ticks) {

Check failure on line 2265 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2265 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2265 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context

Check failure on line 2265 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template w/ GCC (target=template_release, tests=yes, use_mingw=yes)

'uint64_t OS::target_ticks' is private within this context
YieldProcessor();
}

current_ticks = get_ticks_usec();
target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);

Check failure on line 2270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'

Check failure on line 2270 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release, tests=yes)

'OS::target_ticks': cannot access private member declared in class 'OS'
}
}

OS_Windows::OS_Windows(HINSTANCE _hInstance) {
hInstance = _hInstance;

Expand Down
3 changes: 3 additions & 0 deletions platform/windows/os_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class JoypadWindows;
class OS_Windows : public OS {
uint64_t ticks_start = 0;
uint64_t ticks_per_second = 0;
uint64_t ticks_sleep_min = 1000;

HINSTANCE hInstance;
MainLoop *main_loop = nullptr;
Expand Down Expand Up @@ -245,6 +246,8 @@ class OS_Windows : public OS {

virtual String get_system_ca_certificates() override;

void add_frame_delay(bool p_can_draw) override;

void set_main_window(HWND p_main_window) { main_window = p_main_window; }

HINSTANCE get_hinstance() { return hInstance; }
Expand Down

0 comments on commit 2f49423

Please sign in to comment.