9.1 KiB
| id | ticket | title | status | created | accepted | agenda | plans | tags | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| DEC-0014 | render-all-scene-cache-and-camera-integration | Frame Composer Render Integration | accepted | 2026-04-14 | 2026-04-14 | AGD-0026 |
|
|
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
Gfxand to a slot-firstactivemodel.
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(). FrameComposerSHALL live inhardware/drivers, alongsideGfx.HardwareSHALL aggregate bothFrameComposerandGfx.FrameComposerSHALL own the frame-operational state:- active scene binding;
- camera / viewport state;
SceneViewportCache;SceneViewportResolver;- sprite submission state through
SpriteController.
GfxSHALL remain a low-level visual backend responsible for composition, blit, and raster execution.GfxMUST NOT remain the owner of scene state or sprite submission state.
Rationale
This split preserves a clean ownership model:
FrameComposerdecides what the frame is;Gfxexecutes 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.
It also preserves the scene-model contract already accepted below the frame layer:
- tile size is a property of each scene layer;
- the frame orchestrator must consume that contract, not redefine it;
FrameComposermust therefore remain tile-size agnostic rather than hard-coding16x16assumptions.
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). FrameComposerSHALL depend onSceneBankPoolAccess.FrameComposerMUST resolve scenes through the pool, not through copied scene values.- Scene access MUST be pointer-based / shared-reference based only.
- On bind,
FrameComposerSHALL store:scene_bank_id;Arc<SceneBank>for the resolved scene.
- The
SceneViewportCacheSHALL live insideFrameComposerwhile 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.
FrameComposerMUST 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 accepti32pixel coordinates.xandySHALL 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
FrameComposerSHALL own bothSceneViewportCacheandSceneViewportResolver.FrameComposerSHALL applyCacheRefreshRequests to the cache.GfxMUST NOT own cache refresh policy.GfxMUST only consume already prepared render state.
5A. Tile Size Contract
FrameComposerSHALL remain tile-size agnostic.FrameComposerMUST accept scene layers whose canonicaltile_sizeis8x8,16x16, or32x32.FrameComposerMUST NOT impose16x16as a bind-time, cache-time, resolver-time, or render-time precondition.- Cache sizing, resolver math, and world-copy preparation SHALL derive from the
tile_sizedeclared by each bound scene layer. - Compatibility checks, when needed, MUST be derived from canonical scene-layer and glyph-bank metadata rather than from a hard-coded compositor default.
- Any implementation path that only works for
16x16tiles is NON-COMPLIANT with this decision.
6. Sprite Model
Sprite.activeMUST be removed from the canonical operational model.- Sprite submission SHALL become frame-emission based.
SpriteControllerSHALL be the sprite submission subsystem owned byFrameComposer.- The sprite frame capacity SHALL remain capped at
512for 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.
layerSHALL remain numeric for now.- The sprite
layertype SHALL match the scene layer reference type used by the scene model. - Composition SHALL be layer-based.
- Within a layer:
- lower
prioritySHALL render first; - ties SHALL resolve FIFO by emission order.
- lower
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
GfxBridgeand adjacent visual contracts will need to stop treatingrender_all()as the canonical operational frame path.
Drivers / Hardware
Hardwarewill need to aggregateFrameComposernext toGfx.Gfxwill need to lose ownership of scene/sprite operational state.- Sprite submission state will need to move into
SpriteController. FrameComposer, cache, and resolver integration must preserve per-layertile_sizesemantics, including8x8.
Runtime / VM
- The VM runtime will eventually trigger frame composition through the new
FrameComposerpath rather than depending onGfx.render_all(). - The VM/runtime side should not own the detailed cache or scene orchestration policy directly once
FrameComposerexists 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.
- Scene-driven tile-size metadata must propagate unchanged into
FrameComposerorchestration and backend copy preparation.
Referencias
- AGD-0026-render-all-scene-cache-and-camera-integration.md
- LSN-0030-canonical-scene-cache-and-resolver-split.md
Propagacao Necessaria
- A new implementation plan MUST be created from this decision before code changes.
FrameComposerandSpriteControllerneed 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.
- Plan steps and tests that cover world composition MUST explicitly include
8x8tile-size coverage.
Revision Log
- 2026-04-14: Initial accepted decision from
AGD-0026. - 2026-04-15: Revision accepted to make
FrameComposerexplicitly tile-size agnostic and to require8x8support alongside16x16and32x32.