1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Use system timer/wait functions for frame delay when screen reader is active.

This commit is contained in:
Pāvels Nadtočajevs
2025-04-12 16:21:01 +03:00
parent 1b37dacc18
commit 98f377d9d0
9 changed files with 70 additions and 13 deletions

View File

@@ -658,7 +658,25 @@ void OS::close_midi_inputs() {
}
}
void OS::add_frame_delay(bool p_can_draw) {
uint64_t OS::get_frame_delay(bool p_can_draw) const {
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
// 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));
}
return frame_delay + dynamic_delay;
}
void OS::add_frame_delay(bool p_can_draw, bool p_wake_for_events) {
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

View File

@@ -247,7 +247,8 @@ public:
virtual double get_unix_time() const;
virtual void delay_usec(uint32_t p_usec) const = 0;
virtual void add_frame_delay(bool p_can_draw);
virtual void add_frame_delay(bool p_can_draw, bool p_wake_for_events);
virtual uint64_t get_frame_delay(bool p_can_draw) const;
virtual uint64_t get_ticks_usec() const = 0;
uint64_t get_ticks_msec() const;

View File

@@ -4855,11 +4855,9 @@ bool Main::iteration() {
}
SceneTree *scene_tree = SceneTree::get_singleton();
bool skip_delay = scene_tree && scene_tree->is_accessibility_enabled();
bool wake_for_events = scene_tree && scene_tree->is_accessibility_enabled();
if (!skip_delay) {
OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw());
}
OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw(), wake_for_events);
#ifdef TOOLS_ENABLED
if (auto_build_solutions) {

View File

@@ -64,6 +64,7 @@ protected:
JoypadApple *joypad_apple = nullptr;
MainLoop *main_loop = nullptr;
CFRunLoopTimerRef wait_timer = nil;
virtual void initialize_core() override;
virtual void initialize() override;
@@ -75,6 +76,8 @@ protected:
virtual void delete_main_loop() override;
public:
virtual void add_frame_delay(bool p_can_draw, bool p_wake_for_events) override;
virtual void set_cmdline_platform_args(const List<String> &p_args);
virtual List<String> get_cmdline_platform_args() const override;

View File

@@ -49,6 +49,28 @@
#include <os/log.h>
#include <sys/sysctl.h>
void OS_MacOS::add_frame_delay(bool p_can_draw, bool p_wake_for_events) {
if (p_wake_for_events) {
uint64_t delay = get_frame_delay(p_can_draw);
if (delay == 0) {
return;
}
if (wait_timer) {
CFRunLoopTimerInvalidate(wait_timer);
CFRelease(wait_timer);
}
wait_timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + (double(delay) / 1000000.0), 0, 0, 0,
^(CFRunLoopTimerRef timer) {
CFRunLoopTimerInvalidate(wait_timer);
CFRelease(wait_timer);
wait_timer = nil;
});
CFRunLoopAddTimer(CFRunLoopGetCurrent(), wait_timer, kCFRunLoopCommonModes);
return;
}
OS_Unix::add_frame_delay(p_can_draw, p_wake_for_events);
}
void OS_MacOS::initialize() {
crash_handler.initialize();
@@ -995,8 +1017,9 @@ void OS_MacOS_NSApp::start_main() {
ERR_PRINT("NSException: " + String::utf8([exception reason].UTF8String));
}
}
CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
if (wait_timer == nil) {
CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
return;

View File

@@ -180,9 +180,9 @@ String OS_Web::get_name() const {
return "Web";
}
void OS_Web::add_frame_delay(bool p_can_draw) {
void OS_Web::add_frame_delay(bool p_can_draw, bool p_wake_for_events) {
#ifndef PROXY_TO_PTHREAD_ENABLED
OS::add_frame_delay(p_can_draw);
OS::add_frame_delay(p_can_draw, p_wake_for_events);
#endif
}

View File

@@ -99,7 +99,7 @@ public:
// Override default OS implementation which would block the main thread with delay_usec.
// Implemented in web_main.cpp loop callback instead.
void add_frame_delay(bool p_can_draw) override;
void add_frame_delay(bool p_can_draw, bool p_wake_for_events) override;
void vibrate_handheld(int p_duration_ms, float p_amplitude) override;

View File

@@ -2494,7 +2494,21 @@ String OS_Windows::get_system_ca_certificates() {
return certs;
}
void OS_Windows::add_frame_delay(bool p_can_draw) {
void OS_Windows::add_frame_delay(bool p_can_draw, bool p_wake_for_events) {
if (p_wake_for_events) {
uint64_t delay = get_frame_delay(p_can_draw);
if (delay == 0) {
return;
}
DisplayServer *ds = DisplayServer::get_singleton();
DisplayServerWindows *ds_win = Object::cast_to<DisplayServerWindows>(ds);
if (ds_win) {
MsgWaitForMultipleObjects(0, nullptr, false, Math::floor(double(delay) / 1000.0), QS_ALLINPUT);
return;
}
}
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

View File

@@ -193,7 +193,7 @@ public:
virtual Error set_cwd(const String &p_cwd) override;
virtual void add_frame_delay(bool p_can_draw) override;
virtual void add_frame_delay(bool p_can_draw, bool p_wake_for_events) override;
virtual void delay_usec(uint32_t p_usec) const override;
virtual uint64_t get_ticks_usec() const override;