perf(ui-gpui): isolate StatusDotView entity, document 3% CPU floor

Root cause analysis:
- 0% baseline: calloop 60Hz polling has zero measurable CPU cost
- 3% is entirely from ProjectBox::render() rebuilding ~30 header divs
  × 2 boxes × 2/sec = 120 div constructions/sec
- AgentPane + TerminalView cached (into_cached_flex) = 0% contribution
- mark_view_dirty() walks ancestors unconditionally in GPUI 0.2.2
  → StatusDotView isolation doesn't prevent parent re-render
- Fragment shaders not exposed (paint_quad accepts static color only)
- GPUI AnimationElement uses request_animation_frame = 79% CPU (vsync)

StatusDotView pattern is architecturally correct for future GPUI versions
that may implement per-view dirty isolation.
This commit is contained in:
Hibryda 2026-03-19 23:09:56 +01:00
parent 727c7d2e06
commit f797a676f4
2 changed files with 57 additions and 33 deletions

View file

@ -6,12 +6,48 @@
use gpui::*;
use std::time::Duration;
use crate::state::AgentStatus;
use crate::theme;
pub struct BlinkState {
pub visible: bool,
epoch: usize,
}
/// Tiny view entity that renders just the status dot.
/// Reads BlinkState → only this entity re-renders on blink, not ProjectBox.
pub struct StatusDotView {
status: AgentStatus,
blink: Option<Entity<BlinkState>>,
}
impl StatusDotView {
pub fn new(status: AgentStatus, blink: Option<Entity<BlinkState>>) -> Self {
Self { status, blink }
}
}
impl Render for StatusDotView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let blink_visible = self.blink.as_ref()
.map(|bs| bs.read(cx).visible)
.unwrap_or(true);
let color = match self.status {
AgentStatus::Running if !blink_visible => theme::SURFACE1,
AgentStatus::Running => theme::GREEN,
AgentStatus::Idle => theme::OVERLAY0,
AgentStatus::Done => theme::BLUE,
AgentStatus::Error => theme::RED,
};
div()
.w(px(8.0))
.h(px(8.0))
.rounded(px(4.0))
.bg(color)
.flex_shrink_0()
}
}
impl BlinkState {
pub fn new() -> Self {
Self { visible: true, epoch: 0 }