212 lines
9.8 KiB
Markdown
212 lines
9.8 KiB
Markdown
# Tile Bank Packing Materialization Decision
|
|
|
|
Status: Accepted
|
|
Date: 2026-03-20
|
|
Domain Owner: `docs/packer`
|
|
Cross-Domain Impact: `../runtime`, `docs/studio`
|
|
|
|
## Context
|
|
|
|
The packer execution contract for `packWorkspace(...)` is already closed.
|
|
|
|
What remained open for `tile bank` was the format-specific producer contract:
|
|
|
|
- which selected files become part of the packed asset;
|
|
- how those files become the canonical `TILES/indexed_v1` payload;
|
|
- how `AssetEntry` fields are derived for runtime consumption;
|
|
- how bank palettes are declared and normalized;
|
|
- which diagnostics block the build for this format.
|
|
|
|
The agenda that closed this discussion is:
|
|
|
|
- [`../agendas/Tile Bank Packing Materialization Agenda.md`](../agendas/Tile%20Bank%20Packing%20Materialization%20Agenda.md)
|
|
|
|
Relevant upstream references are:
|
|
|
|
- [`../decisions/Pack Wizard Pack Execution Semantics Decision.md`](../decisions/Pack%20Wizard%20Pack%20Execution%20Semantics%20Decision.md)
|
|
- [`../specs/3. Asset Declaration and Virtual Asset Contract Specification.md`](../specs/3.%20Asset%20Declaration%20and%20Virtual%20Asset%20Contract%20Specification.md)
|
|
- [`../specs/4. Build Artifacts and Deterministic Packing Specification.md`](../specs/4.%20Build%20Artifacts%20and%20Deterministic%20Packing%20Specification.md)
|
|
- [`../pull-requests/PR-32-palette-declarations-with-explicit-index-contract.md`](../pull-requests/PR-32-palette-declarations-with-explicit-index-contract.md)
|
|
- [`../../../runtime/docs/runtime/specs/04-gfx-peripheral.md`](../../../runtime/docs/runtime/specs/04-gfx-peripheral.md)
|
|
- [`../../../runtime/docs/runtime/specs/15-asset-management.md`](../../../runtime/docs/runtime/specs/15-asset-management.md)
|
|
- [`../../../runtime/docs/runtime/agendas/024-asset-entry-metadata-normalization-contract.md`](../../../runtime/docs/runtime/agendas/024-asset-entry-metadata-normalization-contract.md)
|
|
|
|
## Decision
|
|
|
|
The first-wave packer materialization contract for `tile bank` adopts the following direction:
|
|
|
|
1. `TILES/indexed_v1` emits one canonical payload per asset.
|
|
2. `1 artifact = 1 tile` in the first wave.
|
|
3. Artifacts are ordered by normalized `artifacts[*].index`.
|
|
4. Tile placement is row-major in one fixed `256 x 256` sheet.
|
|
5. `tile_id` is the linear row-major slot and therefore matches the normalized artifact index.
|
|
6. Tile pixels are serialized as packed `u4` indices.
|
|
7. Bank palettes are serialized as `RGB565` `u16` values.
|
|
8. Palette declarations use explicit semantic identity through `{ index, palette }`.
|
|
9. `AssetEntry.metadata` keeps required runtime fields readable at the root while preserving segmented codec and pipeline subtrees.
|
|
10. Format-structural diagnostics are raised in the walker/materialization path and therefore participate in validation before pack execution reruns the gate.
|
|
|
|
## Adopted Constraints
|
|
|
|
### 1. Canonical Payload Shape
|
|
|
|
- the payload is produced from build-selected artifacts only;
|
|
- the payload is the canonical bank sheet raster, not a concatenation of per-artifact binary fragments;
|
|
- for `TILES/indexed_v1`, the serialized payload shape is:
|
|
1. packed `u4` pixel indices for the full emitted sheet;
|
|
2. one palette block of `64 * 16 * 2` bytes;
|
|
- palettes are always materialized to `RGB565` during pack emission;
|
|
- the producer-side payload contract is aligned to what the runtime loader already requires.
|
|
|
|
### 2. Artifact-to-Tile Contract
|
|
|
|
- `1 artifact = 1 tile` in v1;
|
|
- artifacts are normalized by `artifacts[*].index`;
|
|
- `tile_id = artifacts[*].index` after normalization;
|
|
- the canonical emitted sheet is always `256 x 256`;
|
|
- placement is row-major within that fixed sheet.
|
|
|
|
Resulting tile capacities:
|
|
|
|
- `tile_size = 8` -> `32 x 32 = 1024` tiles
|
|
- `tile_size = 16` -> `16 x 16 = 256` tiles
|
|
- `tile_size = 32` -> `8 x 8 = 64` tiles
|
|
|
|
### 3. Runtime Entry Contract
|
|
|
|
Each emitted `tile bank` runtime entry must populate:
|
|
|
|
- `asset_id`
|
|
- `asset_name`
|
|
- `bank_type = TILES`
|
|
- `offset`
|
|
- `size`
|
|
- `decoded_size`
|
|
- `codec = NONE`
|
|
- `metadata`
|
|
|
|
For v1:
|
|
|
|
- `size = ceil(width * height / 2) + 2048`
|
|
- `decoded_size = (width * height) + 2048`
|
|
- `width = 256`
|
|
- `height = 256`
|
|
- `palette_count = 64`
|
|
|
|
### 4. Palette Contract
|
|
|
|
- bank palettes are declared in `asset.json.output.pipeline.palettes`;
|
|
- each palette declaration must use explicit shape `{ index, palette }`;
|
|
- semantic ordering is ascending numeric `index`, never raw array position;
|
|
- palette ids are the normalized declared `index` values;
|
|
- any tile in the bank may be rendered with any palette in the bank;
|
|
- palette choice is a runtime draw-time concern and is not embedded per tile in the packed payload.
|
|
|
|
### 5. Metadata Normalization Contract
|
|
|
|
`AssetEntry.metadata` keeps runtime-required fields readable while preserving authoring segmentation:
|
|
|
|
- `asset.json.output.metadata` -> `AssetEntry.metadata`
|
|
- `asset.json.output.codec_configuration` -> `AssetEntry.metadata.codec`
|
|
- `asset.json.output.pipeline` -> `AssetEntry.metadata.pipeline`
|
|
|
|
For tile-bank v1 this means:
|
|
|
|
- `tile_size`, `width`, `height`, and `palette_count` remain directly readable at the metadata root;
|
|
- codec-specific data is nested under `metadata.codec`;
|
|
- pipeline-derived data is nested under `metadata.pipeline`.
|
|
|
|
This packer decision intentionally aligns with the runtime-side follow-up agenda rather than flattening everything into one ambiguous map.
|
|
|
|
### 6. Diagnostics and Failure Semantics
|
|
|
|
The following conditions are blocking for tile-bank v1:
|
|
|
|
- duplicate `artifacts[*].index`
|
|
- gaps in normalized `artifacts[*].index`
|
|
- fixed-sheet capacity overflow
|
|
- bank without declared palettes
|
|
- palette declaration count above `64`
|
|
- malformed palette declarations
|
|
- missing or invalid required format metadata
|
|
- any failure to normalize artifacts into one deterministic payload
|
|
|
|
The following condition is warning-only in the first wave:
|
|
|
|
- fragile tile indices, meaning tile indices that are not safely represented across the full declared bank palette set
|
|
|
|
### 7. Walker and Validation Boundary
|
|
|
|
- family walkers remain discovery-oriented;
|
|
- tile-bank structural diagnostics must be produced in the walker/materialization path;
|
|
- validation consumes those diagnostics naturally;
|
|
- pack execution reruns the validation gate on a newly created frozen execution snapshot before materialization begins.
|
|
|
|
## Why This Direction Was Chosen
|
|
|
|
- It matches the runtime consumer contract instead of inventing a producer-local interpretation.
|
|
- It keeps `tile bank` payload semantics explicit and deterministic.
|
|
- It avoids embedding per-artifact or per-draw palette assignment into the packed bytes.
|
|
- It preserves stable palette identity through explicit `index` declarations.
|
|
- It keeps validation early without weakening the pack-time rerun gate.
|
|
- It gives the runtime a metadata shape that is readable and still semantically segmented.
|
|
|
|
## Explicit Non-Decisions
|
|
|
|
This decision does not yet define:
|
|
|
|
- future support for `1 artifact -> many tiles`;
|
|
- non-`256 x 256` tile-bank targets;
|
|
- alternative tile-bank codecs beyond `NONE`;
|
|
- future palette compaction or palette-id remapping strategies;
|
|
- the runtime-side final decision for general `AssetEntry.metadata` normalization helpers;
|
|
- future sprite or tilemap semantic adjustments unrelated to packer-owned payload production.
|
|
|
|
## Implications
|
|
|
|
- tile-bank packing implementation must materialize a full sheet raster rather than artifact fragments;
|
|
- tile-bank payload generation must pack `u4` indices explicitly in the packer;
|
|
- palette declarations and palette overhauling flows must preserve explicit palette `index`;
|
|
- runtime-entry metadata emission must preserve both root-required fields and segmented nested maps;
|
|
- tile-bank validation logic belongs in the walker/materialization path and must be reused by pack execution gate reruns.
|
|
|
|
## Propagation Targets
|
|
|
|
Specs:
|
|
|
|
- [`../specs/3. Asset Declaration and Virtual Asset Contract Specification.md`](../specs/3.%20Asset%20Declaration%20and%20Virtual%20Asset%20Contract%20Specification.md)
|
|
- [`../specs/4. Build Artifacts and Deterministic Packing Specification.md`](../specs/4.%20Build%20Artifacts%20and%20Deterministic%20Packing%20Specification.md)
|
|
|
|
Plans:
|
|
|
|
- [`../pull-requests/PR-32-palette-declarations-with-explicit-index-contract.md`](../pull-requests/PR-32-palette-declarations-with-explicit-index-contract.md)
|
|
- future packer PR for tile-bank payload materialization
|
|
- future packer PR for tile-bank validation diagnostics
|
|
|
|
Cross-domain references:
|
|
|
|
- [`../../../runtime/docs/runtime/specs/04-gfx-peripheral.md`](../../../runtime/docs/runtime/specs/04-gfx-peripheral.md)
|
|
- [`../../../runtime/docs/runtime/specs/15-asset-management.md`](../../../runtime/docs/runtime/specs/15-asset-management.md)
|
|
- [`../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md`](../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md)
|
|
- [`../../../runtime/docs/runtime/agendas/024-asset-entry-metadata-normalization-contract.md`](../../../runtime/docs/runtime/agendas/024-asset-entry-metadata-normalization-contract.md)
|
|
|
|
Implementation surfaces:
|
|
|
|
- `prometeu-packer-v1` tile-bank payload materializer
|
|
- `prometeu-packer-v1` tile-bank diagnostics in walker/materialization path
|
|
- `prometeu-packer-v1` metadata convergence for `AssetEntry`
|
|
- Studio tile-bank authoring and inspection surfaces that expose palettes or bank composition
|
|
|
|
## Validation Notes
|
|
|
|
This decision is correctly implemented only when all of the following are true:
|
|
|
|
- artifact normalization produces one deterministic row-major `256 x 256` bank sheet;
|
|
- emitted tile ids match normalized artifact indices;
|
|
- emitted pixel bytes are packed as `u4`;
|
|
- emitted palette bytes are `RGB565` `u16`;
|
|
- palette declarations are read and written by explicit `index`;
|
|
- runtime-required metadata fields remain readable at the root;
|
|
- codec and pipeline metadata survive under `metadata.codec` and `metadata.pipeline`;
|
|
- structural blockers are visible during validation and are rerun by pack execution before emission.
|