--- id: AGD-0004 ticket: tilemap-and-metatile-runtime-binary-layout title: Tilemap and Metatile Runtime Binary Layout status: open created: 2026-03-26 resolved: decision: tags: - packer - legacy-import - tilemap - metatile - runtime-layout --- ## Pain The repository has conceptual alignment around map responsibility split and active-window residency, but still lacks a closed runtime binary contract for handheld maps. Without a canonical decision: - packer cannot close the output specification for map artifacts; - runtime lacks one stable reading baseline; - Studio cannot align authoring and preview schemas with one binary target. ## Context Legacy source: `docs/packer/agendas/Tilemap and Metatile Runtime Binary Layout Agenda.md` Domain owner: `docs/packer` Cross-domain impact: - `docs/vm-arch` - `docs/studio` - `docs/compiler/pbs` Current discussion premises retained from source: - visual and collision concerns should remain separated; - map cells should not duplicate large metadata per tile; - only a `3x3` active window of maps should remain resident in memory; - a `64 KiB` target budget per map bank has been discussed; - `map bank` and `tileset bank` do not need identical size rules. ## Open Questions - [ ] Is `collisionId` with `5` bits (`32` classes) sufficient for expected projects? - [ ] Should `flag0` be reserved for fast trigger semantics or contextual visual variation? - [ ] Which optional chunks belong in `V1`, and which should wait for a later version? - [ ] Must `map bank` stay strictly at `64 KiB`, or should capacity be variable with explicit metadata? - [ ] What compression policy should the packer adopt by default for the stream? ## Options ### Option A - `u8` cells with auxiliary tables - **Approach:** Store only one `metatileId` per cell and derive collision/flags from side tables. - **Pro:** Smallest RAM and I/O footprint. - **Con:** Pushes too much semantic meaning into external lookup tables. - **Maintainability:** Medium. ### Option B - `u16` bit-packed cells - **Approach:** Use `16` bits per cell, with a compact split such as `visualId`, `collisionId`, and `flag0`. - **Pro:** Balances compactness, deterministic decoding, and enough expressivity for many handheld cases. - **Con:** Leaves less room for inline per-cell growth than `u32`. - **Maintainability:** Strong. ### Option C - `u32` cells - **Approach:** Use larger cells to keep more event/flag space inline. - **Pro:** Highest flexibility for per-cell triggers and future expansion. - **Con:** Doubles memory cost versus `u16` without current evidence that it is needed. - **Maintainability:** Weak for the current budget target. ## Discussion The retained source already converged toward `u16` as the best baseline. The suggested runtime shape is: - fixed map header; - contiguous cell stream; - optional future-proof chunks. The proposed `U16_PACKED_V1` split keeps visual and collision references compact while preserving predictable memory usage and straightforward streaming behavior for a handheld target. ## Resolution Recommended direction: adopt **Option B** as the baseline. Imported retained recommendation: 1. authorship stays JSON-oriented around compact ids; 2. build output packs deterministically into `U16_PACKED_V1`; 3. runtime active-window budget remains explicit; 4. any move to `u32` should happen only through a later versioned encoding with evidence of need. Next step suggestion: convert this agenda into a decision that closes `U16_PACKED_V1`, propagation to `docs/vm-arch`, Studio editing schema alignment, and implementation planning for roundtrip tests.