From 54e68df295d772e068506e53551786051a6fda02 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Thu, 19 Mar 2026 23:47:39 +0100 Subject: [PATCH] docs: deep dive into GPUI dirty propagation, SharedBlink, hierarchy flattening results --- docs/architecture/gpui-findings.md | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/architecture/gpui-findings.md b/docs/architecture/gpui-findings.md index c457c41..56aede7 100644 --- a/docs/architecture/gpui-findings.md +++ b/docs/architecture/gpui-findings.md @@ -26,6 +26,61 @@ Key mechanisms: 3. **`mark_view_dirty()` walks ancestors** — this is GPUI's fundamental constraint 4. **`refreshing=true` on cache miss** cascades down → children can't cache if parent is dirty +## DEEP DIVE: Why 3% is the floor for div-based views (2026-03-19 late) + +### The full dirty propagation chain (confirmed by source analysis) + +``` +cx.notify(entity_id) + → App::notify → WindowInvalidator::invalidate_view(entity_id) + → dirty_views.insert(entity_id) + → draw() → invalidate_entities() → mark_view_dirty(entity_id) + → dispatch_tree.view_path_reversed(entity_id) → walks parent chain + → inserts entity + ALL render-time ancestors into dirty_views + → AnyView::prepaint checks: dirty_views.contains(MY entity_id) + → if true: cache MISS, re-render + → if false: cache HIT, replay GPU commands +``` + +### Why Zed achieves <1% with the SAME mechanism + +Zed's BlinkManager → observer → cx.notify(Editor) → Editor + Pane + Workspace ALL in dirty_views. +ALL re-render. But: +- Workspace::render() = ~5 div builders = <0.1ms +- Pane::render() = ~10 div builders = <0.2ms +- Editor::render() = returns EditorElement struct = <0.01ms +- EditorElement::prepaint/paint = real work, bounded to visible rows + +Total: <1ms per blink frame. Our ProjectBox::render() = ~9 divs + 3 tab buttons = ~15ms. +The 15ms includes Taffy layout + element prepaint + paint pipeline overhead per div. + +### SharedBlink pattern (Arc) + +Replaced Entity with `Arc`: +- Background timer toggles atomic bool every 500ms +- Timer calls cx.notify() on ProjectBox directly (no intermediate entity) +- ProjectBox::render() reads bool atomically — no Entity subscription overhead +- Same 3% CPU — confirms cost is in render() itself, not entity machinery + +### Hierarchy flattening (4 levels → 2 levels) + +Removed ProjectGrid entity level. ProjectBoxes render directly from Workspace. +Dispatch tree: Workspace → ProjectBox (2 levels, same as Zed's Workspace → Pane). +CPU unchanged at 3% — confirms cost is per-frame render(), not ancestor walk count. + +### Path to <1%: Custom Element for ProjectBox header + +Convert header (accent stripe + dot + name + CWD + tab bar) from div tree to +custom `impl Element` that paints directly via `window.paint_quad()` + +`window.text_system().shape_line().paint()`. This eliminates: +- 9 div() constructor calls +- 9 Taffy layout nodes +- 9 prepaint traversals +- 9 paint traversals + +Replaced by ~6 direct GPU primitive insertions (paint_quad × 4 + shape_line × 2). +Expected reduction: 15ms → <1ms per frame. + ## Overview GPUI is the GPU-accelerated UI framework powering the Zed editor. Apache-2.0 licensed (the crate itself; Zed app is GPL-3.0). Pre-1.0 with breaking changes every 2-3 months. 76.7k stars (Zed repo), $32M Sequoia funding.