dev/render-all-scene-cache-and-camera-integration #16

Merged
bquarkz merged 21 commits from dev/render-all-scene-cache-and-camera-integration into master 2026-04-18 16:20:50 +00:00
7 changed files with 100 additions and 2 deletions
Showing only changes of commit 98d2d81882 - Show all commits

View File

@ -18,7 +18,7 @@
{"type":"discussion","id":"DSC-0013","status":"done","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"lessons/DSC-0013-perf-host-debug-overlay-isolation/LSN-0027-host-debug-overlay-isolation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
{"type":"discussion","id":"DSC-0024","status":"done","ticket":"generic-memory-bank-slot-contract","title":"Agenda - Generic Memory Bank Slot Contract","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["runtime","asset","memory-bank","slots","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"lessons/DSC-0024-generic-memory-bank-slot-contract/LSN-0029-slot-first-bank-telemetry-belongs-in-asset-manager.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
{"type":"discussion","id":"DSC-0025","status":"done","ticket":"scene-bank-and-viewport-cache-refactor","title":"Scene Bank and Viewport Cache Refactor","created_at":"2026-04-11","updated_at":"2026-04-14","tags":["gfx","tilemap","runtime","render"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md","status":"done","created_at":"2026-04-14","updated_at":"2026-04-14"}]}
{"type":"discussion","id":"DSC-0026","status":"open","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-14","tags":["gfx","runtime","render","camera","scene"],"agendas":[{"id":"AGD-0026","file":"workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14"}],"decisions":[{"id":"DEC-0014","file":"workflow/decisions/DEC-0014-frame-composer-render-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_agenda":"AGD-0026"}],"plans":[{"id":"PLN-0017","file":"workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0018","file":"workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0019","file":"workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0020","file":"workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0021","file":"workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0026","status":"open","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-15","tags":["gfx","runtime","render","camera","scene"],"agendas":[{"id":"AGD-0026","file":"workflow/agendas/AGD-0026-render-all-scene-cache-and-camera-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15"}],"decisions":[{"id":"DEC-0014","file":"workflow/decisions/DEC-0014-frame-composer-render-integration.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_agenda":"AGD-0026"}],"plans":[{"id":"PLN-0017","file":"workflow/plans/PLN-0017-frame-composer-core-and-hardware-ownership.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0018","file":"workflow/plans/PLN-0018-sprite-controller-and-frame-emission-model.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-14","ref_decisions":["DEC-0014"]},{"id":"PLN-0019","file":"workflow/plans/PLN-0019-scene-binding-camera-and-scene-status.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0020","file":"workflow/plans/PLN-0020-cache-refresh-and-render-frame-path.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]},{"id":"PLN-0021","file":"workflow/plans/PLN-0021-service-retirement-callsite-migration-and-regression.md","status":"accepted","created_at":"2026-04-14","updated_at":"2026-04-15","ref_decisions":["DEC-0014"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]}

View File

@ -4,7 +4,7 @@ ticket: render-all-scene-cache-and-camera-integration
title: Agenda - Integrate render_all with Scene Cache and Camera
status: accepted
created: 2026-04-14
updated: 2026-04-14
updated: 2026-04-15
tags: [gfx, runtime, render, camera, scene]
---
@ -54,6 +54,10 @@ O problema concreto não é só “chamar uma função”. É decidir:
- cache ativo
- resolver ativo
- câmera ativa
- O `FrameComposer` não pode regredir o contrato já aceito no scene model:
- `SceneLayer.tile_size` já é por-layer e aceita `8x8`, `16x16` e `32x32`;
- o decoder de `SCENE` já materializa esses tamanhos;
- fixar o pipeline em `16x16` dentro do orquestrador de frame criaria uma restrição artificial que não existe no modelo canônico.
- Se a integração for mal feita, o renderer pode voltar a misturar:
- política de câmera
- atualização de cache
@ -315,6 +319,60 @@ Aceitar o contrato mínimo acima como base de fechamento da agenda, a menos que
- O HUD entra nesta integração já agora, ou o foco da primeira integração é apenas world + sprites + fades?
- fechado: sem HUD nesta primeira integração.
## Reabertura 2026-04-15
### Contexto adicional
Ao revisitar a thread, apareceu uma restrição indevida: tratar o `FrameComposer` como se aceitasse apenas tilesets `16x16`.
Isso conflita com o estado atual do runtime:
- `TileSize` no HAL já enumera `Size8`, `Size16` e `Size32`;
- `SceneLayer` carrega `tile_size` por layer;
- o decoder de `SCENE` já aceita `8`, `16` e `32`;
- `SceneViewportResolver` e `SceneViewportCache` já calculam offsets, anchors e cópia a partir do `tile_size` da própria layer.
O risco aqui não é apenas de implementação. Se o contrato do `FrameComposer` assumir `16x16` como pré-condição, ele quebra a neutralidade do orquestrador e reabre uma limitação artificial acima do scene model.
### Problema reaberto
Precisamos fechar explicitamente que o `FrameComposer` aceita cenas/layers com `tile_size` `8x8` e não impõe `16x16` como tamanho mínimo ou obrigatório para o world path.
### Opcoes adicionais
### Opcao 4 - Fixar `16x16` no `FrameComposer` e tratar `8x8` como fora de escopo
**Vantagens:**
- reduz casos de teste imediatos;
- simplifica implementação inicial se alguém estiver assumindo viewport/caches calibrados manualmente para `16`.
**Desvantagens:**
- contradiz o scene model já aceito;
- introduz restrição artificial no orquestrador;
- obriga futura revisão de contrato para reaceitar algo que a base já suporta.
### Opcao 5 - Manter `FrameComposer` tile-size agnostic e aceitar `8x8` desde V1
**Vantagens:**
- preserva o contrato canônico já existente em `SceneLayer`;
- mantém o `FrameComposer` como orquestrador, não como redefinidor de formato;
- evita bifurcação entre pipeline de scene e pipeline de composição.
**Desvantagens:**
- exige deixar isso explícito na decisão e nos planos;
- aumenta a exigência de testes para viewport/cache/cópia com `8x8`.
### Recomendacao adicional
Seguir com a **Opcao 5**.
Norma proposta para fechamento desta reabertura:
- `FrameComposer` deve aceitar scenes/layers cujo `tile_size` resolvido seja `8x8`, `16x16` ou `32x32`;
- `FrameComposer` nao deve impor `16x16` como pré-condição para bind, cache, resolver ou composição;
- qualquer validação de compatibilidade deve ser derivada do `tile_size` declarado pela própria layer / glyph bank, nunca de um default rígido no compositor;
- os planos derivados desta thread precisam citar testes explícitos para `8x8`.
## Criterio para Encerrar
Esta agenda pode ser encerrada quando estiver explícito:
@ -324,6 +382,7 @@ Esta agenda pode ser encerrada quando estiver explícito:
- como funciona o bind/unbind da scene ativa;
- quando o cache é atualizado;
- como `render_all()` passa a compor o world path aceito;
- que o `FrameComposer` permanece agnóstico ao `tile_size` canônico da layer e aceita `8x8` sem downgrade contratual;
- e qual é a superfície mínima de integração para implementação sem reabrir a arquitetura base.
## Resolucao

View File

@ -60,6 +60,12 @@ This also preserves future backend freedom:
- 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
@ -113,6 +119,15 @@ This also preserves future backend freedom:
- `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.
@ -158,6 +173,7 @@ This also preserves future backend freedom:
- `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
@ -168,6 +184,7 @@ This also preserves future backend freedom:
- 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
@ -180,7 +197,9 @@ This also preserves future backend freedom:
- `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`.

View File

@ -54,6 +54,7 @@ Create `FrameComposer` as a concrete driver-side subsystem.
- `SceneViewportResolver`
- `SpriteController`
- Keep scene/cache/resolver fields optional where no-scene operation is required.
- Do not introduce any fixed `16x16` assumption into owned state; tile-size-sensitive behavior must derive from bound scene metadata.
**File(s):**
- `crates/console/prometeu-drivers/src/frame_composer.rs`
@ -93,6 +94,7 @@ Give the driver layer a stable initial surface for `FrameComposer`.
### Unit Tests
- `FrameComposer` can be constructed without a bound scene.
- `Hardware` successfully constructs with both `gfx` and `frame_composer`.
- Construction and owned state shape do not encode `16x16` as an implicit world-path invariant.
### Integration Tests
- Shared bank access needed by `FrameComposer` is available through hardware construction.
@ -115,3 +117,4 @@ Give the driver layer a stable initial surface for `FrameComposer`.
- Introducing `FrameComposer` with too much behavior too early can blur later migration steps.
- Introducing too little owned state can leave ownership ambiguous and force rework in later plans.
- Encoding `16x16` into the initial owned-state shape would create a contract violation that later plans would have to unwind.

View File

@ -15,6 +15,7 @@ Implement the `FrameComposer` scene-binding contract, minimal camera state, and
## Background
`DEC-0014` locks scene activation around `bind_scene(scene_bank_id)` with `SceneBankPoolAccess`, pointer-based access only, and `scene_bank_id + Arc<SceneBank>` retained inside `FrameComposer`.
The same decision also requires `FrameComposer` to remain tile-size agnostic and to preserve canonical per-layer `tile_size`, including `8x8`.
## Scope
@ -90,6 +91,7 @@ Align cache/resolver lifetime with the active scene contract.
- create or reinitialize cache/resolver.
- On unbind:
- discard cache/resolver and invalidate the world path.
- Any initialization must derive layer math from the bound scene tile sizes instead of assuming `16x16`.
**File(s):**
- `crates/console/prometeu-drivers/src/frame_composer.rs`
@ -101,6 +103,7 @@ Align cache/resolver lifetime with the active scene contract.
- unbind clears active scene and cache.
- scene status reflects no-scene and active-scene states.
- camera coordinates are stored as top-left pixel-space values.
- bind/unbind remains valid for scenes whose layers use `8x8` tiles.
### Integration Tests
- `FrameComposer` can resolve a scene from the pool and survive no-scene operation.
@ -115,6 +118,7 @@ Align cache/resolver lifetime with the active scene contract.
- [ ] Scene status is explicit.
- [ ] Camera contract is implemented as `i32` top-left viewport coordinates.
- [ ] Cache/resolver lifetime follows scene bind/unbind.
- [ ] Scene bind/cache/resolver setup preserves canonical per-layer tile sizes, including `8x8`.
## Dependencies

View File

@ -15,6 +15,7 @@ Connect `FrameComposer` to `SceneViewportResolver`, apply cache refreshes inside
## 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.
## Scope
@ -41,6 +42,7 @@ Move cache-refresh orchestration fully into `FrameComposer`.
- 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.
**File(s):**
- `crates/console/prometeu-drivers/src/frame_composer.rs`
@ -58,6 +60,7 @@ Introduce the new frame service on `FrameComposer`.
- 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`
@ -87,6 +90,7 @@ Protect the two canonical frame modes.
- 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`
@ -98,9 +102,11 @@ Protect the two canonical frame modes.
- `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.
### 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.
@ -112,6 +118,7 @@ Protect the two canonical frame modes.
- [ ] 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.
## Dependencies
@ -122,3 +129,4 @@ Protect the two canonical frame modes.
- 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.

View File

@ -15,6 +15,7 @@ Retire `Gfx.render_all()` from the canonical flow, migrate callsites to `FrameCo
## Background
`DEC-0014` is explicit that `Gfx.render_all()` must be retired and that `FrameComposer.render_frame()` becomes the canonical frame orchestration entrypoint. This final plan removes the old canonical service shape and validates the migration end-to-end.
The same decision also requires the new canonical path to preserve scene-layer tile sizes such as `8x8`, not just `16x16`.
## Scope
@ -68,6 +69,7 @@ Protect the new service model against fallback to the old renderer path.
- frame-loop code calls `FrameComposer.render_frame()`
- no-scene frames remain valid
- active-scene frames render through cache-backed composition
- active-scene frames remain valid for canonical `8x8` scenes
- sprite emission and ordering survive the full path
- Add assertions or test failures for accidental continued reliance on `Gfx.render_all()`.
@ -96,6 +98,7 @@ Confirm the migration did not break unrelated systems.
### Integration Tests
- runtime tick path renders through `FrameComposer.render_frame()`.
- no-scene and active-scene frame modes both remain valid.
- runtime tick path remains valid when the active scene uses `8x8` tiles.
### Manual Verification
- Run the repository CI path and confirm the final integrated service model is green.
@ -106,6 +109,7 @@ Confirm the migration did not break unrelated systems.
- [ ] `Gfx.render_all()` is retired from the canonical service path.
- [ ] Regression coverage protects against fallback to the old model.
- [ ] Repository validation passes after the migration.
- [ ] Regression coverage includes the canonical `8x8` world-path case.
## Dependencies
@ -116,3 +120,4 @@ Confirm the migration did not break unrelated systems.
- Removing `render_all()` too early can strand intermediate callsites.
- Leaving it in place as a canonical path for too long can create a dual-service model that is harder to remove later.
- Migrating callsites without `8x8` regression coverage can falsely validate only the legacy `16x16` path.