8.6 KiB
8.6 KiB
| id | ticket | title | status | created | updated | resolved | decision | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| AGD-0028 | deferred-overlay-and-primitive-composition | Deferred Overlay and Primitive Composition over FrameComposer | accepted | 2026-04-18 | 2026-04-18 | 2026-04-18 | DEC-0016 |
|
Contexto
FrameComposer.render_frame() hoje recompõe o back no fim da logical frame. Quando há scene bound, o caminho render_scene_from_cache(...) limpa o buffer e desenha scene + sprites, o que apaga qualquer primitive ou draw_text(...) emitido antes via gfx.
Isso expôs um conflito de modelo:
composer.*já é o caminho canônico de orquestração de frame;gfx.draw_text(...)e demais primitives ainda escrevem diretamente noback;- o runtime só chama
render_frame()no final do frame, então a escrita imediata embackdeixou de ser semanticamente estável. - As primitives de
gfxnão são o mecanismo desejado para composição de jogos com scene/tile/sprite; elas existem principalmente como debug, instrumentação visual e artefatos rápidos.
Conteúdo relevante migrado de AGD-0010:
- a arquitetura aceita continua sendo de framebuffer destrutivo em memória, não scene graph ou renderer tipo GPU;
- otimizações em primitives devem preservar a semântica observável, mesmo quando ganharem fast paths internos;
- existe preocupação explícita com custo por classe de primitive e com orçamento de memória no alvo handheld;
- caminhos de spans/linhas/clears são desejáveis como aceleração interna, mas sem reabrir o modelo operacional do pipeline do jogo.
Problema
Precisamos decidir qual é o modelo canônico para primitives e texto no pipeline pós-FrameComposer.
Sem isso:
- texto e primitives continuam com comportamento dependente da ordem interna do renderer;
- o stress test e qualquer cartridge que combine
composer.*comgfx.*terão resultado inconsistente; - fica indefinido se primitives pertencem ao mundo, ao HUD, ou a um overlay final.
Pontos Criticos
draw_text(...)e primitives screen-space não podem depender de escrita imediata emback.- Para esta thread, primitives de
gfxdevem permanecer agnósticas ao pipeline canônico de render do jogo e não devem ser mescladas semanticamente com tiles/sprites. - A ordem de composição precisa ser explícita e estável:
scene -> sprites -> HUD -> primitives/debug overlay, ou outra ordem formal equivalente. - Precisamos decidir se o contrato público de
gfx.*muda semanticamente sem mudar ABI, ou se parte dessa superfície migra paracomposer.*. - A solução deve preservar o caminho sem scene bound.
- A implementação deve evitar contaminar a infraestrutura de
gfxresponsável por scene, sprites e HUD com estado misto de overlay/debug; se necessário, o overlay deve ter fila/fase própria. - melhorias internas de primitive path devem continuar permitidas, desde que não mudem a semântica de overlay final e não exijam buffers extras incompatíveis com o orçamento de memória aceito.
Opcoes
Opcao 1 - Manter escrita direta em back
- Abordagem: manter
gfx.draw_text(...)e primitives rasterizando imediatamente. - Pro: zero mudança estrutural agora.
- Contra: o modelo continua quebrado sempre que
render_frame()recompõe o buffer depois. - Tradeoff: só funciona de forma confiável fora do caminho canônico do
FrameComposer.
Opcao 2 - Fila única de draw commands pós-scene/pós-sprite
- Abordagem: transformar texto e primitives em comandos diferidos, drenados depois de
scene + sprites. - Pro: resolve o problema imediato de overlay/HUD e estabiliza o stress test.
- Contra: mistura HUD e primitives/debug sob o mesmo conceito, reduzindo clareza contratual mesmo quando a ordem prática for a mesma.
- Tradeoff: simples para V1, mas semanticamente mais fraco do que separar overlay de jogo e overlay de debug.
Opcao 3 - Separar HUD diferido de primitives/debug overlay final
- Abordagem: tratar
gfx.draw_text(...)e demais primitives degfxcomo overlay/debug final, separado da composição canônica de jogo (scene + sprites + HUD). - Pro: casa com a intenção declarada para
gfx.*: debug, artefato rápido e instrumentação visual acima do frame do jogo. - Contra: exige modelar explicitamente uma fase extra no pipeline.
- Tradeoff: aumenta a clareza contratual e evita mesclar primitives com o domínio de jogo.
Opcao 4 - Manter HUD e primitives no mesmo estágio final, mas com categorias separadas
- Abordagem: drenar HUD e primitives ambos no fim do frame, porém com filas/categorias distintas e ordem formal
HUD -> primitives. - Pro: preserva implementação próxima entre caminhos similares, mantendo contrato separado.
- Contra: é mais custoso que a opção 3 sem entregar muito valor adicional imediato.
- Tradeoff: bom se já houver expectativa de HUD canônico separado no curtíssimo prazo.
Sugestao / Recomendacao
Seguir com a Opcao 3.
Minha recomendação é:
- retirar a escrita direta em
backcomo contrato operacional paragfx.draw_text(...)e demais primitives degfx; - introduzir uma fila diferida canônica de primitives/debug overlay drenada no fim do frame;
- tratar
gfx.*primitive/text como superfície agnóstica ao pipeline de jogo e explicitamente acima da composição canônica; - não misturar semanticamente primitives com scene/tile/sprite/HUD.
- evitar compartilhar indevidamente o mesmo mecanismo operacional de composição entre overlay/debug e os caminhos de scene/sprite/HUD, mesmo quando o backend de rasterização reutilizado for o mesmo.
Ordem recomendada para o frame canônico:
- limpar/compor scene;
- compor sprites;
- compor HUD canônico, se existir;
- aplicar
scene_fade; - aplicar
hud_fade; - drenar primitives/debug overlay de
gfx.*.
Perguntas em Aberto
draw_text(...)e as demais primitives degfxentram todas na mesma família de overlay final já na V1, ou começamos só comdraw_text(...)?render_no_scene_frame()deve usar a mesma fila diferida para manter semântica idêntica com e sem scene?- HUD canônico precisa existir explicitamente nesta mesma thread, ou pode continuar implícito/externo enquanto as primitives já migram para overlay final?
- quais fast paths internos de primitives continuam desejáveis nessa nova fase, por exemplo spans horizontais/verticais, fills e clears, sem misturar isso com a composição do jogo?
- o overlay/debug final precisa de dirtying próprio por classe de primitive ou isso pode ficar fora da primeira migração?
Criterio para Encerrar
Esta agenda pode ser encerrada quando tivermos uma resposta explícita para:
- o destino semântico de
draw_text(...); - se haverá uma fila própria para primitives/debug overlay e qual a relação dela com HUD;
- a ordem canônica de composição do frame;
- o escopo exato da primeira migração implementável sem reabrir o restante do pipeline.
Resolucao Parcial
Direção já aceita nesta agenda:
- primitives e
draw_text(...)degfx.*devem ser tratadas como overlay/debug final; - esse overlay deve ser drenado depois de
hud_fade; - scene, sprites e HUD canônico não devem ser semanticamente misturados com o overlay/debug;
- a implementação deve preservar separação operacional suficiente para que o
gfxusado pelo pipeline do jogo não passe a depender do estado transitório de primitives/debug; - otimizações de primitive path discutidas na
AGD-0010continuam válidas, mas passam a operar dentro do domínio de overlay/debug final, não como parte da composição canônica de scene/sprite/HUD.
Resolucao
Esta agenda fica aceita com os seguintes pontos fechados:
gfx.draw_text(...)e as demais primitives públicas degfx.*pertencem à mesma família V1 de overlay/debug final;- esse overlay/debug fica fora do
FrameComposer; FrameComposercontinua restrito à composição canônica do jogo (scene,spritese HUD canônico quando existir);- o overlay/debug deve ser drenado depois de
hud_fade; - o caminho sem scene bound deve observar a mesma semântica final de overlay/debug;
- HUD canônico explícito não faz parte desta thread e pode permanecer implícito/externo por enquanto;
- fast paths internos de primitives continuam permitidos, desde que preservem a semântica observável do overlay/debug final;
- dirtying granular ou otimizações finas por classe de primitive não fazem parte da primeira migração normativa desta thread.