Copied to clipboard
Architecture · Wraps & swaps

Pick your swaps, wrap it, or cook your own.

Why maintain an engine fork? Swap your text shaper or layout solver with a single line in Cargo.toml, or wrap the frame pipeline to pace it and read every pass. Your existing div() views stay completely untouched.

5 Trait boundaries Exposed at the facade
2 Ready to swap Live on crates.io
1 Ready to wrap bite-gp-pass 1.21.3
0 Engine forks Maintained across all
Two tiers

Replace it, or decorate it.

The distinction is not stylistic: it decides which seam a crate can plug into and what publishing it commits the project to. Everything below is one of the two.

Swap

Replace

Implements one of the seam traits and is installed at bootstrap in place of the default. One implementation per seam, chosen when the application is built.

  • replaces the algorithm
  • one per seam, never beside it
  • freezes the trait on publication

Wrap

Decorate

Implements FramePipeline and delegates: handed every pass of every frame it admits, it forwards the ones it does not change and wraps whatever pipeline is already installed.

  • decorates the frame pipeline
  • stacks in any order, any number
  • consumes a trait rather than freezing one
Swaps · The shelf

What you can swap out.

Every swappable boundary is defined in a standalone layer crate, backed by a default implementation, and re-exported under use gpui::*. Two have a published second implementation; the other three do not, and this page says so rather than implying one.

TextSystem published

Text shaping, line layout, and glyph rasterization.

hook at the facade Application::with_text_system(Arc<dyn TextSystem>)
defined in
gpui_engine
default
DefaultTextSystem
over the host's own text backend
second implementation
bite-gp-parley · ParleyTextSystem · 1.21.1
Using gpui_parley →
LayoutEngine published

Translating the reactive element tree into positioned layout boxes.

hook at the facade Application::with_layout_engine(impl Fn() -> Box<dyn LayoutEngine>)
defined in
gpui_engine
default
TaffyLayoutEngine
the engine the facade bundles
second implementation
bite-gp-morphorm · MorphormLayoutEngine · 1.21.2
Using gpui_morphorm →
FramePipeline open

Pacing, presentation order, and render instrumentation.

hook at the facade Application::with_frame_pipeline(impl Fn(WindowId) -> Box<dyn FramePipeline>)
defined in
gpui_authoring
default
StandardImmediatePipeline
and its shipped decorators: .max_fps(n) and .instrumented(metrics)
second implementation
none published

The frame governor the one-pager shows is configuration of this default — ThrottledPipeline and InstrumentedPipeline, both in gpui_runtime — rather than a second implementation. Nothing out of tree publishes one. It is also the one seam a wrap can plug into.

The open seams →
Platform open

OS event loop, display handles, clipboard, and input routing.

hook at the facade Application::with_platform(Rc<dyn Platform>)
defined in
gpui_platform
default
one per host
LinuxPlatform over X11 or Wayland, MacPlatform, WindowsPlatform, WebPlatform — compiled in by cfg, never by a manifest line
second implementation
none published

The hook is the constructor, so a platform is chosen with the application rather than chained onto it.

The open seams →
SceneRenderer open

Lowering the scene intermediate representation (IR) into GPU drawing commands.

hook at the facade none — the trait is the boundary
defined in
gpui_engine
default
one per backend
MetalRenderer, DirectXRenderer, WgpuRenderer on Linux, and a headless one the test harness draws through
second implementation
none published

No Application setter installs one today: the trait is the boundary, and the platform picks the implementation. A second one needs a door before it can be a crate.

The open seams →
Wraps · The inventory

Decorate the pipeline.

One wrap is published from its own repository; two ship inside the facade as decorators you can stack without depending on anything. All three implement the same trait, and all three forward the passes they do not change.

bite-gp-pass

Published

Reads the frame's passes and the asks that did not draw: a policy-driven rate on one side, a ledger of what each pass cost on the other. A decorator you install without changing a view.

  • a rate and a ledger
  • one runtime dependency
  • exercisable in CI

lib: gpui_pass

Using gpui_pass →

ThrottledPipeline

In the facade

The facade's own rate cap: draw every Nth ask, decided from one number and the last frame.

  • caps a rate
  • whole intervals only

lib: gpui_runtime

InstrumentedPipeline

In the facade

The facade's own accounting: an Instant around each root pass, summed into running phase totals.

  • running totals
  • drawn frames only

lib: gpui_runtime

A wrap implements FramePipeline and delegates. Every pass has a default that calls the Window method behind it, so a decorator is only as transparent as the passes it explicitly forwards — which is the whole of the contract.

Compose with any pipeline

A wrap goes where any pipeline goes — the factory you hand to with_frame_pipeline — and stacks in any order and any number. Nothing in the tree is forked, so there is no patch to rebase when the engine moves. The published one is bite-gp-pass, and cargo add bite-gp-pass is the whole of its install.

Installation

A swap is installed, not applied.

The two published swaps are ordinary crate dependencies. Nothing in the tree is forked, so there is no patch to rebase when the engine moves — and because each published package keeps its library target name, use gpui_parley::… and use gpui_morphorm::… are what your code already says.

# both published swaps, one manifest
[dependencies]
bite-gpui = "1.21"
bite-gp-parley = "1.21"
bite-gp-morphorm = "1.21"
use gpui::*;
use gpui_morphorm::MorphormLayoutEngine;
use gpui_parley::ParleyTextSystem;

fn main() {
    application()
        .with_text_system(ParleyTextSystem::new())
        .with_layout_engine(|| Box::new(MorphormLayoutEngine::new()))
        .run(|cx: &mut App| { /* the same div() tree */ });
}
  • The trait stays where it is. It is declared in a layer crate — gpui_engine for three of the five — so implementing it is an impl in your crate, not an edit to somebody else's.
  • The hook is one method, called before the first window. A layout engine and a frame pipeline take factories because both are per-window; a text system is one Arc for the process.
  • The library target name is preserved. The package is namespaced, the library is not, so an existing use line keeps compiling and the manifest is the only thing that moves.
  • Each swap lives in a repository of its own, with the publisher that releases it, so it versions independently of the engine it plugs into.
Open seams

What it takes to build a new backend.

We explicitly document the three unbuilt seams so external contributors know exactly where the architectural boundaries lie:

What a second implementation would take filed as usage projects, not as crates
seam what it would take filed as
FramePipeline An out-of-tree pipeline. The shipped decorators are the pattern: a decorator forwards the passes it does not change and decides the ones it does, so a third-party one would wrap or replace StandardImmediatePipeline at the same hook. pipeline-decorator
Platform A platform that needs no display, so a CI job can render. The built-in route is the facade with its platform bundle dropped — default-features = false — plus the TestPlatform the test harness already runs on. headless-platform
SceneRenderer A renderer out of tree, which is what a raw render-pass blit would actually have to be — and a door at the facade first, because there is no setter to install one through. scene-renderer

These are the categories the project files its own work under — a new flavour of a swappable, a decorator, a new authoring surface — and the demos page carries the whole taxonomy with what each category is for.

The two that ship instead

The wgpu renderer and the frame governor are on the one-pager's shelf, but neither is a swap: WgpuRenderer is the scene renderer the Linux platform installs, and the governor is StandardImmediatePipeline.max_fps(60) with .instrumented(…) — shipped decorators, configured rather than installed. The seam that replaces the renderer is SceneRenderer, and it has no hook yet.

Limits

Two of five, not five of five.

  • The shelf is intentionally sparse: We only publish an alternate crate when there is a distinct, measurable performance or feature advantage. We don't build duplicate backends for the sake of checking boxes.
  • Swaps are independent decisions: bite-gp-parley and bite-gp-morphorm operate on entirely different pipeline phases. You can use either one alone, combine both, or keep the defaults. Neither implies or requires the other.
  • Every swap involves a trade-off: Parley adds BiDi layout and font fallback at the cost of slightly higher per-glyph instantiation overhead. Morphorm cuts layout frame time by up to 3.8×, but omits complex CSS Grid track rules.
  • A wrap is the frame pipeline, and only the frame pipeline: FramePipeline is the one seam that hands a crate the frame's passes, so a decorator on TextSystem or LayoutEngine would have to be the implementation — which is a swap.
Compose them on the one-pager

The Combo Studio is the interactive version of this shelf: it stacks the add-ons and the platform backend, and shows the main.rs they produce. Two of its four add-ons are the published crates above; the others are shipped configuration, as the panel itself says.

Where they live

The published crates.

The three crates, and the repositories they are developed and released from.