Copied to clipboard
Architecture · Ways to extend

Wrap it, swap it, shove it in, serve it.

Why maintain an engine fork? Replace the text shaper or the layout solver, decorate the frame pipeline, composite pixels GPUI did not draw, or bring the platform itself. Four ways to change the engine, and not one of them edits a div().

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
The stack, and where each way plugs in
gpui_runtime Application harness
gpui_authoring Reactive DSL
gpui_types Scalar leaf
gpui_engine Scene & render
gpui_platform Platform SPI
01 Wrap gpui_runtime · FramePipeline
03 Shove gpui_authoring · surface()
02 Swap gpui_engine · text · layout · render
04 Serve gpui_platform · Platform
no seam — the leaf every way keeps
Four ways

Wrap it, swap it, shove it in, serve it.

The distinction is not stylistic: it decides what you hand over, which boundary it plugs into, and what publishing it commits the project to. Everything below is one of the four.

01

Wrap

Decorate

Implements FramePipeline and delegates. It is handed every pass of every frame it admits, forwards the ones it does not change, and decides the ones it does — including whether the frame runs at all.

  • decorates the frame pipeline
  • stacks in any order, any number
  • consumes a trait rather than freezing one
The wrap inventory →
02

Swap

Replace

Implements one of the algorithm seams 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
The swap shelf →
03

Shove

Insert

Replaces no seam and decorates no pipeline. It builds on the published surface and pushes foreign pixels into the frame — a 3D viewport, a decoded video frame, or a texture you computed.

  • surface() and gpu_canvas()
  • same-device or exported handle
  • sits above the facade
Bring your own pixels →
04

Serve

Host

Supplies the platform the application runs on: one of the four built-in backends compiled in by cfg, or a Platform implementation of your own.

  • Metal, Direct3D 11, X11/Wayland, WebGPU
  • one backend per build, by cfg
  • or a Platform of your own
Bring your own platform →
Swap · 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 third — the scene renderer — has no hook to install one through, and this page says so rather than implying otherwise.

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 →
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 →
Wrap · 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.

The seam is FramePipeline, declared in gpui_authoring and installed with Application::with_frame_pipeline. Its default is StandardImmediatePipeline with the shipped .max_fps(n) and .instrumented(metrics) decorators — the frame governor the one-pager shows is that default configured, not a second implementation.

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.

Shove · The surface

Bring your own pixels.

A surface is content GPUI did not draw: a decoded video frame, a 3D viewport on its own device, or a texture computed off-thread. It replaces no seam and decorates no pipeline — it is an ordinary element that composites a foreign buffer with no CPU copy.

  • surface(source) takes what you already hold. A SurfaceSource is a CoreVideo buffer on Apple, a Direct3D texture or view on Windows, or a dma-buf on Linux, and the element fits it into its bounds with the same ObjectFit modes an image uses.
  • gpu_canvas(|ctx| …) hands you the device. The callback receives a GpuCanvasContext: paint_surface for a handle you exported, paint_texture for a texture made on the window renderer’s own device, and device::<R>() to get that device.
  • Nothing is frozen and nothing is installed. There is no with_surface: the element sits above the facade and the engine never learns what produced the pixels.
  • The crates that build on it are public. gpui_interop bridges a producer’s own device (Linux dma-buf, Windows shared texture; macOS planned) and gpui_video is a hardware-decoded player on the same path.
Two doors, and a cost for each

A same-device texture is sampled with no export and no fence; an exported handle costs the producer a hand-off per frame. The measured difference, the platform matrix, and the 3D and video producers are on Bring your own surface.

Serve · The host

Use one of ours, or bring your own.

The platform is the seam the application does not switch at runtime: it is chosen when the app is built, and it supplies the event loop, windows, displays, clipboard and input. Four ship; a platform of your own is an impl, not a fork.

The platforms that ship one backend per build, selected by cfg
platform backend selected by
macOS MacPlatform over gpui_apple’s Metal renderer cfg(target_os = "macos")
Windows WindowsPlatform over Direct3D 11 cfg(target_os = "windows")
Linux LinuxPlatform over X11 or Wayland, and gpui_wgpu cfg(any(linux, freebsd))
Web WebPlatform on wasm32, through wgpu to WebGPU cfg(target_family = "wasm")
Headless the test platform and the headless Linux platform test-support, or ZED_HEADLESS
iOS · Android the third-party gpui-mobile crate, not yet ported to these crates its own Platform impl

The hook is Application::with_platform, and the trait is declared in gpui_platform — the leaf that links no drivers. What a platform owns, and what bringing one takes, is on Bring your own platform.

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.25"
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 two unbuilt seams so external contributors know exactly where the architectural boundaries lie. The frame pipeline is no longer one: bite-gp-pass is that out-of-tree wrap.

What a second implementation would take filed as usage projects, not as crates
seam what it would take filed as
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 four crates, and the repositories they are developed and released from.