perf: measure an edit from the input to the frame that answers it

Drawing a point in a large drawing feels slow, and nothing currently
measures the thing the user actually waits for. `update` times the message
handler, `wires-build` and friends time the rebuild, but the span between
them — press to the frame that shows the new geometry — has no number.

Navigation already has exactly that plumbing: `record_nav_perf` stamps an
instant when the input arrives, `prepare` and `encode` pick it up, and
`nav-prepare` / `nav-render` report `input`, `build`, `prepare` and
`elapsed` from the original event.

`NavPerfOp::Edit` reuses it. `update` snapshots `geometry_epoch` before
dispatch and stamps the sample when it moved, so any message that changed
geometry — a drawn point, a grip commit, an undo — reports its full
latency rather than only the handler's share.

Diagnostic only: `record_nav_perf` sets a pending sample and nothing else,
so no LOD or settle behaviour changes.

Nothing is optimised here on purpose. Four call sites feed the snapper
along different branches, and picking one to instrument before knowing
which dominates is how the earlier rounds of this work were wasted.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
PanosandClaude Opus 5 committed 2026-09-08 16:42:20 +03:00
1 parent a754fac6d4
commit b4ae4cad4e
2 files changed
+19

No files matched your search

+14
View File
@@ -265,6 +265,13 @@ impl OpenCADStudio {
self.control_observe_user_message(&msg);
let perf_started = crate::perf::enabled().then(Instant::now);
let perf_label = perf_message_label(&msg);
// Geometry epoch before dispatch. When it moves, this message drew or
// changed something, and the latency the user feels runs from here to
// the frame that shows it — not to the end of this handler. Stamping
// the nav-perf sample makes `nav-prepare` / `nav-render` report that
// span, the same way they already do for pan and zoom.
let perf_epoch_before = perf_started
.map(|_| self.tabs[self.active_tab].scene.geometry_epoch);
// A modal dialog must capture the keyboard the same way it already
// captures the mouse. Otherwise keystrokes from the global key
// subscription leak past the modal into the command line and fire as
@@ -324,6 +331,13 @@ impl OpenCADStudio {
self.otrack_active = None;
self.otrack_kind = None;
}
if let (Some(started), Some(before)) = (perf_started, perf_epoch_before) {
let tab = &self.tabs[self.active_tab];
if tab.scene.geometry_epoch != before {
tab.scene
.record_nav_perf(crate::scene::NavPerfOp::Edit, started);
}
}
if let Some(started) = perf_started {
let elapsed_ms = started.elapsed().as_secs_f64() * 1000.0;
if elapsed_ms >= 5.0 {
+5
View File
@@ -1432,6 +1432,10 @@ pub(crate) enum NavPerfOp {
Pan,
Zoom,
Rotate,
/// A message that changed geometry. Reported through the same line as
/// navigation because the question is the same one: how long from the
/// input to the frame that answers it.
Edit,
}
impl NavPerfOp {
@@ -1440,6 +1444,7 @@ impl NavPerfOp {
Self::Pan => "pan",
Self::Zoom => "zoom",
Self::Rotate => "rotate",
Self::Edit => "edit",
}
}
}