Skip to main content

Crate retroglyph_wgpu

Crate retroglyph_wgpu 

Source
Expand description

GPU rendering backend for retroglyph: Vulkan, Metal, and D3D12 from a single codebase via [wgpu].

§Architecture

WgpuBackendBuilder holds configuration (fonts, grid size, integer scale) and builds a WgpuRenderer. The glyph source is a static FontChain (a single BitmapFont is a chain of one); every font in the chain is grid-packed into one R8 array-texture atlas and addressed by a flat slot id (see retroglyph_window::atlas). The renderer keeps one CPU-side instance array per grid layer and creates its device lazily, when the windowing loop calls Presenter::init_surface:

WgpuBackendBuilder (font, grid size, scale)
  |  .build()
  v
WgpuRenderer
  implements retroglyph_window::Presenter (an Output supertrait)
  wrapped by retroglyph_window::WindowBackend to become a full Backend
  (WindowBackend owns the input event queue and the no-op Cursor)
  |
  |  init_surface(window) -> wgpu Device + Queue + Surface, then GpuResources
  v
one render pass per present(): every grid layer back to front, each drawn as
backgrounds then coverage-blended glyphs (then sprites), from one instance
buffer uploaded once per frame.

A cell costs 16 bytes and no index buffer: the vertex shader derives the quad’s corners from the vertex index and the cell’s (column, row) from the instance index, so the only per-instance data is the glyph slot, two colors, the sub-cell offset, and the compositing flags.

This backend composites grid layers itself on the GPU (composites_layers returns true): it receives the raw layered stream from the core Terminal and draws each layer back to front, so an empty cell in a higher layer lets the layer beneath show through while an occupied cell is opaque, matching retroglyph-software’s per-pixel occlusion. It requests full frames (needs_full_frame returns true) and redraws every cell of every layer each frame, so there is no orphaned-pixel problem from sub-cell glyph spill.

§Choosing between this and retroglyph-gl

Both are GPU backends drawing the same instanced-quad pipeline, and both produce pixel-identical output (each is checked against the retroglyph-software CPU rasterizer). They differ in which driver stack they reach and what they cost to depend on:

retroglyph-wgpuretroglyph-gl
APIsVulkan, Metal, D3D12OpenGL 3.3, WebGL2
Browseryes, WebGPU (see Platform support)yes, WebGL2
unsafe in the backendnoneunavoidable (every GL call)
Direct dependencies, transitively85 crates54 crates
Clean debug build of the crate15s7s
Offscreen render testsevery platformLinux/EGL only

The dependency and build-time figures are for one host (macOS, --all-features); the ratio is what matters, not the absolute numbers. The difference is naga (the shader front end that compiles this crate’s WGSL) plus wgpu-core (the validation and state-tracking layer that makes the API safe), not the three backend APIs: only one hardware abstraction layer compiles per platform, since wgpu-hal’s backends are target-gated. A macOS build pulls the Metal stack and no Vulkan; a Linux build pulls ash and no Metal.

Both cover the browser today (WebGPU here, WebGL2 there); pick retroglyph-gl when a smaller dependency tree matters more than the rest, or this one for validation-layer diagnostics, a modern driver path, and a backend with no unsafe in it. On wasm32 this backend’s device is ready asynchronously (see Platform support), where retroglyph-gl’s WebGL2 context is ready synchronously; that is the one real difference the browser adds to the choice.

§Performance

A 200x60 grid with three layers (36000 cells, 562 KiB of instance data) costs 266us per frame end to end: flattening the layers, uploading, encoding six draws, submitting, and waiting for the GPU to finish. That is roughly 4% of a 60 Hz frame budget, on an M-series laptop at --release, and it is larger than any grid the examples use.

The number is here to set expectations, not to invite tuning: a character grid is a trivial workload for a modern GPU, and the design choices above (deriving position from the instance index, packing every layer into one upload) are about keeping the per-frame bandwidth small rather than about winning a benchmark. Emitting four real vertices per cell instead, with explicit positions and UVs, is the other common shape for this renderer and would cost roughly six times the per-frame bytes.

§Platform support

Native (Vulkan, Metal, D3D12) and the browser (WebGPU) both work.

Presenter::init_surface is synchronous, but request_adapter and request_device are not, and a browser’s main thread has no way to block on a future the way native does. So the two targets take different paths through the same call: Instance::create_surface is synchronous everywhere, so on wasm32 init_surface creates the surface from the window’s canvas, spawns the adapter and device request with wasm_bindgen_futures::spawn_local, and returns immediately; present polls the result each frame and draws nothing until it lands. Native, by contrast, blocks on the same requests with pollster::block_on inside init_surface itself, so the device is always ready by the time it returns. See crate::gpu::PendingGpu for the shared-state cell this deferral is built on.

The one user-visible consequence is on wasm32 only: the first frames after startup are blank (whatever was drawn before the device exists is not shown) until the adapter and device resolve, typically well under a second. Native has no such gap.

retroglyph-gl also covers the browser, through WebGL2, whose context creation is synchronous and so needs no equivalent deferral. Pick between the two using the table above and the dependency/build-time comparison it links to.

§Environment variables

wgpu reads its own configuration from the environment, and this crate passes it through rather than overriding it:

VariableEffect
WGPU_BACKENDRestricts the backends to try (vulkan, metal, dx12).
WGPU_POWER_PREFlow or high; defaults to low, since a character grid is a handful of draw calls per frame.
WGPU_VALIDATION / WGPU_DEBUGToggle the driver’s validation and debug layers.

§Features

This crate has no default features; every feature below is optional and off unless enabled.

§default-font

⚪ Optional.

Embeds the Unscii 16 default font so a caller can build a renderer with no font of its own.

Forwards to retroglyph-window’s default-font feature.

§dev

⚪ Optional.

Forwards retroglyph-core’s dev feature, which forces development diagnostics on in a build that would otherwise compile them out (see retroglyph_core::dev).

§tilesets

⚪ Optional.

PNG sprite/tileset support: decodes sprite sheets into an RGBA array-texture atlas and draws them in a third, source-over blended pass per grid layer.

Forwards to retroglyph-window’s shared tileset decode.

Re-exports§

pub use config::WgpuBackendBuilder;
pub use config::WgpuBackendError;

Modules§

config
Configuration, builder, and error types for the wgpu backend.
font
Bitmap glyph fonts and CP437 mapping, shared by retroglyph’s graphical backends.

Structs§

BitmapFont
A 1-bit-per-pixel bitmap glyph font.
FontChain
The glyph source a backend draws from: a primary BitmapFont plus an ordered list of fallback fonts.
WgpuRenderer
The live wgpu renderer: a Presenter, wrapped in WindowBackend to form a full Backend for the windowing loop.

Enums§

SurfaceError
A failure creating or driving the wgpu device and surface.