Reference7 min read8 sections

Presentation protocol, versions 2–17

docs/PRESENTATION_PROTOCOL.mdSource updated 2026-10-26
Markdown
On this page 8 sections

The owned WebGPU renderer now consumes a render-only scene through present(message). It no longer imports room peers or reads a simulation save. render(camera, time) applies camera, cosmetic motion and traveler smoothing to that independent scene. Main-thread gameplay and the OffscreenCanvas observatory use the same receiver.

The protocol is implemented in packages/render/presentation.ts. The current pinned envelope fixture is tests/fixtures/presentation-v2.json (presentation.2). Version 1 remains pinned as the explicit migration source; upgradePresentationMessage validates/copies its TRS envelopes into version 2, leaving the original unchanged. The live receiver accepts versions 2–17; version 1 still requires explicit migration. The production simulation worker compiles authored objects into canonical ECS rows and consumes their journal through packages/presentation; see the compiler and ECS bridge contracts. apps/shared/presentation.ts remains a compatibility adapter and independent projection oracle in tests.

#Worker transport

Local worker hops now wrap the unchanged scene message in transfer-envelope format 1: a channel epoch, uint64 transfer sequence, used byte length and one reusable 4 MiB buffer. The live player returns that buffer with its frame acknowledgement; the render-worker probe uses the same codec. Transport sequence/epoch are independent of scene generation/revision and do not weaken structural recovery. The payload remains validated UTF-8 JSON; compact binary encoding is still future work. See ownership, admission and failure rules.

#Envelope and ordering

All messages have format, generation and revision. Formats 2–17 are accepted; the version-2 primitive envelope remains pinned below, with model/skin/stamped-actor extensions described at the end. Counters are canonical decimal uint64 strings; comparisons use BigInt. Structural revisions and motion sequences are separate.

Message Payload Ordering rule
baseline Complete environment, objects, actors; motion sequence Atomically replace the CPU scene. A newer generation resets structural and motion history.
delta Upserted/removed objects and actors, optional environment, baseRevision Apply only to the exact base revision and advance by one. Missing history requests a baseline.
motion Actor IDs, positions/yaws; sequence Apply only at the current structural revision, to existing actors, and above the accepted sequence.

A receiver returns applied, ignored or baseline-required, together with its accepted generation/revision. Older generations, revisions and motion sequences are ignored. A future-generation update raises the minimum generation needed for recovery, so an old baseline cannot reopen the retired scene. A structural gap parks both structural and motion updates until a complete baseline arrives. The last accepted scene remains drawable throughout recovery. Future-revision motion also requests a baseline; it cannot create actors before their structural definitions arrive.

A same-generation baseline cannot move either structural revision or accepted motion sequence backward. An equal-revision baseline is accepted only when recovery is pending. Malformed messages throw before changing the accepted state. Unknown removals, impossible total budgets and unknown-actor motion request a baseline without applying any part of the message.

#Composed transforms

Objects carry a column-major affine matrix with 16 float64 JSON numbers instead of decomposed Euler position/rotation/scale fields. Hierarchical composition therefore retains shear; the renderer converts to float32 only when preparing GPU instances. Matrix coefficients are bounded to ±1e9, basis-column lengths to [1e-12, 1e9], and normalized positive determinant to at least 1e-6. Perspective, mirrored, singular and excessively ill-conditioned matrices fail before publication. These are declared renderer limits, not changes to the canonical ECS transform schema.

Normals use cofactors of normalized basis columns, equivalent to the inverse transpose up to a positive factor removed by normalization. This supports nonuniform scale with shear without expanding the instance buffer. Cosmetic translation/flicker remain presentation-only matrix operations. The v1 upgrade uses the existing right-handed XYZ Euler convention and validates the resulting matrix against the new GPU limits.

#Data ownership and budgets

The parser copies all accepted values and freezes the owned CPU description, including nested material/transform values. Sender mutation cannot change a receiver's scene. A producer retains one current scene; a receiver retains one current scene. There is no growing delta log. Generation-local render IDs are strings, not process-local ECS handles. The production player uses compiled persistent ECS IDs. The retained beta adapter qualifies its compatibility keys as the JSON pair [place ID, object ID].

Scene budgets are 4,096 primitive instances including 25 instances per procedural traveler, at most 32 travelers, and eight lights. Per-message update/removal arrays and all strings/numbers are bounded. Schemas are closed: simulation flags, interactions, colliders, room credentials and other unexpected fields are rejected. Object replacement preserves insertion order; ordering changes require a baseline. Transparent draw sorting is a later renderer requirement.

The simulation worker sends at most one unacknowledged frame, containing accepted UI state, optional room state and a presentation message. Main acknowledges its decimal uint64 delivery ID after applying the frame and requests a baseline if the receiver detects missing history. While a frame is outstanding, further requests retain only a dirty flag; the next acknowledgement samples the latest simulation state. Independent ECS journal overflow produces a complete baseline. Wrong or duplicate acknowledgements do not release a current frame. Disposal clears callbacks and retained state. A failed projection/send leaves recovery requested for the next explicit attempt.

This channel uses structured-clone messages and asynchronous worker acknowledgements. It bounds production state snapshots across the worker boundary, including when rendering stalls. It does not implement pooled binary transform buffers, generation-checked buffer returns, a render worker in the production player, or a bound on all application event traffic. Those remain separate requirements. The existing diagnostics test transferred canvas/buffer ownership.

PresentationSource.patch() provides validated partial publication with the same version-2 delta envelope. It retains unchanged owned records, rejects unknown removals/conflicts/budget overflow atomically, and consumes no revision for a semantic no-op. Full-scene update() uses this path and preserves delta insertion order; ordering changes require a baseline.

Primitive GPU geometry remains resident in the renderer. Mesh/material changes replace CPU draw records and reuse those buffers; no imported GPU asset cache or general resource lifetime manager is claimed. Disposal releases the GPU resources and the CPU presentation description. A generation change clears traveler smoothing history.

#Verification and remaining work

Headless tests exercise loss/reordering, stale generations, exact sequence ordering above 2^53, unknown actors, deletion, malformed data, combined instance limits, owned values, both shipped worlds and actual adapter recovery. The browser observatory deliberately drops a structural revision in a real render worker. It compares the preserved frame bytes, applies a newer baseline, rejects a late deletion delta, then starts a new generation. The existing gameplay suite continues to cover doors, pickups, shared traveler movement, travel and saved progress.

At the original protocol increment, general transform batches, bounded transferable-buffer transport, resource hashes/refcounts, imported-asset lifetime/replacement, culling, render graph, shader replacement and broad device acceptance remained open. Later additions are tracked in implementation status. The engine-zoo probe tests eight primitive mesh/material replacement frames with stable explicit GPU ownership; this does not prove a long-session allocation plateau. At that stage, presentation interpolation still used the beta's smoothing rather than the later bounded timestamped interpolation buffer. No renderer milestone is marked complete by this protocol increment.

The historical version-1 clean committed-source acceptance is b53e3ba719702a2073c947327d4244e00895f013: 19 browser results, capability report with gap/recovery revisions, and shared traveler capture. The report identifies the build, Chromium/software-WebGPU environment and complete worker resource counts.

Version-2 acceptance is recorded with the ECS bridge results: migrated legacy envelopes, a pinned composed matrix in Node/Chromium, rendered lifecycle recovery and the unchanged multiplayer journey. The benchmark explicitly records the remaining full-projection cost.

#Skeletal palettes

Skeletal presentation extends the message format to 4 for scenes containing skinned meshes. Each such object owns a skin.matrices array of 1–64 positive affine matrices; the mesh ID ends in :skin. The mesh/palette pair is required together. Whole scenes and automatic model reservations share a 4,096-matrix budget. Receivers validate referenced joint coverage and current-pose bounds before publication. Baseline failure preserves the accepted scene; invalid deltas request a new baseline. Unskinned producers retain formats 2 or 3.

#Stamped actors and rig reservations

Shared travelers extend the envelope to format 5 when actors carry a relay-owned motion stamp. The optional sample record contains stream, decimal uint64 sequence and monotonic millisecond time, copied and validated on baseline, delta and motion messages. Formats 2–4 reject this extra field. Producers choose format 5 when needed and keep earlier formats for unchanged content. PresentationReceiver.reserveActors() includes the configured per-actor palette cost in the same 4,096-matrix budget; failed reservation or baseline admission preserves the accepted scene. Interpolation and gait caches belong to the renderer and are not serialized into presentation snapshots.

#Terrain detail (version 17)

Static imported objects may carry detail: "terrain". Older envelopes reject this field; primitives, skin palettes and cosmetic object motion cannot use it. The ECS projection derives it from declared streaming ground, and changes produce ordinary structural updates. Renderer preparation independently certifies LOD boundary edges before selecting reduced ground; uncertified variants retain the original mesh. This render-only hint does not alter collision, authored models, world format or ownership.

End of Presentation protocol