All checks were successful
Intrepid/Prometeu/Runtime/pipeline/head This commit looks good
85 lines
4.7 KiB
Markdown
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.
|