All checks were successful
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good
75 lines
3.7 KiB
Markdown
75 lines
3.7 KiB
Markdown
---
|
|
id: LSN-0031
|
|
ticket: render-all-scene-cache-and-camera-integration
|
|
title: Frame Composition Belongs Above the Render Backend
|
|
created: 2026-04-18
|
|
tags: [gfx, runtime, render, camera, scene, sprites, frame-composer]
|
|
---
|
|
|
|
## Context
|
|
|
|
`DSC-0025` split canonical scene ownership from viewport caching and resolver policy, but the runtime still treated `Gfx.render_all()` as the operational frame entrypoint. That left scene binding, camera state, cache refresh, and sprite submission spread across the wrong layer.
|
|
|
|
`DSC-0026` completed the integration by making `FrameComposer` the owner of frame orchestration and reducing `Gfx` to a backend that consumes already prepared render state.
|
|
|
|
## Key Decisions
|
|
|
|
### Frame Orchestration Must Not Live in the Backend
|
|
|
|
**What:**
|
|
`FrameComposer.render_frame()` became the canonical frame service, while `Gfx.render_all()` was retired from the runtime-facing flow.
|
|
|
|
**Why:**
|
|
The backend should execute composition, not decide scene binding, camera policy, cache refresh, or sprite lifecycle. Keeping those responsibilities above `Gfx` preserves a cleaner ownership model and avoids re-entangling policy with raster code.
|
|
|
|
**Trade-offs:**
|
|
This adds an explicit orchestration layer between runtime callsites and the renderer, but the resulting boundaries are easier to evolve and test.
|
|
|
|
### Scene Binding, Camera, Cache, and Sprites Form One Operational Unit
|
|
|
|
**What:**
|
|
`FrameComposer` owns:
|
|
- active scene binding by bank id and shared scene reference;
|
|
- camera coordinates in top-left world pixel space;
|
|
- `SceneViewportCache` and `SceneViewportResolver`;
|
|
- a frame-emission `SpriteController`.
|
|
|
|
**Why:**
|
|
These concerns all define what a frame is. Splitting them across multiple owners would recreate stale-state bugs and make no-scene behavior ambiguous.
|
|
|
|
**Trade-offs:**
|
|
The composer becomes a richer subsystem, but it carries policy in one place instead of leaking it into unrelated APIs.
|
|
|
|
### The World Path Must Stay Tile-Size Agnostic
|
|
|
|
**What:**
|
|
The integrated frame path derives cache sizing, resolver math, and world-copy behavior from per-layer scene metadata instead of a hard-coded `16x16` assumption.
|
|
|
|
**Why:**
|
|
The scene contract already allows canonical `8x8`, `16x16`, and `32x32` tile sizes. The frame service has to consume that contract faithfully or it becomes a hidden compatibility break.
|
|
|
|
**Trade-offs:**
|
|
The integration and tests need to exercise more than the legacy default path, but the renderer no longer bakes in a false invariant.
|
|
|
|
## Patterns and Algorithms
|
|
|
|
- Put frame policy in a dedicated orchestration layer and keep the renderer backend-oriented.
|
|
- Treat scene binding, camera state, cache lifetime, and sprite submission as one cohesive frame model.
|
|
- Refresh cache state inside the orchestrator before composition instead of letting the renderer discover refresh policy.
|
|
- Prefer frame-emission sprite submission with internal ordering over caller-owned sprite slots.
|
|
- Keep the no-scene path valid so world composition remains optional, not mandatory.
|
|
|
|
## Pitfalls
|
|
|
|
- Leaving `render_all()` alive as a canonical path creates a fragile dual-service model.
|
|
- Letting `Gfx` own cache refresh semantics collapses the boundary between policy and execution.
|
|
- Requiring a scene for every frame quietly breaks sprite-only or fade-only operation.
|
|
- Testing only `16x16` scenes hides regressions against valid `8x8` or `32x32` content.
|
|
|
|
## Takeaways
|
|
|
|
- Frame composition belongs in a subsystem that owns policy, not in the backend that draws pixels.
|
|
- Scene binding, camera, cache, resolver, and sprite submission should converge under one frame owner.
|
|
- No-scene rendering is part of the contract and should stay valid throughout integration work.
|
|
- Tile-size assumptions must be derived from canonical scene metadata, never from renderer habit.
|