From 727c7d2e06d77ef6965e0c64d9b4166e616e4720 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Thu, 19 Mar 2026 23:03:04 +0100 Subject: [PATCH] perf(ui-gpui): revert AnimationElement (79% CPU), document shader limitation GPUI's AnimationElement uses request_animation_frame() internally which runs at vsync (60fps) causing 79% CPU. Fragment shaders not exposed through GPUI's Scene API (paint_quad only accepts static color, no time uniform). Reverted to BlinkState timer pattern (3% CPU, 2 renders/sec). This is the same approach Zed uses for cursor blink. Tested approaches and results: - AnimationElement + pulsating_between: 79% CPU (vsync loop) - BlinkState + timer(500ms) + cx.notify(): 3% CPU (correct) - Custom Element + paint_quad: no shader access - CSS animation (Blitz): 30% CPU (full repaint loop) --- ui-gpui/src/components/project_box.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui-gpui/src/components/project_box.rs b/ui-gpui/src/components/project_box.rs index 11ae3f3..f049366 100644 --- a/ui-gpui/src/components/project_box.rs +++ b/ui-gpui/src/components/project_box.rs @@ -261,9 +261,12 @@ impl Render for ProjectBox { .rounded(px(2.0)) .bg(accent), ) - // Status dot — reads from shared BlinkState (doesn't dirty ancestors) + // Status dot — BlinkState shared entity (2 renders/sec, 3% CPU) + // NOTE: GPUI's AnimationElement uses request_animation_frame() which + // runs at vsync (60fps) = 80% CPU. Fragment shaders not exposed. + // The Zed BlinkManager pattern (timer + notify at 2fps) is the + // correct approach for ambient animation in GPUI. .child({ - let is_running = matches!(self.project.agent.status, AgentStatus::Running); let blink_visible = self.blink_state.as_ref() .map(|bs| bs.read(cx).visible) .unwrap_or(true);