Selaa lähdekoodia

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

Pāvels Nadtočajevs 5 kuukautta sitten
vanhempi
commit
98f377d9d0

+ 19 - 1
core/os/os.cpp

@@ -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

+ 2 - 1
core/os/os.h

@@ -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;

+ 2 - 4
main/main.cpp

@@ -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) {

+ 3 - 0
platform/macos/os_macos.h

@@ -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;
 

+ 25 - 2
platform/macos/os_macos.mm

@@ -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;

+ 2 - 2
platform/web/os_web.cpp

@@ -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
 }
 

+ 1 - 1
platform/web/os_web.h

@@ -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;
 

+ 15 - 1
platform/windows/os_windows.cpp

@@ -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

+ 1 - 1
platform/windows/os_windows.h

@@ -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;