forked from 1b-agent/opencadstudio-upstream
perf: break the view frame down by widget-tree section
The first Model→Layout switch costs ~9.2 s, and the largest single piece of it is `view` at 5810 ms — iced widget-tree construction, not the scene. The scene work that follows it in `prepare` (two `wires-build` calls plus `frame-prepare`) accounts for 3377 ms, and the log's ordering shows the two are sequential, not nested: `view` prints before any of them. `view` being one number could not say which part of a 1950-line `view_main` that is. It now reports eight sections — viewport_3d, model_input, grid, selection_overlay, viewport_mouse, viewport_stack, active_vp_rect, chrome — plus the preamble as `head`. The marks go through a closure over a `RefCell` rather than one binding per section: two of the sections sit inside a nested block, where per-section bindings are out of scope by the time the report runs. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
4e6a9c5177
commit
e0f5126a2b
1 file changed
+50
@@ -197,6 +197,20 @@ impl OpenCADStudio {
|
||||
/// `view` so the single-window web build can render it directly, bypassing
|
||||
/// the multi-window id dispatch above (the web build has no extra windows).
|
||||
pub fn view_main(&self) -> Element<'_, Message> {
|
||||
// Section marks for `view-detail`. A closure over a RefCell rather
|
||||
// than one binding per section: two of these sections sit inside a
|
||||
// nested block and would otherwise be out of scope at the report.
|
||||
let view_t0 = crate::perf::enabled().then(iced::time::Instant::now);
|
||||
let view_marks: std::cell::RefCell<Vec<(&'static str, f64)>> =
|
||||
std::cell::RefCell::new(Vec::with_capacity(8));
|
||||
let mark = |name: &'static str| {
|
||||
if let Some(t0) = view_t0 {
|
||||
view_marks
|
||||
.borrow_mut()
|
||||
.push((name, t0.elapsed().as_secs_f64() * 1000.0));
|
||||
}
|
||||
};
|
||||
|
||||
let i = self.active_tab;
|
||||
let tab = &self.tabs[i];
|
||||
let thumbnail_capture_clean = self.thumbnail_capture_clean;
|
||||
@@ -257,6 +271,7 @@ impl OpenCADStudio {
|
||||
// the same shader as model space: a full-canvas top-locked "sheet"
|
||||
// viewport draws the layout's own geometry (white sheet + entities +
|
||||
// borders) and the floating content viewports blit on top.
|
||||
mark("viewport_3d");
|
||||
let viewport_3d: Element<'_, Message> = if tab.is_start {
|
||||
start_page_view(
|
||||
&self.patrons,
|
||||
@@ -334,6 +349,7 @@ impl OpenCADStudio {
|
||||
// the shader pane_grid (same `model_panes` → identical layout). Layered
|
||||
// above the crosshair overlay so it actually receives mouse events, and
|
||||
// it owns the divider resize. Only built for the Model layout.
|
||||
mark("model_input");
|
||||
let model_input_layer: Option<Element<'_, Message>> = if is_paper || tab.is_start {
|
||||
None
|
||||
} else {
|
||||
@@ -351,6 +367,7 @@ impl OpenCADStudio {
|
||||
)
|
||||
};
|
||||
|
||||
mark("grid");
|
||||
let grid_overlay = {
|
||||
let (vw, vh) = tab.scene.selection.borrow().vp_size;
|
||||
let model_basis = {
|
||||
@@ -420,6 +437,7 @@ impl OpenCADStudio {
|
||||
crate::ui::overlay::grid_overlay(grid, grid_style)
|
||||
};
|
||||
|
||||
mark("selection_overlay");
|
||||
let selection_overlay = {
|
||||
// Hold a single `Ref<'_, SelectionState>` for the whole overlay
|
||||
// block. The `vp_size` and `last_move_pos` reads below become
|
||||
@@ -790,6 +808,7 @@ impl OpenCADStudio {
|
||||
)
|
||||
};
|
||||
|
||||
mark("viewport_mouse");
|
||||
let viewport_mouse = mouse_area(container(
|
||||
iced::widget::Space::new().width(Fill).height(Fill),
|
||||
))
|
||||
@@ -935,6 +954,7 @@ impl OpenCADStudio {
|
||||
None
|
||||
};
|
||||
|
||||
mark("viewport_stack");
|
||||
let mut viewport_stack = if tab.is_start {
|
||||
// Start tab: only the welcome widget over a flat background.
|
||||
// Skip every drawing-only overlay (selection markers, snap info,
|
||||
@@ -1053,6 +1073,7 @@ impl OpenCADStudio {
|
||||
// both layered ABOVE the viewport mouse_area so they receive
|
||||
// clicks (the shader viewport sits below it). Positioned with
|
||||
// leading Spaces sized to the viewport's screen rectangle.
|
||||
mark("active_vp_rect");
|
||||
let active_vp_rect: Option<(acadrust::Handle, iced::Rectangle)> =
|
||||
if is_paper && !tab.is_start {
|
||||
tab.scene.active_viewport.and_then(|h| {
|
||||
@@ -1383,6 +1404,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
// Reserve the overlaid command line when placing cursor-anchored panels.
|
||||
mark("chrome");
|
||||
let command_line_inset = if self.command_line.history_open {
|
||||
self.command_line.history_height.clamp(
|
||||
crate::ui::command_line::HISTORY_HEIGHT_MIN,
|
||||
@@ -2102,6 +2124,34 @@ impl OpenCADStudio {
|
||||
};
|
||||
// Shared CAD colour picker. Indexed ACI colours use OpenCADStudio's own
|
||||
// dialog; True Color keeps the existing iced_aw gradient picker.
|
||||
// Which part of the widget tree the frame went into. Reported as a
|
||||
// breakdown rather than one number, because a first switch to a paper
|
||||
// layout spends seconds here and `view` alone cannot say where.
|
||||
if let Some(t0) = view_t0 {
|
||||
mark("end");
|
||||
let total = t0.elapsed().as_secs_f64() * 1000.0;
|
||||
if total >= 1.0 {
|
||||
let marks = view_marks.borrow();
|
||||
// A mark opens a section; the following mark closes it, so a
|
||||
// section's cost is the gap to its successor. The first mark
|
||||
// is preceded by the preamble, reported as `head`.
|
||||
let mut detail = String::new();
|
||||
if let Some((_, first)) = marks.first() {
|
||||
let _ = std::fmt::Write::write_fmt(
|
||||
&mut detail,
|
||||
format_args!(" head={first:.1}"),
|
||||
);
|
||||
}
|
||||
for pair in marks.windows(2) {
|
||||
let _ = std::fmt::Write::write_fmt(
|
||||
&mut detail,
|
||||
format_args!(" {}={:.1}", pair[0].0, pair[1].1 - pair[0].1),
|
||||
);
|
||||
}
|
||||
crate::perf_record!("[perf] view-detail total={total:.1}ms{detail}");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, current)) = self.color_pick_target.as_ref() {
|
||||
match self.color_picker_tab {
|
||||
super::ColorPickerTab::Index => {
|
||||
|
||||
Reference in new issue
Block a user