perf(canvas): skip idle reveal overlay walk

This commit is contained in:
Kayshen-X 2026-06-15 04:07:44 +08:00
parent c3660e9ba2
commit 095e51a25b

View file

@ -9,6 +9,8 @@ use crate::widgets::PaintCx;
use crate::{Color, Point2D, Rect, TextLayout};
use op_editor_core::agent_indicators::AgentIndicators;
use std::collections::HashMap;
#[cfg(test)]
use std::sync::atomic::{AtomicUsize, Ordering};
/// One full breathe (0 → 1 → 0) per this many ms.
const GLOW_PERIOD_MS: u64 = 1200;
@ -19,6 +21,24 @@ const REVEAL_ACCENT: Color = Color {
a: 1.0,
};
#[cfg(test)]
static REVEAL_WALK_VISITS: AtomicUsize = AtomicUsize::new(0);
#[cfg(test)]
fn reset_reveal_walk_visits() {
REVEAL_WALK_VISITS.store(0, Ordering::Relaxed);
}
#[cfg(test)]
fn reveal_walk_visits() -> usize {
REVEAL_WALK_VISITS.load(Ordering::Relaxed)
}
#[cfg(test)]
fn record_reveal_walk_visit() {
REVEAL_WALK_VISITS.fetch_add(1, Ordering::Relaxed);
}
/// Paint a breathing border around every active-page root frame that an
/// agent currently owns. `roots` are the page's top-level scene nodes;
/// `viewport_origin` already folds in the pan offset.
@ -49,14 +69,16 @@ pub(crate) fn paint_agent_frame_indicators_with_snapshot(
now_ms: u64,
indicators: &AgentIndicators,
) {
paint_node_reveal_indicators(
cx,
roots,
viewport_origin,
zoom,
now_ms,
&indicators.reveals,
);
if !indicators.reveals.is_empty() {
paint_node_reveal_indicators(
cx,
roots,
viewport_origin,
zoom,
now_ms,
&indicators.reveals,
);
}
if indicators.frames.is_empty() {
return;
}
@ -102,6 +124,9 @@ fn paint_node_reveal_indicators(
now_ms: u64,
reveals: &HashMap<String, u64>,
) {
if reveals.is_empty() {
return;
}
for root in roots {
paint_node_reveal_indicator(cx, root, viewport_origin, zoom, now_ms, reveals, false);
}
@ -116,6 +141,9 @@ fn paint_node_reveal_indicator(
reveals: &HashMap<String, u64>,
ancestor_revealing: bool,
) {
#[cfg(test)]
record_reveal_walk_visit();
let reveal = reveals
.get(&node.id)
.and_then(|started_at| reveal_phase(*started_at, now_ms));
@ -349,6 +377,45 @@ mod tests {
vec![node]
}
fn reveal_tree_with_many_nodes(count: usize) -> Vec<SceneNode> {
let mut root = SceneNode::leaf("root", NodeKind::Frame);
root.bounds = Rect::xywh(0.0, 0.0, 100.0, 100.0);
root.children = (0..count)
.map(|i| {
let mut child = SceneNode::leaf(format!("child-{i}"), NodeKind::Rect);
child.bounds = Rect::xywh(i as f32, 0.0, 10.0, 10.0);
child
})
.collect();
vec![root]
}
#[test]
fn empty_reveal_snapshot_skips_reveal_tree_walk() {
let roots = reveal_tree_with_many_nodes(128);
let indicators = AgentIndicators::default();
let mut backend = RevealCaptureBackend::default();
let mut cx = PaintCx {
backend: &mut backend,
};
reset_reveal_walk_visits();
paint_agent_frame_indicators_with_snapshot(
&mut cx,
&roots,
Point2D::ZERO,
1.0,
1_000,
&indicators,
);
assert_eq!(
reveal_walk_visits(),
0,
"empty reveal snapshots should not walk the scene tree"
);
}
#[test]
fn reveal_overlay_paints_lifted_highlight_sweep_and_prunes_after_window() {
let _guard = crate::agent_indicator_test_support::lock();