mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-13 12:23:38 +01:00
perf(animation): do not update a ghost that has not changed
Asking DWM to put a thumbnail where it already is, as opaque as it already is, still has it compose the frame again, and an eased animation spends its first and last frames where it is. Remember where a ghost was last put and how opaque it was made, and leave it alone when that is asked of it again.
This commit is contained in:
1 parent
fd6fb3047b
commit
699f31f8bc
1 file changed
+50
@@ -17,6 +17,7 @@ use color_eyre::eyre;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_channel::bounded;
|
||||
use crossbeam_channel::unbounded;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::sync::OnceLock;
|
||||
@@ -490,6 +491,8 @@ pub struct GhostWindow {
|
||||
hthumb: isize,
|
||||
/// Where the host's client area starts in screen coordinates
|
||||
origin: (i32, i32),
|
||||
/// Where the ghost was last put, and how opaque it was made
|
||||
shown: Mutex<Option<(Rect, u8)>>,
|
||||
disposed: bool,
|
||||
}
|
||||
|
||||
@@ -521,6 +524,8 @@ impl GhostWindow {
|
||||
Ok(Self {
|
||||
hthumb,
|
||||
origin,
|
||||
// it is registered where the window is, and opaque
|
||||
shown: Mutex::new(Some((start_rect, 255))),
|
||||
disposed: false,
|
||||
})
|
||||
}
|
||||
@@ -528,7 +533,14 @@ impl GhostWindow {
|
||||
/// Move and scale the ghost to `rect`, and make it `opacity` opaque.
|
||||
/// Updating a registered thumbnail has no thread affinity, so this is
|
||||
/// applied on the calling thread and is done by the time it returns.
|
||||
///
|
||||
/// A ghost that is already there is left alone: asking DWM for what is
|
||||
/// already so still has it compose the frame again.
|
||||
pub fn update(&self, rect: Rect, opacity: u8) -> eyre::Result<()> {
|
||||
if !needs_update(&mut self.shown.lock(), rect, opacity) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
WindowsApi::dwm_update_thumbnail_properties(
|
||||
self.hthumb,
|
||||
&thumbnail_properties(destination(rect, self.origin), opacity),
|
||||
@@ -571,6 +583,17 @@ impl Drop for GhostWindow {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether putting a ghost at `rect` with `opacity` changes anything, given
|
||||
/// where it was last put, which is remembered if it does
|
||||
fn needs_update(shown: &mut Option<(Rect, u8)>, rect: Rect, opacity: u8) -> bool {
|
||||
if *shown == Some((rect, opacity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*shown = Some((rect, opacity));
|
||||
true
|
||||
}
|
||||
|
||||
/// How many frames a ghost is faded out over once its animation is over
|
||||
const FADE_OUT_FRAMES: u32 = 6;
|
||||
|
||||
@@ -679,6 +702,33 @@ mod tests {
|
||||
assert!(!is_swap_chain_class(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_ghost_is_only_updated_when_it_changes() {
|
||||
let rect = |left| Rect {
|
||||
left,
|
||||
top: 0,
|
||||
right: 100,
|
||||
bottom: 50,
|
||||
};
|
||||
let mut shown = Some((rect(0), 255));
|
||||
|
||||
// where it is already
|
||||
assert!(!needs_update(&mut shown, rect(0), 255));
|
||||
|
||||
// moved, or faded, or both
|
||||
assert!(needs_update(&mut shown, rect(10), 255));
|
||||
assert!(!needs_update(&mut shown, rect(10), 255));
|
||||
assert!(needs_update(&mut shown, rect(10), 200));
|
||||
assert!(needs_update(&mut shown, rect(20), 100));
|
||||
|
||||
// and back, which is a change from where it was last
|
||||
assert!(needs_update(&mut shown, rect(10), 200));
|
||||
|
||||
// one that was never put anywhere is put somewhere
|
||||
let mut never = None;
|
||||
assert!(needs_update(&mut never, rect(0), 255));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_owner_thread_polls_slowly_when_there_are_no_ghosts() {
|
||||
assert_eq!(poll_interval(false), BUSY_POLL_INTERVAL);
|
||||
|
||||
Reference in new issue
Block a user