You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-12-01 16:38:31 +00:00
Merge pull request #99833 from Faless/fix/sleepy_windows_becomes_a_heater
[Windows] Improve frame pacing by busy waiting as needed
This commit is contained in:
@@ -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) {
|
||||
delay_resolution = time_caps.wPeriodMin * 1000;
|
||||
timeBeginPeriod(time_caps.wPeriodMin);
|
||||
} else {
|
||||
ERR_PRINT("Unable to detect sleep timer resolution.");
|
||||
delay_resolution = 1000;
|
||||
timeBeginPeriod(1);
|
||||
}
|
||||
|
||||
process_map = memnew((HashMap<ProcessID, ProcessInfo>));
|
||||
|
||||
@@ -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;
|
||||
uint64_t current_ticks = get_ticks_usec();
|
||||
|
||||
// The minimum sleep resolution on windows is 1 ms on most systems.
|
||||
if (current_ticks < (target_ticks - delay_resolution)) {
|
||||
delay_usec((target_ticks - delay_resolution) - current_ticks);
|
||||
}
|
||||
// Busy wait for the remainder of time.
|
||||
while (get_ticks_usec() < target_ticks) {
|
||||
YieldProcessor();
|
||||
}
|
||||
|
||||
current_ticks = get_ticks_usec();
|
||||
target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);
|
||||
}
|
||||
}
|
||||
|
||||
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
|
||||
hInstance = _hInstance;
|
||||
|
||||
|
||||
@@ -94,8 +94,10 @@ public:
|
||||
class JoypadWindows;
|
||||
|
||||
class OS_Windows : public OS {
|
||||
uint64_t target_ticks = 0;
|
||||
uint64_t ticks_start = 0;
|
||||
uint64_t ticks_per_second = 0;
|
||||
uint64_t delay_resolution = 1000;
|
||||
|
||||
HINSTANCE hInstance;
|
||||
MainLoop *main_loop = nullptr;
|
||||
@@ -188,6 +190,7 @@ public:
|
||||
|
||||
virtual Error set_cwd(const String &p_cwd) override;
|
||||
|
||||
virtual void add_frame_delay(bool p_can_draw) override;
|
||||
virtual void delay_usec(uint32_t p_usec) const override;
|
||||
virtual uint64_t get_ticks_usec() const override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user