Building for WASM & iOS
Nimbus is dual-target: it runs natively on desktop and in the browser via WebAssembly, with an additional iOS target via UniFFI Swift bindings.
WASM
Building
The release build enables SIMD128 for vectorized geometry processing:
RUSTFLAGS='--cfg=web_sys_unstable_apis -C target-feature=+simd128' \
wasm-pack build --target web --release --out-dir pkg
Output goes to pkg/ as an ES module ready for <script type="module">.
WASM Entry Point
src/web.rs is the WASM entry point. It:
- Creates a wgpu surface from an HTML
<canvas>element - Initializes
MapRendererwith a WASM-compatible HTTP client (src/wasm_fetch.rs) - Loads style, sprite, and glyph data asynchronously
- Drives the render loop via
requestAnimationFrame
Module Workers
Tile decode and tessellation are offloaded to module workers to keep the main thread free for rendering.
graph LR
Main[Main Thread] -->|postMessage tile bytes| Worker[Module Worker]
Worker -->|decode MVT + tessellate| Worker
Worker -->|postMessage vertices| Main
- Workers use
src/tile_loader/wasm_worker_entry.rsas their entry point - Data is serialized with postcard (compact binary, no JSON overhead)
WorkerTileResultis the wire type between worker and main thread- During initialization (before workers are ready),
spawn_localis used as a fallback
WASM-Specific Optimizations
- Global allocator:
talc::TalckWasmreplaces the default allocator for better WASM performance - Release profile:
opt-level = 2(not"s"),wasm-opt = ["-O3"] - Gzip decompression: Uses browser's
DecompressionStreamAPI instead of flate2 - Async HTTP:
src/wasm_fetch.rswraps the Fetch API
Glyph Loading on WASM
Glyphs are loaded asynchronously after style application:
get_glyph_info()returns the glyphs URL template + unique font stacks from resolved layerswasm_fetch::fetch_bytes()fetches PBF glyph rangesparse_pbf_glyphs()+atlas.add_glyphs()populate the atlas
iOS (UniFFI)
Architecture
src/ffi.rs exposes a Swift-friendly API via UniFFI 0.28 (proc-macro style).
MapEngineInner holds the wgpu Instance/Device/Queue and optional Surface/MapRenderer. unsafe impl Send/Sync is used because wgpu types are thread-safe on Metal.
Callback Interface
The Swift side implements MapViewDelegate as a callback interface:
The delegate is wrapped in Arc<dyn MapViewDelegate> internally for the waker closure that signals new tile arrivals.
Surface Creation
The surface is created from a raw CAMetalLayer pointer:
Building the XCFramework
This runs build-xcframework.sh, which:
- Builds for
aarch64-apple-darwin(macOS ARM) - Builds for
x86_64-apple-darwin(macOS Intel) - Builds for
aarch64-apple-ios(iOS device) - Builds for
aarch64-apple-ios-sim(iOS Simulator) - Packages into an
.xcframeworkwith generated Swift bindings
The Swift package is defined in WoosMapView/Package.swift as a binary target.
MSAA Disabled
MSAA is disabled in the FFI path (sample_count = 1) because MTKView's MSAA resolve triggers a Metal texture size mismatch. sRGB correction is controlled via a uniform flag instead.