prometeu-runtime/discussion/lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md
bQUARKz 73cf96ed6c
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good
housekeep
2026-04-18 16:31:23 +01:00

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.