prometeu-runtime/discussion/lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md
bQUARKz 1ad7554762
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/head This commit looks good
housekeep
2026-04-14 05:31:34 +01:00

85 lines
4.7 KiB
Markdown

---
id: LSN-0030
ticket: scene-bank-and-viewport-cache-refactor
title: Canonical Scene Ownership Must Stay Separate from Viewport Caching
created: 2026-04-14
tags: [gfx, runtime, tilemap, scene, render, asset]
---
## Context
This discussion started as a ringbuffer exploration for tilemap rendering, but the work converged on a broader renderer and scene-model refactor. The key pressure was not world streaming in the open-world sense; it was keeping the canonical scene model simple while still allowing efficient viewport materialization, parallax, and future renderer evolution.
The final implementation introduced `SceneBank` as the canonical loaded scene, `SceneViewportCache` as the materialized render cache, `SceneViewportResolver` as the movement and rematerialization policy owner, and a versioned binary `SCENE` asset payload.
## Key Decisions
### Canonical Scene Data Must Not Inherit Viewport Mechanics
**What:**
`SceneBank` owns the canonical scene as four `SceneLayer`s, each with its own `TileMap`. Viewport concerns such as ringbuffer movement, cache origin, hysteresis, and copy offsets do not live in the canonical scene types.
**Why:**
Once camera motion, parallax, and cache refresh strategy leak into the source-of-truth model, physics, tooling, serialization, and renderer evolution all start depending on transient viewport state. Keeping the scene canonical and static preserves simpler reasoning across the runtime.
**Trade-offs:**
This creates more explicit layers in the architecture: canonical scene, cache, and resolver. The model is cleaner, but integration requires a little more plumbing.
### Ringbuffer Is Safer Inside the Cache Than Inside the Scene
**What:**
The ringbuffer is an implementation detail of `SceneViewportCache`, with one internal ringbuffer per layer.
**Why:**
The cache is the right place to optimize for partial refresh, wraparound, and parallax movement. The scene itself should remain plain, indexable, and serialization-friendly.
**Trade-offs:**
The renderer must consume cache materialization rather than reading the canonical scene directly. That is an extra step, but it avoids contaminating the domain model with cyclical storage concerns.
### Resolver Owns Movement Policy, Not the Renderer
**What:**
`SceneViewportResolver` owns the master anchor, per-layer anchors, clamp, hysteresis, drift handling, and the copy instrumentation needed by the renderer.
**Why:**
If the renderer starts owning anchor advancement or rematerialization policy, world composition and camera policy become entangled. The resolver keeps that logic centralized and testable.
**Trade-offs:**
The renderer becomes dependent on `ResolverUpdate`, but in return it stays focused on composition instead of movement semantics.
### Binary Scene Assets Need a Versioned Contract Early
**What:**
`SCENE` assets now use a versioned binary payload with fixed four-layer layout, per-layer metadata, and packed tile records. The decoder validates version, layer count, tile size, tile count, and decoded size.
**Why:**
JSON payloads were convenient as a temporary unblocker but were fundamentally the wrong runtime contract for scene assets. A binary format is smaller, deterministic, and easier to validate.
**Trade-offs:**
Authoring and tooling need to respect a stricter binary format, but the runtime contract becomes more stable and future-ready.
## Patterns and Algorithms
- Split world rendering into three responsibilities:
canonical scene ownership;
viewport cache storage;
movement and refresh policy.
- Keep camera input in pixel space, but advance anchors in tile space.
- Use hysteresis to prevent refresh thrash at tile boundaries.
- Prefer cache-backed renderer inputs over direct canonical map reads once materialization exists.
- Put binary asset validation close to decode and fail loudly on malformed payloads.
## Pitfalls
- Starting from a ringbuffer optimization can accidentally turn into a scene-model redesign if the boundaries are not made explicit.
- Letting the renderer read `SceneBank` directly after introducing `SceneViewportCache` leaves the codebase in a fragile dual-model state.
- Storing anchor-like state in `SceneLayer` would mix canonical scene data with transient viewport state.
- Treating binary scene format as “to be decided later” blocks load and preload integration much longer than necessary.
## Takeaways
- Canonical scene ownership, viewport caching, and viewport movement policy should be separate runtime concerns.
- Ringbuffer is valuable as an internal cache mechanism, not as the canonical world model.
- Hysteresis belongs in the resolver when cache refresh must avoid boundary flicker and churn.
- Scene asset formats should become binary and versioned before they become widely depended on.