GPUI: Zed's 120fps GPU-Accelerated UI
Source
GPUI is Zed’s in-house GPU-accelerated UI framework. It renders at 120fps using techniques borrowed from game development, not traditional GUI frameworks.
Core Architecture
┌─────────────────────────────────────────────────┐
│ Application │
│ (Elements, Views, Components) │
└─────────────────────┬───────────────────────────┘
↓
┌─────────────────────┴───────────────────────────┐
│ GPUI Framework │
│ • Immediate-mode API with retained optimization│
│ • Entity-based state management │
│ • Declarative element composition │
└─────────────────────┬───────────────────────────┘
↓
┌─────────────────────┴───────────────────────────┐
│ Three-Phase Renderer │
│ 1. Prepaint — compute layout │
│ 2. Paint — build scene graph │
│ 3. Present — render to GPU │
└─────────────────────┬───────────────────────────┘
↓
┌─────────────────────┴───────────────────────────┐
│ Graphics Backend │
│ • macOS: Metal (MSL shaders) │
│ • Linux: Blade (Vulkan) │
│ • Windows: Blade (Vulkan/DX12) │
└─────────────────────────────────────────────────┘
The Videogame Approach
Zed is rendered “like a videogame” — every frame is a fresh render pass with no widget tree diffing. This enables:
- Camera manipulation (3D view exploding for debugging)
- Smooth 120fps animations
- No accumulated layout debt
Traditional UI frameworks diff a tree of widgets. GPUI just draws everything fresh each frame, like a game.
Signed Distance Functions (SDFs)
GPUI draws primitives using SDFs — mathematical functions that return distance to a shape’s boundary.
// SDF for a rounded rectangle
fn sdf_rounded_rect(p: vec2<f32>, half_size: vec2<f32>, radius: f32) -> f32 {
let q = abs(p) - half_size + radius;
return length(max(q, vec2<f32>(0.0))) + min(max(q.x, q.y), 0.0) - radius;
}
// Fragment shader usage
@fragment
fn fs_main(@location(0) local_pos: vec2<f32>) -> @location(0) vec4<f32> {
let d = sdf_rounded_rect(local_pos, rect_size, corner_radius);
// Smooth anti-aliasing at the edge
let alpha = 1.0 - smoothstep(0.0, 1.5, d);
return vec4<f32>(color.rgb, color.a * alpha);
}
Why SDFs:
- Anti-aliasing is trivial (smoothstep at boundary)
- No texture sampling for basic shapes
- Resolution-independent
- Composable (union, intersection, difference)
120fps Performance
Hitting 120fps requires <8.33ms frame times. Key techniques:
1. GPU-Side Layout Hints
Heavy layout work stays on GPU. CPU just provides bounding boxes.
2. Metal Transaction Coordination
// Ensure frame doesn't tear with system UI
layer.presents_with_transaction = true;
command_buffer.wait_until_completed();
This prevents partial frame presentation but adds latency. Zed accepts the tradeoff for visual coherence.
3. No Blocking on Main Thread
Any blocking call drops frames:
// BAD: 10ms sleep = frame drop
sleep(Duration::from_millis(10));
// GOOD: Async work offloaded
spawn(async move {
do_slow_work().await;
});
4. Batched Primitives
All rectangles, glyphs, and shadows batched into single draw calls:
// Scene collects primitives
scene.add_rect(bounds, color);
scene.add_text(position, glyphs);
scene.add_shadow(bounds, radius, color);
// One draw call per primitive type
gpu.draw_rects(scene.rects);
gpu.draw_glyphs(scene.glyphs);
Blade Renderer
Blade is Zed’s low-level graphics abstraction by Dzmitry Malyshau (ex-Mozilla, gfx-rs maintainer):
| Feature | Implementation |
|---|---|
| macOS | Metal via metal-rs |
| Linux | Vulkan via ash |
| Windows | DX12/Vulkan |
| Shaders | WGSL → MSL/SPIR-V |
Blade provides a minimal, Vulkan-inspired API that compiles to native backends. ~4000 lines of code enabled Linux support.
Hybrid Rendering Model
GPUI combines immediate and retained modes:
Immediate-mode characteristics:
- No persistent widget tree
- Function calls build UI each frame
- State lives in application, not framework
Retained-mode optimizations:
- Entity system caches expensive computations
- Layout cached when inputs unchanged
- Texture atlases for glyphs
// Immediate-mode API
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
div()
.bg(cx.theme().background)
.child(
text(self.label.clone())
.color(cx.theme().text)
)
}
Performance Comparison
| Framework | Typical FPS | Approach |
|---|---|---|
| Electron | 30-60 | DOM diffing |
| Qt | 60 | Retained widget tree |
| Dear ImGui | 60-120 | Pure immediate mode |
| GPUI | 120 | Hybrid + GPU |
GPUI’s advantage: It doesn’t pay the DOM/widget tree tax, but caches enough to avoid redundant work.
Lessons for Web
GPUI runs native, not in browser. But concepts transfer:
- SDF rendering — WebGPU compute can do this
- Immediate-mode UI — React is already pseudo-immediate
- Batched draw calls — Canvas/WebGPU naturally batch
- 120fps target — Requires matching display refresh, harder in browser
The challenge: Browsers add layers (compositor, V8, etc.) that GPUI bypasses.
Sources:
- Zed: Leveraging Rust and the GPU for 120 FPS
- Zed: Optimizing Metal for 120 FPS
- GPUI Technical Overview
- GPUI Docs
- Zed: Linux Support via Blade
Related: vello gpu vector graphics, building figma today, rust wasm graphics