--- id: PLN-0020 ticket: render-all-scene-cache-and-camera-integration title: Plan - Cache Refresh and render_frame Path status: accepted created: 2026-04-14 completed: tags: [gfx, runtime, render, cache, resolver, frame-composer] --- ## Objective Connect `FrameComposer` to `SceneViewportResolver`, apply cache refreshes inside `FrameComposer`, and establish `render_frame()` as the canonical composition path for world + sprites + fades. ## Background `DEC-0014` requires that cache refresh policy remain inside `FrameComposer` and that `FrameComposer.render_frame()` become the canonical frame entry while `Gfx` remains only the low-level execution backend. `DEC-0014` also requires the world path to remain tile-size agnostic, with explicit support for `8x8`, `16x16`, and `32x32` scene-layer tile sizes. For per-layer camera scaling, this plan treats `parallax_factor` as the canonical scene-layer field name. ## Scope ### Included - apply `CacheRefreshRequest`s in `FrameComposer` - connect camera/scene state to resolver updates - use cache-backed world rendering in the frame path - keep valid no-scene rendering (`sprites + fades`) ### Excluded - HUD integration - final retirement cleanup of legacy callsites ## Execution Steps ### Step 1 - Apply resolver refreshes inside `FrameComposer` **What:** Move cache-refresh orchestration fully into `FrameComposer`. **How:** - On active-scene frames: - call resolver update with current camera and scene - consume returned `CacheRefreshRequest`s - apply them to `SceneViewportCache` - Keep `Gfx` unaware of refresh semantics. - Ensure resolver and refresh math follow the bound layer `tile_size` values rather than any fixed `16x16` default. - Ensure per-layer camera math is expressed through `parallax_factor` naming in the resolver/cache path. **File(s):** - `crates/console/prometeu-drivers/src/frame_composer.rs` ### Step 2 - Define `render_frame()` as the canonical frame path **What:** Introduce the new frame service on `FrameComposer`. **How:** - Add `render_frame()` to `FrameComposer`. - If a scene is active and renderable: - prepare resolver update - refresh cache - call the cache-backed world path in `Gfx` - If no scene is active: - call the no-scene path for `sprites + fades` - World rendering must remain valid when the active scene uses `8x8` tiles. **File(s):** - `crates/console/prometeu-drivers/src/frame_composer.rs` - `crates/console/prometeu-drivers/src/gfx.rs` ### Step 3 - Keep `Gfx` as backend only **What:** Narrow `Gfx` to backend-oriented composition responsibilities. **How:** - Ensure `Gfx` consumes prepared state from `FrameComposer`. - Do not let `Gfx` regain ownership of cache refresh or scene orchestration. - Keep low-level helpers for cache-backed copy paths, sprite drawing, and fades in `Gfx`. **File(s):** - `crates/console/prometeu-drivers/src/gfx.rs` ### Step 4 - Cover scene and no-scene frame paths **What:** Protect the two canonical frame modes. **How:** - Add tests for: - active-scene world composition - no-scene `sprites + fades` - scene transition through unbind/rebind - cache refresh behavior staying inside `FrameComposer` - active-scene composition with `8x8` tile-size layers **File(s):** - `crates/console/prometeu-drivers/src/frame_composer.rs` - `crates/console/prometeu-drivers/src/gfx.rs` ## Test Requirements ### Unit Tests - `render_frame()` with no scene produces valid no-scene composition. - `render_frame()` with a scene applies resolver refreshes before composition. - cache refresh requests are applied by `FrameComposer`, not `Gfx`. - `render_frame()` with an `8x8` scene uses resolver/cache math derived from layer tile size rather than a `16x16` assumption. - Resolver/cache-facing tests use `parallax_factor` terminology for per-layer camera scaling. ### Integration Tests - scene bind + camera set + sprite emission + `render_frame()` produces the expected composed frame. - scene bind + camera set + `8x8` scene + `render_frame()` produces the expected composed frame. ### Manual Verification - Verify that no-scene frames still render sprites/fades without crashes or hidden clears. ## Acceptance Criteria - [ ] `FrameComposer.render_frame()` exists and is the canonical frame path. - [ ] Cache refreshes are applied inside `FrameComposer`. - [ ] World rendering consumes the cache-backed path. - [ ] No-scene `sprites + fades` behavior remains valid. - [ ] `Gfx` remains backend-only for this path. - [ ] The world path is explicitly covered for `8x8` scenes without `16x16`-specific assumptions. - [ ] Resolver/cache/frame-path terminology is aligned on `parallax_factor` for scene-layer camera scaling. ## Dependencies - Depends on `PLN-0017`, `PLN-0018`, and `PLN-0019` - Source decision: `DEC-0014` ## Risks - If refresh application leaks into `Gfx`, the ownership split from `DEC-0014` collapses. - If no-scene behavior is not tested explicitly, scene integration can accidentally make scene binding mandatory. - If tests cover only `16x16`, a latent compositor regression against canonical `8x8` scenes can ship unnoticed.