228 lines
11 KiB
Markdown
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.
|