Copied to clipboard
v1.21.0 · Modular GPUI Architecture

GPUI.
Take a bite.

Stop forking. Start swapping.

Zed’s GPUI is fast, but tightly coupled. bite-gpui breaks the engine into five standalone crates. Tweak the frame pipeline, drop in BiDi text layout, or run headless tests. Your div() code stays 100% compatible.

Scroll to unstack the layers
gpui_platform Platform SPI
Hardware and OS bindings. One of macOS, Linux, Windows or Web compiles in via cfg(target_os).
gpui_engine Scene & Render
Scene intermediate representation (IR), text layout bindings, and renderer traits.
gpui_types Zero-dep Leaf
Pixels, Bounds, Point, and Rgba. Pure geometry primitives with zero GPUI dependencies.
gpui_authoring Reactive DSL
Reactive stores, div() builders, and a window split into render and event halves.
gpui_runtime Application Harness
App lifecycle, native OS event dispatch, and pluggable FramePipeline decorators.
Combo Studio · Sides & Drinks

Build your combo.

Upstream GPUI gives you a fixed meal—take it or leave it. bite-gpui lets you customize every layer. Pick your add-on sides and sauces (Step 1), pair your platform drink (Step 2), and ship. Zero engine forks, and your UI views never recompile.

➔
Extra Sides & Sauces Stack as many pipeline add-ons as you want. They snap directly into your frame loop.
3 sides added Zero engine forks
Benchmarks · Reproducible data

Measured, not promised.

Every bar below maps directly to an executable benchmark in the repository. Tested on real hardware — an Intel i7-8750H laptop running rustc 1.98.1 under normal background desktop load (no synthetic CPU pinning). Clone bite-gpui/gpui_parley , run the commands, and verify the timings yourself.

Raster size lattice 3.3 ms → 41.1 µs per frame · 80× faster

The problem: transforming font size dynamically on 30 lines per frame constantly thrashes standard glyph atlases.

The fix: rounding raster sizes to a deterministic lattice drops atlas cache misses from 154 a frame to zero, shrinking the atlas footprint from 32,508 entries down to 644.

Verify:  $ cargo run --release --example raster_cost

Glyph hinting cache 3,480 → 30 instances per frame · 116× fewer

The problem: upstream GPUI runs a face's bytecode hinting program per glyph instance (~31 µs each).

The fix: keying the cache directly to (face, size) builds just 30 instances per frame where an uncached hinting run would execute 3,480 times.

Verify:  $ cargo run --release --example raster_cost

Text as vector geometry 26.7 ms → 0.69 ms per screenful · 39× CPU reduction

The problem: rasterizing dense text on every frame devours CPU cycles.

The fix: outlines are tessellated once (639 µs). Subsequent frames stream 218,322 pre-computed vertices straight to the GPU in 48 µs. You trade bus traffic for massive frame savings.

Verify:  $ cargo run --release --example vector_cost

Quickstart

Up and running in four lines.

Start with the facade, then peel off layers as you need them.

# The whole stack, behind one facade
cargo add bite-gpui

# …or cherry-pick individual tiers
cargo add bite-gp-platform bite-gp-engine bite-gp-types

# …then swap a seam for a published alternative
cargo add bite-gp-parley
use gpui::*;

fn main() {
    application().run(|cx: &mut App| {
        cx.open_window(WindowOptions::default(), |_, cx| {
            cx.new(|_| MyView)
        }).unwrap();
    });
}
use std::cell::RefCell;
use std::rc::Rc;
use gpui::*;
use gpui_parley::ParleyTextSystem;

fn main() {
    let metrics = Rc::new(RefCell::new(PhaseMetrics::default()));

    application()
        .with_text_system(ParleyTextSystem::new())
        .with_frame_pipeline({
            let metrics = metrics.clone();
            move |_window_id| {
                Box::new(
                    StandardImmediatePipeline
                        .max_fps(60)
                        .instrumented(metrics.clone())
                )
            }
        })
        .run(|cx: &mut App| { /* … */ });
}
#[gpui::test]
fn renders_without_a_display(cx: &mut TestAppContext) {
    let window = cx.update(|cx| {
        cx.open_window(Default::default(), |_, cx| cx.new(|_| MyView))
            .unwrap()
    });
    let mut cx = VisualTestContext::from_window(window.into(), cx);

    // The window is drawn in-process: no display server, no GPU.
    let bounds = cx.debug_bounds("submit").unwrap();
    assert_eq!(bounds.size.height, px(32.));
}