315 lines
9.0 KiB
Markdown
315 lines
9.0 KiB
Markdown
# Asset Management
|
|
|
|
Domain: asset runtime surface
|
|
Function: normative
|
|
|
|
This chapter defines the runtime-facing asset model of PROMETEU.
|
|
|
|
## 1 Scope
|
|
|
|
PROMETEU asset management is bank-centric.
|
|
|
|
Assets are:
|
|
|
|
- cold bytes stored in the cartridge;
|
|
- described by cartridge metadata;
|
|
- materialized into host-managed banks;
|
|
- separate from VM heap ownership.
|
|
|
|
This chapter describes the runtime contract currently visible in the codebase. It is not a full tooling pipeline specification.
|
|
|
|
## 2 Core Principles
|
|
|
|
1. asset residency is explicit;
|
|
2. asset memory belongs to the machine, not to the VM heap;
|
|
3. banks and slots are hardware/runtime concepts;
|
|
4. loading and activation are explicit operations;
|
|
5. asset memory does not participate in GC.
|
|
|
|
## 3 Cartridge Asset Artifact
|
|
|
|
The runtime currently consumes one primary cartridge asset artifact:
|
|
|
|
- `assets.pa`: autocontained asset artifact.
|
|
|
|
`assets.pa` carries, inside the same binary:
|
|
|
|
- fixed binary prelude;
|
|
- JSON header;
|
|
- payload bytes.
|
|
|
|
The JSON header carries:
|
|
|
|
- `asset_table`: metadata entries describing asset content;
|
|
- `preload`: optional initial residency requests consumed during cartridge initialization.
|
|
|
|
This chapter describes the runtime-facing asset contract. It does not define the Studio packer workflow or the shipper pipeline that publishes the cartridge.
|
|
|
|
### 3.1 `assets.pa` v1 envelope
|
|
|
|
`assets.pa` v1 is structured as:
|
|
|
|
```text
|
|
[fixed binary prelude]
|
|
[json header]
|
|
[binary payload region]
|
|
```
|
|
|
|
The fixed binary prelude contains, at minimum:
|
|
|
|
- `magic`
|
|
- `schema_version`
|
|
- `header_len`
|
|
- `payload_offset`
|
|
|
|
It may additionally include:
|
|
|
|
- `flags`
|
|
- `reserved`
|
|
- `header_checksum`
|
|
|
|
### 3.2 Header and payload contract
|
|
|
|
The runtime loads:
|
|
|
|
- `asset_table` from the JSON header and keeps it live during cartridge execution;
|
|
- `preload` from the JSON header and consumes it only during boot.
|
|
|
|
Payload bytes are addressed from the payload region using offsets relative to `payload_offset`, not relative to the start of the whole file.
|
|
|
|
## 4 Asset Table
|
|
|
|
Current runtime-facing asset metadata includes:
|
|
|
|
```text
|
|
AssetEntry {
|
|
asset_id
|
|
asset_name
|
|
bank_type
|
|
offset
|
|
size
|
|
decoded_size
|
|
codec
|
|
metadata
|
|
}
|
|
```
|
|
|
|
This table describes content identity and storage layout, not live residency.
|
|
|
|
`asset_id` is the stable runtime-facing asset identity and uses 32-bit signed integer semantics compatible with Java `int`.
|
|
|
|
`offset` is relative to the start of the payload region inside `assets.pa`.
|
|
|
|
`size` is the serialized byte count stored in the payload region.
|
|
|
|
`decoded_size` is the byte count of the materialized runtime bank after decode, not necessarily the same as the serialized payload size.
|
|
|
|
`codec` identifies how the runtime must interpret the serialized payload slice before the asset becomes a resident bank.
|
|
|
|
### 4.1 `TILES` asset contract in v1
|
|
|
|
For `BankType::TILES`, the runtime-facing v1 contract is:
|
|
|
|
- `codec = RAW`
|
|
- serialized pixels use packed `u4` palette indices
|
|
- serialized palettes use `RGB565` (`u16`, little-endian)
|
|
- `palette_count = 64`
|
|
- runtime materialization may expand pixel indices to one `u8` per pixel
|
|
|
|
`RAW` for `TILES` means there is no compression stage beyond deterministic reconstruction of the tile bank from its serialized payload. It does not require the serialized byte layout to be identical to the in-memory layout.
|
|
|
|
Required `AssetEntry.metadata` fields for `TILES`:
|
|
|
|
- `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32`
|
|
- `width`: full bank sheet width in pixels
|
|
- `height`: full bank sheet height in pixels
|
|
- `palette_count`: number of palettes serialized for the bank
|
|
|
|
Validation rules for `TILES` v1:
|
|
|
|
- `palette_count` must be `64`
|
|
- `width * height` defines the number of logical indexed pixels in the decoded sheet
|
|
- additional metadata may exist, but the runtime contract must not depend on it to reconstruct the bank in memory
|
|
|
|
Serialized payload layout for `TILES` v1:
|
|
|
|
1. packed indexed pixels for the full sheet, using `ceil(width * height / 2)` bytes;
|
|
2. palette table, using `palette_count * 16 * 2` bytes.
|
|
|
|
The tile-bank payload therefore separates serialized storage form from runtime memory form:
|
|
|
|
- serialized pixel plane: packed `4bpp`
|
|
- decoded pixel plane: expanded `u8` indices, one entry per pixel
|
|
- palette table: `64 * 16` colors in `RGB565`
|
|
|
|
For `TILES` v1:
|
|
|
|
- `size` must match `ceil(width * height / 2) + (palette_count * 16 * 2)`
|
|
- `decoded_size` must match `(width * height) + (palette_count * 16 * 2)`
|
|
|
|
## 5 Banks and Slots
|
|
|
|
The current runtime exposes bank types:
|
|
|
|
- `TILES`
|
|
- `SOUNDS`
|
|
|
|
Assets are loaded into explicit slots identified by bank context plus index.
|
|
|
|
Conceptual slot reference:
|
|
|
|
```text
|
|
SlotRef { bank_type, index }
|
|
```
|
|
|
|
This prevents ambiguity between graphics and audio residency.
|
|
|
|
## 6 Load Lifecycle
|
|
|
|
The runtime asset manager exposes a staged lifecycle:
|
|
|
|
- `PENDING`
|
|
- `LOADING`
|
|
- `READY`
|
|
- `COMMITTED`
|
|
- `CANCELED`
|
|
- `ERROR`
|
|
|
|
High-level flow:
|
|
|
|
1. request load of an asset into a slot;
|
|
2. resolve the asset entry from live `asset_table`;
|
|
3. open the payload slice in `assets.pa`;
|
|
4. perform read/decode/materialization work;
|
|
5. mark the load `READY`;
|
|
6. explicitly `commit`;
|
|
7. activate the resident asset in the slot.
|
|
|
|
The canonical payload paths are:
|
|
|
|
- `ROM -> open_slice -> CODEX/decode -> Bank`
|
|
- `ROM -> open_slice -> temporary in-memory blob -> CODEX/decode -> Bank`
|
|
|
|
`open_slice` is the runtime-facing concept for opening a limited view over a payload slice. The runtime must not require the whole `assets.pa` payload to remain resident in RAM as its baseline operating mode.
|
|
|
|
`OP_MODE` selects between direct slice consumption and temporary materialization in memory.
|
|
|
|
For v1:
|
|
|
|
- `OP_MODE` is derived from `codec`/CODEX;
|
|
- explicit per-asset hinting is not part of the baseline contract.
|
|
- `TILES` with `codec = RAW` may still stage in memory before bank installation because runtime decode expands packed pixel indices into the resident representation.
|
|
|
|
The runtime does not treat asset installation as implicit side effect.
|
|
|
|
## 7 Residency and Ownership
|
|
|
|
Asset banks are host/runtime-owned memory.
|
|
|
|
Therefore:
|
|
|
|
- VM heap does not own asset residency;
|
|
- GC does not scan asset bank memory;
|
|
- shutting down a cartridge can release bank residency independently of VM heap behavior.
|
|
- the runtime must not keep the full `assets.pa` payload resident in RAM as a baseline requirement.
|
|
|
|
## 8 Bank Telemetry
|
|
|
|
The runtime surfaces bank and slot statistics such as:
|
|
|
|
- total bytes;
|
|
- used bytes;
|
|
- free bytes;
|
|
- inflight bytes;
|
|
- slot occupancy;
|
|
- resident asset identity per slot.
|
|
|
|
These metrics support debugging, telemetry, and certification-oriented inspection.
|
|
|
|
## 9 Preload
|
|
|
|
`preload` is stored in the JSON header of `assets.pa`.
|
|
|
|
The normative preload shape is:
|
|
|
|
```text
|
|
PreloadEntry {
|
|
asset_id
|
|
slot
|
|
}
|
|
```
|
|
|
|
These preload entries are consumed during cartridge initialization so the asset manager can establish initial residency before normal execution flow.
|
|
|
|
Validation rules:
|
|
|
|
- `preload` is resolved by `asset_id`, not by `asset_name`;
|
|
- every `preload.asset_id` must exist in the same `asset_table`;
|
|
- no two preload entries may resolve to the same `(bank_type, slot)` pair;
|
|
- legacy preload keyed by `asset_name` is invalid for the current contract.
|
|
|
|
Lifecycle rule:
|
|
|
|
- `preload` is boot-time input only;
|
|
- it does not need to remain live after initialization completes.
|
|
|
|
Bootstrap rule:
|
|
|
|
- invalid preload is a structural cartridge error and must fail cartridge bootstrap before normal execution begins.
|
|
|
|
## 10 Relationship to Other Specs
|
|
|
|
- [`13-cartridge.md`](13-cartridge.md) defines the cartridge package and the requirement that `assets.pa` carries its own asset header.
|
|
- [`16-host-abi-and-syscalls.md`](16-host-abi-and-syscalls.md) defines the syscall boundary used to manipulate assets.
|
|
- [`03-memory-stack-heap-and-allocation.md`](03-memory-stack-heap-and-allocation.md) defines the distinction between VM heap memory and host-owned memory.
|
|
|
|
## 11 Syscall Surface and Status Policy
|
|
|
|
`asset` follows status-first policy.
|
|
|
|
Fault boundary:
|
|
|
|
- `Trap`: structural ABI misuse (type/arity/capability/shape mismatch);
|
|
- `status`: operational failure;
|
|
- `Panic`: internal invariant break only.
|
|
|
|
### 11.1 MVP syscall shape
|
|
|
|
- `asset.load(name, kind, slot) -> (status:int, handle:int)`
|
|
- `asset.status(handle) -> status:int`
|
|
- `asset.commit(handle) -> status:int`
|
|
- `asset.cancel(handle) -> status:int`
|
|
|
|
Rules:
|
|
|
|
- `handle` is valid only when `load` status is `OK`;
|
|
- failed `load` returns `handle = 0`;
|
|
- `commit` and `cancel` must not be silent no-op for unknown/invalid handle state.
|
|
- slot validation, kind mismatch, and residency/lifecycle rejection remain in `asset` status space and are not delegated to `bank`.
|
|
|
|
### 11.2 Minimum status tables
|
|
|
|
`asset.load` request statuses:
|
|
|
|
- `0` = `OK`
|
|
- `3` = `ASSET_NOT_FOUND`
|
|
- `4` = `SLOT_KIND_MISMATCH`
|
|
- `5` = `SLOT_INDEX_INVALID`
|
|
- `6` = `BACKEND_ERROR`
|
|
|
|
`asset.status` lifecycle statuses:
|
|
|
|
- `0` = `PENDING`
|
|
- `1` = `LOADING`
|
|
- `2` = `READY`
|
|
- `3` = `COMMITTED`
|
|
- `4` = `CANCELED`
|
|
- `5` = `ERROR`
|
|
- `6` = `UNKNOWN_HANDLE`
|
|
|
|
`asset.commit` and `asset.cancel` operation statuses:
|
|
|
|
- `0` = `OK`
|
|
- `1` = `UNKNOWN_HANDLE`
|
|
- `2` = `INVALID_STATE`
|