Skip to content

Commit e2689c7

Browse files
Merge pull request #2533 from flamboh/perf/screenshot-mask-cache
perf(desktop-gpui): rebuild only changed screenshot privacy masks
2 parents aed6687 + e06647b commit e2689c7

5 files changed

Lines changed: 982 additions & 107 deletions

File tree

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use cap_gpui_kernels::mask_cache::{MaskEntry, MaskFrame, MaskRequest, mask_region, update_masks};
2+
use std::{hint::black_box, sync::Arc, time::Instant};
3+
4+
const SIZE: (u32, u32) = (3840, 2160);
5+
const WARMUP: usize = 12;
6+
const EVENTS: usize = 120;
7+
#[derive(Clone)]
8+
struct Overlay {
9+
rect: [f64; 4],
10+
image: Arc<image::RgbaImage>,
11+
}
12+
13+
type CachedOverlay = MaskEntry<Overlay>;
14+
15+
fn overlay(request: &MaskRequest, rgba: &[u8]) -> Option<Overlay> {
16+
let processed = request.filter(rgba)?;
17+
Some(Overlay {
18+
rect: [
19+
f64::from(request.region.0),
20+
f64::from(request.region.1),
21+
f64::from(processed.width()),
22+
f64::from(processed.height()),
23+
],
24+
image: Arc::new(processed),
25+
})
26+
}
27+
28+
fn requests(count: usize, pixelate: bool, event: usize) -> Vec<MaskRequest> {
29+
let content = [0., 0., f64::from(SIZE.0), f64::from(SIZE.1)];
30+
(0..count)
31+
.map(|index| {
32+
let x = 80. + (index % 4) as f64 * 720. + if index == 0 { event as f64 } else { 0. };
33+
let y = 80. + (index / 4) as f64 * 440.;
34+
MaskRequest {
35+
id: index.to_string(),
36+
frame: MaskFrame {
37+
revision: 1,
38+
size: SIZE,
39+
content: content.map(f64::to_bits),
40+
padding: 0_f64.to_bits(),
41+
crop: None,
42+
},
43+
region: mask_region([x, y, 640., 360.], content, SIZE).unwrap(),
44+
pixelate,
45+
level: 16_f64.to_bits(),
46+
}
47+
})
48+
.collect()
49+
}
50+
51+
fn rebuild_all(requests: &[MaskRequest], rgba: &[u8], calls: &mut usize) -> Vec<Overlay> {
52+
let mut overlays = Vec::new();
53+
for request in requests {
54+
*calls += 1;
55+
if let Some(overlay) = overlay(request, rgba) {
56+
overlays.push(overlay);
57+
}
58+
}
59+
overlays
60+
}
61+
62+
fn cached(
63+
previous: &[CachedOverlay],
64+
requests: &[MaskRequest],
65+
rgba: &[u8],
66+
calls: &mut usize,
67+
) -> Vec<CachedOverlay> {
68+
update_masks(previous, requests, |request| {
69+
*calls += 1;
70+
overlay(request, rgba)
71+
})
72+
}
73+
74+
fn stats(samples: &mut [f64]) -> (f64, f64) {
75+
samples.sort_by(f64::total_cmp);
76+
(
77+
samples.iter().sum::<f64>() / samples.len() as f64,
78+
(samples[samples.len() / 2 - 1] + samples[samples.len() / 2]) / 2.,
79+
)
80+
}
81+
82+
fn main() {
83+
let rgba: Vec<u8> = (0..SIZE.0 * SIZE.1)
84+
.flat_map(|i| {
85+
let x = i % SIZE.0;
86+
let y = i / SIZE.0;
87+
[
88+
(x * 13 + y * 7) as u8,
89+
(x * 3 + y * 17) as u8,
90+
(x ^ (y * 11)) as u8,
91+
(128 + (x * 5 + y * 3) % 128) as u8,
92+
]
93+
})
94+
.collect();
95+
println!(
96+
"Screenshot mask overlay preparation: {} / {}",
97+
std::env::consts::OS,
98+
std::env::consts::ARCH
99+
);
100+
println!(
101+
"3840x2160 RGBA; 640x360 masks; level 16; {WARMUP} warmup pairs; {EVENTS} measured drag events"
102+
);
103+
println!(
104+
"Alternating old/new execution order; warm cache; geometry and byte comparison after every event outside timing."
105+
);
106+
println!(
107+
"Includes cache diff, filtering, Arc allocation and retired CPU image release. Excludes GPUI conversion, scheduling, atlas and painting."
108+
);
109+
println!(
110+
"| Mode | Masks | Old mean ms | New mean ms | Old median ms | New median ms | Mean speedup | Old calls | New calls |"
111+
);
112+
println!("| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |");
113+
for pixelate in [false, true] {
114+
for count in [1, 4, 8, 16] {
115+
let initial = requests(count, pixelate, 0);
116+
let mut old = rebuild_all(&initial, &rgba, &mut 0);
117+
let mut new = cached(&[], &initial, &rgba, &mut 0);
118+
let mut old_samples = Vec::with_capacity(EVENTS);
119+
let mut new_samples = Vec::with_capacity(EVENTS);
120+
let mut old_calls = 0;
121+
let mut new_calls = 0;
122+
for event in 1..=WARMUP + EVENTS {
123+
let requests = requests(count, pixelate, event);
124+
let mut run_old = || {
125+
let start = Instant::now();
126+
old = rebuild_all(black_box(&requests), black_box(&rgba), &mut old_calls);
127+
start.elapsed().as_secs_f64() * 1000.
128+
};
129+
let mut run_new = || {
130+
let start = Instant::now();
131+
new = cached(
132+
black_box(&new),
133+
black_box(&requests),
134+
black_box(&rgba),
135+
&mut new_calls,
136+
);
137+
start.elapsed().as_secs_f64() * 1000.
138+
};
139+
let (old_ms, new_ms) = if event % 2 == 0 {
140+
let new_ms = run_new();
141+
(run_old(), new_ms)
142+
} else {
143+
let old_ms = run_old();
144+
(old_ms, run_new())
145+
};
146+
assert_eq!(old.len(), count);
147+
assert_eq!(new.len(), count);
148+
for (old, new) in old.iter().zip(&new) {
149+
let expected_rect = [
150+
f64::from(new.request.region.0),
151+
f64::from(new.request.region.1),
152+
f64::from(new.request.region.2),
153+
f64::from(new.request.region.3),
154+
];
155+
assert_eq!(old.rect, expected_rect);
156+
let new_overlay = new.value.as_ref().unwrap();
157+
assert_eq!(old.rect, new_overlay.rect);
158+
assert_eq!(
159+
old.image.as_raw(),
160+
new_overlay.image.as_raw(),
161+
"event {event}, mask {}",
162+
new.request.id
163+
);
164+
}
165+
if event <= WARMUP {
166+
old_calls = 0;
167+
new_calls = 0;
168+
} else {
169+
old_samples.push(old_ms);
170+
new_samples.push(new_ms);
171+
}
172+
}
173+
assert_eq!(old_calls, count * EVENTS);
174+
assert_eq!(new_calls, EVENTS);
175+
let (old_mean, old_median) = stats(&mut old_samples);
176+
let (new_mean, new_median) = stats(&mut new_samples);
177+
let mode = if pixelate { "pixelate" } else { "blur" };
178+
println!(
179+
"| {mode} | {count} | {old_mean:.3} | {new_mean:.3} | {old_median:.3} | {new_median:.3} | {:.2}x | {old_calls} | {new_calls} |",
180+
old_mean / new_mean
181+
);
182+
}
183+
}
184+
println!("All overlay geometry and bytes matched at every warmup and measured event.");
185+
}

‎apps/desktop-gpui/kernels/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod codec;
22
pub mod frame;
3+
pub mod mask_cache;
34
pub mod png;
45
pub mod screenshot;
56
pub mod texture;

0 commit comments

Comments
 (0)