Copied to clipboard
Compare · Both columns runnable

Forking, or depending.

Every comparison here is between two things we can build and run — a fork checkout against a published crate, raster text against vector text. Where something is not comparable yet, this page says so rather than reaching for a number.

A branch is not a dependency.

The thing the rearchitecture changes for a consumer is not the API. It is what sits in Cargo.toml.

A branch dependency A published version
How you add it gpui = { git = "…", rev = "…" } bite-gpui = "1.21"
What the pin means A commit — a point in time A version derived from the upstream release it retargets
Resolution A revision; the graph cannot express a range Ordinary SemVer, so ranges and lockfiles work
Updating Re-pin the revision and rebuild Change the version
Provenance Whatever the branch happened to be [package.metadata.bite] records branch, commit and upstream release
What your source says use gpui::… use gpui::… — unchanged

The rename is the whole trick, and it is deliberately split in two: the package name moves into the bite-* namespace, while the library target name does not. So bite-gpui still exports a crate called gpui, and an existing project changes one line — the dependency — with no view code touched. That is what makes it a drop-in rather than a porting exercise.

The prefix is not decoration either. The name being reimplemented is already taken: gpui on crates.io is owned by the upstream project it comes from.

Five seams, and where each one is swapped.

The architecture is only a claim until a boundary has an outside: each of these is a trait a second implementation can satisfy without touching the tree.

seam what it owns how you swap it second implementation
TextSystem Shaping, line layout and glyph rasterization. Application::with_text_system(Arc<dyn TextSystem>) gpui_parley
LayoutEngine Turning the element tree into laid-out boxes. Application::with_layout_engine(impl Fn() -> Box<dyn LayoutEngine>) gpui_morphorm
FramePipeline Pacing, presentation order and per-frame instrumentation. Application::with_frame_pipeline(impl Fn(WindowId) -> Box<dyn FramePipeline>) none published yet
Platform The OS event loop, displays and input. Application::with_platform(Rc<dyn Platform>) none published yet
SceneRenderer The scene IR becoming pixels. No Application setter — the trait is the boundary none published yet

Two of the five have a second implementation today, and both are published: TextSystem is gpui_parley and LayoutEngine is gpui_morphorm —one guide and another. The other three are the work of giving them a user, because until a seam has one it is a boundary in the type system and not yet a boundary in practice.

Wraps & Swaps is the same inventory with the wiring: the crate each trait is declared in, the default the facade installs, and what each open seam would take.

Raster or vector: it is a trade about bus traffic.

One cost example draws a screenful of dense text on every frame. The other tessellates the glyph outlines once and then streams triangles. Neither is free, and the two failures are different kinds.

route per frame what it does what it spends
Raster 26.7 ms Rasterize a screenful of text every frame. CPU
Vector 48 µs Stream 218,322 pre-tessellated vertices for a screenful, 42.6 MB of traffic. Bus bandwidth

The vector route pays once — outlines tessellated in 639 µs — and then 3.8 µs to look the result up each frame. Rasterizing the same content every frame is 26.7 ms, so the vector route lands a screenful in 0.69 ms: 39× less CPU, bought with 42.6 MB of vertex traffic a frame. On a frame budget of 16.7 ms that is roughly 3.8 ms against 16.7 ms.

Two smaller effects ride along, and both are the same idea — stop doing per-instance work in the frame:

  • Rounding raster sizes to a deterministic lattice drops atlas cache misses from 154 a frame to zero, shrinking the atlas from 32,508 entries to 644.
  • Keying the hinting cache to (face, size) builds 30 bytecode instances a frame where an uncached run would execute 3,480 — 116× fewer.

What we won't claim.

A comparison is only worth reading if you know which column would survive scrutiny.

  • The bars are cost examples, not benchmarks. They are cargo run --release --example binaries timing Instant deltas. There is no criterion target in the published layer crates, so there is no statistical treatment and no run-to-run spread. Every count in them reproduces exactly; the absolute milliseconds are one machine's.
  • Two measurements have a second column and real statistics. The vanilla-against-Parley table on Using gpui_parley is criterion, 100 samples per operation, two implementations behind the same trait; the taffy-against-morphorm tables on Using gpui_morphorm are the same shape. Both hosts are laptops, not controlled benchmarking hosts — which is stated on those pages rather than buried.
  • The layout comparison is two trees, not a suite of them. A second LayoutEngine exists now, and it puts the same nodes in the same places as taffy — 0.00 px apart on a 12-node tree and on 176–3501 nodes of changing buttons — while costing 3.4–3.8× less for 50 to 1000 buttons. At a thousand, that is 44% of a 16.7 ms frame against 12%. It is still two trees, on one laptop, built from the constructs the conversion claims to handle: it does not say the two engines are equivalent in general, and the constructs morphorm cannot express — flex_grow among them — are listed with the numbers rather than left out. The run is recorded in the crate's own repository, with its command, toolchain and host.
  • No competitor numbers. Nothing here is measured against another UI framework. The comparisons are between two things in this project, run on the same host, on the same day, through the same boundary.