141 lines
8.6 KiB
Markdown
141 lines
8.6 KiB
Markdown
---
|
|
id: AGD-0028
|
|
ticket: deferred-overlay-and-primitive-composition
|
|
title: Deferred Overlay and Primitive Composition over FrameComposer
|
|
status: accepted
|
|
created: 2026-04-18
|
|
updated: 2026-04-18
|
|
resolved: 2026-04-18
|
|
decision: DEC-0016
|
|
tags: [gfx, runtime, render, frame-composer, overlay, primitives, hud]
|
|
---
|
|
|
|
## 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 no `back`;
|
|
- o runtime só chama `render_frame()` no final do frame, então a escrita imediata em `back` deixou de ser semanticamente estável.
|
|
- As primitives de `gfx` nã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](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md):
|
|
|
|
- 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.*` com `gfx.*` 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 em `back`.
|
|
- Para esta thread, primitives de `gfx` devem 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 para `composer.*`.
|
|
- A solução deve preservar o caminho sem scene bound.
|
|
- A implementação deve evitar contaminar a infraestrutura de `gfx` responsá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 de `gfx` como 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 `back` como contrato operacional para `gfx.draw_text(...)` e demais primitives de `gfx`;
|
|
- 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:
|
|
|
|
1. limpar/compor scene;
|
|
2. compor sprites;
|
|
3. compor HUD canônico, se existir;
|
|
4. aplicar `scene_fade`;
|
|
5. aplicar `hud_fade`;
|
|
6. drenar primitives/debug overlay de `gfx.*`.
|
|
|
|
## Perguntas em Aberto
|
|
|
|
- `draw_text(...)` e as demais primitives de `gfx` entram todas na mesma família de overlay final já na V1, ou começamos só com `draw_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(...)` de `gfx.*` 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 `gfx` usado pelo pipeline do jogo não passe a depender do estado transitório de primitives/debug;
|
|
- otimizações de primitive path discutidas na `AGD-0010` continuam 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 de `gfx.*` pertencem à mesma família V1 de overlay/debug final;
|
|
- esse overlay/debug fica **fora** do `FrameComposer`;
|
|
- `FrameComposer` continua restrito à composição canônica do jogo (`scene`, `sprites` e 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.
|