dev/render-all-scene-cache-and-camera-integration #16
@ -1,4 +1,4 @@
|
||||
{"type":"meta","next_id":{"DSC":26,"AGD":26,"DEC":14,"PLN":17,"LSN":31,"CLSN":1}}
|
||||
{"type":"meta","next_id":{"DSC":27,"AGD":27,"DEC":15,"PLN":22,"LSN":31,"CLSN":1}}
|
||||
{"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
||||
{"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||
{"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]}
|
||||
@ -18,6 +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-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"}]}
|
||||
|
||||
@ -0,0 +1,346 @@
|
||||
---
|
||||
id: AGD-0026
|
||||
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
|
||||
tags: [gfx, runtime, render, camera, scene]
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
A thread `DSC-0025` fechou a base arquitetural para `SceneBank`, `SceneViewportCache`, `SceneViewportResolver` e o decoder binário de `SCENE`. O renderer já possui um caminho explícito `render_scene_from_cache(&SceneViewportCache, &ResolverUpdate)`, mas o loop operacional do runtime ainda chama apenas `render_all()`.
|
||||
|
||||
Hoje, em [tick.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs:148), o frame segue pelo `hw.gfx_mut().render_all()`. Isso significa que o caminho novo de world render ainda não está integrado ao ciclo normal do runtime.
|
||||
|
||||
Ao mesmo tempo, a integração correta depende de fechar o contrato mínimo da câmera, porque o `SceneViewportResolver` já assume uma posição de câmera em pixel space e produz `ResolverUpdate` a partir dela. Sem essa integração, o runtime fica com duas verdades práticas:
|
||||
|
||||
- a arquitetura aceita para world rendering;
|
||||
- o caminho ainda efetivamente usado pelo frame loop.
|
||||
|
||||
## Problema
|
||||
|
||||
Precisamos integrar `render_scene_from_cache()` ao `render_all()` e ao ciclo real do runtime sem reabrir a arquitetura já aceita para `SceneBank` / `SceneViewportCache` / `SceneViewportResolver`.
|
||||
|
||||
O problema concreto não é só “chamar uma função”. É decidir:
|
||||
|
||||
- quem é dono do estado de câmera mínimo;
|
||||
- onde `SceneBank`, `SceneViewportCache` e `SceneViewportResolver` passam a residir em runtime;
|
||||
- quando o cache é atualizado;
|
||||
- como o `render_all()` deixa de ser um caminho “scene-blind” e vira o entrypoint normal da composição final.
|
||||
|
||||
## Pontos Criticos
|
||||
|
||||
- `render_all()` deve continuar funcional mesmo quando nenhuma scene estiver carregada.
|
||||
- O `render_all()` atual não desenha world layers; ele só compõe sprites de prioridade 0 e fades.
|
||||
- O modelo atual de `Sprite.priority` mistura duas responsabilidades:
|
||||
- em qual faixa de composição o sprite entra;
|
||||
- qual a ordem relativa entre sprites naquela faixa.
|
||||
- `render_scene_from_cache()` existe, mas exige `SceneViewportCache` e `ResolverUpdate` já preparados por fora.
|
||||
- O modelo atual de sprites ainda é slot-first para o chamador:
|
||||
- há armazenamento fixo;
|
||||
- o dev informa índice;
|
||||
- e o renderer precisa filtrar `active`.
|
||||
- O `SceneViewportResolver` já carrega política importante:
|
||||
- câmera em pixel space
|
||||
- anchors
|
||||
- clamp
|
||||
- histerese
|
||||
- refresh requests
|
||||
- copy requests
|
||||
- Ainda não existe um dono explícito do estado operacional:
|
||||
- cena ativa
|
||||
- cache ativo
|
||||
- resolver ativo
|
||||
- câmera ativa
|
||||
- Se a integração for mal feita, o renderer pode voltar a misturar:
|
||||
- política de câmera
|
||||
- atualização de cache
|
||||
- composição final
|
||||
|
||||
## Opcoes
|
||||
|
||||
### Opcao 1 - Integrar tudo diretamente dentro de `Gfx`
|
||||
|
||||
**Como seria:**
|
||||
`Gfx` passa a possuir a scene ativa, o cache, o resolver e a câmera; `render_all()` atualiza resolver/cache e já compõe tudo.
|
||||
|
||||
**Vantagens:**
|
||||
- caminho curto de integração;
|
||||
- menos objetos atravessando o runtime;
|
||||
- fácil de chamar a partir do tick.
|
||||
|
||||
**Desvantagens:**
|
||||
- empurra para `Gfx` responsabilidade demais;
|
||||
- mistura composição com estado de cena/câmera;
|
||||
- reduz clareza para testes e evolução futura.
|
||||
|
||||
### Opcao 2 - Integrar no runtime com um controlador explícito de scene viewport
|
||||
|
||||
**Como seria:**
|
||||
O runtime ou um pequeno controlador operacional passa a possuir:
|
||||
- scene ativa
|
||||
- cache
|
||||
- resolver
|
||||
- câmera
|
||||
|
||||
Esse controlador atualiza o cache quando a câmera muda e entrega ao `Gfx` apenas o que ele precisa para compor.
|
||||
|
||||
**Vantagens:**
|
||||
- separa melhor estado operacional de composição;
|
||||
- mantém `Gfx` mais focado em render;
|
||||
- preserva a ideia de que o resolver é dono da política de movimento/rematerialização.
|
||||
- permite manter um caminho explícito de `render_all()` sem scene carregada.
|
||||
|
||||
**Desvantagens:**
|
||||
- adiciona mais um objeto operacional no runtime;
|
||||
- exige definir uma superfície clara entre runtime e renderer.
|
||||
|
||||
### Opcao 3 - Fazer uma integração mínima temporária em `render_all()` e postergar a arquitetura do dono da câmera
|
||||
|
||||
**Como seria:**
|
||||
Criar um caminho temporário para que `render_all()` receba ou consulte estado suficiente para chamar `render_scene_from_cache()`, mas sem ainda fechar onde mora a câmera a longo prazo.
|
||||
|
||||
**Vantagens:**
|
||||
- acelera a ligação do caminho novo ao frame loop;
|
||||
- destrava testes end-to-end rapidamente.
|
||||
|
||||
**Desvantagens:**
|
||||
- alto risco de solução transitória virar definitiva;
|
||||
- deixa ambiguidade operacional exatamente no ponto mais sensível da integração.
|
||||
|
||||
## Sugestao / Recomendacao
|
||||
|
||||
Seguir com a **Opcao 2**.
|
||||
|
||||
Ou seja:
|
||||
|
||||
- `FrameComposer` passa a ser o orquestrador de frame/scene;
|
||||
- `FrameComposer` deve morar em `hardware/drivers`;
|
||||
- `Hardware` passa a agregar `FrameComposer` ao lado de `Gfx`;
|
||||
- `Gfx` permanece como backend de composição e blit;
|
||||
- a política do frame não deve ficar presa ao hardware atual;
|
||||
- isso preserva espaço para:
|
||||
- fast paths com diretivas/capacidades de GPU quando existirem;
|
||||
- uma futura implementação mais próxima de PPU / bare metal;
|
||||
- `render_all()` deve continuar sendo o entrypoint normal de composição;
|
||||
- `render_all()` deve continuar funcionando mesmo sem scene ativa;
|
||||
- mas ele não deve virar dono da câmera nem do ciclo de atualização do cache;
|
||||
- precisamos de um orquestrador operacional no runtime, ou imediatamente adjacente a ele, que:
|
||||
- mantenha a scene ativa opcional;
|
||||
- mantenha a câmera / viewport mínima;
|
||||
- mantenha o controlador de sprites do frame;
|
||||
- atualize o `SceneViewportResolver` quando houver scene;
|
||||
- aplique refreshes ao `SceneViewportCache` quando houver scene;
|
||||
- e entregue ao `Gfx` o estado pronto para compor.
|
||||
|
||||
Mais explicitamente:
|
||||
|
||||
- `FrameComposer` passa a ser dono de:
|
||||
- scene ativa;
|
||||
- câmera / viewport;
|
||||
- `SceneViewportCache`;
|
||||
- `SceneViewportResolver`;
|
||||
- sprites emitidos no frame;
|
||||
- o state de scene/sprite que hoje esteja em `Gfx` deve migrar para `FrameComposer`;
|
||||
- `Gfx` deve ficar focado em:
|
||||
- composição;
|
||||
- blit;
|
||||
- raster;
|
||||
- execução visual do frame preparado.
|
||||
|
||||
Para V1, o contrato mínimo de câmera pode continuar pequeno:
|
||||
|
||||
- `camera_x_px: i32`
|
||||
- `camera_y_px: i32`
|
||||
- representando o canto superior esquerdo da viewport no mundo
|
||||
|
||||
Sem follow/smoothing/shake/cut nesta etapa.
|
||||
|
||||
O comportamento mínimo recomendado fica:
|
||||
|
||||
- sem scene ativa:
|
||||
- `FrameComposer` continua válido;
|
||||
- `render_all()` compõe apenas o que já existe fora do pipeline de world (`sprites`, `fades`, e futuramente `HUD` quando aplicável);
|
||||
- não existe `clear` implícito;
|
||||
- limpar o `back` continua sendo responsabilidade explícita do chamador / dev;
|
||||
- com scene ativa:
|
||||
- `FrameComposer` atualiza resolver/cache;
|
||||
- `render_all()` compõe o world a partir do cache e preserva a ordem já aceita.
|
||||
|
||||
Esta direção é provisoriamente aceita mesmo sem a figura final completa, justamente para permitir que a integração avance e revele os pontos onde a separação runtime/backend ainda precise de ajuste.
|
||||
|
||||
Para sprites, a direção provisória recomendada fica:
|
||||
|
||||
- cada `Sprite` deve carregar:
|
||||
- `layer`
|
||||
- `priority`
|
||||
- `Sprite.active` deve ser removido;
|
||||
- `layer` define em qual faixa de composição o sprite entra;
|
||||
- `priority` define a ordenação entre sprites daquela mesma faixa;
|
||||
- a composição observável passa a ser por camada:
|
||||
- `(sprites -> scene) layer_0`
|
||||
- `(sprites -> scene) layer_1`
|
||||
- `(sprites -> scene) layer_2`
|
||||
- `(sprites -> scene) layer_3`
|
||||
|
||||
Isso substitui o modelo atual em que um único `priority` tenta representar ao mesmo tempo posição macro na composição e ordenação fina.
|
||||
|
||||
O modelo operacional recomendado para sprites passa a ser:
|
||||
|
||||
- capacidade máxima interna de `512` sprites por frame;
|
||||
- contador zerado a cada frame;
|
||||
- o dev não informa mais índice de sprite;
|
||||
- cada emissão ocupa o próximo slot interno disponível;
|
||||
- o registro já coloca o sprite no bucket correto da layer;
|
||||
- a composição consome apenas os sprites emitidos naquele frame.
|
||||
|
||||
## Perguntas em Aberto
|
||||
|
||||
- Fechado provisoriamente:
|
||||
- `FrameComposer` em `hardware/drivers`;
|
||||
- `Hardware` agrega `FrameComposer` e `Gfx`;
|
||||
- `Gfx` atua como backend operacional de composição.
|
||||
- O contrato mínimo do `FrameComposer` precisa ser fechado normativamente.
|
||||
- Fechado:
|
||||
- o subsistema interno de sprites se chama `SpriteController`.
|
||||
- `Sprite.layer` deve ser um enum fechado (`Layer0..Layer3`) ou um tipo mais genérico?
|
||||
- fechado provisoriamente:
|
||||
- manter numérico;
|
||||
- usar o mesmo tipo/referência de layer do `SceneBank`.
|
||||
- A composição por camada deve ser:
|
||||
- `sprites -> scene` dentro de cada layer, como direção inicial,
|
||||
- ou `scene -> sprites` para alguma camada específica?
|
||||
- A ordenação entre sprites de uma mesma layer será:
|
||||
- fechado:
|
||||
- `priority` menor blita primeiro;
|
||||
- em empate, FIFO por ordem de registro.
|
||||
- Overflow de sprite no frame:
|
||||
- fechado: excedentes são ignorados;
|
||||
- deve existir espaço para log/telemetria;
|
||||
- futuramente isso pode virar sinal negativo para certificação.
|
||||
- `emit_sprite(...)` precisa retornar algo, ou ter reset separado além de `begin_frame()`?
|
||||
- fechado por enquanto:
|
||||
- não;
|
||||
- usar apenas log do sistema para overflow/eventos operacionais;
|
||||
- não introduzir reset extra além do fluxo normal do frame.
|
||||
- `render_all()` deve:
|
||||
- continuar sem parâmetros e consultar estado já preparado,
|
||||
- ou ganhar uma nova superfície interna para receber o scene state preparado?
|
||||
- direção aceita:
|
||||
- `FrameComposer` chama o entrypoint de composição do backend visual;
|
||||
- `Gfx.render_all()` deve morrer;
|
||||
- o serviço deve migrar para `FrameComposer.renderFrame()`.
|
||||
|
||||
## Contrato Minimo Proposto
|
||||
|
||||
Direção proposta para V1 do `FrameComposer`:
|
||||
|
||||
- `bind_scene(...)`
|
||||
- recebe um `scene bank id`;
|
||||
- `FrameComposer` deve possuir acesso a `SceneBankPoolAccess`;
|
||||
- resolve a scene ativa através do pool;
|
||||
- o acesso ao bank deve ser sempre por ponteiro / referência compartilhada, nunca por cópia;
|
||||
- ao bindar, o compositor guarda:
|
||||
- `scene_bank_id`;
|
||||
- `Arc<SceneBank>` já resolvido;
|
||||
- consegue verificar se a scene está carregada;
|
||||
- inicializa ou reinicializa cache/resolver conforme necessário.
|
||||
|
||||
- `unbind_scene()`
|
||||
- remove a scene ativa;
|
||||
- invalida o pipeline de world;
|
||||
- descarta o cache associado à scene bindada;
|
||||
- mantém o compositor funcional para `sprites + fades`.
|
||||
|
||||
- `set_camera(x, y)`
|
||||
- atualiza a posição da câmera em pixel space;
|
||||
- `x, y` representam o canto superior esquerdo da viewport no mundo.
|
||||
|
||||
- `begin_frame()`
|
||||
- zera o contador de sprites emitidos;
|
||||
- limpa buckets internos de sprite;
|
||||
- prepara o estado transitório do frame.
|
||||
|
||||
- `emit_sprite(...)`
|
||||
- registra um sprite no próximo slot interno disponível;
|
||||
- associa o sprite à sua `layer`;
|
||||
- insere no bucket correspondente;
|
||||
- overflow é ignorado com espaço para log/telemetria.
|
||||
|
||||
- `compose_frame()`
|
||||
- se houver scene ativa:
|
||||
- atualiza `SceneViewportResolver`;
|
||||
- aplica `CacheRefreshRequest`s ao `SceneViewportCache`;
|
||||
- aciona o caminho de composição world + sprites;
|
||||
- se não houver scene ativa:
|
||||
- aciona o caminho `sprites + fades`;
|
||||
- delega a composição efetiva ao `Gfx`.
|
||||
|
||||
### Observacoes
|
||||
|
||||
- `end_frame()` não parece obrigatório na V1.
|
||||
- `begin_frame()` + `compose_frame()` já cobrem o ciclo mínimo.
|
||||
- `FrameComposer` decide e prepara;
|
||||
`Gfx` executa a composição.
|
||||
- o binding de scene deve ser por `scene bank id`, não por ownership direto de `SceneBank`.
|
||||
- o `SceneViewportCache` vive dentro do `FrameComposer` enquanto a scene estiver bindada.
|
||||
- troca do conteúdo do slot/bank exige novo `bind_scene(...)`;
|
||||
o `FrameComposer` não deve ficar fazendo polling constante do pool para revalidar a scene ativa.
|
||||
- o fluxo operacional aceito é:
|
||||
- `FrameComposer.compose_frame()`
|
||||
- chama o serviço `FrameComposer.renderFrame()`.
|
||||
- `FrameComposer` deve ser capaz de renderizar algo 100% do tempo:
|
||||
- cache/resolver ficam `None` sem bind;
|
||||
- deve existir uma forma explícita de saber se a scene está disponível para render.
|
||||
- `bind_scene(...)` substitui completamente a scene anterior.
|
||||
|
||||
## Sugestao / Recomendacao Atualizada
|
||||
|
||||
Aceitar o contrato mínimo acima como base de fechamento da agenda, a menos que apareça alguma necessidade concreta de:
|
||||
|
||||
- separar `compose_frame()` em múltiplas fases públicas;
|
||||
- expor refresh manual de cache para o chamador;
|
||||
- ou introduzir um `end_frame()` com semântica real além do reset que já ocorre em `begin_frame()`.
|
||||
- manter o binding de scene como:
|
||||
- `scene_bank_id + Arc<SceneBank>`;
|
||||
- com rebind explícito quando o slot mudar.
|
||||
- Quem é responsável por aplicar `CacheRefreshRequest` ao `SceneViewportCache`:
|
||||
- fechado: sempre o `FrameComposer`.
|
||||
- Qual é o contrato explícito de “nenhuma scene carregada”:
|
||||
- fechado: `sprites + fades`, sem `clear` implícito.
|
||||
- Como a cena ativa é selecionada e trocada no ciclo real:
|
||||
- fechado: `bind_scene(scene_bank_id)` com resolução através de `SceneBankPoolAccess`.
|
||||
- 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.
|
||||
|
||||
## Criterio para Encerrar
|
||||
|
||||
Esta agenda pode ser encerrada quando estiver explícito:
|
||||
|
||||
- quem é dono do estado mínimo de câmera;
|
||||
- quem é dono da scene/cache/resolver ativos;
|
||||
- como funciona o bind/unbind da scene ativa;
|
||||
- quando o cache é atualizado;
|
||||
- como `render_all()` passa a compor o world path aceito;
|
||||
- e qual é a superfície mínima de integração para implementação sem reabrir a arquitetura base.
|
||||
|
||||
## Resolucao
|
||||
|
||||
Esta agenda fica aceita com a seguinte direcao:
|
||||
|
||||
- `Gfx.render_all()` deve ser aposentado;
|
||||
- o fluxo operacional deve convergir para `FrameComposer.render_frame()`;
|
||||
- `FrameComposer` vive em `hardware/drivers`, ao lado de `Gfx`, e passa a ser dono do estado operacional do frame;
|
||||
- `FrameComposer` deve manter:
|
||||
- scene ativa opcional;
|
||||
- camera/viewport;
|
||||
- `SceneViewportCache`;
|
||||
- `SceneViewportResolver`;
|
||||
- `SpriteController`;
|
||||
- scene ativa e acessada por `scene_bank_id + Arc<SceneBank>` via `SceneBankPoolAccess`, sem copias;
|
||||
- troca de slot exige novo `bind_scene(...)`;
|
||||
- sem scene ativa, o frame continua valido com `sprites + fades`, sem `clear` implicito;
|
||||
- sprites passam a ser emitidos por frame, sem `Sprite.active`, com capacidade maxima de `512`, overflow ignorado e ordenacao por `layer`, `priority`, e FIFO em empate;
|
||||
- HUD fica fora desta primeira integracao.
|
||||
@ -0,0 +1,186 @@
|
||||
---
|
||||
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`.
|
||||
@ -0,0 +1,117 @@
|
||||
---
|
||||
id: PLN-0017
|
||||
ticket: render-all-scene-cache-and-camera-integration
|
||||
title: Plan - FrameComposer Core and Hardware Ownership
|
||||
status: accepted
|
||||
created: 2026-04-14
|
||||
completed:
|
||||
tags: [gfx, runtime, render, hardware, frame-composer]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Introduce `FrameComposer` as a first-class hardware-side subsystem and move canonical frame orchestration ownership out of `Gfx`.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0014` locks `FrameComposer` as the canonical frame orchestration service. The first implementation step is to create the owning type, place it in `hardware/drivers`, and make `Hardware` aggregate it next to `Gfx` without yet completing the full render-path migration.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
- Create the `FrameComposer` type in `crates/console/prometeu-drivers`.
|
||||
- Define the minimal owned state shape:
|
||||
- active scene binding state;
|
||||
- camera / viewport state;
|
||||
- optional cache;
|
||||
- optional resolver;
|
||||
- owned `SpriteController`.
|
||||
- Aggregate `FrameComposer` inside `Hardware`.
|
||||
- Expose the minimum driver-facing surface required for subsequent plans.
|
||||
|
||||
### Excluded
|
||||
- full sprite-model migration
|
||||
- full scene binding implementation
|
||||
- cache refresh application
|
||||
- render-path retirement of `Gfx.render_all()`
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Introduce the `FrameComposer` module and owned state
|
||||
|
||||
**What:**
|
||||
Create `FrameComposer` as a concrete driver-side subsystem.
|
||||
|
||||
**How:**
|
||||
- Add a new module such as `crates/console/prometeu-drivers/src/frame_composer.rs`.
|
||||
- Define a `FrameComposer` struct with explicit placeholders for:
|
||||
- `active_scene_id`
|
||||
- `active_scene`
|
||||
- `scene_status`
|
||||
- `camera_x_px`
|
||||
- `camera_y_px`
|
||||
- `SceneViewportCache`
|
||||
- `SceneViewportResolver`
|
||||
- `SpriteController`
|
||||
- Keep scene/cache/resolver fields optional where no-scene operation is required.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
- `crates/console/prometeu-drivers/src/lib.rs`
|
||||
|
||||
### Step 2 - Aggregate `FrameComposer` in `Hardware`
|
||||
|
||||
**What:**
|
||||
Make `Hardware` own `FrameComposer` alongside `Gfx`.
|
||||
|
||||
**How:**
|
||||
- Extend `Hardware` with a `frame_composer` field.
|
||||
- Wire construction so `FrameComposer` receives the shared bank access it needs for later plans.
|
||||
- Keep ownership boundaries explicit: `FrameComposer` prepares frame state, `Gfx` remains backend.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/hardware.rs`
|
||||
- `crates/console/prometeu-drivers/src/memory_banks.rs`
|
||||
|
||||
### Step 3 - Define the minimum public driver-facing surface
|
||||
|
||||
**What:**
|
||||
Give the driver layer a stable initial surface for `FrameComposer`.
|
||||
|
||||
**How:**
|
||||
- Expose minimal constructor and accessor paths.
|
||||
- Do not yet overdesign HAL-facing traits.
|
||||
- Ensure the code compiles with no implicit dependence on `Gfx.render_all()` ownership for frame state.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
- `crates/console/prometeu-drivers/src/hardware.rs`
|
||||
- `crates/console/prometeu-drivers/src/lib.rs`
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
- `FrameComposer` can be constructed without a bound scene.
|
||||
- `Hardware` successfully constructs with both `gfx` and `frame_composer`.
|
||||
|
||||
### Integration Tests
|
||||
- Shared bank access needed by `FrameComposer` is available through hardware construction.
|
||||
|
||||
### Manual Verification
|
||||
- Inspect the resulting type ownership and confirm scene/sprite state is no longer being newly introduced into `Gfx`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `FrameComposer` exists as a dedicated driver-side subsystem.
|
||||
- [ ] `Hardware` aggregates `FrameComposer` next to `Gfx`.
|
||||
- [ ] `FrameComposer` has explicit owned placeholders for scene/camera/cache/resolver/sprites.
|
||||
- [ ] The build remains green with the new ownership structure in place.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Source decision: `DEC-0014`
|
||||
|
||||
## Risks
|
||||
|
||||
- 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.
|
||||
@ -0,0 +1,127 @@
|
||||
---
|
||||
id: PLN-0018
|
||||
ticket: render-all-scene-cache-and-camera-integration
|
||||
title: Plan - SpriteController and Frame Emission Model
|
||||
status: accepted
|
||||
created: 2026-04-14
|
||||
completed:
|
||||
tags: [gfx, runtime, render, sprites, frame-composer]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Replace the slot-first sprite model with a `FrameComposer`-owned `SpriteController` that emits sprites per frame instead of relying on `Sprite.active` and caller-provided indices.
|
||||
|
||||
## Background
|
||||
|
||||
`DEC-0014` removes `Sprite.active` from the canonical operational model and locks sprite submission to a frame-emission model owned by `SpriteController`.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
- Introduce `SpriteController`.
|
||||
- Remove the operational dependence on `Sprite.active`.
|
||||
- Remove caller-owned sprite indices from the canonical submission path.
|
||||
- Add layer + priority ordering with FIFO tie-breaking.
|
||||
- Preserve the capacity cap of `512` sprites per frame.
|
||||
|
||||
### Excluded
|
||||
- HUD integration
|
||||
- scene binding
|
||||
- cache refresh logic
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Redefine the sprite operational model
|
||||
|
||||
**What:**
|
||||
Move canonical sprite submission semantics from slot-first to frame-emission.
|
||||
|
||||
**How:**
|
||||
- Update `Sprite` and adjacent APIs so the canonical path no longer depends on `active`.
|
||||
- Keep layer numeric and aligned with the scene layer reference type.
|
||||
- Preserve `priority` as the within-layer ordering field.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-hal/src/sprite.rs`
|
||||
- any adjacent driver-side sprite helpers
|
||||
|
||||
### Step 2 - Implement `SpriteController`
|
||||
|
||||
**What:**
|
||||
Create the owned sprite subsystem under `FrameComposer`.
|
||||
|
||||
**How:**
|
||||
- Add a `SpriteController` type with:
|
||||
- storage capacity `512`
|
||||
- frame counter
|
||||
- per-layer buckets
|
||||
- stable FIFO semantics for equal priority
|
||||
- Add `begin_frame()` behavior that clears counters and buckets.
|
||||
- Add `emit_sprite(...)` behavior that appends to the next internal slot.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
- optional dedicated `sprite_controller.rs`
|
||||
|
||||
### Step 3 - Handle overflow and logging semantics
|
||||
|
||||
**What:**
|
||||
Implement overflow behavior without turning it into a hard runtime failure.
|
||||
|
||||
**How:**
|
||||
- Ignore sprites emitted after capacity is reached.
|
||||
- Leave explicit room for system logging / telemetry.
|
||||
- Do not add a special reset API beyond the normal frame lifecycle.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
- related telemetry/log hooks if needed
|
||||
|
||||
### Step 4 - Remove stale slot-first sprite entrypoints
|
||||
|
||||
**What:**
|
||||
Retire the old “set sprite by explicit index” path from the canonical model.
|
||||
|
||||
**How:**
|
||||
- Identify the current caller-facing `Gfx` sprite mutation surface.
|
||||
- Migrate it toward `FrameComposer`-owned submission.
|
||||
- Keep transitional shims only if required to preserve buildability for the next plan.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/gfx.rs`
|
||||
- `crates/console/prometeu-hal/src/gfx_bridge.rs`
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
- `begin_frame()` resets sprite count and buckets.
|
||||
- `emit_sprite(...)` appends without caller-provided index.
|
||||
- lower `priority` renders first within a layer.
|
||||
- equal `priority` resolves FIFO by registration order.
|
||||
- overflow drops excess sprites without panicking.
|
||||
|
||||
### Integration Tests
|
||||
- `FrameComposer` can emit sprites and provide ordered sprite state for rendering.
|
||||
|
||||
### Manual Verification
|
||||
- Confirm no canonical submission path requires `Sprite.active` or explicit slot index anymore.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `SpriteController` exists under `FrameComposer`.
|
||||
- [ ] `Sprite.active` is no longer required by the canonical frame path.
|
||||
- [ ] Caller-provided sprite indices are retired from the canonical submission path.
|
||||
- [ ] Layer/priority/FIFO ordering is implemented and tested.
|
||||
- [ ] Overflow is ignored with space left for logging.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Depends on `PLN-0017`
|
||||
- Source decision: `DEC-0014`
|
||||
|
||||
## Risks
|
||||
|
||||
- Keeping compatibility shims too long can leave the codebase in a dual sprite model.
|
||||
- Removing index-based APIs too early may break callsites before `FrameComposer` integration is ready.
|
||||
@ -0,0 +1,127 @@
|
||||
---
|
||||
id: PLN-0019
|
||||
ticket: render-all-scene-cache-and-camera-integration
|
||||
title: Plan - Scene Binding, Camera, and Scene Status
|
||||
status: accepted
|
||||
created: 2026-04-14
|
||||
completed:
|
||||
tags: [gfx, runtime, render, scene, camera, frame-composer]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Implement the `FrameComposer` scene-binding contract, minimal camera state, and explicit scene-availability status without yet completing the cache-refresh render path.
|
||||
|
||||
## 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`.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
- scene bind/unbind contract
|
||||
- active scene identity and shared reference storage
|
||||
- scene availability status
|
||||
- minimal camera state (`i32`, top-left viewport)
|
||||
|
||||
### Excluded
|
||||
- applying cache refreshes
|
||||
- full render-path migration
|
||||
- HUD behavior
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Add scene binding state to `FrameComposer`
|
||||
|
||||
**What:**
|
||||
Implement the canonical bind/unbind surface.
|
||||
|
||||
**How:**
|
||||
- Add `bind_scene(scene_bank_id)` and `unbind_scene()`.
|
||||
- Resolve scenes from `SceneBankPoolAccess`.
|
||||
- Store both:
|
||||
- `scene_bank_id`
|
||||
- `Arc<SceneBank>`
|
||||
- Replace prior scene binding completely on a new bind.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
- `crates/console/prometeu-drivers/src/memory_banks.rs`
|
||||
|
||||
### Step 2 - Add explicit scene status
|
||||
|
||||
**What:**
|
||||
Expose scene availability through status, not just implicit option checks.
|
||||
|
||||
**How:**
|
||||
- Define a scene status enum or equivalent status object.
|
||||
- Distinguish at least:
|
||||
- no scene bound
|
||||
- bound and available
|
||||
- bound but not renderable if such intermediate state is needed
|
||||
- Ensure no-scene rendering remains valid.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
- optional HAL-facing status surface if needed later
|
||||
|
||||
### Step 3 - Add camera contract
|
||||
|
||||
**What:**
|
||||
Implement the V1 camera ownership inside `FrameComposer`.
|
||||
|
||||
**How:**
|
||||
- Add `set_camera(x, y)`.
|
||||
- Store camera coordinates as `i32`.
|
||||
- Treat them as top-left viewport coordinates in world space.
|
||||
- Keep all advanced camera behavior out of scope.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
|
||||
### Step 4 - Tie cache/resolver lifetime to scene binding
|
||||
|
||||
**What:**
|
||||
Align cache/resolver lifetime with the active scene contract.
|
||||
|
||||
**How:**
|
||||
- Cache and resolver remain `None` / absent when no scene is bound.
|
||||
- On bind:
|
||||
- create or reinitialize cache/resolver.
|
||||
- On unbind:
|
||||
- discard cache/resolver and invalidate the world path.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-drivers/src/frame_composer.rs`
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
- bind stores `scene_bank_id + Arc<SceneBank>`.
|
||||
- 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.
|
||||
|
||||
### Integration Tests
|
||||
- `FrameComposer` can resolve a scene from the pool and survive no-scene operation.
|
||||
|
||||
### Manual Verification
|
||||
- Confirm scene access remains pointer-based and no scene copies are introduced.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `FrameComposer` binds scenes by bank id through `SceneBankPoolAccess`.
|
||||
- [ ] Active binding stores both scene id and shared scene reference.
|
||||
- [ ] Scene status is explicit.
|
||||
- [ ] Camera contract is implemented as `i32` top-left viewport coordinates.
|
||||
- [ ] Cache/resolver lifetime follows scene bind/unbind.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Depends on `PLN-0017`
|
||||
- Source decision: `DEC-0014`
|
||||
|
||||
## Risks
|
||||
|
||||
- Weak scene-status semantics can make no-scene behavior ambiguous in later render integration.
|
||||
- If cache/resolver lifetime is not tied cleanly to binding, stale world state can leak across scene transitions.
|
||||
@ -0,0 +1,124 @@
|
||||
---
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
**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`
|
||||
|
||||
**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`
|
||||
|
||||
**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`.
|
||||
|
||||
### Integration Tests
|
||||
- scene bind + camera set + sprite emission + `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.
|
||||
|
||||
## 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.
|
||||
@ -0,0 +1,118 @@
|
||||
---
|
||||
id: PLN-0021
|
||||
ticket: render-all-scene-cache-and-camera-integration
|
||||
title: Plan - Service Retirement, Callsite Migration, and Regression Coverage
|
||||
status: accepted
|
||||
created: 2026-04-14
|
||||
completed:
|
||||
tags: [gfx, runtime, render, migration, regression]
|
||||
---
|
||||
|
||||
## Objective
|
||||
|
||||
Retire `Gfx.render_all()` from the canonical flow, migrate callsites to `FrameComposer.render_frame()`, and add the regression coverage needed to lock the new service model.
|
||||
|
||||
## 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.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
- retire `Gfx.render_all()` from the canonical path
|
||||
- migrate frame-loop callsites
|
||||
- align bridge surfaces as needed
|
||||
- add regression coverage for the final service model
|
||||
|
||||
### Excluded
|
||||
- HUD integration
|
||||
- future certification behavior for sprite overflow
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1 - Migrate frame-loop callsites
|
||||
|
||||
**What:**
|
||||
Switch runtime frame execution from `Gfx.render_all()` to `FrameComposer.render_frame()`.
|
||||
|
||||
**How:**
|
||||
- Identify all canonical callsites that currently trigger `Gfx.render_all()`.
|
||||
- Update them to go through `FrameComposer`.
|
||||
- Preserve present/swap behavior after the render call.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs`
|
||||
- any additional runtime frame-loop callsites
|
||||
|
||||
### Step 2 - Retire `Gfx.render_all()` from the canonical service surface
|
||||
|
||||
**What:**
|
||||
Remove the old frame service as the operational entry.
|
||||
|
||||
**How:**
|
||||
- Remove or deprecate `render_all()` from `Gfx` and `GfxBridge` as the canonical render entry.
|
||||
- Keep only backend-oriented helpers that `FrameComposer` calls.
|
||||
- Ensure the naming and public path converge to Rust-style `render_frame()`.
|
||||
|
||||
**File(s):**
|
||||
- `crates/console/prometeu-hal/src/gfx_bridge.rs`
|
||||
- `crates/console/prometeu-drivers/src/gfx.rs`
|
||||
|
||||
### Step 3 - Add end-to-end regression coverage
|
||||
|
||||
**What:**
|
||||
Protect the new service model against fallback to the old renderer path.
|
||||
|
||||
**How:**
|
||||
- Add tests that prove:
|
||||
- frame-loop code calls `FrameComposer.render_frame()`
|
||||
- no-scene frames remain valid
|
||||
- active-scene frames render through cache-backed composition
|
||||
- sprite emission and ordering survive the full path
|
||||
- Add assertions or test failures for accidental continued reliance on `Gfx.render_all()`.
|
||||
|
||||
**File(s):**
|
||||
- runtime tests
|
||||
- driver tests
|
||||
- bridge tests where needed
|
||||
|
||||
### Step 4 - Validate full repository behavior
|
||||
|
||||
**What:**
|
||||
Confirm the migration did not break unrelated systems.
|
||||
|
||||
**How:**
|
||||
- Run the repository validation command required by current practice.
|
||||
- Keep regression evidence attached to the plan execution.
|
||||
|
||||
**File(s):**
|
||||
- repository-wide CI / validation entrypoints
|
||||
|
||||
## Test Requirements
|
||||
|
||||
### Unit Tests
|
||||
- `Gfx` no longer exposes `render_all()` as the canonical operational frame path.
|
||||
|
||||
### Integration Tests
|
||||
- runtime tick path renders through `FrameComposer.render_frame()`.
|
||||
- no-scene and active-scene frame modes both remain valid.
|
||||
|
||||
### Manual Verification
|
||||
- Run the repository CI path and confirm the final integrated service model is green.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Frame-loop callsites use `FrameComposer.render_frame()`.
|
||||
- [ ] `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.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Depends on `PLN-0017`, `PLN-0018`, `PLN-0019`, and `PLN-0020`
|
||||
- Source decision: `DEC-0014`
|
||||
|
||||
## Risks
|
||||
|
||||
- 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.
|
||||
Loading…
x
Reference in New Issue
Block a user