perf: remove large drawing stalls

Keep resident geometry and GPU resources stable across edits, MSPACE zoom, and Model/Paper switches. Add unified PERF output for tracing remaining costs.
This commit is contained in:
Hakan Seven 2026-07-25 13:34:48 +03:00
commit 12bf126646
26 changed files with 794 additions and 325 deletions

View file

@ -9,8 +9,8 @@ use crate::scene::{VIEWCUBE_PAD, VIEWCUBE_REGION_PX};
use crate::ui::wrap_bar::DensitySwap;
use crate::ui::wrap_bar::WrapFlow;
use iced::widget::{
button, canvas, column, container, mouse_area, pane_grid, responsive, row, shader, stack, text,
Row, Space,
button, canvas, column, container, mouse_area, pane_grid, responsive, row, scrollable, shader,
stack, text, Row, Space,
};
use iced::window;
use iced::{keyboard, Background, Border, Color, Element, Fill, Subscription, Task, Theme};
@ -1121,32 +1121,44 @@ impl OpenCADStudio {
}
}
// Frame-budget HUD (Phase 5.3): toggle with the PERF command. Shows
// the cost of the most recent wire re-tessellation — the work avoided
// by a warm wire cache — so render-path changes can be compared
// PR-to-PR. Reads ~0 ms while panning/zooming on a hit cache.
if self.perf_hud && !tab.is_start {
// Shared performance panel: terminal PERF lines plus the current
// tessellation summary. Copy / Clear mirror the command-history panel.
if self.perf_hud {
let s = &tab.scene;
let label = format!(
let perf_w = if render_bar_w.is_finite() && render_bar_w > 1.0 {
render_bar_w
} else {
320.0
};
let summary = format!(
"tess {:.1} ms · {} wires · epoch {}",
s.last_tess_ms.get(),
s.last_tess_wires.get(),
s.geometry_epoch,
);
let panel = container(text(label).size(12).color(Color {
r: 0.6,
g: 1.0,
b: 0.6,
a: 1.0,
}))
.padding(6)
.style(|_: &Theme| container::Style {
background: Some(Background::Color(Color {
r: 0.08,
g: 0.08,
b: 0.08,
a: 0.85,
let trace = crate::perf::snapshot_text();
let trace = if trace.is_empty() {
"No samples yet".to_string()
} else {
trace
};
let perf_button_style = |_: &Theme, status: button::Status| button::Style {
background: Some(Background::Color(if matches!(status, button::Status::Hovered) {
Color {
r: 0.24,
g: 0.24,
b: 0.24,
a: 1.0,
}
} else {
Color {
r: 0.14,
g: 0.14,
b: 0.14,
a: 1.0,
}
})),
text_color: Color::WHITE,
border: Border {
color: Color {
r: 0.35,
@ -1158,6 +1170,62 @@ impl OpenCADStudio {
radius: 3.0.into(),
},
..Default::default()
};
let copy_btn = button(
row![
crate::ui::icons::tinted(
crate::ui::icons::COPY,
11.0,
Color::from_rgb(0.7, 0.85, 1.0),
),
text("Copy").size(11),
]
.spacing(4)
.align_y(iced::Center),
)
.on_press(Message::PerfCopy)
.style(perf_button_style)
.padding([2, 6]);
let clear_btn = button(
row![
crate::ui::icons::tinted(
crate::ui::icons::TRASH,
11.0,
Color::from_rgb(1.0, 0.55, 0.55),
),
text("Clear").size(11),
]
.spacing(4)
.align_y(iced::Center),
)
.on_press(Message::PerfClear)
.style(perf_button_style)
.padding([2, 6]);
let header = row![
text("PERF").size(12).color(Color::from_rgb(0.6, 1.0, 0.6)),
Space::new().width(iced::Length::Fill),
copy_btn,
clear_btn,
]
.spacing(6)
.align_y(iced::Center);
let log = scrollable(text(trace).size(11).color(Color::from_rgb(0.8, 0.9, 0.8)))
.height(iced::Length::Fixed(220.0))
.width(iced::Length::Fill);
let panel = container(
column![
header,
text(summary).size(11).color(Color::from_rgb(0.6, 1.0, 0.6)),
log,
]
.spacing(5),
)
.width(iced::Length::Fixed(perf_w))
.padding(6)
.style(|_: &Theme| container::Style {
background: None,
border: Border::default(),
..Default::default()
});
viewport_stack = viewport_stack.push(position_canvas_overlay(
iced::Point::new(12.0, 40.0),