perf(animation): do not wake the ghost thread every 8ms for nothing

The thread that owns the ghost host window looked for messages every 8ms for
as long as komorebi ran once ghost movement was on, about 125 wakeups a second
doing nothing, which keeps a processor from sleeping. Poll at that rate only
while there are ghosts, and every 250ms while there are none: it is woken at
once by a command either way.
This commit is contained in:
LGUG2Z committed 2026-10-30 15:45:37 -07:00
1 parent 388a17d682
commit fd6fb3047b
1 file changed
+29 -1
+29 -1
View File
@@ -273,6 +273,11 @@ impl GhostHost {
(self.bounds.0, self.bounds.1) (self.bounds.0, self.bounds.1)
} }
/// Whether there are no ghosts
fn is_idle(&self) -> bool {
self.thumbnails.is_empty()
}
fn register( fn register(
&mut self, &mut self,
snapshot: &Snapshot, snapshot: &Snapshot,
@@ -396,6 +401,22 @@ unsafe fn init_layered_transparent(hwnd: HWND) {
} }
} }
/// How often the owner thread looks for messages for its windows when no
/// command comes. While there are ghosts that is often, so that they are never
/// kept waiting; while there are none the windows it has are a host nobody can
/// see and nothing is sent to, and waking up every few milliseconds for them
/// keeps the processor from sleeping.
fn poll_interval(idle: bool) -> Duration {
if idle {
IDLE_POLL_INTERVAL
} else {
BUSY_POLL_INTERVAL
}
}
const BUSY_POLL_INTERVAL: Duration = Duration::from_millis(8);
const IDLE_POLL_INTERVAL: Duration = Duration::from_millis(250);
fn run_owner_loop(cmd_rx: crossbeam_channel::Receiver<GhostCmd>) { fn run_owner_loop(cmd_rx: crossbeam_channel::Receiver<GhostCmd>) {
let mut host = match GhostHost::create() { let mut host = match GhostHost::create() {
Ok(host) => Some(host), Ok(host) => Some(host),
@@ -415,7 +436,7 @@ fn run_owner_loop(cmd_rx: crossbeam_channel::Receiver<GhostCmd>) {
} }
} }
match cmd_rx.recv_timeout(Duration::from_millis(8)) { match cmd_rx.recv_timeout(poll_interval(host.as_ref().is_some_and(GhostHost::is_idle))) {
Ok(GhostCmd::Create { Ok(GhostCmd::Create {
snapshot, snapshot,
start_rect, start_rect,
@@ -658,6 +679,13 @@ mod tests {
assert!(!is_swap_chain_class("")); assert!(!is_swap_chain_class(""));
} }
#[test]
fn the_owner_thread_polls_slowly_when_there_are_no_ghosts() {
assert_eq!(poll_interval(false), BUSY_POLL_INTERVAL);
assert_eq!(poll_interval(true), IDLE_POLL_INTERVAL);
assert!(IDLE_POLL_INTERVAL >= BUSY_POLL_INTERVAL * 10);
}
#[test] #[test]
fn the_ghost_is_opaque_until_the_window_is_shown_and_gone_at_the_end() { fn the_ghost_is_opaque_until_the_window_is_shown_and_gone_at_the_end() {
assert_eq!(ghost_opacity(0.0, REVEAL_AT), 255); assert_eq!(ghost_opacity(0.0, REVEAL_AT), 255);