prometeu-runtime/discussion/workflow/decisions/DEC-0013-scene-bank-and-viewport-cache-model.md
2026-04-13 20:13:16 +01:00

228 lines
11 KiB
Markdown

---
id: DEC-0013
ticket: scene-bank-and-viewport-cache-refactor
title: Decision - Scene Bank and Viewport Cache Model
status: accepted
created: 2026-04-13
accepted: 2026-04-13
agenda: AGD-0025
plans: [PLN-0011, PLN-0012, PLN-0013, PLN-0014, PLN-0015]
tags: [gfx, tilemap, runtime, render]
---
## Status
Accepted.
This decision normatively locks the V1 model for scene-backed tilemap rendering around:
- `Scene` as canonical loaded state;
- `SceneLayer` as the canonical layer unit;
- `SceneViewportCache` as the materialized render cache;
- `SceneViewportResolver` as the owner of totems, drift, hysteresis, clamp, and cache update decisions.
## Contexto
The current runtime couples three concerns too tightly:
- canonical layer data;
- scroll state;
- direct renderer consumption.
In the current implementation, the `64x64` maps created for scrollable layers are not an independent viewport cache. They are the actual layer maps consumed directly by the renderer. That shape makes it harder to:
- keep canonical world state simple;
- materialize only the viewport-relevant subset for render;
- evolve camera separately from the tilemap model;
- reduce repeated tilemap resolution work before the final destructive blit into `back`.
During agenda `AGD-0025`, the discussion initially explored ringbuffer as a general topic, but converged on a different architectural target: a clean split between canonical scene state and render-oriented viewport materialization.
## Decisao
The runtime SHALL adopt the following V1 model:
1. Canonical loaded tilemap state SHALL be represented by `Scene`.
2. A `Scene` SHALL contain four canonical `SceneLayer`s.
3. Each `SceneLayer` SHALL own its canonical `TileMap`.
4. Each `TileMap` SHALL remain the canonical grid of `Tile`s for that layer.
5. The renderer MUST NOT consume canonical `Scene` data directly for normal world composition.
6. The renderer SHALL consume a `SceneViewportCache` derived from `Scene`.
7. `SceneViewportCache` SHALL be operational render state only and MUST NOT become the semantic source of truth for gameplay or physics.
8. `SceneViewportCache` SHOULD use internal ringbuffer storage as the preferred V1 implementation strategy.
9. Ringbuffer details MUST remain encapsulated inside `SceneViewportCache` and MUST NOT leak into the semantic contract of `Scene`, `SceneLayer`, `TileMap`, or `Tile`.
10. `ScrollableTileLayer` SHALL be removed from the architecture and MUST NOT be preserved as a canonical runtime concept.
11. `background` and `parallax` SHALL remain expressible through `SceneLayer` itself, not through a separate V1 background type.
12. Each `SceneLayer` SHALL carry a relative movement factor against a master totem, allowing normal layers, background-like layers, and parallax-like layers under the same layer contract.
## Rationale
This decision prefers architectural clarity over preserving the current type graph.
The key arguments are:
- `Tile` remains light enough that keeping canonical tilemaps resident is acceptable in the current baseline.
- The more pressing cost appears to be repeated world resolution and composition work, not merely the existence of tilemap data in memory.
- A dedicated viewport cache gives the renderer a better contract without forcing gameplay and physics to depend on residency mechanics.
- Internal ringbuffering in the cache is useful because the first expected gain is avoiding repeated cache-side copies, even if final composition into `back` remains destructive in V1.
- Keeping parallax inside `SceneLayer` avoids prematurely splitting the scene model into separate layer families while still allowing differentiated movement behavior.
This model also keeps room for later refinement:
- richer camera semantics can evolve separately;
- background-specific types can still be introduced later if the layer contract becomes insufficient;
- bank-type wiring can still be finalized during planning/implementation;
- cache implementation can improve while the canonical scene contract remains stable.
## Invariantes / Contrato
### 1. Canonical Scene Model
- `Scene` SHALL be the canonical loaded aggregate.
- `Scene` SHALL contain four canonical `SceneLayer`s.
- `SceneLayer` SHALL be the canonical replacement for the old `TileLayer`.
- `SceneLayer` SHALL minimally include:
- `glyph_bank_id`
- `tile_size`
- `active`
- `totem_factor (x, y)`
- `tilemap`
- `TileMap` SHALL remain a canonical grid of `Tile`.
- `Tile` SHALL remain lightweight and MUST NOT be expanded into resolved RGB pixel payload for canonical storage.
### 2. Viewport Cache Model
- `SceneViewportCache` SHALL be a single cache aggregate for one `Scene`.
- It SHALL contain four internal layer caches, one per canonical scene layer.
- The preferred V1 implementation is one internal ringbuffer per layer cache.
- Ringbuffer details MUST stay internal to the cache implementation.
- `SceneViewportCache` MAY store lightweight derived fields to accelerate raster, including:
- resolved `glyph_id`
- resolved `palette_id`
- packed flip flags
- `active/empty` markers
- fast layer-local glyph bank references
- `SceneViewportCache` MUST NOT duplicate full physics state unless a later decision explicitly requires it.
- `SceneViewportCache` SHALL be the immediate source of copy data for world blits, but MUST NOT become the source of truth for world semantics.
### 3. Resolver Contract
- `SceneViewportResolver` SHALL own:
- master totem
- per-layer derived totems
- drift calculation
- hysteresis
- clamp logic
- cache update decisions
- An external caller SHALL provide camera position to the resolver.
- The resolver SHALL treat that camera position as input for the master totem flow.
- The resolver SHALL decide whether cache updates are needed.
- The resolver SHALL trigger cache update operations when needed.
- The resolver SHALL know how to instrument per-layer copy requests from the cache to the final compositor.
- The resolver MUST NOT perform the actual framebuffer copy itself.
### 4. Viewport Size and Cache Update Policy
- V1 world viewport cache size SHALL be materially larger than the minimum visible world tile window and MUST include explicit halo for cache stability.
- An initial working target in the order of `25x16` tiles is accepted as planning guidance, but exact numeric sizing MAY be finalized in the implementation plan.
- Normal cache update SHALL occur by line and/or column.
- When simultaneous X and Y movement requires corner refresh, the cache MUST support area/region refresh to avoid reloading tiles already present.
- Swapping to a different `Scene` SHALL fully invalidate `SceneViewportCache`.
- Normal camera motion MUST NOT invalidate the whole viewport cache as the default path.
### 5. Totem, Drift, and Hysteresis
- The resolver SHALL use a master totem in tile space.
- Camera input SHALL remain in pixel space.
- For `16x16` tiles, the master totem center SHALL be computed as:
- `cx = 16 * i + 8`
- `cy = 16 * j + 8`
- Drift SHALL be computed as:
- `dx = x - cx`
- `dy = y - cy`
- V1 SHALL use hysteresis with:
- an internal safe band where no cache movement occurs;
- an external trigger band that advances the totem and requests cache refresh.
- Initial working values in the order of `safe = 12 px` and `trigger = 20 px` are accepted as planning guidance, but exact numeric tuning MAY be finalized in the implementation plan.
- Inside the safe band, no cache movement SHALL occur.
- Between safe and trigger, the system SHALL tolerate drift without rematerialization.
- Beyond trigger, the resolver SHALL move the totem discretely in tile steps and request cache refresh.
- Hysteresis is mandatory in V1 to prevent edge flick/thrash.
### 6. Clamp Behavior
- The resolver SHALL clamp the master totem against scene bounds.
- Initial clamp reasoning SHALL be based on minimums around `(w/2, h/2)` and maximums around `layer_size - (w/2, h/2)`.
- Near bounds, the cache MAY remain asymmetrically aligned relative to camera expectations.
- That asymmetry at scene edges SHALL be considered expected behavior, not an error.
### 7. Composition Contract
- `HUD` SHALL remain above world composition.
- `sprites` MAY appear between canonical world layers.
- The observable composition order SHALL remain:
- `layer 0`
- intermediate sprites
- `layer 1`
- intermediate sprites
- `layer 2`
- intermediate sprites
- `layer 3`
- intermediate sprites
- `HUD`
- `sprites`, `HUD`, and `fades` MUST remain outside the invalidation contract of `SceneViewportCache`.
- V1 MAY still use destructive full composition ordering in `back`.
- The expected win in V1 comes from avoiding repeated brute-force canonical tilemap resolution before that final destructive composition.
## Impactos
This decision impacts the runtime model directly:
- `TileLayer` will be replaced by `SceneLayer`.
- `ScrollableTileLayer` will be removed.
- bank integration and naming at the asset-bank enum level SHALL be aligned during planning/implementation.
- The renderer contract will shift from direct map consumption to cache consumption.
- Scene loading, viewport cache maintenance, and composition responsibilities become explicitly separated.
Expected propagation areas:
- bank/domain model types;
- tilemap/layer runtime structures;
- renderer world composition flow;
- camera-to-render adapter logic;
- future plan/implementation artifacts derived from this decision.
## Referencias
- Agenda: [AGD-0025-scene-bank-and-viewport-cache-refactor.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0025-scene-bank-and-viewport-cache-refactor.md)
- Current tile/layer model:
- [tile_layer.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-hal/src/tile_layer.rs:1)
- [tile.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-hal/src/tile.rs:1)
- [glyph.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-hal/src/glyph.rs:1)
- Current renderer usage:
- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs:291)
- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs:594)
- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs:673)
## Propagacao Necessaria
The following work MUST derive from this decision:
1. Introduce the new canonical runtime types:
- `Scene`
- `SceneLayer`
- `SceneViewportCache`
- `SceneViewportResolver`
2. Remove or retire `ScrollableTileLayer`.
3. Migrate renderer world composition to consume `SceneViewportCache`.
4. Define the concrete cache update API for:
- line
- column
- area/region
5. Define the concrete blit instrumentation API emitted by the resolver.
6. Write an implementation plan before code changes.
## Revision Log
- 2026-04-13: Initial accepted decision from AGD-0025.