730 lines
47 KiB
Markdown
730 lines
47 KiB
Markdown
---
|
|
id: AGD-0040
|
|
ticket: vm-render-parallel-execution-boundary
|
|
title: VM and Render Parallel Execution Boundary
|
|
status: accepted
|
|
created: 2026-06-04
|
|
resolved:
|
|
decision:
|
|
tags: [runtime, renderer, vm, concurrency, architecture, perf]
|
|
---
|
|
|
|
# Agenda - VM and Render Parallel Execution Boundary
|
|
|
|
## Contexto
|
|
|
|
`DSC-0038` estabeleceu `RenderSubmission` como snapshot fechado. A lesson `LSN-0047` registra explicitamente que uma submissao fechada e o primitivo util para concorrencia futura, mesmo que o v1 ainda consuma tudo de forma sincrona.
|
|
|
|
No codigo atual, `RenderManager` guarda apenas `latest_complete_submission`, fecha frames no fluxo da VM/runtime e publica para uma `RenderSurface` via chamada direta. Isso e correto para o v1, mas ainda nao decide como mover rasterizacao/publicacao para outra thread ou core.
|
|
|
|
A demanda aqui e separar a VM continuando em um core/thread de execucao logica enquanto o pipeline grafico consome submissions fechadas em outro worker/core, sem acesso mutavel vivo a VM, `Hardware` ou `Gfx`.
|
|
|
|
## Problema
|
|
|
|
Prometeu ainda nao tem contrato operacional para paralelismo VM/render.
|
|
|
|
A fronteira de dados ja aponta a direcao, mas faltam decisoes sobre:
|
|
|
|
- quem cria e possui a fila ou slot de submissao;
|
|
- se a politica `latest complete submission wins` basta quando ha worker real;
|
|
- como lidar com frame pacing, backpressure e queda de frames;
|
|
- quais recursos podem ser compartilhados read-only;
|
|
- como manter determinismo da VM;
|
|
- como representar isso em desktop host, handheld proprio e testes;
|
|
- se o render worker e parte do runtime, do host, da HAL ou apenas uma abstracao futura.
|
|
|
|
## Pontos Criticos
|
|
|
|
### 1. A VM nao pode depender do consumidor de render
|
|
|
|
A VM deve produzir estado/comandos e avançar segundo seu proprio budget. O render worker pode atrasar, pular frames ou publicar a ultima submissao completa, mas nao deve exigir acesso mutavel ao estado vivo da VM para rasterizar.
|
|
|
|
### 2. Snapshot fechado nao e automaticamente thread-safe
|
|
|
|
`RenderSubmission` owned ajuda, mas qualquer referencia indireta a assets, scene cache, bancos de glyphs ou surfaces precisa ter politica clara: copia, handle estavel, Arc/read-only, snapshot de recurso, ou acesso mediado.
|
|
|
|
### 3. `latest complete submission wins` precisa virar protocolo
|
|
|
|
Como principio, nao queremos fila infinita. Mas uma implementacao com worker precisa decidir se usa single-slot atomico, double/triple buffering, canal bounded, sequence numbers, acknowledgements ou outro mecanismo.
|
|
|
|
### 4. Frame pacing muda quando render sai do fluxo principal
|
|
|
|
Hoje tick, fechamento de frame, consumo e host presentation estao acoplados. Com worker separado, a VM pode produzir mais rapido que o render ou o render pode apresentar em cadencia diferente. Isso precisa de uma regra de tempo.
|
|
|
|
### 5. Desktop thread nao e automaticamente modelo de handheld core
|
|
|
|
No host desktop, a implementacao natural pode ser uma thread. No hardware proprio, pode ser outro core, DMA, coprocessador, scanout ou rotina cooperativa. A decisao deve separar contrato runtime de implementacao host.
|
|
|
|
## Opcoes
|
|
|
|
### Opcao A - Manter consumo sincrono, apenas preservar contrato de snapshot
|
|
|
|
**Abordagem:**
|
|
|
|
Nao criar worker agora. Consolidar somente as invariantes: submissions owned/fechadas, sem acesso mutavel vivo durante consumo, e testes que impedem regressao para immediate rendering.
|
|
|
|
**Pro:**
|
|
|
|
- menor risco;
|
|
- evita concorrencia prematura;
|
|
- mantem foco em estabilizar o novo renderer;
|
|
- ainda prepara a arquitetura.
|
|
|
|
**Con:**
|
|
|
|
- nao reduz latencia/custo no curto prazo;
|
|
- pode adiar decisoes dificeis sobre recursos compartilhados;
|
|
- o codigo pode voltar a assumir consumo sincrono se nao houver guardrails.
|
|
|
|
**Maintainability:**
|
|
|
|
Alta como etapa intermediaria, mas incompleta para a demanda de core/thread separado.
|
|
|
|
### Opcao B - Render worker com slot bounded `latest-complete`
|
|
|
|
**Abordagem:**
|
|
|
|
Criar um worker de render que consome um slot/canal bounded de `RenderSubmission`. O produtor substitui a submissao pendente quando uma mais nova fica pronta; o consumidor sempre pega a ultima completa disponivel.
|
|
|
|
**Pro:**
|
|
|
|
- implementa a politica ja aceita;
|
|
- evita fila infinita;
|
|
- desacopla VM e rasterizacao;
|
|
- aproxima desktop e hardware proprio por contrato de single/latest slot.
|
|
|
|
**Con:**
|
|
|
|
- exige `Send`/ownership/read-only real nos dados;
|
|
- precisa regra de shutdown, erro e sincronizacao;
|
|
- pode mascarar frames descartados sem telemetria adequada.
|
|
|
|
**Maintainability:**
|
|
|
|
Boa se o protocolo for pequeno e testavel.
|
|
|
|
### Opcao C - Fila bounded com N submissions
|
|
|
|
**Abordagem:**
|
|
|
|
Usar canal bounded com 2 ou 3 submissions para suavizar picos e permitir que o render trabalhe atrasado.
|
|
|
|
**Pro:**
|
|
|
|
- simples em desktop;
|
|
- reduz bloqueios imediatos;
|
|
- permite medicao de atraso.
|
|
|
|
**Con:**
|
|
|
|
- contradiz parcialmente latest-wins se N crescer;
|
|
- aumenta memoria e latencia;
|
|
- pode apresentar frames velhos;
|
|
- menos adequado para handheld com memoria restrita.
|
|
|
|
**Maintainability:**
|
|
|
|
Media. Pode ser util como implementacao, mas precisa de regra forte para descarte.
|
|
|
|
### Opcao D - Render thread como detalhe exclusivo do host desktop
|
|
|
|
**Abordagem:**
|
|
|
|
Mover rasterizacao/publicacao para thread do host, sem mudar contrato runtime/HAL.
|
|
|
|
**Pro:**
|
|
|
|
- melhora desktop sem mexer no console;
|
|
- menor impacto no runtime;
|
|
- facilita experimentos.
|
|
|
|
**Con:**
|
|
|
|
- pode criar divergencia entre host e alvo real;
|
|
- nao resolve contrato para hardware proprio;
|
|
- risco de esconder dependencia mutavel por baixo do host.
|
|
|
|
**Maintainability:**
|
|
|
|
Media a baixa como solucao canonica. Boa apenas para experimento ou profiling.
|
|
|
|
## Sugestao / Recomendacao
|
|
|
|
A recomendacao inicial e uma decisao em duas fases:
|
|
|
|
1. Fechar agora as invariantes da Opcao A como contrato obrigatorio.
|
|
2. Planejar a Opcao B como primeiro modelo real de worker quando a base estiver pronta.
|
|
|
|
O contrato desejado deve ser:
|
|
|
|
```text
|
|
VM/runtime core
|
|
builds domain buffers
|
|
closes RenderSubmission snapshot
|
|
publishes latest complete submission to bounded handoff
|
|
|
|
render worker/core
|
|
takes latest complete submission
|
|
consumes immutable packet/resource handles
|
|
rasterizes/publishes through RenderSurface
|
|
reports telemetry/backpressure
|
|
```
|
|
|
|
O worker nao deve receber `&mut Hardware`, `&mut Gfx`, `&mut VirtualMachineRuntime` ou ponte que permita voltar ao estado vivo da VM.
|
|
|
|
## Perguntas Respondidas
|
|
|
|
- [x] O handoff deve ser single-slot latest-wins, canal bounded N=2, ou outro protocolo?
|
|
- Resposta: single-slot latest-wins, sem bloquear VM/produtor.
|
|
- [x] Quais tipos dentro de `RenderSubmission` precisam ser `Send + Sync` ou owned?
|
|
- Resposta: submissions/packets cruzam a fronteira como dados owned pequenos; APIs read-only compartilhadas de bancos/cache precisam provar seguranca de compartilhamento antes do worker real.
|
|
- [x] Como assets, scene cache e glyph banks cruzam a fronteira sem copia pesada?
|
|
- Resposta: por IDs/handles estaveis e APIs read-only; bancos sao imutaveis por contrato, e o viewport cache pertence ao core logico.
|
|
- [x] Quem e dono do worker: `RenderManager`, host desktop, SystemOS service ou HAL?
|
|
- Resposta: `SystemOS`/lifecycle decide politica/AppMode; `RenderManager` coordena handoff, epoch, ownership e telemetry; host/HAL fornece capacidades e execucao concreta.
|
|
- [x] O render worker pode bloquear a VM ou apenas descartar/substituir submissions?
|
|
- Resposta: nao bloqueia a VM/produtor; descarta/substitui pending submissions conforme latest-wins.
|
|
- [x] Como medir frames produzidos, consumidos, descartados e apresentados?
|
|
- Resposta: counters e ultimos frame IDs separados para produced, replaced/dropped, consumed, presented, repeated, errors e stale epoch discards.
|
|
- [x] Como shutdown, crash, mode transition e troca de cart interagem com worker?
|
|
- Resposta: transicoes incrementam epoch/generation, invalidam pending/current obsoletos e impedem present de frames do owner anterior.
|
|
- [x] Qual parte deve ser especificada agora e qual deve esperar implementacao real?
|
|
- Resposta: especificar agora o contrato de packet fechado, handoff latest-wins, frame pacing, AppMode policy, epoch ownership e telemetry minima; implementacao concreta do worker/render backend fica para decision/plan posterior.
|
|
|
|
## Grandes Problemas Esperados
|
|
|
|
### Topico 1 - Game2DFramePacket como snapshot completo
|
|
|
|
**Status:** direcao respondida; execucao pendente.
|
|
|
|
**Pergunta de fechamento:** o consumidor de `RenderSubmissionPacket::Game2D` consegue compor o frame completo a partir do packet fechado, sem depender de caminho alternativo por estado vivo?
|
|
|
|
Hoje `Hardware::publish_render_submission` ignora parte do `Game2DFramePacket` quando ha scene ativa, porque chama `frame_composer.render_frame(&mut self.gfx)` em vez de consumir o packet completo. Isso quebra a premissa de que o worker pode receber um snapshot fechado e rasterizar sem estado vivo.
|
|
|
|
Antes de qualquer render worker, `Game2DFramePacket` precisa ser a fonte completa de composicao: scene/layers/sprites, `gfx2d` overlay e publicacao.
|
|
|
|
**Direcao atual:** nao ha debate arquitetural relevante restante para este topico. `gfx2d` deve ser overlay apos a composicao Game 2D, e o consumidor de `RenderSubmissionPacket::Game2D` deve aplicar a mesma ordem em frames com scene ativa e sem scene ativa. O que falta e corrigir o caminho atual que contorna o packet quando ha scene ativa e adicionar teste de regressao cobrindo scene ativa + primitive overlay.
|
|
|
|
### Topico 2 - Recursos pesados e fronteira read-only
|
|
|
|
**Status:** direcao respondida.
|
|
|
|
**Pergunta de fechamento:** como scene cache, glyph banks, memory banks, assets e recursos futuros cruzam a fronteira sem `&mut Hardware`, `&mut Gfx` ou `FrameComposer` vivo?
|
|
|
|
`RenderSubmission` e owned, mas a composicao real ainda depende de `FrameComposer`, scene cache, resolver, glyph banks, memory banks e `Gfx`. Um worker nao pode receber `&mut Hardware` nem consultar `FrameComposer` vivo. Precisamos decidir como recursos pesados entram no snapshot: handles estaveis, `Arc` read-only, snapshots pequenos, ou acesso mediado.
|
|
|
|
**O que falta discutir:** o ponto nao e copiar todos os recursos pesados para dentro do packet. Isso seria caro e provavelmente errado. O que falta e separar tres categorias:
|
|
|
|
- estado de frame que deve estar no packet fechado: camera, scene binding, sprites, HUD, primitives, copy/update requests ja calculados;
|
|
- recursos residentes grandes que podem ser referenciados por handle/slot e lidos via `Arc`/read-only: glyph banks, scene banks, caches materializados;
|
|
- estado mutavel de preparacao que deve ficar no core logico antes do handoff: resolver update, cache refresh, asset installation, bank residency changes.
|
|
|
|
**Direcao provavel:** o render worker deve receber um snapshot pequeno com comandos e handles estaveis, mais acesso read-only aos bancos/caches necessarios. A atualizacao de resolver/cache e a instalacao/troca de assets devem acontecer antes do handoff, no core logico ou em um servico proprietario, nunca durante rasterizacao no worker.
|
|
|
|
**Perguntas respondidas:**
|
|
|
|
- [x] O `Game2DFramePacket` deve carregar apenas `BoundScenePacket { bank_id }` ou tambem um snapshot/read handle do cache materializado?
|
|
- Resposta: IDs somente. Os bancos sao imutaveis por definicao; o packet nao deve capturar handles pesados por frame.
|
|
- [x] O cache de viewport pertence ao produtor logico, ao render worker, ou a um recurso compartilhado read-only apos refresh?
|
|
- Resposta: o viewport cache pertence ao core logico. O render worker deve acessa-lo apenas por uma API de leitura.
|
|
- [x] Uma troca de asset/bank pode invalidar recursos enquanto uma submission antiga ainda esta sendo rasterizada?
|
|
- Resposta: a submission aponta por ID para o recurso. Se o recurso for trocado, a submission ainda pode executar e o resultado pode ficar confuso/diferente. Disciplina de troca de assets e responsabilidade do dev ou framework.
|
|
- [x] O handoff deve capturar `Arc<GlyphBank>`/`Arc<SceneBank>` por frame para garantir liveness, ou apenas IDs de slot com regra de nao substituir enquanto houver frame em voo?
|
|
- Resposta: somente IDs. Nao vamos garantir integridade do resultado por captura de recurso; disciplina de troca e responsabilidade do dev ou framework.
|
|
- [x] Quais tipos precisam provar `Send + Sync` antes de qualquer worker real?
|
|
- Esclarecimento: esta pergunta e tecnica. Em Rust, qualquer dado/API compartilhado entre threads precisa ser seguro para envio/leitura concorrente. Com a resposta "IDs somente" no packet, a exigencia sai do `Game2DFramePacket` pesado e recai principalmente sobre a API de leitura dos bancos/cache e sobre o protocolo de handoff.
|
|
- Resposta: essa prova fica como criterio tecnico de implementacao do worker real; a decisao arquitetural e que a fronteira compartilhada deve ser owned ou read-only, sem `&mut Hardware`, `&mut Gfx`, `FrameComposer` vivo ou acesso mutavel a VM.
|
|
|
|
**Direcao respondida:** packet pequeno com IDs, bancos imutaveis por contrato, viewport cache pertencente ao core logico e acesso do worker por API read-only. O sistema nao deve tentar garantir integridade visual de submissions antigas contra troca disciplinarmente incorreta de assets/banks; essa disciplina pertence ao dev/framework.
|
|
|
|
### Topico 3 - Protocolo de handoff latest-wins
|
|
|
|
**Status:** direcao respondida.
|
|
|
|
**Pergunta de fechamento:** qual protocolo substitui o `Option<RenderSubmission>` local quando houver outro core/thread?
|
|
|
|
O `latest_complete_submission` atual e apenas um `Option<RenderSubmission>` dentro do runtime. Para outro core/thread, isso precisa virar protocolo: single-slot latest-wins, canal bounded, sequence numbers, descarte, acknowledgement e telemetria.
|
|
|
|
**O que falta discutir:** este topico define o contrato de handoff, nao a politica de frame pacing completa. Precisamos escolher como uma submission sai do core logico e chega ao render worker:
|
|
|
|
- single-slot latest-wins: produtor substitui a submission pendente por uma mais nova, e o worker consome a ultima disponivel;
|
|
- canal bounded N=1/2: produtor tenta enviar, e o protocolo decide se descarta antigo, descarta novo ou bloqueia;
|
|
- acknowledgement: worker informa qual `frame_id` consumiu/apresentou, ou o produtor apenas publica e esquece;
|
|
- ownership: a submission e movida para o handoff ou clonada do `RenderManager`;
|
|
- sequence numbers: `frame_id` basta ou precisamos de counters separados para produzido/consumido/apresentado;
|
|
- escopo por pipeline: Game pode usar handoff; Shell pode publicar sincrono.
|
|
|
|
**Direcao provavel:** para o primeiro modelo real, preferir single-slot latest-wins, sem fila crescente e sem bloquear a VM. `RenderManager` deixa de ser apenas dono de `Option<RenderSubmission>` e passa a publicar uma submission fechada para um handoff pequeno. O worker pega a ultima submission disponivel; se uma submission antiga for substituida antes do consumo, isso conta como drop/substituicao mensuravel.
|
|
|
|
**Direcao aceita:** single-slot latest-wins, sem bloquear a VM/produtor.
|
|
|
|
**Perguntas respondidas:**
|
|
|
|
- [x] O slot guarda exatamente uma pending submission alem da currently rendering, ou apenas a ultima submission atomica disponivel?
|
|
- Resposta: o handoff guarda uma pending submission owned. Se o worker ja pegou uma submission, ela esta fora do slot e em consumo. O slot representa apenas a proxima/latest pending.
|
|
- [x] Quando o worker esta renderizando frame N e o core produz N+1 e N+2, N+1 deve ser descartado?
|
|
- Resposta: sim. `N+2` substitui `N+1` se `N+1` ainda nao foi retirado pelo worker. Isso e counted como replaced/dropped-before-consume.
|
|
- [x] O produtor precisa saber que uma submission foi consumida, ou so que foi substituida/publicada?
|
|
- Resposta: o produtor nao espera acknowledgement para continuar. O worker deve reportar consumed/presented como telemetria assíncrona, mas isso nao bloqueia nem participa da progressao da VM.
|
|
- Observacao: a preocupacao restante e a semantica do `tick`. Hoje VM e render estao dentro do mesmo tick sincrono. Com worker, o tick deve fechar/publicar a submission, mas nao esperar ACK de render. A relacao entre tick, present efetivo e telemetria fica para o Topico 4.
|
|
- [x] `frame_id` e suficiente para telemetria, ou precisamos de contadores separados de produced/dropped/consumed/presented?
|
|
- Resposta: `frame_id` e suficiente para ordenacao, mas nao para observabilidade. Precisamos de contadores separados: produced, replaced/dropped-before-consume, consumed, presented, render_errors/present_errors, e ultimos frame ids por etapa.
|
|
- [x] Shell UI fica fora do protocolo worker por default, ou usa o mesmo handoff quando configurado?
|
|
- Resposta: Shell UI fica fora por default e pode continuar sincrono/local. O mesmo protocolo pode ser reutilizado se uma configuracao futura quiser worker para Shell, mas a politica inicial e por pipeline/AppMode.
|
|
|
|
**Direcao respondida:** o protocolo base e single-slot latest-wins com ownership moved para o handoff, pending slot substituivel, worker consumindo owned submission, VM/produtor nunca bloqueado, ack apenas para telemetria e politica por pipeline.
|
|
|
|
Confirmacoes de 2026-06-04:
|
|
|
|
- ownership moved para o handoff: aceito;
|
|
- produtor sem ACK bloqueante: aceito, com a preocupacao de definir a nova semantica de tick;
|
|
- contadores separados: seguir com essa direcao;
|
|
- Shell UI fora do worker por default: aceito.
|
|
|
|
### Topico 4 - Frame pacing e backpressure
|
|
|
|
**Status:** direcao respondida.
|
|
|
|
**Pergunta de fechamento:** a VM bloqueia, descarta, substitui ou apenas mede atraso quando o render worker esta atrasado?
|
|
|
|
Hoje tick, fechamento, raster e present acontecem no mesmo fluxo. Com worker separado, a VM pode produzir frames mais rapido que o render consome, ou o render pode apresentar em outra cadencia. Precisamos decidir se a VM bloqueia, descarta, substitui, ou apenas mede atraso.
|
|
|
|
**Direcao em discussao:** se o render real for para worker/core, o `present` passa a ser responsabilidade do worker/render surface e deve mirar a cadencia de display, inicialmente 60Hz. O worker nao consome uma fila crescente; ele consome a latest submission disponivel pelo protocolo single-slot e apresenta em sua cadencia.
|
|
|
|
O core logico/VM deixa de precisar reservar tempo de render dentro do mesmo tick sincrono, mas nao deve rodar livremente. O frame logico da VM e parte do modelo de tempo do jogo: animacoes, input, scripts e simulacao dependem dele. Portanto, a VM deve continuar paced por frame logico, mirando 1:1 com a cadencia do worker/display sempre que possivel.
|
|
|
|
Idealmente, ha relacao 1:1:
|
|
|
|
```text
|
|
1 logical VM frame
|
|
-> 1 RenderSubmission
|
|
-> 1 presented worker frame
|
|
```
|
|
|
|
Mas o contrato nao deve depender de sempre conseguir isso. A direcao revisada e:
|
|
|
|
- o worker/render surface e dono do `present` em 60Hz;
|
|
- o worker apresenta o ultimo frame processado/disponivel;
|
|
- quando o worker aceita/pega uma nova submission para apresentacao, isso avanca o frame counter visivel;
|
|
- se a VM ainda nao disponibilizou uma nova submission, o worker mantem a ultima renderizacao e registra skip/no-new-frame;
|
|
- a VM nao roda frames logicos ilimitados a frente do display; ela so deve iniciar novo frame logico quando a cadencia permitir produzir o proximo frame esperado;
|
|
- se o worker ainda esta processando um frame anterior, a VM pode preparar/publicar o proximo frame logico dentro do limite 1:1, mas nao deve acumular multiplos frames logicos pendentes.
|
|
|
|
Isso muda a leitura do latest-wins: ele continua util para substituir uma pending submission que ainda nao foi pega pelo worker, mas nao deve virar autorizacao para a VM rodar muito a frente do display. O objetivo e desacoplar custo de raster/present, nao desacoplar o tempo logico do jogo da cadencia de frame.
|
|
|
|
Se o worker nao rasterizar dentro do budget de 60Hz, o sistema deve contar atraso/drop/skip em telemetria. A VM nao deve bloquear esperando ACK de render, mas deve respeitar o pace de frame logico definido pelo display/worker.
|
|
|
|
Refinamento importante:
|
|
|
|
A VM nao espera ACK de render/present de uma submission especifica, mas deve esperar a autorizacao de pace derivada do frame counter/display. Ou seja, o render worker nao confirma "terminei seu frame N, pode seguir"; em vez disso, o sistema avanca uma cadencia de frame e a VM so inicia o proximo logical frame quando essa cadencia permite. Isso evita que a VM rode livremente sem transformar present em bloqueio sincrono.
|
|
|
|
### Corner cases de frame pacing
|
|
|
|
1. Worker em 60Hz, VM pronta antes do proximo refresh.
|
|
- A VM deve ficar aguardando o proximo frame pace para iniciar novo logical frame.
|
|
- Nao deve produzir multiplas submissions futuras.
|
|
|
|
2. Worker apresenta refresh sem nova submission.
|
|
- O worker repete/mantem a ultima renderizacao.
|
|
- O sistema registra skip/no-new-frame.
|
|
- O frame counter visivel pode avancar, mas a VM nao deve simular um novo logical frame retroativamente.
|
|
|
|
3. VM produz submission, worker ainda esta rasterizando frame anterior.
|
|
- A submission nova fica no single-slot pending.
|
|
- Se outra submission fosse produzida antes do worker pegar a pending, ela substituiria a pending; porem o pace da VM deve impedir esse acúmulo na pratica.
|
|
|
|
4. Worker pega pending submission para rasterizar.
|
|
- A pending sai do slot e passa a pertencer ao worker.
|
|
- A VM nao espera ACK dessa submission, mas so inicia novo logical frame quando o pace permitir.
|
|
|
|
5. Worker perde prazo de 16.67ms.
|
|
- O sistema registra atraso/render-overrun.
|
|
- A VM nao deve tentar compensar produzindo frames extras.
|
|
- A proxima permissao de logical frame deve seguir a politica de pace, nao backlog.
|
|
|
|
6. VM demora mais que a janela esperada de logical frame.
|
|
- O runtime nao deve encerrar o frame por budget como mecanismo normal.
|
|
- `FRAME_SYNC` continua sendo o fim canonico do logical frame.
|
|
- O sistema registra logical-frame-overrun para certificacao/watchdog/diagnostico.
|
|
- O worker continua apresentando a ultima renderizacao.
|
|
- Quando a VM finalmente publica a submission, ela representa o proximo logical frame sequencial, nao um frame pulado.
|
|
|
|
7. Mode transition Game/Shell enquanto ha pending ou currently-rendering submission.
|
|
- A submission antiga nao deve ser apresentada depois que o modo mudou, a menos que a politica de transition permita explicitamente.
|
|
- O handoff precisa carregar generation/mode epoch ou equivalente para invalidar frames obsoletos.
|
|
|
|
8. Cartridge/app troca enquanto worker tem frame antigo.
|
|
- Submissions de app/cart anterior devem ser descartadas por generation/epoch.
|
|
- O worker deve limpar ou substituir a surface conforme politica de lifecycle.
|
|
|
|
9. Crash/panic durante logical frame antes de publicar submission.
|
|
- O worker segue apresentando ultimo frame valido ate receber crash/shell submission.
|
|
- O crash path deve poder invalidar pending game frame.
|
|
|
|
10. Crash/panic no render worker.
|
|
- Nao pode corromper determinismo da VM.
|
|
- Deve virar telemetria/erro de render e possivelmente fallback para sync/local render ou crash surface.
|
|
|
|
11. Input sampling.
|
|
- Input deve ser amostrado no inicio do logical frame autorizado pelo pace.
|
|
- Se o worker repetir frame por falta de submission nova, nao deve haver novo input sampling/logical update correspondente.
|
|
|
|
12. Frame counter semantics.
|
|
- Precisamos separar pelo menos logical_frame_id, produced_submission_frame_id e presented_frame_id.
|
|
- O frame counter que autoriza a VM deve representar cadencia logica/display, nao ACK de uma submission especifica.
|
|
|
|
### Avaliacao de desenho
|
|
|
|
O desenho fica mais complexo se o render worker tambem virar a autoridade do frame counter que libera a VM. Isso mistura duas responsabilidades:
|
|
|
|
- cadencia logica do sistema;
|
|
- consumo/rasterizacao/publicacao de submissions.
|
|
|
|
Uma alternativa mais limpa e introduzir uma autoridade de pace separada, por exemplo `FrameClock` ou scheduler de frame, que emite ticks de 60Hz. A VM e o render worker observam essa cadencia, mas nenhum dos dois e dono exclusivo dela.
|
|
|
|
Modelo preferido para reduzir complexidade:
|
|
|
|
```text
|
|
FrameClock / frame scheduler
|
|
emits 60Hz frame ticks
|
|
|
|
VM/core logico
|
|
runs at most one logical frame per frame tick
|
|
samples input on authorized logical frame
|
|
publishes at most one latest submission
|
|
|
|
render worker/surface
|
|
presents on display cadence
|
|
consumes latest available submission
|
|
repeats last rendered frame if no new submission exists
|
|
```
|
|
|
|
Assim, a VM nao espera ACK do worker e tambem nao roda livremente. Ela espera o frame clock. O worker nao precisa "desbloquear" a VM; ele apenas consome e apresenta. Isso preserva 1:1 como alvo sem acoplar progresso logico a sucesso/falha de rasterizacao.
|
|
|
|
Conclusao provisoria: preferir `FrameClock`/scheduler como autoridade de cadencia, `RenderManager` como produtor de submissions, e render worker como consumidor/presenter. Evitar que o worker seja dono do frame counter canonico.
|
|
|
|
Refinamento sobre Shell/App lifecycle:
|
|
|
|
O `FrameClock`/scheduler nao deve impor um game loop ao Shell. O loop de um game e diferente do lifecycle de um app UI, e em Prometeu o game loop e entregue no hardware/runtime como parte do modelo de console. Shell/SystemOS deve ser tratado como lifecycle/event-driven: input, janelas, foco, app lifecycle e invalidacao visual determinam quando ha trabalho logico ou render novo.
|
|
|
|
Portanto, `FrameClock` deve autorizar cadencia de logical frame para pipelines de game, mas Shell UI nao deve necessariamente consumir um frame logico de VM a cada 60Hz. Para Shell:
|
|
|
|
```text
|
|
SystemOS/Shell lifecycle events
|
|
-> input/window/app updates when needed
|
|
-> build ShellUiFramePacket on invalidation/event
|
|
-> present/repeat last surface as needed
|
|
```
|
|
|
|
Isso reforca que `FrameScheduler` nao e "scheduler da VM". Ele e uma fonte de cadencia para workloads que precisam de frame pacing, especialmente Game2D/Game3D. Shell/App lifecycle pode usar outro modelo, com render por invalidacao ou por eventos, ainda que o host/display continue apresentando em 60Hz.
|
|
|
|
Resposta sobre budget e frames lentos:
|
|
|
|
`FRAME_SYNC` e o fim canonico do logical frame. O budget antigo era um mecanismo grosseiro para pausar a VM e permitir que o tick continuasse; nesse desenho ele nao deve ser usado como forma normal de encerrar ou cortar um frame logico.
|
|
|
|
Budget passa a existir para certificacao, watchdog, diagnostico e deteccao de overrun. Se um logical frame demora mais que aproximadamente 16.67ms, nao pulamos logical frames da VM para alcançar o display. Frames logicos devem ser sequenciais. O resultado visivel e stutter/repeticao do ultimo frame pelo worker, nao time-skip semantico.
|
|
|
|
```text
|
|
VM logical frame N demora demais
|
|
-> worker repete ultimo frame disponivel
|
|
-> runtime registra overrun
|
|
-> VM continua ate FRAME_SYNC/trap/halt/watchdog fatal
|
|
-> submission publicada e N, nao N+k
|
|
```
|
|
|
|
**Perguntas respondidas:**
|
|
|
|
- [x] O worker deve repetir o ultimo frame quando nao houver submission nova no refresh de 60Hz?
|
|
- Resposta: sim. O worker mantem a ultima renderizacao e registra skip/no-new-frame.
|
|
- [x] A VM deve continuar limitada a um logical frame por tick de sistema, ou pode produzir multiplas submissions entre presents?
|
|
- Resposta: a VM nao deve produzir multiplos frames logicos a frente do display. O alvo e 1 logical frame por frame worker/display, com no maximo uma pending submission pelo protocolo latest-wins.
|
|
- [x] `FRAME_SYNC` continua sendo o limite canonico de logical frame mesmo sem present sincrono?
|
|
- Resposta: sim. `FRAME_SYNC` continua delimitando logical frame da VM. O present deixa de ser sincrono, mas o frame logico continua paced.
|
|
- [x] Qual budget permanece na VM: ciclos/instrucoes por logical frame, tempo real, ou ambos?
|
|
- Resposta: `FRAME_SYNC` encerra o logical frame. Budget nao e mecanismo normal de tick/frame advance. Budget/ciclos/tempo real ficam para certificacao, watchdog, diagnostico e overrun policy.
|
|
- [x] Como detectar game rodando rapido demais se o render worker esta desacoplado?
|
|
- Resposta: o runtime nao permite que a VM rode livremente a frente do frame counter/worker. A telemetria deve contar produced, accepted/presented e skips para detectar desalinhamento.
|
|
|
|
### Topico 5 - Politica por pipeline/AppMode
|
|
|
|
**Status:** direcao respondida; detalhamento de lifecycle movido para `DSC-0041`.
|
|
|
|
**Pergunta de fechamento:** quais pipelines devem poder usar worker e quais podem continuar sincronos/localmente?
|
|
|
|
Shell UI pode continuar sincrona/local. O contrato comum deve ser packet fechado, mas a politica de execucao deve variar por pipeline. Forcar Shell UI para render worker pode aumentar complexidade sem ganho.
|
|
|
|
**O que precisamos acertar:** este topico define a matriz de politica por `AppMode`/pipeline. O sistema hoje tem `AppMode::Game` e `AppMode::Shell`, mas esses modos nao devem receber a mesma semantica de loop/render.
|
|
|
|
Direcao ja encaminhada:
|
|
|
|
- `Game2D` e futuro `Game3D` sao workloads frame-paced. Devem poder usar `FrameScheduler` e render worker.
|
|
- `ShellUi` e Shell/SystemOS sao lifecycle/event-driven. Devem poder continuar sincronos/localmente e renderizar por invalidacao/evento.
|
|
- O contrato comum continua sendo `RenderSubmission` fechado.
|
|
- A politica de execucao do consumidor pode variar por pipeline/AppMode.
|
|
|
|
**Perguntas respondidas:**
|
|
|
|
- [x] `AppMode::Game` sempre usa `FrameScheduler`, mesmo se o game nao tiver render pesado?
|
|
- Resposta: sim. Game e frame-paced por definicao. Futuramente `AppMode::Game` pode virar algo mais explicito como `AppMode::Game2D`, mas cada AppMode deve ter uma politica de render explicita.
|
|
- [x] `AppMode::Game` deve usar render worker por default, ou isso deve ser capability/configuracao de host?
|
|
- Resposta: Game usa render worker por default quando o host/runtime suporta, com fallback sincrono/local.
|
|
- [x] `AppMode::Shell` deve ficar explicitamente fora do `FrameScheduler` logico de game?
|
|
- Resposta: sim. Shell nao herda game loop.
|
|
- [x] Shell UI deve publicar sincrono/local por default, com worker apenas opt-in?
|
|
- Resposta: sim. Shell UI e sincrono/local por default; worker so como opt-in futuro.
|
|
- [x] Apps VM-backed dentro do Shell seguem lifecycle de Shell ou podem declarar workload frame-paced proprio?
|
|
- Resposta: seguem lifecycle de Shell, sem direito a declarar workload frame-paced proprio.
|
|
- [x] Firmware screens como splash/crash seguem politica Shell/local ou politica propria?
|
|
- Resposta: seguem politica Shell/local.
|
|
- [x] A escolha de politica fica no `RenderManager`, no SystemOS/lifecycle, ou em uma camada de host/runtime configuration?
|
|
- Resposta: SystemOS/lifecycle decide o perfil/AppMode; `RenderManager` executa a politica; host/runtime fornece capacidades e fallback.
|
|
|
|
**Direcao respondida:** cada AppMode deve ter politica explicita de render. Game atual e frame-paced e pode usar worker por default quando disponivel. Shell e lifecycle/event-driven e sincrono/local por default. Apps VM-backed dentro do Shell seguem lifecycle de Shell, sem opt-in para game pacing proprio.
|
|
|
|
### Topico 6 - Lifecycle, transitions e ownership da surface
|
|
|
|
**Status:** direcao respondida; detalhamento de lifecycle movido para `DSC-0041`.
|
|
|
|
**Pergunta de fechamento:** quem possui a surface/submission durante troca Game/Shell, crash, splash, troca de cart e shutdown?
|
|
|
|
Troca Game/Shell, crash screen, splash, troca de cart e shutdown precisam saber quem possui a surface e qual submission ainda pode ser publicada. O worker nao pode ficar publicando frame velho depois que o modo ou cartridge mudou.
|
|
|
|
**O que precisamos acertar:** quando existe worker/render assíncrono, podem existir submissions em tres estados:
|
|
|
|
- pending no single-slot;
|
|
- currently-rendering no worker;
|
|
- ultimo frame apresentado/mantido na surface.
|
|
|
|
Lifecycle precisa dizer o que acontece com cada um quando o sistema muda de dono visual.
|
|
|
|
**Direcao provavel:** toda submission deve carregar um contexto de ownership alem de `frame_id` e `app_mode`, por exemplo `render_epoch`/`generation`, `app_id` ou `surface_owner`. O `RenderManager`/SystemOS incrementa essa generation quando troca app, cart, modo ou crash state. O worker deve descartar pending/current frames cujo ownership nao combine com o epoch ativo antes de apresentar.
|
|
|
|
**Perguntas respondidas para o escopo desta agenda:**
|
|
|
|
- [x] Troca Game -> Shell invalida imediatamente pending/current Game submissions?
|
|
- Resposta: sim. A troca de foreground visual owner incrementa epoch/generation e torna obsoletas as submissions do owner anterior.
|
|
- [x] Troca Shell -> Game deve limpar surface ou pode manter ultimo Shell frame ate primeiro Game frame?
|
|
- Resposta: pode manter a ultima surface apresentada ate a primeira submission valida do novo owner, desde que nenhum frame antigo seja apresentado novamente como nova submission. Limpar e uma politica visual opcional.
|
|
- [x] Crash screen invalida pending/current Game frame antes de publicar crash Shell/local frame?
|
|
- Resposta: sim. Crash troca o owner visual para a tela de erro/sistema e deve invalidar frames pendentes/correntes do Game.
|
|
- [x] Splash/crash/hub usam uma surface compartilhada ou ownership proprio?
|
|
- Resposta: podem usar a mesma surface fisica, mas precisam de ownership logico proprio via epoch/generation/surface owner.
|
|
- [x] Troca de cartridge/app incrementa `render_epoch` mesmo se `AppMode` continuar igual?
|
|
- Resposta: sim. `AppMode` nao e identidade suficiente; troca de app/cart precisa invalidar submissions antigas.
|
|
- [x] Worker pode terminar raster de um frame obsoleto, mas deve checar epoch antes de present?
|
|
- Resposta: sim. O worker pode desperdiçar trabalho ja iniciado, mas nao pode apresentar se o epoch/owner nao for mais atual.
|
|
- [x] Quem incrementa epoch: SystemOS/lifecycle, `RenderManager`, host, ou todos via API central?
|
|
- Resposta: `SystemOS`/lifecycle decide a transicao semantica; `RenderManager` deve oferecer a API central que incrementa/aplica epoch. Host nao decide ownership.
|
|
- [x] Shutdown deve drenar worker, cancelar pending/current, ou apenas parar present?
|
|
- Resposta: shutdown deve parar present, descartar pending, invalidar current por epoch/stop token e encerrar/joinar o worker de forma bounded. Nao deve drenar frames obsoletos para a tela.
|
|
|
|
**Levantamento de material existente:**
|
|
|
|
- `DSC-0031` / `LSN-0040` ja estabeleceu `AppMode` como discriminador de perfil. Game e Shell/System usam a mesma VM/transporte internamente, mas nao compartilham o mesmo perfil autoral. Game segue pipeline de jogo; Shell/System segue pipeline Runtime/Hub.
|
|
- `DSC-0032` / `LSN-0041` ja estabeleceu `SystemOS` como autoridade semantica de lifecycle para task/process. Firmware nao deve coordenar manualmente task/process fora dessa fronteira.
|
|
- `DSC-0035` / `LSN-0044` ja estabeleceu que Shell liveness depende da janela focada pertencer ao proprio task; perder a janela elegivel fecha o Shell task via lifecycle e volta ao Hub.
|
|
- `DSC-0036` / `LSN-0045` ja estabeleceu o fluxo visual/lifecycle `Hub/Home -> Shell app -> task-owned window -> close -> lifecycle close -> Hub/Home`.
|
|
- `DSC-0038` / `LSN-0047` ja estabeleceu que `RenderManager` coordena active app mode, submission closure, transitions, capabilities e publication flow, mas sem virar renderer.
|
|
|
|
**Estado atual de implementacao:**
|
|
|
|
- Firmware possui estados explicitos (`GameRunning`, `ShellRunning`, `AppCrashes`, splash/hub/load) e `change_state` executa `on_exit`/`on_enter` imediatamente para evitar frames vazios.
|
|
- `LoadCartridgeStep` roteia `AppMode::Shell` para task/window Shell e `ShellRunning`; caso contrario cria task de game e entra em `GameRunning`.
|
|
- `GameRunningStep` exige task foreground e executa `ctx.os.vm().tick(...)`.
|
|
- `ShellRunningStep` exige task foreground e janela focada pertencente ao task; caso contrario fecha pelo lifecycle e volta ao Hub.
|
|
- `RenderManager` atual tem `RenderTransitionState::Pending { from, to }`, mas isso e placeholder/no-op: nao ha epoch/generation, invalidacao de pending/current frames, nem ownership de surface.
|
|
|
|
**Gap para este topico:** as politicas de lifecycle Game/Shell existem, mas ainda nao foram propagadas para ownership visual assíncrono. Para `DSC-0040`, a direcao e suficiente: toda transicao de foreground visual owner passa por uma API central do `RenderManager`, incrementa epoch/generation, invalida pending/current do owner anterior e impede present de frames obsoletos.
|
|
|
|
### Caso principal - Home durante Game e retorno ao Game
|
|
|
|
Cenario esperado:
|
|
|
|
```text
|
|
GameRunning
|
|
-> usuario aperta Home
|
|
-> Game e suspenso/pausado como task foreground
|
|
-> Hub/Home assume Shell UI
|
|
-> usuario navega/checa algo no Shell
|
|
-> usuario retorna ao Game
|
|
-> Game volta como foreground
|
|
```
|
|
|
|
Mesmo sem transicoes suaves, esse ciclo precisa funcionar. O estado atual cobre partes do modelo, mas nao o ciclo completo:
|
|
|
|
- ja existe `GameRunningStep` para game fullscreen foreground;
|
|
- ja existe `HubHomeStep` e `ShellRunningStep` para Hub/Shell lifecycle;
|
|
- ja existe lifecycle authority em `SystemOS`;
|
|
- ja existe Shell task/window liveness;
|
|
- ainda nao ha evidencia de um fluxo Home durante `GameRunning` que suspenda o game, entre no Hub, e depois retorne ao mesmo game;
|
|
- o `RenderManager` so possui `RenderTransitionState::Pending { from, to }` como placeholder; nao ha ownership/epoch para invalidar frames de Game enquanto o Hub assume a surface.
|
|
|
|
Politica provavel para esse caso:
|
|
|
|
- Home em Game nao fecha o game; suspende/pausa o game task/process.
|
|
- Ao entrar no Hub, qualquer pending/current Game submission deve ser invalidada por epoch/generation antes de Shell/Hub assumir a surface.
|
|
- Hub/Shell renderiza local/sincrono por politica Shell.
|
|
- Retornar ao Game incrementa epoch novamente e retoma o game task/process.
|
|
- O primeiro frame de retorno ao Game deve vir de uma nova Game submission; ate la a surface pode manter ultimo Hub frame ou limpar conforme politica visual.
|
|
- Transicoes suaves ficam fora do escopo; o requisito minimo e ownership correto e ausencia de frame obsoleto apresentado apos troca de dono.
|
|
|
|
Questao de escopo levantada:
|
|
|
|
No ciclo `Game -> Shell -> Game`, pode existir um game pausado e um app Shell VM-backed aberto ao mesmo tempo. Isso cruza render ownership com lifecycle/process policy.
|
|
|
|
Direcao provisoria para esta agenda:
|
|
|
|
- somente um Game foreground/pausado por vez;
|
|
- somente um Shell foreground aberto por vez no v1;
|
|
- background execution fica fora deste contrato;
|
|
- Shell VM-backed durante Game pausado deve seguir lifecycle de Shell e nao ganha politica frame-paced propria;
|
|
- a VM/game pausado nao deve continuar executando frames logicos enquanto o Shell VM-backed esta foreground;
|
|
- a existencia simultanea de um Game pausado e um Shell VM-backed foreground precisa ser tratada como politica de SystemOS/lifecycle, nao como detalhe do render worker.
|
|
|
|
Essa pergunta foi separada na `DSC-0041`. Para `DSC-0040`, o ponto minimo esta respondido: render ownership deve acompanhar o foreground visual owner e invalidar submissions do owner anterior por epoch/generation.
|
|
|
|
### Topico 7 - Erros, drops e telemetria de render
|
|
|
|
**Status:** direcao respondida.
|
|
|
|
**Pergunta de fechamento:** quais eventos minimos o sistema registra para render produzido, consumido, descartado, atrasado, apresentado e com erro?
|
|
|
|
Quando rasterizacao sai do fluxo da VM, panic/erro de render, frame drop, atraso, consumo e present precisam ser reportados sem quebrar determinismo da VM. Sem telemetria minima, latest-wins pode esconder perda de frames.
|
|
|
|
**Direcao aceita:** render assíncrono e best-effort observavel, nao handshake semantico com a VM. A VM produz frames sequenciais no ritmo do `FrameScheduler`; o worker consome/apresenta o mais recente possivel; drops, repeats, erros e descartes por epoch/stale ownership aparecem em telemetry, mas nao alteram diretamente a semantica do programa dentro da VM.
|
|
|
|
**Contrato minimo:**
|
|
|
|
- a VM nao observa erro/drop de render como retorno sincrono nem como semantica de programa;
|
|
- render telemetry e preocupacao de host/runtime/system, debugger, profiler e certificacao;
|
|
- erro de raster descarta a submission afetada, registra telemetry e preserva o ultimo frame valido/surface atual quando possivel;
|
|
- erro de present registra telemetry e pode escalar para politica de host/system fault se for recorrente;
|
|
- panic no worker deve virar falha controlada de worker/backend, nao panic propagado para a VM;
|
|
- o programa dentro da VM nao deve depender de confirmacao de present para avancar logica.
|
|
|
|
**Counters minimos:**
|
|
|
|
- `produced_submissions`;
|
|
- `replaced_before_consume`;
|
|
- `consumed_submissions`;
|
|
- `presented_frames`;
|
|
- `repeated_presents`;
|
|
- `render_errors`;
|
|
- `present_errors`;
|
|
- `stale_epoch_discards`;
|
|
- `shutdown_discards`.
|
|
|
|
**IDs/estado minimo:**
|
|
|
|
- `last_produced_frame_id`;
|
|
- `last_consumed_frame_id`;
|
|
- `last_presented_frame_id`;
|
|
- `last_dropped_frame_id`;
|
|
- `last_error_frame_id`;
|
|
- `active_render_epoch`.
|
|
|
|
**Eventos minimos:**
|
|
|
|
- submission substituida no single-slot antes de consumo;
|
|
- worker consumiu uma submission;
|
|
- worker descartou submission por epoch/owner obsoleto;
|
|
- worker apresentou frame;
|
|
- worker repetiu ultimo frame por ausencia de submission nova;
|
|
- erro de raster;
|
|
- erro de present;
|
|
- shutdown/cancel descartou pending/current.
|
|
|
|
## Fechamento dos Topicos
|
|
|
|
Esta agenda pode ser considerada madura para decisao quando os sete topicos acima tiverem uma resposta de direcao. As respostas nao precisam implementar render worker agora, mas precisam ser concretas o suficiente para produzir uma decision sem reabrir a arquitetura base durante o plan.
|
|
|
|
## Criterio para Encerrar
|
|
|
|
Esta agenda pode virar decisao quando houver consenso sobre:
|
|
|
|
- protocolo de handoff VM/render;
|
|
- ownership de submissions e recursos compartilhados;
|
|
- politica de backpressure;
|
|
- fronteira entre runtime contract e implementacao host/hardware;
|
|
- telemetria minima;
|
|
- o que fica fora de escopo antes do primeiro worker real.
|
|
|
|
## Discussion
|
|
|
|
Aberta em 2026-06-04 apos verificar que `DSC-0038` preparou snapshots fechados e latest-wins, mas nao decidiu thread/core separado.
|
|
|
|
Atualizacao de 2026-06-04:
|
|
|
|
Antes de discutir worker/thread de render, precisamos fechar uma pre-condicao de composicao do pacote atual. A pergunta operacional foi: comandos de primitivas em `gfx2d` sao desenhados apos qualquer blit/raster dos layers?
|
|
|
|
O estado atual e misto:
|
|
|
|
- No caminho direto `Gfx::render_game2d_frame_packet`, `render_no_scene_frame()` roda antes e depois `packet.gfx2d` e aplicado. Nesse caso, primitivas ficam depois da composicao base.
|
|
- No caminho real de `Hardware::publish_render_submission`, quando `frame_composer.active_scene_id().is_some()`, o codigo ignora o `Game2DFramePacket` recebido e chama `frame_composer.render_frame(&mut self.gfx)`.
|
|
- `FrameComposer::render_frame` atualiza cache/resolver e chama `gfx.render_scene_from_cache(...)`, que faz o blit/raster dos layers, mas nao aplica `packet.gfx2d`.
|
|
|
|
Conclusao provisoria: para frames com scene ativa, ainda nao ha garantia de que `gfx2d` seja desenhado apos os layer blits; na pratica, esses comandos podem ser descartados nesse branch. Isso deve ser resolvido antes de transformar `RenderSubmission` em fronteira de handoff para outro worker/core, porque o worker precisa consumir o pacote fechado completo, nao reconstruir comportamento a partir de estado vivo do `FrameComposer`.
|
|
|
|
Resposta de direcao em 2026-06-04:
|
|
|
|
As primitivas `gfx2d` devem funcionar como overlay do frame Game 2D. Elas precisam ser desenhadas apos a composicao canonical de scene/layers/sprites tanto em frames sem scene ativa quanto em frames com scene ativa. O comportamento esperado e:
|
|
|
|
```text
|
|
Game2D composer/layers/sprites
|
|
-> gfx2d primitives overlay
|
|
-> present/publication
|
|
```
|
|
|
|
Isso implica que o branch com scene ativa em `Hardware::publish_render_submission` nao deve contornar o `Game2DFramePacket` nem perder `packet.gfx2d`. A solucao futura deve fazer o consumidor de `RenderSubmissionPacket::Game2D` aplicar a mesma ordem de composicao nos dois caminhos.
|
|
|
|
Essa regra tambem reforca a fronteira necessaria para render worker/thread: o pacote fechado deve conter tudo que o consumidor precisa para compor o frame visivel. O worker nao deve depender de chamar um caminho alternativo baseado em estado vivo do `FrameComposer` que ignora parte do packet.
|
|
|
|
Atualizacao sobre Shell UI em 2026-06-04:
|
|
|
|
`ShellUi` e `Game2D` sao separados no contrato e no roteamento, mas compartilham os mesmos helpers finais de desenho no `Gfx`. `ShellUiFramePacket` contem comandos `gfxui`, enquanto `Game2DFramePacket` contem `composer` + `gfx2d`. No consumidor local, `apply_gfxui_command` e `apply_gfx2d_command` chamam os mesmos primitives finais (`clear`, `fill_rect`, `draw_line`, `draw_text`, etc.).
|
|
|
|
Mover o render real para outro core/thread nao deve atrapalhar Shell UI se a fronteira for respeitada: Shell update/layout/input continuam no core logico, produzem um `ShellUiFramePacket` fechado, e o render worker consome apenas esse snapshot imutavel.
|
|
|
|
O risco especifico do Shell UI nao e rasterizar em outro core. O risco e deixar o worker depender de estado vivo de Shell/SystemOS, janelas, ponteiros ou input enquanto desenha. Hoje o Hub monta o packet lendo estado de OS/janelas/input e publica imediatamente. Num modelo paralelo, essa montagem deve continuar antes do handoff; o worker deve receber so comandos fechados e recursos estaveis.
|
|
|
|
Portanto, para Shell UI, a regra de handoff deve ser:
|
|
|
|
```text
|
|
Shell/SystemOS update + input sampling
|
|
-> build ShellUiFramePacket from current UI state
|
|
-> hand off immutable ShellUi submission
|
|
-> render worker consumes commands only
|
|
```
|
|
|
|
Isso preserva a UI sobre primitivas sem exigir que o render worker entenda window manager, foco, input ou lifecycle.
|
|
|
|
Direcao refinada em 2026-06-04:
|
|
|
|
Render paralelo faz mais sentido como capacidade para workloads de game 2D/3D do que como obrigacao universal do runtime. Um app UI/Shell pode nao se beneficiar de separar rasterizacao em outro core, porque seu custo principal tende a estar em estado de UI, layout, input, foco, janelas e lifecycle, nao em um pipeline grafico pesado.
|
|
|
|
Portanto, a decisao futura nao deve assumir que todo `RenderSubmission` passa obrigatoriamente por um worker dedicado. A politica deve poder variar por `AppMode`/pipeline:
|
|
|
|
- `Game2D`/futuro `Game3D` podem usar handoff para render worker/core quando houver ganho real de frame time ou isolamento.
|
|
- `ShellUi` pode continuar em consumo sincrono/local se isso for mais simples, deterministico e barato.
|
|
- O contrato comum deve continuar sendo o packet fechado; a politica de execucao do consumidor pode ser diferente por pipeline.
|
|
|
|
Essa distincao evita otimizar a Shell UI como se fosse um renderer de jogo. Para UI, o valor principal de `ShellUiFramePacket` e manter a fronteira limpa e testavel; paralelismo deve ser opcional.
|
|
|
|
## Resolution
|
|
|
|
Direcao da agenda respondida. Os sete topicos tem recomendacao suficiente para virar decision:
|
|
|
|
- `Game2DFramePacket` deve ser snapshot completo, com `gfx2d` como overlay apos composicao de scene/layers/sprites;
|
|
- recursos pesados cruzam a fronteira por IDs/handles estaveis e APIs read-only, nao por handles mutaveis vivos;
|
|
- handoff base e single-slot latest-wins, sem bloquear a VM/produtor;
|
|
- frame pacing fica sob `FrameClock`/`FrameScheduler`, nao sob ACK do render worker;
|
|
- politica de render varia por `AppMode`/pipeline: Game frame-paced; Shell lifecycle/event-driven e local/sincrono por default;
|
|
- transicoes de foreground visual owner usam epoch/generation para invalidar submissions obsoletas;
|
|
- telemetria minima torna drops, repeats, erros e stale discards observaveis sem virar semantica da VM.
|
|
|
|
Detalhes de foreground stack, Game pausado e coexistencia com Shell VM-backed foram separados na `DSC-0041`.
|
|
|
|
## Next Step
|
|
|
|
Transformar esta agenda em decision antes de qualquer spec ou codigo de render worker.
|