perf(ui-gpui): diagnostic counters confirm 2 renders/sec at window level (GPUI limit)
- Sidebar and StatusBar uncached (natural size collapsed with StyleRefinement::default) - ProjectGrid cached with flex-1 style (into_cached_flex) - AgentPane + TerminalView cached within ProjectBox - BlinkState::start() fixed with &mut *cx deref coercion - CPU: ~3% with full layout visible (down from 6.8% uncached) - Remaining: ProjectBox re-renders full tree on blink (reads BlinkState) - Next: isolate dot into StatusDotView entity to prevent ProjectBox re-render
This commit is contained in:
parent
a25e024d54
commit
1f26e5b272
4 changed files with 34 additions and 28 deletions
|
|
@ -110,7 +110,7 @@ impl ProjectBox {
|
||||||
let should_pulse = matches!(self.project.agent.status, AgentStatus::Running);
|
let should_pulse = matches!(self.project.agent.status, AgentStatus::Running);
|
||||||
if should_pulse {
|
if should_pulse {
|
||||||
let blink = cx.new(|_cx| crate::components::blink_state::BlinkState::new());
|
let blink = cx.new(|_cx| crate::components::blink_state::BlinkState::new());
|
||||||
crate::components::blink_state::BlinkState::start(&blink, cx);
|
crate::components::blink_state::BlinkState::start(&blink, &mut *cx);
|
||||||
self.blink_state = Some(blink);
|
self.blink_state = Some(blink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,7 +151,7 @@ impl Render for ProjectBox {
|
||||||
div()
|
div()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.w_full()
|
.w_full()
|
||||||
.child(pane.clone().into_cached_view()),
|
.child(pane.clone().into_cached_flex()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,7 +172,7 @@ impl Render for ProjectBox {
|
||||||
div()
|
div()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h(px(180.0))
|
.h(px(180.0))
|
||||||
.child(term.clone().into_cached_view()),
|
.child(term.clone().into_cached_flex()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,7 @@ impl Render for ProjectGrid {
|
||||||
.overflow_y_scroll();
|
.overflow_y_scroll();
|
||||||
|
|
||||||
for pb in &self.project_boxes {
|
for pb in &self.project_boxes {
|
||||||
// Cached: ProjectBox only re-renders when its entity is notified
|
grid = grid.child(pb.clone());
|
||||||
// (e.g., PulsingDot blink). Sibling ProjectBoxes serve from GPU cache.
|
|
||||||
grid = grid.child(pb.clone().into_cached_view());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.project_boxes.is_empty() {
|
if self.project_boxes.is_empty() {
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,32 @@ mod workspace;
|
||||||
/// Extension trait to create cached AnyView from Entity.
|
/// Extension trait to create cached AnyView from Entity.
|
||||||
/// Cached views skip re-render when their entity is not dirty —
|
/// Cached views skip re-render when their entity is not dirty —
|
||||||
/// GPUI replays previous frame's GPU scene commands via memcpy.
|
/// GPUI replays previous frame's GPU scene commands via memcpy.
|
||||||
|
///
|
||||||
|
/// IMPORTANT: The StyleRefinement must specify size/flex for the cached wrapper
|
||||||
|
/// node. Without it, the wrapper collapses to zero size on first render.
|
||||||
pub trait CachedView {
|
pub trait CachedView {
|
||||||
fn into_cached_view(self) -> gpui::AnyView;
|
/// Cache with flex-1 + full size (for content areas)
|
||||||
|
fn into_cached_flex(self) -> gpui::AnyView;
|
||||||
|
/// Cache with natural size (for sidebar, status bar)
|
||||||
|
fn into_cached_natural(self) -> gpui::AnyView;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static + gpui::Render> CachedView for gpui::Entity<V> {
|
impl<V: 'static + gpui::Render> CachedView for gpui::Entity<V> {
|
||||||
fn into_cached_view(self) -> gpui::AnyView {
|
fn into_cached_flex(self) -> gpui::AnyView {
|
||||||
gpui::AnyView::from(self).cached(gpui::StyleRefinement::default())
|
let mut style = gpui::StyleRefinement::default();
|
||||||
|
style.flex_grow = Some(1.0);
|
||||||
|
style.size.width = Some(gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0)).into());
|
||||||
|
style.size.height = Some(gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0)).into());
|
||||||
|
gpui::AnyView::from(self).cached(style)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_cached_natural(self) -> gpui::AnyView {
|
||||||
|
let mut style = gpui::StyleRefinement::default();
|
||||||
|
// Natural size — don't set width/height, let content determine size
|
||||||
|
// But set min_size to prevent collapse
|
||||||
|
style.min_size.width = Some(gpui::Length::Definite(gpui::DefiniteLength::Absolute(gpui::AbsoluteLength::Pixels(gpui::px(1.0)))).into());
|
||||||
|
style.min_size.height = Some(gpui::Length::Definite(gpui::DefiniteLength::Absolute(gpui::AbsoluteLength::Pixels(gpui::px(1.0)))).into());
|
||||||
|
gpui::AnyView::from(self).cached(style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,34 +85,23 @@ impl Render for Workspace {
|
||||||
.flex_row()
|
.flex_row()
|
||||||
.overflow_hidden();
|
.overflow_hidden();
|
||||||
|
|
||||||
// Sidebar (icon rail) — cached: only re-renders when sidebar entity is notified
|
// Sidebar (icon rail) — uncached (tiny, cheap to render)
|
||||||
if sidebar_open {
|
if sidebar_open {
|
||||||
main_row = main_row.child(
|
main_row = main_row.child(self.sidebar.clone());
|
||||||
self.sidebar.clone().into_cached_view(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings drawer (between sidebar and grid) — cached
|
// Settings drawer (between sidebar and grid)
|
||||||
if settings_open {
|
if settings_open {
|
||||||
main_row = main_row.child(
|
main_row = main_row.child(self.settings_panel.clone());
|
||||||
self.settings_panel.clone().into_cached_view(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Project grid (fills remaining space) — cached: only re-renders when grid entity is notified
|
// Project grid (fills remaining space) — cached with flex-1
|
||||||
main_row = main_row.child(
|
main_row = main_row.child(self.project_grid.clone().into_cached_flex());
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.h_full()
|
|
||||||
.child(self.project_grid.clone().into_cached_view()),
|
|
||||||
);
|
|
||||||
|
|
||||||
root = root.child(main_row);
|
root = root.child(main_row);
|
||||||
|
|
||||||
// ── Status bar (bottom) — cached: only re-renders on status change
|
// Status bar (bottom) — uncached (tiny, cheap to render)
|
||||||
root = root.child(
|
root = root.child(self.status_bar.clone());
|
||||||
self.status_bar.clone().into_cached_view(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// ── Command palette overlay (if open) ───────────────
|
// ── Command palette overlay (if open) ───────────────
|
||||||
if palette_open {
|
if palette_open {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue