perf(canvas): skip idle reveal node checks

This commit is contained in:
Kayshen-X 2026-06-15 04:17:45 +08:00
parent 095e51a25b
commit 7a30644bb0
2 changed files with 46 additions and 12 deletions

View file

@ -481,19 +481,28 @@ impl<'a> Widget for CanvasViewport<'a> {
rect.size.y + CULL_MARGIN * 2.0,
),
};
let reveal_schedule = reveal_schedule_for_paint(&indicators.reveals, self.now_ms);
for child in page.children.iter().rev() {
super::canvas_viewport_paint::paint_node_with_reveals(
cx,
child,
viewport_origin,
viewport.zoom,
edit_caret.clone(),
cull,
super::canvas_viewport_paint::RevealSchedule {
starts: &indicators.reveals,
now_ms: self.now_ms,
},
);
if let Some(reveals) = reveal_schedule {
super::canvas_viewport_paint::paint_node_with_reveals(
cx,
child,
viewport_origin,
viewport.zoom,
edit_caret.clone(),
cull,
reveals,
);
} else {
super::canvas_viewport_paint::paint_node(
cx,
child,
viewport_origin,
viewport.zoom,
edit_caret.clone(),
cull,
);
}
}
super::canvas_agent_overlay::paint_agent_frame_indicators_with_snapshot(
cx,
@ -710,6 +719,16 @@ impl<'a> Widget for CanvasViewport<'a> {
}
}
pub(crate) fn reveal_schedule_for_paint<'a>(
reveals: &'a std::collections::HashMap<String, u64>,
now_ms: u64,
) -> Option<super::canvas_viewport_paint::RevealSchedule<'a>> {
(!reveals.is_empty()).then_some(super::canvas_viewport_paint::RevealSchedule {
starts: reveals,
now_ms,
})
}
/// Stroke a dashed rectangle as 4 dashed edges (4 px on / 4 px off,
/// screen-space) — the `RenderBackend` trait has no path-effect
/// surface, so the dash is segmented by hand; backend-agnostic.

View file

@ -6,6 +6,7 @@
use super::*;
use crate::layout_scene::{LayoutScene, SceneFillType, SceneNode, ScenePage, SceneStroke};
use crate::{Color, Point2D, Rect, TextLayout};
use std::collections::HashMap;
/// Records op order; clip-isolated paint = `Save, Clip, Fill, …, Restore`.
#[derive(Debug, PartialEq, Eq)]
@ -217,6 +218,20 @@ fn grid_dot_count_matches_painted_dot_batch() {
);
}
#[test]
fn empty_reveals_use_plain_node_paint_path() {
let empty = HashMap::new();
assert!(
reveal_schedule_for_paint(&empty, 1_000).is_none(),
"idle canvas paint should not give every node an empty reveal lookup"
);
let active = HashMap::from([("n1".to_string(), 1_000)]);
let schedule = reveal_schedule_for_paint(&active, 1_250).expect("active reveal schedule");
assert_eq!(schedule.now_ms, 1_250);
assert!(std::ptr::eq(schedule.starts, &active));
}
#[test]
fn unselected_scene_skips_overlay_stroke() {
let state = sample_state();