Commit graph

24 commits

Author SHA1 Message Date
Hibryda
3bbaefa9a2 perf(ui-gpui): ProjectBoxFullElement wraps entire card, 2.1% CPU
Single custom Element for header+tabs (paint_chrome) + content AnyElement.
Only 1 div remains (content wrapper). Down from 90% → 2.1% over 11 iterations.
2026-03-20 00:32:11 +01:00
Hibryda
5a7a5ad621 perf(ui-gpui): combined header+tabbar custom Element, 2.63% CPU
Header + tab bar in single custom Element (10 GPU primitives).
ProjectBox::render() now only 2 divs (root + content).
Down from 90% → 2.63% through 10 optimization iterations.
2026-03-20 00:05:57 +01:00
Hibryda
f0c55403b8 perf(ui-gpui): custom Element header, 2.73% CPU (down from 90%)
ProjectBoxHeaderElement: 5 paint_quad + 2 text runs, zero Taffy nodes.
Flattened hierarchy + SharedBlink + cached children + focus-gated blink.
GPUI architectural floor: cx.notify() always walks ancestors.
2026-03-19 23:55:39 +01:00
Hibryda
6f0f276400 perf(ui-gpui): flatten hierarchy + SharedBlink (Arc<AtomicBool>)
- Eliminated ProjectGrid entity level: Workspace renders ProjectBoxes directly
  (3 dispatch tree levels: Workspace → ProjectBox → inline divs)
- Replaced Entity<BlinkState> + Entity<StatusDotView> with SharedBlink
  (Arc<AtomicBool> toggled by background timer, read atomically in render)
- Timer calls cx.notify() directly on ProjectBox (no intermediate entities)
- CPU: 2.93% (unchanged from 3.07% — confirms cost is ProjectBox::render()
  overhead, not hierarchy depth or entity count)
- Each blink frame: ~15ms total (render + layout + prepaint + paint + GPU submit)
- Zed comparison: ~5ms per blink frame (EditorElement is custom Element, not div tree)
2026-03-19 23:45:05 +01:00
Hibryda
ddec12db3f perf(ui-gpui): simplify ProjectBox render, confirm 3% is hierarchy depth cost
Each blink = ~15ms CPU (1-2 ticks/500ms). Cost is Entity dispatch tree
walk through 4 levels (Workspace→ProjectGrid→ProjectBox→StatusDotView),
not div count. Zed achieves ~5ms with 3 levels. To match: flatten hierarchy.
2026-03-19 23:31:28 +01:00
Hibryda
5d69f6b28f perf(ui-gpui): focus-gated blink + comprehensive rendering analysis
Focus-gated blink: only first (focused) project blinks.
Root cause: 14 header divs × 2 boxes × 2/sec = 2.8% CPU.
Cached children (AgentPane, TerminalView) confirmed at 0%.
Path to <1%: custom Element for ProjectBox (major refactor).
2026-03-19 23:26:20 +01:00
Hibryda
f797a676f4 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.
2026-03-19 23:09:56 +01:00
Hibryda
727c7d2e06 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)
2026-03-19 23:03:04 +01:00
Hibryda
640e2bd494 fix(ui-gpui): re-enable start_blinking (was disabled for testing) 2026-03-19 22:50:16 +01:00
Hibryda
3859317477 perf(ui-gpui): add render counters, remove redundant cx.observe, reduce repaints
- BlinkState.start_from_context<V>() uses Context<V> spawn (not App)
  which correctly registers the async task in the window's executor
- ProjectGrid NOT cached (blocks child dirty propagation)
- AgentPane + TerminalView cached with into_cached_flex()
- Render diagnostic: ProjectBox renders 2x/sec (blink), both boxes
  re-render because shared parent, but inner cached views are free
- CPU: ~3% with pulsing dot visible
2026-03-19 22:48:29 +01:00
Hibryda
1f26e5b272 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
2026-03-19 22:45:21 +01:00
Hibryda
73cfdf6752 perf(ui-gpui): cache SharedStrings, remove diagnostics, zero alloc per render frame
- BlinkState as shared Entity (not child) → cx.notify() only dirties ProjectBox,
  NOT ancestors (Workspace, ProjectGrid). Siblings serve from GPU cache.
- .cached(StyleRefinement::default()) on all Entity children in Workspace,
  ProjectGrid, ProjectBox → GPUI replays previous frame's GPU commands via memcpy
- CachedView trait: Entity<V>.into_cached_view() → AnyView::from().cached()
- Result: 4.5% CPU → 0.83% CPU (25 ticks / 30s) for pulsing dot animation
2026-03-19 22:35:41 +01:00
Hibryda
5dbf5bd43c perf(ui-gpui): cache SharedStrings, remove diagnostics, zero alloc per render frame 2026-03-19 09:38:00 +01:00
Hibryda
b557aeb833 perf(ui-gpui): diagnostic counters confirm 2 renders/sec at window level (GPUI limit) 2026-03-19 09:33:34 +01:00
Hibryda
7ab5d97352 perf(ui-gpui): add render counters, remove redundant cx.observe, reduce repaints 2026-03-19 09:27:08 +01:00
Hibryda
8dd3d82d31 fix(ui-gpui): re-enable start_blinking (was disabled for testing) 2026-03-19 09:21:21 +01:00
Hibryda
ba74e19ff2 feat(ui-gpui): Zed-style BlinkManager pattern for pulsing dot (epoch guard, 500ms timer) 2026-03-19 09:20:04 +01:00
Hibryda
3383334821 feat(ui-gpui): custom Element with direct paint_quad() for zero-overhead pulse 2026-03-19 08:16:44 +01:00
Hibryda
713b53ba0c perf(ui-gpui): throttle pulse to 5fps via spawn+timer instead of request_animation_frame 2026-03-19 08:11:22 +01:00
Hibryda
c4d0707514 fix(ui-gpui): set first project to Running status so pulse animation triggers 2026-03-19 08:08:53 +01:00
Hibryda
18cfe7979c fix(ui-gpui): color interpolation instead of alpha (GPUI ignores alpha on bg) 2026-03-19 08:03:24 +01:00
Hibryda
573105eae6 feat(ui-gpui): render-driven pulse via request_animation_frame() (GPUI native) 2026-03-19 07:59:32 +01:00
Hibryda
57e0e3a087 feat(ui-gpui): add pulsing dot via GPUI async runtime (entity-scoped repaint) 2026-03-19 07:51:25 +01:00
Hibryda
f3d2ca78ba feat: add Dioxus and GPUI UI prototypes for framework comparison
Dioxus (ui-dioxus/): 2,169 lines, WebView mode (same wry as Tauri),
  Catppuccin theme, 12 components, agor-core integration, compiles clean.
  Evolution path — keeps xterm.js, gradual migration from Tauri.

GPUI (ui-gpui/): 2,490 lines, GPU-accelerated rendering, alacritty_terminal
  for native terminal, 17 files, Catppuccin palette, demo data.
  Revolution path — pure Rust UI, 120fps target, no WebView.

Both are standalone (not in workspace), share agor-core backend.
Created for side-by-side comparison to inform framework decision.
2026-03-19 06:05:58 +01:00