prometeu-runtime/discussion/workflow/decisions/DEC-0014-frame-composer-render-integration.md
bQUARKz 98d2d81882
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/head This commit looks good
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good
adjustments over frame composer contract - agnostic tile size
2026-04-15 08:42:46 +01:00

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
PLN-0017
PLN-0018
PLN-0019
PLN-0020
PLN-0021
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.

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;
  • FrameComposer must therefore remain tile-size agnostic rather than hard-coding 16x16 assumptions.

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 CacheRefreshRequests to the cache.
  • Gfx MUST NOT own cache refresh policy.
  • Gfx MUST only consume already prepared render state.

5A. Tile Size Contract

  • FrameComposer SHALL remain tile-size agnostic.
  • FrameComposer MUST accept scene layers whose canonical tile_size is 8x8, 16x16, or 32x32.
  • FrameComposer MUST NOT impose 16x16 as a bind-time, cache-time, resolver-time, or render-time precondition.
  • Cache sizing, resolver math, and world-copy preparation SHALL derive from the tile_size declared 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 16x16 tiles is NON-COMPLIANT with this decision.

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.
  • FrameComposer, cache, and resolver integration must preserve per-layer tile_size semantics, including 8x8.

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.
  • Scene-driven tile-size metadata must propagate unchanged into FrameComposer orchestration and backend copy preparation.

Referencias

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.
  • Plan steps and tests that cover world composition MUST explicitly include 8x8 tile-size coverage.

Revision Log

  • 2026-04-14: Initial accepted decision from AGD-0026.
  • 2026-04-15: Revision accepted to make FrameComposer explicitly tile-size agnostic and to require 8x8 support alongside 16x16 and 32x32.