perf: instrument the file-open load path behind PERF

The 52 s silent stretch in a large-DWG open could not be attributed:
OpenTimings covered parse / purge / xref / caches, but the FINALIZING
phase -- prepare_open_geometry, which does the first full Model
tessellation and builds the interaction index -- was unmeasured and
reports only two progress ticks, so it was indistinguishable from parse
time.

Add finalize_ms to OpenTimings and a per-stage PERF breakdown:

  open-phases     parse / purge / xref / caches / finalize
  open-finalize   tessellation vs interaction index
  open-index      index build vs prepare_screen
  wires-build     sort cache, visible filter, block cache, classify,
                  memo hits, parallel tessellate, materialize, sort
  resident-post   scene markers, refedit fade, resident layout
  wire-upload     build_wire_buffers, which runs on the render thread

The wires-build line separates the parallel tessellate from the serial
materialize loop that deep-clones every WireModel while the memo retains
a second copy -- the leading hypothesis for both the stall and the
6.45 GB RSS.

No behaviour change. Every timer is gated on crate::perf::enabled(), so
nothing runs without PERF=1 or the in-app PERF panel. The localized
open-summary format string is left alone; changing it would rekey the
string across every locale, so the full breakdown goes to PERF instead.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
PanosandClaude Opus 5 committed 2026-09-04 16:20:30 +03:00
1 parent 886d5777d8
commit 2787dcc1a3
4 files changed
+137 -2

No files matched your search

+11
View File
@@ -917,8 +917,19 @@ impl shader::Primitive for Primitive {
let built = match cached {
Some(entry) => entry,
None => {
// Runs on the main/render thread inside `prepare`,
// so its cost is UI stall, not background load.
let t_upload = iced::time::Instant::now();
let entry =
inner.build_wire_buffers(device, &vp_wires[..], &draw_depths);
if crate::perf::enabled() {
crate::perf_record!(
"[perf] wire-upload {:.1}ms wires={} content_id={}",
t_upload.elapsed().as_secs_f64() * 1000.0,
vp_wires.len(),
vp.wire_content_id,
);
}
pipeline
.wire_buffer_cache
.insert(vp.wire_content_id, entry.clone());