11 KiB
| id | ticket | title | status | created | accepted | agenda | plans | tags | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| DEC-0013 | scene-bank-and-viewport-cache-refactor | Decision - Scene Bank and Viewport Cache Model | accepted | 2026-04-13 | 2026-04-13 | AGD-0025 |
|
|
Status
Accepted.
This decision normatively locks the V1 model for scene-backed tilemap rendering around:
Sceneas canonical loaded state;SceneLayeras the canonical layer unit;SceneViewportCacheas the materialized render cache;SceneViewportResolveras 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:
- Canonical loaded tilemap state SHALL be represented by
Scene. - A
SceneSHALL contain four canonicalSceneLayers. - Each
SceneLayerSHALL own its canonicalTileMap. - Each
TileMapSHALL remain the canonical grid ofTiles for that layer. - The renderer MUST NOT consume canonical
Scenedata directly for normal world composition. - The renderer SHALL consume a
SceneViewportCachederived fromScene. SceneViewportCacheSHALL be operational render state only and MUST NOT become the semantic source of truth for gameplay or physics.SceneViewportCacheSHOULD use internal ringbuffer storage as the preferred V1 implementation strategy.- Ringbuffer details MUST remain encapsulated inside
SceneViewportCacheand MUST NOT leak into the semantic contract ofScene,SceneLayer,TileMap, orTile. ScrollableTileLayerSHALL be removed from the architecture and MUST NOT be preserved as a canonical runtime concept.backgroundandparallaxSHALL remain expressible throughSceneLayeritself, not through a separate V1 background type.- Each
SceneLayerSHALL 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:
Tileremains 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
backremains destructive in V1. - Keeping parallax inside
SceneLayeravoids 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
SceneSHALL be the canonical loaded aggregate.SceneSHALL contain four canonicalSceneLayers.SceneLayerSHALL be the canonical replacement for the oldTileLayer.SceneLayerSHALL minimally include:glyph_bank_idtile_sizeactivetotem_factor (x, y)tilemap
TileMapSHALL remain a canonical grid ofTile.TileSHALL remain lightweight and MUST NOT be expanded into resolved RGB pixel payload for canonical storage.
2. Viewport Cache Model
SceneViewportCacheSHALL be a single cache aggregate for oneScene.- 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.
SceneViewportCacheMAY store lightweight derived fields to accelerate raster, including:- resolved
glyph_id - resolved
palette_id - packed flip flags
active/emptymarkers- fast layer-local glyph bank references
- resolved
SceneViewportCacheMUST NOT duplicate full physics state unless a later decision explicitly requires it.SceneViewportCacheSHALL be the immediate source of copy data for world blits, but MUST NOT become the source of truth for world semantics.
3. Resolver Contract
SceneViewportResolverSHALL 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
25x16tiles 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
SceneSHALL fully invalidateSceneViewportCache. - 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
16x16tiles, the master totem center SHALL be computed as:cx = 16 * i + 8cy = 16 * j + 8
- Drift SHALL be computed as:
dx = x - cxdy = 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 pxandtrigger = 20 pxare 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 aroundlayer_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
HUDSHALL remain above world composition.spritesMAY 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, andfadesMUST remain outside the invalidation contract ofSceneViewportCache.- 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:
TileLayerwill be replaced bySceneLayer.ScrollableTileLayerwill 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
- Current tile/layer model:
- Current renderer usage:
Propagacao Necessaria
The following work MUST derive from this decision:
- Introduce the new canonical runtime types:
SceneSceneLayerSceneViewportCacheSceneViewportResolver
- Remove or retire
ScrollableTileLayer. - Migrate renderer world composition to consume
SceneViewportCache. - Define the concrete cache update API for:
- line
- column
- area/region
- Define the concrete blit instrumentation API emitted by the resolver.
- Write an implementation plan before code changes.
Revision Log
- 2026-04-13: Initial accepted decision from AGD-0025.