implements PLN-0040 scene glyph spec and wire layout

This commit is contained in:
bQUARKz 2026-04-24 10:57:48 +01:00
parent c52f196b7a
commit 26686c288c
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
6 changed files with 91 additions and 13 deletions

View File

@ -53,7 +53,11 @@ pub const SCENE_PAYLOAD_MAGIC_V1: [u8; 4] = *b"SCNE";
pub const SCENE_PAYLOAD_VERSION_V1: u16 = 1;
pub const SCENE_LAYER_COUNT_V1: usize = 4;
pub const SCENE_HEADER_BYTES_V1: usize = 12;
pub const SCENE_LAYER_HEADER_BYTES_V1: usize = 28;
// Per-layer header:
// flags:u8 + glyph_asset_id:i32 + tile_size:u8 + reserved:u16
// + parallax_x:f32 + parallax_y:f32 + width:u32 + height:u32
// + tile_count:u32 + reserved:u32
pub const SCENE_LAYER_HEADER_BYTES_V1: usize = 32;
pub const SCENE_TILE_RECORD_BYTES_V1: usize = 4;
pub const SCENE_DECODED_LAYER_OVERHEAD_BYTES_V1: usize = 16;

View File

@ -21,7 +21,7 @@
{"type":"discussion","id":"DSC-0026","status":"done","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-18","tags":["gfx","runtime","render","camera","scene"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0027","status":"done","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-18","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0028","status":"done","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0029","status":"open","ticket":"scene-bank-glyph-runtime-binding-leak","title":"Scene Bank Glyph Runtime Binding Leak","created_at":"2026-04-24","updated_at":"2026-04-24","tags":["gfx","runtime","asset","scene","glyph","format","architecture"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-scene-bank-glyph-runtime-binding-leak.md","status":"accepted","created_at":"2026-04-24","updated_at":"2026-04-24"}],"decisions":[{"id":"DEC-0021","file":"DEC-0021-scene-bank-glyph-dependencies-must-bind-by-asset-id.md","status":"in_progress","created_at":"2026-04-24","updated_at":"2026-04-24","ref_agenda":"AGD-0029"}],"plans":[{"id":"PLN-0040","file":"PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0041","file":"PLN-0041-scene-glyph-runtime-resolution-and-fatal-paths.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0042","file":"PLN-0042-scene-glyph-tooling-and-fixture-migration.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0029","status":"open","ticket":"scene-bank-glyph-runtime-binding-leak","title":"Scene Bank Glyph Runtime Binding Leak","created_at":"2026-04-24","updated_at":"2026-04-24","tags":["gfx","runtime","asset","scene","glyph","format","architecture"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-scene-bank-glyph-runtime-binding-leak.md","status":"accepted","created_at":"2026-04-24","updated_at":"2026-04-24"}],"decisions":[{"id":"DEC-0021","file":"DEC-0021-scene-bank-glyph-dependencies-must-bind-by-asset-id.md","status":"in_progress","created_at":"2026-04-24","updated_at":"2026-04-24","ref_agenda":"AGD-0029"}],"plans":[{"id":"PLN-0040","file":"PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md","status":"done","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0041","file":"PLN-0041-scene-glyph-runtime-resolution-and-fatal-paths.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0042","file":"PLN-0042-scene-glyph-tooling-and-fixture-migration.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0014","status":"done","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0035","file":"lessons/DSC-0014-perf-vm-allocation-and-copy-pressure/LSN-0035-first-materialization-is-not-the-same-as-hot-path-copy-pressure.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]}
{"type":"discussion","id":"DSC-0015","status":"abandoned","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-04-20","_override_reason":"User explicitly chose to close the discussion without decision because FS->memory copy for the program is already acceptable."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon the discussion without creating a decision because FS->memory copy for the program is already acceptable."}
{"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]}

View File

@ -2,9 +2,9 @@
id: PLN-0040
ticket: scene-bank-glyph-runtime-binding-leak
title: Scene glyph dependency spec and wire layout migration
status: review
status: done
created: 2026-04-24
completed:
completed: 2026-04-24
tags: [gfx, runtime, asset, scene, glyph, format, spec]
---
@ -102,4 +102,3 @@ Remove proposed/legacy wording that frames missing dependencies as recoverable s
- Spec-only edits can drift from the runtime implementation if later plans are not executed promptly.
- Scene payload layout changes may require touching multiple docs; missing one will leave contradictory contract text.
- If HAL constants are updated here before runtime code, temporary compile or test fallout may appear in later plans and must be handled intentionally.

View File

@ -579,7 +579,7 @@ Fault boundary:
| `gfx.draw_square` | `void` | no real operational failure path in v1 |
| `gfx.draw_text` | `void` | no real operational failure path in v1 |
| `gfx.clear_565` | `void` | no real operational failure path in v1 |
| `composer.bind_scene` | `status:int` | explicit orchestration-domain operational result |
| `composer.bind_scene` | `status:int` | status-returning API, but missing scene glyph dependencies are fatal runtime errors |
| `composer.unbind_scene` | `status:int` | explicit orchestration-domain operational result |
| `composer.set_camera` | `void` | no real operational failure path in v1 |
| `composer.emit_sprite` | `status:int` | explicit orchestration-domain operational rejection |
@ -596,6 +596,18 @@ The public `gfx.*` primitive family remains valid in v1, but its stable operatio
This means callers MUST NOT rely on stable immediate writes to the working back buffer as the public contract for `gfx.draw_text(...)` or sibling primitives.
### 19.1.b Scene dependency fatal boundary
`composer.bind_scene` remains a status-returning syscall in the public ABI, but scene glyph dependency absence is outside the accepted passive operational-error model.
Rules:
- the target scene slot may still be rejected through ordinary status-returning behavior when the scene itself is unavailable;
- missing glyph dependencies referenced by a resident scene are not a passive `status:int` case;
- if scene activation discovers that a layer dependency cannot be resolved to a committed glyph asset, the machine MUST fail fatally and emit a clear log;
- if scene composition later discovers that a layer dependency can no longer be resolved, the machine MUST fail fatally and emit a clear log;
- runtime MUST NOT continue canonical scene composition after such a dependency failure.
### 19.2 `composer.emit_sprite`
`composer.emit_sprite` returns `status:int`.

View File

@ -108,9 +108,9 @@ This table describes content identity and storage layout, not live residency.
`codec` does not define the bank-specific serialized layout itself. Specialized banks may still have normative decode rules even when `codec = NONE`.
### 4.1 `TILES` asset contract in v1
### 4.1 `GLYPH` asset contract in v1
For `BankType::TILES`, the v1 runtime-facing contract is:
For `BankType::GLYPH`, the v1 runtime-facing contract is:
- `codec = NONE`
- serialized pixels use packed `u4` palette indices
@ -118,20 +118,20 @@ For `BankType::TILES`, the v1 runtime-facing contract is:
- `palette_count = 64`
- runtime materialization may expand pixel indices to one `u8` per pixel
For `TILES`, `NONE` means there is no additional generic codec layer beyond the bank contract itself.
For `GLYPH`, `NONE` means there is no additional generic codec layer beyond the bank contract itself.
For the current transition window:
- `RAW` is a legacy deprecated alias of `NONE`
- newly published material must use `NONE` as the canonical value
Even with `codec = NONE`, `TILES` still requires bank-specific decode from its serialized payload. The serialized byte layout therefore does not need to match the in-memory layout.
Even with `codec = NONE`, `GLYPH` still requires bank-specific decode from its serialized payload. The serialized byte layout therefore does not need to match the in-memory layout.
#### 4.1.1 Metadata Normalization
Following `DEC-0004`, the `AssetEntry.metadata` field must be structured in segmented form to avoid ambiguity.
Required effective metadata fields for `TILES` at the root level:
Required effective metadata fields for `GLYPH` at the root level:
- `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32`
- `width`: total sheet width in pixels
@ -143,7 +143,7 @@ Optional informative subtrees:
- `metadata.codec`: codec/compressor-specific configuration (for example dictionaries or compression flags).
- `metadata.pipeline`: informative metadata from the Studio build process (for example source hashes, timestamps, or tool versions).
Validation rules for `TILES` v1:
Validation rules for `GLYPH` v1:
- `palette_count` must be `64`
- `width * height` defines the number of logical indexed pixels in the decoded sheet
@ -160,17 +160,74 @@ The tile-bank payload therefore separates serialized storage form from runtime m
- decoded pixel plane: expanded `u8` indices, one entry per pixel
- palette table: `64 * 16` colors in `RGB565`
For `TILES` v1:
For `GLYPH` v1:
- `size` must match `ceil(width * height / 2) + (palette_count * 16 * 2)`
- `decoded_size` must match `(width * height) + (palette_count * 16 * 2)`
### 4.2 `SCENE` asset contract in v1
For `BankType::SCENE`, the v1 runtime-facing contract is:
- `codec = NONE`
- payload magic is `SCNE`
- payload version is `1`
- layer count is fixed at `4`
- each layer carries its own glyph dependency
- the glyph dependency is serialized as canonical `AssetId` using signed 32-bit little-endian width
- the payload MUST describe cold asset dependencies only and MUST NOT encode runtime glyph slot topology
Even with `codec = NONE`, `SCENE` still has a bank-specific binary contract. The serialized layout is normative and distinct from any transient runtime residency state.
#### 4.2.1 Layer Header Layout
Each scene layer header is:
1. `flags: u8`
2. `glyph_asset_id: i32` (little-endian)
3. `tile_size: u8`
4. `reserved: u16`
5. `parallax_x: f32`
6. `parallax_y: f32`
7. `width: u32`
8. `height: u32`
9. `tile_count: u32`
10. `reserved: u32`
This makes the canonical per-layer header size `32` bytes.
Normative rules:
- `glyph_asset_id` MUST be interpreted as the cold identity of the glyph dependency for that layer;
- `glyph_asset_id` MUST NOT be interpreted as a runtime slot id;
- each layer MUST own exactly one glyph dependency reference;
- no shared dependency table exists in this contract;
- `tile_count` MUST equal `width * height`.
#### 4.2.2 Runtime Binding Semantics
`SCENE` assets do not own runtime residency.
Therefore:
- the runtime MUST resolve scene glyph dependencies through runtime-owned residency state;
- the runtime MUST maintain a runtime-owned mapping from committed glyph `AssetId` to glyph slot;
- scene bind and scene draw MUST consult runtime residency, not serialized slot topology;
- a missing scene glyph dependency is not a passive asset-status condition.
If a scene glyph dependency cannot be resolved:
- `bind_scene` MUST fail as a fatal machine error with clear logging;
- draw/composition MUST fail as a fatal machine error with clear logging;
- runtime MUST NOT continue scene composition after the missing dependency is detected.
## 5 Banks and Slots
The current runtime exposes bank types:
- `GLYPH`
- `SOUNDS`
- `SCENE`
Assets are loaded into explicit slots identified by slot index at the public ABI boundary.

View File

@ -229,6 +229,12 @@ For mutating composer operations:
- `set_camera` remains `void` in v1;
- no caller-provided sprite index or `active` flag is part of the canonical contract.
Fatal boundary clarification for `bind_scene`:
- `bind_scene` remains status-returning for ordinary orchestration-domain rejection such as unresolved target scene slot;
- missing glyph dependencies inside a resident scene are not accepted as passive `ComposerOpStatus` outcomes;
- if scene activation or later composition detects an unresolved scene glyph dependency, runtime MUST escalate that condition as a fatal machine error with clear logging.
## 7 Syscalls as Callable Entities (Not First-Class)
Syscalls behave like call sites, not like first-class guest values.