prometeu-runtime/discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md

187 lines
7.7 KiB
Markdown

---
id: DEC-0014
ticket: render-all-scene-cache-and-camera-integration
title: Frame Composer Render Integration
status: accepted
created: 2026-04-14
accepted: 2026-04-14
agenda: AGD-0026
plans: [PLN-0017, PLN-0018, PLN-0019, PLN-0020, PLN-0021]
tags: [gfx, runtime, render, camera, scene, sprites]
---
## Status
Accepted.
## Contexto
`DSC-0025` closed the canonical scene model around `SceneBank`, `SceneViewportCache`, and `SceneViewportResolver`, but the operational frame loop still remained split. `Gfx` still exposed `render_all()`, while the new world path already existed separately as `render_scene_from_cache(...)`.
This left the runtime with an incomplete composition model:
- canonical scene/camera/cache architecture had already changed;
- the normal frame entrypoint had not yet been integrated with that architecture;
- sprite ownership was still too coupled to `Gfx` and to a slot-first `active` model.
This decision closes the ownership and composition model for the next integration phase.
## Decisao
The runtime SHALL converge to a `FrameComposer`-owned frame orchestration model.
Normatively:
- `Gfx.render_all()` MUST be retired as the canonical frame service.
- The canonical operational frame entrypoint SHALL become `FrameComposer.render_frame()`.
- `FrameComposer` SHALL live in `hardware/drivers`, alongside `Gfx`.
- `Hardware` SHALL aggregate both `FrameComposer` and `Gfx`.
- `FrameComposer` SHALL own the frame-operational state:
- active scene binding;
- camera / viewport state;
- `SceneViewportCache`;
- `SceneViewportResolver`;
- sprite submission state through `SpriteController`.
- `Gfx` SHALL remain a low-level visual backend responsible for composition, blit, and raster execution.
- `Gfx` MUST NOT remain the owner of scene state or sprite submission state.
## Rationale
This split preserves a clean ownership model:
- `FrameComposer` decides what the frame is;
- `Gfx` executes how the frame is drawn.
Keeping orchestration in `FrameComposer` avoids re-entangling renderer code with camera policy, cache refresh policy, and scene binding. Keeping `FrameComposer` in `hardware/drivers` instead of `hal` preserves room for backend-specific acceleration while avoiding a policy-heavy abstraction in HAL.
This also preserves future backend freedom:
- software path today;
- hardware-assisted blit path later;
- or a more PPU-like backend in bare-metal environments.
## Invariantes / Contrato
### 1. Frame Entry
- The canonical public frame orchestration path SHALL be `FrameComposer.render_frame()`.
- `FrameComposer.render_frame()` SHALL be capable of producing a valid frame 100% of the time.
- A valid frame MUST NOT require a scene to be bound.
### 2. No-Scene Behavior
- If no scene is bound, `FrameComposer.render_frame()` SHALL compose only:
- emitted sprites;
- fades already owned by the visual backend.
- No implicit clear SHALL be performed.
- Clearing the back buffer SHALL remain the responsibility of the caller / developer.
- In the no-scene state:
- cache MUST be absent or inert;
- resolver MUST be absent or inert;
- the system SHALL expose explicit scene-availability status.
### 3. Scene Binding
- Scene binding SHALL be performed by `bind_scene(scene_bank_id)`.
- `FrameComposer` SHALL depend on `SceneBankPoolAccess`.
- `FrameComposer` MUST resolve scenes through the pool, not through copied scene values.
- Scene access MUST be pointer-based / shared-reference based only.
- On bind, `FrameComposer` SHALL store:
- `scene_bank_id`;
- `Arc<SceneBank>` for the resolved scene.
- The `SceneViewportCache` SHALL live inside `FrameComposer` while the scene remains bound.
- `unbind_scene()` SHALL:
- remove the active scene;
- discard the associated cache;
- invalidate the world path;
- keep the frame path valid for no-scene composition.
- Replacing the contents of a bound scene slot SHALL require a new explicit bind.
- `FrameComposer` MUST NOT poll the scene bank pool each frame to revalidate the binding.
- A new `bind_scene(...)` SHALL replace the previous bound scene completely.
### 4. Camera
- The V1 camera contract SHALL be minimal.
- `set_camera(x, y)` SHALL accept `i32` pixel coordinates.
- `x` and `y` SHALL represent the top-left of the viewport in world space.
- Camera follow, smoothing, shake, cinematic transitions, and similar behaviors are OUT OF SCOPE for this decision.
### 5. Cache and Resolver
- `FrameComposer` SHALL own both `SceneViewportCache` and `SceneViewportResolver`.
- `FrameComposer` SHALL apply `CacheRefreshRequest`s to the cache.
- `Gfx` MUST NOT own cache refresh policy.
- `Gfx` MUST only consume already prepared render state.
### 6. Sprite Model
- `Sprite.active` MUST be removed from the canonical operational model.
- Sprite submission SHALL become frame-emission based.
- `SpriteController` SHALL be the sprite submission subsystem owned by `FrameComposer`.
- The sprite frame capacity SHALL remain capped at `512` for V1.
- The sprite counter SHALL be reset at the start of each frame.
- The caller MUST NOT provide sprite indices directly.
- Each `emit_sprite(...)` call SHALL occupy the next available internal slot.
- Overflow beyond capacity SHALL be ignored.
- Overflow SHOULD leave room for system logging / telemetry.
- Future certification MAY penalize sprite overflow, but that is not part of this decision.
- `emit_sprite(...)` SHALL NOT require a dedicated reset API beyond the normal frame lifecycle.
### 7. Sprite Ordering
- Each sprite SHALL carry:
- `layer`;
- `priority`.
- `layer` SHALL remain numeric for now.
- The sprite `layer` type SHALL match the scene layer reference type used by the scene model.
- Composition SHALL be layer-based.
- Within a layer:
- lower `priority` SHALL render first;
- ties SHALL resolve FIFO by emission order.
### 8. Composition Scope
- HUD integration is OUT OF SCOPE for the first integration phase covered by this decision.
- The first integration phase SHALL focus on:
- world scene path;
- sprites;
- fades.
## Impactos
### HAL
- `GfxBridge` and adjacent visual contracts will need to stop treating `render_all()` as the canonical operational frame path.
### Drivers / Hardware
- `Hardware` will need to aggregate `FrameComposer` next to `Gfx`.
- `Gfx` will need to lose ownership of scene/sprite operational state.
- Sprite submission state will need to move into `SpriteController`.
### Runtime / VM
- The VM runtime will eventually trigger frame composition through the new `FrameComposer` path rather than depending on `Gfx.render_all()`.
- The VM/runtime side should not own the detailed cache or scene orchestration policy directly once `FrameComposer` exists in hardware/drivers.
### Asset / Scene Flow
- Scene activation will become explicit through bank-id binding.
- Scene slot replacement will require explicit rebinding behavior from callers.
## Referencias
- [AGD-0026-render-all-scene-cache-and-camera-integration.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md)
- [LSN-0030-canonical-scene-cache-and-resolver-split.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md)
## Propagacao Necessaria
- A new implementation plan MUST be created from this decision before code changes.
- `FrameComposer` and `SpriteController` need explicit planning and migration sequencing.
- `Gfx.render_all()` retirement MUST be planned rather than removed ad hoc.
- The frame service rename and integration path MUST be propagated through the frame loop callsites.
## Revision Log
- 2026-04-14: Initial accepted decision from `AGD-0026`.