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

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
PLN-0011
PLN-0012
PLN-0013
PLN-0014
PLN-0015
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 SceneLayers.
  3. Each SceneLayer SHALL own its canonical TileMap.
  4. Each TileMap SHALL remain the canonical grid of Tiles 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 SceneLayers.
  • 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

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.