From 8f37d971cda41a5918eb4731c7b7ab12dd6f78c8 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sun, 28 Jun 2026 09:35:08 +0100 Subject: [PATCH] [PERF] Async Background Work Lanes for Assets and FS --- discussion/index.ndjson | 6 +- ...background-work-lanes-for-assets-and-fs.md | 564 ++++++++++++++++-- ...f-gfx-render-pipeline-and-dirty-regions.md | 409 ------------- ...nc-work-lane-and-asset-backlog-contract.md | 336 +++++++++++ ...ync-work-lane-specification-propagation.md | 118 ++++ ...-async-work-lane-runtime-infrastructure.md | 119 ++++ ...5-asset-backlog-and-stable-slot-handles.md | 127 ++++ ...t-backlog-public-api-and-status-surface.md | 124 ++++ ...e-memcard-and-fs-integration-boundaries.md | 120 ++++ ...t-pipeline-contract-tests-and-telemetry.md | 126 ++++ 10 files changed, 1591 insertions(+), 458 deletions(-) delete mode 100644 discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md create mode 100644 discussion/workflow/decisions/DEC-0034-async-work-lane-and-asset-backlog-contract.md create mode 100644 discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md create mode 100644 discussion/workflow/plans/PLN-0124-async-work-lane-runtime-infrastructure.md create mode 100644 discussion/workflow/plans/PLN-0125-asset-backlog-and-stable-slot-handles.md create mode 100644 discussion/workflow/plans/PLN-0126-asset-backlog-public-api-and-status-surface.md create mode 100644 discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md create mode 100644 discussion/workflow/plans/PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index f2a5c205..eb2097e5 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":43,"AGD":44,"DEC":34,"PLN":123,"LSN":50,"CLSN":1}} +{"type":"meta","next_id":{"DSC":43,"AGD":44,"DEC":35,"PLN":129,"LSN":50,"CLSN":1}} {"type":"discussion","id":"DSC-0039","status":"abandoned","ticket":"render-pipeline-family-and-future-3d","title":"Render Pipeline Family and Future 3D","created_at":"2026-06-04","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","architecture","pipeline"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-render-pipeline-family-and-future-3d.md","status":"abandoned","created_at":"2026-06-04","updated_at":"2026-06-04","_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."} {"type":"discussion","id":"DSC-0040","status":"done","ticket":"vm-render-parallel-execution-boundary","title":"VM and Render Parallel Execution Boundary","created_at":"2026-06-04","updated_at":"2026-06-06","tags":["runtime","renderer","vm","concurrency","architecture","perf"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0040-vm-render-parallel-execution-boundary/LSN-0048-render-workers-need-a-closed-packet-contract.md","status":"done","created_at":"2026-06-06","updated_at":"2026-06-06"}]} {"type":"discussion","id":"DSC-0041","status":"open","ticket":"foreground-stack-game-pause-shell-vm-backed","title":"Foreground Stack, Game Pause, and VM-Backed Shell Coexistence","created_at":"2026-06-05","updated_at":"2026-06-05","tags":["runtime","os","lifecycle","shell","game","vm","foreground","architecture"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-foreground-stack-game-pause-shell-vm-backed.md","status":"open","created_at":"2026-06-05","updated_at":"2026-06-05"}],"decisions":[],"plans":[],"lessons":[]} @@ -18,9 +18,9 @@ {"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0008","status":"done","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-06-04","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0008-perf-runtime-telemetry-hot-path/LSN-0026-push-based-telemetry-model.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} -{"type":"discussion","id":"DSC-0009","status":"open","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0009","status":"in_progress","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-06-28","tags":["perf","asset","fs","async","scheduler","runtime"],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-06-28"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-async-work-lane-and-asset-backlog-contract.md","status":"accepted","created_at":"2026-06-28","updated_at":"2026-06-28","ref_agenda":"AGD-0008"}],"plans":[{"id":"PLN-0123","file":"PLN-0123-async-work-lane-specification-propagation.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0124","file":"PLN-0124-async-work-lane-runtime-infrastructure.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0125","file":"PLN-0125-asset-backlog-and-stable-slot-handles.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0126","file":"PLN-0126-asset-backlog-public-api-and-status-surface.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0127","file":"PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0128","file":"PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]}],"lessons":[]} {"type":"discussion","id":"DSC-0010","status":"done","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]} -{"type":"discussion","id":"DSC-0011","status":"open","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0010","file":"AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0011","status":"abandoned","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-06-28","tags":[],"agendas":[{"id":"AGD-0010","file":"AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-06-28","_override_reason":"User explicitly chose to remove this agenda because it no longer applies to the current render scenario; future optimizations can be discussed when needed."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon this discussion because it no longer applies to the current render scenario; future optimizations can be discussed when needed."} {"type":"discussion","id":"DSC-0012","status":"done","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-04-19","tags":["perf","runtime","syscall","telemetry","debug","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0034","file":"discussion/lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19"}]} {"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":"discussion/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":"discussion/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"}]} diff --git a/discussion/workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md b/discussion/workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md index 28eee281..a7dc7a2e 100644 --- a/discussion/workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md +++ b/discussion/workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md @@ -2,84 +2,556 @@ id: AGD-0008 ticket: perf-async-background-work-lanes-for-assets-and-fs title: Agenda - [PERF] Async Background Work Lanes for Assets and FS -status: open +status: accepted created: 2026-03-27 -resolved: -decision: -tags: [] +resolved: 2026-06-28 +decision: DEC-0034 +tags: [perf, asset, fs, async, scheduler, runtime] --- # Agenda - [PERF] Async Background Work Lanes for Assets and FS +## Contexto + +Depois das decisoes de render worker, a concorrencia de render saiu do escopo +desta agenda. O problema restante e a lane de trabalho assincrono para IO, +decode e persistencia. + +O modelo de execucao que queremos discutir agora assume uma terceira core/lane +dedicada a trabalhos assincronos de runtime: + +```text +Core/lane 1: VM, firmware, SystemOS e frame logico +Core/lane 2: render worker / rasterizacao assincrona +Core/lane 3: async work lane para asset IO/decode e possivelmente FS +``` + +Essa terceira lane nao deve virar um pool grande e solto. A intencao e ter uma +fila previsivel, com capacidade limitada, telemetria e regras de saturacao. + +Para assets, a necessidade e concreta: `asset.load()` ainda cria uma thread do +SO por requisicao. Para FS, a motivacao provavel era evitar que operacoes de +leitura/escrita/listagem/delete, hoje chamadas de forma sincrona pelo dispatch, +roubem tempo do frame quando o backend real envolver IO. Precisamos verificar +se FS deve usar a mesma lane agora ou apenas reservar compatibilidade. + ## Problema -`asset.load()` hoje cria uma thread do SO por requisicao. Ao mesmo tempo, `fs` ainda nao tem uma politica clara para sync assincrono barato em hardware simples. +`asset.load()` hoje dispara `thread::spawn` por requisicao de asset nao +residente. Isso escala mal, cria jitter e deixa o runtime sem uma politica +unica de backpressure. -O projeto precisa de paralelismo para IO/decode/sync, mas o target inclui handheld DIY e hardware barato, onde um pool grande ou explosao de threads pode piorar a latencia em vez de melhorar. +O dominio `fs` tambem tem operacoes potencialmente bloqueantes (`read`, +`write`, `list_dir`, `delete`, memcard commit/read/write) e a tabela de +syscalls ja as marca como nao deterministicas/custosas. Ainda assim, a +implementacao atual executa essas chamadas diretamente no caminho de dispatch. -## Dor +Sem um contrato explicito, cada dominio pode inventar sua propria concorrencia: +asset criando threads, FS bloqueando o frame, e memcard herdando comportamento +acidental do backend. + +## Pontos Criticos - `thread::spawn` por request escala mal e cria jitter. -- assets e `fs` competem por IO sem uma politica unica de fila/prioridade. -- sem lane dedicada, operacoes de background tendem a vazar custo para o main loop. -- sem disciplina, o host desktop vira referencia errada para hardware fraco. +- A terceira core/lane deve ser um recurso de plataforma controlado, nao uma + permissao geral para criar threads. +- Asset loading precisa de um pool/fila limitada para multiplos loads + pendentes. +- FS pode justificar a mesma lane quando envolver IO real, mas nao devemos + assinar uma API assincrona ampla sem entender a semantica de retorno. +- Commit/install em bancos residentes pode ter restricoes de determinismo e + ownership; decode pode ser assincrono, mas publicacao/instalacao talvez tenha + que cruzar de volta para a lane principal. +- A estrategia precisa funcionar em hardware alvo com poucos cores e tambem no + host desktop sem transformar desktop em referencia de custo errada. +- Render worker ja tem seu proprio contrato e nao deve compartilhar essa lane. ## Hotspots Atuais -- [asset.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/asset.rs#L353) -- [tick.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs#L53) -- [runner.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/host/prometeu-host-desktop-winit/src/runner.rs#L315) +- [asset.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/asset.rs#L438) - `AssetManager::load`. +- [asset.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/asset.rs#L507) - `thread::spawn` por load. +- [fs.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-hal/src/syscalls/domains/fs.rs#L4) - `fs` e `mem` marcados como superficies custosas/nao deterministicas. +- [dispatch.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs#L549) - chamadas `fs.*` executam no dispatch. +- [virtual_fs.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-system/src/services/fs/virtual_fs.rs#L69) - VFS normaliza path e chama backend diretamente. ## Alvo da Discussao -Fechar um modelo de execucao assincrona para `asset` e `fs` que seja previsivel em hardware simples. +Fechar um modelo de terceira core/lane para trabalho assincrono de runtime, +comecando por asset loading e avaliando se FS deve entrar na mesma lane no v1. + +O alvo nao e criar multitarefa geral, callbacks para guest, ou um scheduler +paralelo da VM. A lane deve executar trabalho de IO/decode/sync e publicar +resultados observaveis por status/polling em pontos previsiveis. ## O Que Precisa Ser Definido -1. Topologia de workers. - Escolher entre: - - uma thread dedicada para `asset` e outra para `fs`; - - um worker unico multiplexando filas; - - um pool minimo e fixo; - - proibicao explicita de `spawn` por request. +1. Topologia da terceira lane. + Definir: + - uma async work lane dedicada, separada da VM e do render worker; + - se a lane e uma thread fixa no host e uma core dedicada no hardware alvo; + - fallback quando nao houver core fisico dedicado; + - proibicao explicita de `thread::spawn` por request no caminho normal. -2. Separacao por dominio. - Decidir se `asset` e `fs` compartilham scheduler/fila ou se cada dominio tem lane propria. +2. Pool/fila de asset loading. + Definir: + - backlog de pedidos sem limite operacional fixo; + - exatamente um job ativo por vez na lane; + - o "pool" significa backlog e estados de job, nao multiplos workers reais; + - politica de substituicao/cancelamento para pedidos que miram o mesmo + bank/slot; + - status para pending/backlog, ativo, progresso, cancelamento, erro de + decode e sucesso. -3. Politica de prioridade. +3. Fases do asset load. + Separar: + - request e alocacao de handle; + - leitura de bytes; + - decode/materializacao; + - staging de resultado; + - install/commit em banco residente. + + Ponto a fechar: quais fases rodam na terceira lane e quais voltam para a + lane principal por determinismo/ownership. + +4. Inclusao de FS na mesma lane. + Verificar se `fs` deve usar a terceira lane no v1 para: + - leituras grandes; + - escritas; + - listagem; + - delete; + - memcard commit/read/write. + + Ponto a fechar: FS sync publico continua bloqueante/status-first, vira + request/poll, ou ganha apenas uma implementacao interna que drena trabalho em + pontos de frame. + +5. Separacao por dominio. + Decidir se `asset` e `fs` compartilham uma fila com classes de prioridade ou + se a terceira lane contem subfilas dedicadas. + +6. Politica de prioridade. Definir: - prioridade de loads visuais vs audio; - - prioridade de sync de save/config; - - limite de trabalho por frame para install/commit. + - prioridade de save/config; + - prioridade de memcard commit; + - starvation prevention; + - limite de trabalho de instalacao/publicacao por frame. -4. Modelo de retorno. - Fechar como o guest observa backlog, cancelamento e saturacao: - - status imediato de fila cheia; - - status de pending; - - metrica de backlog; - - politica de cancelamento. +7. Modelo de retorno e telemetria. + Fechar como guest/runtime observa: + - `pending`; + - `loading`; + - `ready`; + - `canceled`; + - `backend_unavailable`; + - backlog; + - posicao de um asset no backlog; + - progresso percentual do asset ativo; + - drops/retries quando aplicavel. -5. Orçamento para hardware barato. - Definir quantas threads o runtime pode assumir como baseline. +8. Orcamento para hardware barato. + Definir o baseline: + - VM principal; + - render worker; + - terceira async work lane; + - limites de memoria para jobs pendentes e buffers temporarios. -## Open Questions de Arquitetura +## Opcoes -1. O v1 precisa de lanes separadas para `asset` e `fs` ou basta uma fila central com classes de prioridade? -2. Decode de asset fica no mesmo worker do IO ou em fase distinta? -3. Commit/install no device continua no main thread por determinismo? +### Opcao A - Terceira lane unica com subfilas e pool limitado de assets + +- **Abordagem:** criar uma async work lane dedicada. Assets usam um pool/fila + de jobs nessa lane; FS tambem pode submeter jobs na mesma lane, com + prioridade menor ou classe separada. +- **Pro:** modelo simples de budget; evita explosao de threads; encaixa no alvo + de terceira core; permite compartilhar backpressure e telemetria. +- **Contra:** assets e FS podem competir por IO; precisa politica clara para + evitar que save/config fique atras de loads longos. +- **Manutenibilidade:** boa se a lane tiver tipos de job e estados explicitos. + +### Opcao B - Lane de assets agora, FS apenas reservado + +- **Abordagem:** fechar v1 somente para asset loading, removendo `spawn` por + request. FS continua sincrono por enquanto, mas o contrato da lane reserva + compatibilidade para FS no futuro. +- **Pro:** menor escopo; resolve a dor concreta de assets rapidamente; evita + alterar semantica de FS antes da agenda de app home filesystem. +- **Contra:** nao resolve stalls reais de FS/memcard; pode exigir refatorar a + lane depois para aceitar FS. +- **Manutenibilidade:** boa no curto prazo, com risco de segunda migracao. + +### Opcao C - Lanes separadas para asset e FS + +- **Abordagem:** criar uma lane/pool para assets e outra para FS. +- **Pro:** isolamento forte; FS/save nao compete diretamente com loads de + textura/audio/cena. +- **Contra:** consome mais threads/cores; contraria a premissa de terceira core + unica; pior para hardware simples. +- **Manutenibilidade:** media; isolamento ajuda, mas a topologia fica pesada. + +### Opcao D - Pool global pequeno sem nocao de terceira lane + +- **Abordagem:** trocar `spawn` por request por um pool global fixo. +- **Pro:** simples de implementar em host desktop. +- **Contra:** perde a arquitetura de lanes por core; pode mascarar custo em + desktop; fica menos claro como mapear para hardware alvo. +- **Manutenibilidade:** baixa para o modelo de console se virar dependencia + acidental de scheduler do SO. + +## Sugestao / Recomendacao + +Recomendo seguir com a Opcao A como direcao de arquitetura, mas fechar a +decisao em duas camadas: + +1. Contrato obrigatorio agora: + - terceira async work lane como recurso runtime; + - assets deixam de usar `thread::spawn` por requisicao; + - asset loading passa por backlog consultavel/reordenavel; + - status, progresso e telemetria de backlog ficam explicitos. + +2. Contrato condicionado para FS: + - FS deve ser analisado como consumidor legitimo da mesma lane porque suas + operacoes podem envolver IO real e ja sao custosas/nao deterministicas; + - a decisao precisa escolher entre incluir FS no v1 ou reservar a lane para + FS sem mudar ainda a superficie publica; + - memcard precisa ser considerado junto com FS, porque usa a mesma + capacidade e backend de persistencia. + +O ponto importante: a terceira core/lane deve ser a unidade de concorrencia. +O pool de assets deve existir dentro desse modelo, nao como criacao livre de +threads por load. + +## Respostas Consolidadas Ate Aqui + +1. O pool de assets deve ser uma lane serial. + - Ha exatamente um job ativo na async work lane. + - O pool representa backlog, estados e controle de ordem, nao multiplos + workers executando em paralelo. + +2. O backlog de pedidos deve ser semanticamente ilimitado. + - Nao existe `queue_full` como status normal do dominio. + - Como cada pedido mira um `bank_type/slot` e pedidos novos para o mesmo + alvo substituem os anteriores, o backlog efetivo fica limitado pela soma + dos slots dos bancos. + - O contrato nao precisa expor limite de fila, mas a implementacao deve + garantir que metadados de handles cancelados/superseded nao crescam sem + coleta. + +3. Asset loading precisa expor consulta e reordenacao do backlog. + - Deve existir API publica/runtime para consultar onde um asset esta no + backlog. + - Deve existir capacidade de adiantar ou atrasar um pedido de asset na fila. + - O asset ativo deve expor progresso percentual. + - Requests novos para o mesmo `bank_type/slot` devem cancelar pedidos + anteriores pendentes para aquele alvo, evitando carregar asset que ja foi + substituido antes de executar. + - Se o alvo ja estiver carregado com o mesmo `asset_id`, o novo request nao + entra no backlog; ele retorna imediatamente pronto com handle disponivel. + - Se um novo request substituir o alvo atualmente ativo, a lane deve cancelar + em ponto cooperativo quando possivel; se o trabalho nao puder ser + interrompido, o resultado antigo deve ser descartado ao final por geracao + superseded. + - `superseded` significa que um request foi substituido por outro request + mais novo para o mesmo `bank_type/slot`. Nao e erro e nao e cancelamento + manual; e obsolescencia operacional do pedido antigo. + - Handles representam o slot do bank como alvo estavel. Dentro do estado do + handle deve existir uma secao de request/backlog para aquele slot, + informando se ha pedido pendente/ativo, qual asset foi solicitado, + progresso, posicao e geracao. + - Handles devem ser capazes de consultar o estado do alvo `bank_type/slot` + a qualquer momento. Se nao houver asset valido carregado e tambem nao + houver request pendente para aquele alvo, a consulta deve retornar estado + explicito de vazio/invalido, nao falhar por ausencia de request ativo. + +4. Decode/materializacao fria rodam na terceira lane. + - Leitura e decode de asset podem acontecer fora da lane principal. + - O resultado deve voltar como material pronto/staged. + +5. Install/commit em bancos residentes continua na lane principal. + - A async lane prepara trabalho. + - A lane principal publica/instala em pontos previsiveis por ownership, + determinismo e integracao com bancos/render/audio. + +6. FS e memcard entram como consumidores possiveis da mesma lane, mas precisam + ser melhor amarrados com a agenda de app home filesystem. + - A `DSC-0007` / `AGD-0006` deve ser ajustada para estabelecer melhor a + superficie publica e o modelo async/sync de FS. + - Esta agenda deve afirmar a existencia da lane para trabalhos async de IO + desse tipo e permitir que FS a consuma. + - Questoes de API publica de FS, request/poll e semantica de app home devem + ficar a cargo da agenda 6. + - FS, memcard e assets nao devem operar sobrepostos quando isso gerar + concorrencia real de backend; se houver competicao, a fila unica resolve a + ordem. + +7. Prioridade quando houver competicao: + - memcard commit/write; + - FS write/config; + - asset visual/audio load; + - list/read nao critico. + +8. Status-first continua valendo. + - `backend_unavailable`, `canceled`, erro de decode e estados de job sao + status operacionais. + - Cancelamento por superseding de pedido no mesmo bank/slot e resultado + operacional normal, nao fault. + - Nao ha `queue_full` no contrato esperado. + - Trap permanece para violacao estrutural, como argumento invalido, + capability ausente ou handle estruturalmente invalido. + +9. Telemetria minima: + - backlog atual; + - posicao por asset; + - progresso percentual do asset ativo; + - jobs submetidos, concluidos, falhos e cancelados; + - tempo por job; + - percentis por asset enquanto estiver carregando na lane. + +10. Escopo da agenda. + - O foco desta agenda e a lane, assets e memcard/game persistence. + - FS entra como consumidor permitido da lane de IO, mas as decisoes de API + publica de FS pertencem a `AGD-0006`. + +11. API de backlog. + - A API inicial sugerida cobre os casos esperados. + - `asset.backlog_promote(handle)` deve ser mantido como atalho oficial para + `asset.backlog_move(handle, 1)`. + - `asset.backlog_demote(handle)` deve ser mantido como atalho oficial para + mover o pedido para o fim dos pendentes. + - Os atalhos nao criam semantica nova; apenas evitam que o caller calcule + posicoes comuns. + - O handle deve permitir consulta estavel do slot do bank mesmo quando nao ha + asset valido carregado. + - O estado do handle deve separar: + - estado do slot/residencia atual; + - estado do request/backlog atual para aquele slot. + - Operacoes que mudam estado, como commit/cancel/reorder, atuam sobre a + secao de request/backlog do handle e precisam validar geracao/request atual + para nao agir sobre um pedido substituido. + +12. Cancelamento por substituicao. + - Quando um request novo chega para o mesmo `bank_type/slot`, o request + anterior e marcado imediatamente como `superseded`. + - Se o request anterior ainda estiver no backlog, ele sai da fila. + - Se o request anterior estiver ativo, a lane tenta cancelar + cooperativamente quando a fase atual permitir. + - Se a fase ativa nao puder ser interrompida com baixo custo, ela termina e + o resultado e descartado ao final porque a geracao do alvo ja mudou. + - Como o handle pertence ao slot do bank, ele continua consultavel; sua + secao de request deve indicar que a geracao anterior foi `superseded` ou + que nao ha request ativo para aquele alvo. + +13. Fases de progresso. + - `read/decode/stage` e suficiente como modelo minimo para `GLYPH`, `SOUND` + e `SCENE`. + - Subfases futuras podem detalhar progresso dentro de `decode` sem mudar o + contrato geral. + +14. Percentis. + - Percentis devem existir por `bank_type` como telemetria principal. + - Percentis por `asset_id` tambem sao uteis, mas podem usar janela pequena + ou agregador leve. + - Percentis sao atualizados no fechamento do job, nao durante o hot path. + +15. Ajuste esperado na agenda de FS. + - `AGD-0006` deve registrar que existe uma async IO lane compartilhavel por + FS/memcard/assets. + - Esta agenda so decide que FS pode consumir a lane. + - A API publica de FS, incluindo request/poll ou sync aparente, pertence a + `AGD-0006`. + +## API Inicial Sugerida Para Assets + +Esta agenda ainda nao fecha nomes finais de ABI, mas a direcao inicial sugerida +e manter `asset.load/status/commit/cancel` e adicionar uma superficie pequena +para backlog: + +1. `asset.backlog_info() -> (status, pending_count, active_handle, active_asset_id, active_bank_type, active_slot, active_progress)` + - Consulta o estado geral da lane de assets. + - `active_progress` usa escala inteira, preferencialmente `0..10000` para + permitir duas casas decimais sem float. + +2. `asset.backlog_position(handle) -> (status, state, position, progress)` + - Consulta um pedido especifico. + - `position = 0` significa job ativo. + - `position > 0` significa posicao no backlog. + - `position = -1` pode representar nao enfileirado/pronto/cancelado, + dependendo do `state`. + +3. `asset.backlog_move(handle, new_position) -> status` + - Move um pedido pendente para uma posicao absoluta. + - `new_position = 1` significa primeiro pendente depois do ativo. + - Nao interrompe diretamente o job ativo; substituicao do mesmo + `bank_type/slot` usa a regra de superseding. + +4. `asset.backlog_promote(handle) -> status` + - Atalho para mover o pedido para a frente do backlog pendente. + +5. `asset.backlog_demote(handle) -> status` + - Atalho para mover o pedido para o fim do backlog pendente. + +6. `asset.target_status(bank_type, slot) -> (status, asset_id, handle, state, position, progress)` + - Consulta por alvo operacional, que e o que realmente controla superseding. + - Ajuda quando o caller quer saber "o que esta programado para este slot". + +Estados sugeridos para `state`: + +- `empty`; +- `invalid`; +- `queued`; +- `active`; +- `ready`; +- `committed`; +- `canceled`; +- `superseded`; +- `error`; +- `backend_unavailable`. + +Regra de design: APIs de backlog sao operacionais e status-first. Reordenar um +handle inexistente, ja concluido ou ja superseded retorna status operacional +apropriado quando o handle e conhecido; violações estruturais continuam Trap. + +Handles sao observaveis e representam slots de banks. Um handle conhecido pode +ser consultado mesmo quando o slot alvo nao contem asset valido carregado; nesse +caso a consulta deve retornar estado como `empty` ou `invalid`. + +O estado do handle deve conter pelo menos duas partes: + +```text +slot_state: + loaded_asset_id + resident_state + +request_state: + requested_asset_id + generation + queued/active/ready/canceled/superseded/error + backlog_position + progress +``` + +Mutacoes seguem regras mais estritas e devem falhar operacionalmente quando a +secao de request/backlog do handle nao representa a geracao atual. + +## Progresso e Percentis + +### Progresso percentual + +Sugestao: + +- usar progresso em base inteira `0..10000`; +- quando o tamanho total de bytes for conhecido, calcular progresso por fases + ponderadas; +- quando o decode nao for linear ou nao houver tamanho confiavel, expor + progresso por fase com marcos conservadores. + +Modelo inicial de fases: + +```text +queued -> 0 +read -> 0..4000 +decode -> 4000..9000 +stage -> 9000..10000 +ready -> 10000 +``` + +Se uma fase nao consegue reportar progresso interno, ela deve manter o ultimo +marco e saltar para o proximo marco ao concluir. Isso evita progresso falso +sem perder visibilidade de fase. + +### Percentis por asset + +Sugestao: + +- registrar duracoes por asset no fim de cada job, nao por tick/frame; +- manter agregadores pequenos por `asset_id` e por `bank_type`; +- calcular percentis a partir de janela/ring buffer pequeno ou histograma + barato por bucket de duracao; +- nunca atualizar percentis no loop quente de decode por item/pixel; +- o job ativo pode expor tempo decorrido e fase atual; percentis sao atualizados + apenas no fechamento do job. + +## Perguntas em Aberto + +- [x] A agenda esta madura para virar decisao normativa ou ainda falta algum + caso de asset/memcard/lane? +- [x] A estrutura minima de `slot_state` e `request_state` no handle esta + completa ou falta algum campo para commit/cancel/reorder seguro? + +## Resolucao + +A agenda esta madura para decisao normativa. + +A decisao deve formalizar: + +- a terceira async work lane serial; +- um job ativo por vez; +- backlog efetivamente limitado pela unicidade de `bank_type/slot`; +- handles como observadores estaveis de slots de bank; +- `slot_state` e `request_state` dentro do handle; +- cancelamento/superseding por novo request para o mesmo alvo; +- load pronto imediato quando o asset ja esta residente no alvo; +- decode/materializacao na lane async; +- commit/install na lane principal; +- memcard como consumidor prioritario da mesma lane; +- FS como consumidor permitido, deixando API publica de FS para `AGD-0006`; +- progresso por fases e percentis atualizados no fechamento do job. ## Dependencias -- `../specs/09-events-and-concurrency.md` -- `../specs/15-asset-management.md` -- `../specs/16a-syscall-policies.md` -- `014-app-home-filesystem-surface-and-semantics.md` +- `docs/specs/runtime/09-events-and-concurrency.md` +- `docs/specs/runtime/15-asset-management.md` +- `docs/specs/runtime/16a-syscall-policies.md` +- `discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md` -## Criterio de Saida Desta Agenda +## Criterio para Encerrar Pode virar PR quando houver decisao escrita sobre: -- numero e tipo de workers aceitos no baseline; -- fila/prioridade de `asset` e `fs`; -- proibicao ou aceitacao limitada de `thread::spawn` por request; -- modelo de status/telemetria para backlog e saturacao. +- existencia e papel da terceira async work lane; +- relacao dessa lane com a terceira core no hardware alvo e fallback no host; +- desenho do backlog serial de asset loading; +- regra de cancelamento/substituicao para requests no mesmo bank/slot; +- proibicao de `thread::spawn` por load no caminho normal; +- fases de asset load que rodam na lane vs lane principal; +- inclusao condicionada de FS/memcard nessa mesma lane; +- API de consulta e reordenacao do backlog de assets; +- comportamento de request imediato quando o asset ja esta residente no alvo; +- regra de bounded backlog por unicidade de `bank_type/slot`; +- politica de prioridade entre asset, FS e memcard; +- status e telemetria para backlog, progresso, percentis, cancelamento e falha. + +## Discussao + +- 2026-06-28: Agenda atualizada para remover render worker do escopo, assumir + uma terceira core/lane para trabalhos assincronos, exigir pool/fila limitada + para asset loading e reavaliar o papel de FS/memcard como consumidores da + mesma lane. +- 2026-06-28: Discussao consolidou que a async work lane deve ser serial, com + um job ativo, backlog semanticamente ilimitado, consulta/reordenacao publica + de backlog de assets, progresso percentual do asset ativo, decode na lane + async, install/commit na lane principal, e FS/memcard como consumidores a + alinhar com a agenda de app home filesystem. +- 2026-06-28: Acrescentada regra de asset pipe: requests para o mesmo bank/slot + devem cancelar pedidos anteriores pendentes para aquele alvo, tratando o + pedido mais novo como substituto operacional. +- 2026-06-28: Discussao refinou que o alvo canonico e `bank_type/slot`; um + asset ja residente deve retornar handle pronto sem entrar no backlog; o + backlog efetivo e limitado pela soma dos slots dos bancos; FS fica como + consumidor permitido da lane, enquanto API publica de FS permanece na agenda + 6; progresso deve usar fases conservadoras e percentis devem ser atualizados + no fechamento do job, nao no hot path. +- 2026-06-28: Open questions restantes foram consolidadas: API de backlog + aceita com `promote/demote` como atalhos; superseding marca requests + substituidos por outro request para o mesmo `bank_type/slot`; cancelamento do + ativo e cooperativo quando barato e descarte por geracao quando nao for; + `read/decode/stage` cobre GLYPH/SOUND/SCENE; percentis ficam por `bank_type` + e por `asset_id`; API publica de FS permanece em `AGD-0006`. +- 2026-06-28: Adicionada a expectativa de que handles possam consultar o alvo + `bank_type/slot` a qualquer momento, inclusive quando nao houver asset valido + carregado; mutacoes devem continuar protegidas por request/geracao para evitar + commit/cancel acidental de pedidos substituidos. +- 2026-06-28: Refinado o modelo de handle: o handle pertence ao slot do bank e + contem uma secao de estado de request/backlog para aquele slot. Consulta e + residencia ficam estaveis por alvo; mutacoes atuam sobre a geracao atual do + request dentro do handle. diff --git a/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md b/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md deleted file mode 100644 index 42307ee1..00000000 --- a/discussion/workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md +++ /dev/null @@ -1,409 +0,0 @@ ---- -id: AGD-0010 -ticket: perf-gfx-render-pipeline-and-dirty-regions -title: Agenda - [PERF] GFX Render Pipeline and Dirty Regions -status: open -created: 2026-03-27 -resolved: -decision: -tags: [] ---- - -# Agenda - [PERF] GFX Render Pipeline and Dirty Regions - -## Problema - -O renderer `gfx` recompõe a cena inteira a cada frame logico, mesmo quando a mudanca visual e pequena. - -Hoje `render_all()` reconstrói buckets de sprites, escaneia os 512 sprites, redesenha layers e aplica dois passes fullscreen de fade sem politica de invalidacao. - -Ao mesmo tempo, a arquitetura atual ja opera como um framebuffer destrutivo em memoria: draws escrevem diretamente no buffer de trabalho e operacoes posteriores sobrescrevem o que estiver por baixo. A discussao nao e migrar para um modelo tipo GPU, scene graph ou retained rendering. - -Existe ainda um driver de produto importante: o objetivo continua sendo viabilizar hardware handheld proprio com limitacoes reais de orcamento e com economia de memoria o mais agressiva possivel. Isso significa que ganhos de CPU nao podem ser avaliados isoladamente; custo de RAM adicional, buffers extras e estruturas auxiliares entram diretamente no criterio de aceitacao. - -## Dor - -- custo visual basico cresce demais para hardware simples. -- pequenos updates pagam preco de full redraw. -- fade, HUD e world composition ficam sempre no caminho critico. - -## Hotspots Atuais - -- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs#L563) -- [gfx.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/gfx.rs#L671) - -## Alvo da Discussao - -Definir ate onde o v1 pode sofisticar o modelo de desenho destrutivo em memoria para reduzir custo e manter previsibilidade, sem migrar para um renderer de estilo GPU. - -## O Que Precisa Ser Definido - -1. Perfil real de custo. - Medir separadamente: - - rebuild de buckets; - - rasterizacao de layers; - - rasterizacao de sprites; - - fades fullscreen; - - conversao/copia no host; - - apresentacao no host. - -2. Granularidade minima de invalidacao compativel com framebuffer destrutivo. - Escolher entre: - - dirty flag por frame inteiro; - - dirty flags por subsistema (`world`, `hud`, `fade`, `sprites`); - - dirty regions restritas e derivadas de primitivas/sprites; - - combinacao minima viavel. - -3. Buckets e traversal de sprites. - Decidir se os buckets continuam sendo reconstruidos por frame ou se viram estrutura incremental. - -4. Fades. - Definir se fade neutro precisa bypass total e se fade parcial pode operar por regiao/camada. - -5. Cache de composicao. - Delimitar se layers estaticas/HUD podem manter cache intermediario. - -6. Primitivas e contrato operacional. - Definir quais draws devem continuar sendo vistos como escrita destrutiva direta no framebuffer e onde valem aceleracoes internas sem mudar a semantica observavel. - -7. Meta de custo. - Fechar qual teto de pixels/trabalho por frame e aceitavel no baseline. - -8. Orcamento de memoria. - Definir quais otimizacoes aceitam memoria adicional e quais precisam caber no modelo mais economico possivel para handheld barato. - -## Open Questions de Arquitetura - -1. Quais partes do custo atual estao no `render_all()` e quais partes estao na conversao/apresentacao do host? -2. Qual o maior ganho de curto prazo dentro do modelo atual: buckets, fades, HUD, primitivas ou host copy/convert? -3. Dirty regions restritas quebram alguma expectativa de determinismo visivel ou sao apenas um detalhe interno de implementacao? -4. O HUD deve continuar no mesmo pipeline da world scene? -5. Quais primitivas merecem tratamento especial para reduzir overdraw sem mudar a semantica de sobrescrita? -6. Qual o teto aceitavel de memoria extra para caches, buffers separados ou estruturas auxiliares no handheld alvo? - -## Problemas de Medio e Grande Porte Identificados - -### 1. Composicao completa do framebuffer a cada frame logico - -`render_all()` recompõe o `back` inteiro por software, mesmo quando a mudanca visual e pequena. - -Impacto: - -- custo cresce com a quantidade de pixels tocados, nao apenas com a quantidade de estado alterado; -- layers, sprites e fades disputam o mesmo budget de CPU; -- o modelo atual favorece previsibilidade sem ainda ter atalhos internos suficientes. - -### 2. Rasterizacao pixel a pixel de tilemaps - -Cada layer visivel percorre tiles e, para cada tile nao vazio, resolve indices e escreve pixel a pixel no framebuffer. - -Impacto: - -- o custo real esta mais na escrita de pixels do que na manutencao do `TileMap`; -- scroll e barato como estado, mas caro na hora de compor a imagem se tudo for repintado; -- layers estaticas continuam pagando custo de rasterizacao em toda recomposicao. - -### 3. Rasterizacao pixel a pixel de sprites e escolha por bucket full scan - -Os 512 sprites sao varridos para reconstruir buckets e os sprites ativos sao desenhados pixel a pixel. - -Impacto: - -- quando poucos sprites mudam, ainda existe custo fixo de rebuild dos buckets; -- overdraw entre sprites e layers pode explodir o numero de writes no `back`; -- a escolha de sprites ativos/prioridade ainda e simples demais para cenarios com alta ocupacao. - -### 4. Fades fullscreen com custo proporcional ao frame inteiro - -`scene fade` e `hud fade` percorrem o framebuffer inteiro quando ativos. - -Impacto: - -- custo grande e previsivel, mas potencialmente desproporcional; -- fade neutro ja pode ser bypass, mas fade parcial ainda custa uma passada completa; -- dois passes fullscreen no mesmo frame podem virar gargalo antes de layers ou sprites. - -### 5. HUD no mesmo caminho critico da composicao principal - -O HUD e redesenhado como tilemap depois da world scene e antes do `hud fade`. - -Impacto: - -- HUD estatico continua gerando trabalho por frame; -- mistura responsabilidades de world/UI no mesmo budget de composicao; -- dificulta isolar ganho de cache ou dirtying so para interface. - -### 6. Conversao obrigatoria de RGB565 para RGBA8 no host - -No `RedrawRequested`, o host percorre todo o `front_buffer` e converte para o frame do `pixels`. - -Impacto: - -- ha custo de leitura do framebuffer inteiro e escrita de um segundo buffer inteiro; -- esse custo existe mesmo quando o host esta apenas apresentando o mesmo quadro logico; -- pode competir com o custo do renderer sem estar visivel na discussao do `gfx`. - -### 7. Pipeline serializado no host desktop - -`tick`, `render_all()`, conversao de formato e apresentacao ocorrem no mesmo fluxo operacional. - -Impacto: - -- o frame time final acumula custo de runtime, composicao, copy/convert e present; -- falta desacoplamento para esconder latencia entre producao e apresentacao; -- dificulta atribuir gargalo sem instrumentacao por etapa. - -## Estudo Inicial de Possiveis Optimizacoes - -### A. Dirty flags por subsistema em vez de dirty rect generico - -Ideia: - -- flags independentes para `world`, `sprites`, `hud`, `scene_fade`, `hud_fade`, `host_present`. - -Vantagens: - -- preserva a semantica de framebuffer destrutivo; -- reduz recomposicao desnecessaria quando so um subsistema mudou; -- e muito mais simples de validar do que dirty regions arbitrarias. - -Riscos: - -- exige definir dependencias claras entre world, sprites, HUD e fades; -- pode induzir falsos positivos conservadores, o que e aceitavel no v1. - -### B. Rebuild incremental ou condicional dos buckets de sprite - -Ideia: - -- so reconstruir buckets quando algum `GfxSetSprite` mudar atividade, prioridade, banco ou tile; -- opcionalmente manter contadores/sinais de mutacao da OAM. - -Vantagens: - -- elimina custo fixo por frame quando OAM permanece estavel; -- combina com a ideia de sprites como estado, nao como draw list efemera. - -Riscos: - -- precisa invalidacao correta para evitar bucket desatualizado; -- ganho pode ser pequeno se o custo dominante estiver no draw dos sprites, nao no rebuild. - -### C. Separar world e HUD em buffers logicos distintos - -Ideia: - -- manter um buffer da world scene e um buffer do HUD, compondo no final apenas quando necessario. - -Vantagens: - -- HUD estatico deixa de participar da recomposicao da world; -- permite cache e fade especificos para cada dominio; -- se alinha bem com a separacao conceitual entre cena e interface. - -Riscos: - -- aumenta uso de memoria e pontos de sincronizacao; -- precisa manter semantica clara de sobrescrita entre mundo e HUD. -- pode ser inviavel no perfil de handheld barato se exigir buffers adicionais permanentes. - -### D. Cache de layer estatica ou composicao parcial da world - -Ideia: - -- layers que nao mudam podem manter imagem intermediaria pronta; -- scroll, tile updates ou troca de bank invalidam o cache correspondente. - -Vantagens: - -- reduz rasterizacao repetida de cenario estavel; -- aproxima o ganho de hardware tile-based sem abandonar software raster. - -Riscos: - -- cache amplo demais vira complexidade estrutural; -- se scroll muda constantemente, o ganho cai bastante. -- pode consumir memoria demais para um hardware com orcamento agressivo. - -### E. Otimizacao de fades - -Ideia: - -- bypass total para fade neutro; -- opcionalmente aplicar fade apenas sobre buffers relevantes; -- estudar LUTs ou blend mais barato por pixel. - -Vantagens: - -- ataca um custo fullscreen claramente delimitado; -- baixo risco conceitual. - -Riscos: - -- fade parcial por regiao pode complicar demais o contrato; -- LUT ajuda aritmetica, mas nao elimina custo de varrer memoria. - -### F. Melhorias em primitivas e spans de memoria - -Ideia: - -- acelerar `fill_rect`, linhas horizontais/verticais, clears e possiveis blits contiguos; -- explorar caminhos de row spans contiguos em vez de loops mais genericos. - -Vantagens: - -- melhora direta do modelo de desenho destrutivo; -- baixo risco arquitetural; -- cria fundacao para outros atalhos internos. - -Riscos: - -- ganho localizado se o workload dominante vier de tile/sprite compositing; -- precisa medir por primitive class. - -### G. Reduzir custo de copy/convert no host - -Ideia: - -- medir separadamente `draw_rgb565_to_rgba8` e `pixels.render()`; -- estudar formato mais proximo do host ou conversao mais barata; -- evitar redraw host quando nao houver novo front relevante. - -Vantagens: - -- ataca custo fora do `gfx` que ainda entra no frame time total; -- pode render ganho imediato no desktop host. - -Riscos: - -- parte do custo depende da stack `pixels/wgpu`, nao apenas do runtime; -- alguns ganhos podem ser especificos do host desktop e nao do contrato do console. - -## Restricao de Plataforma - -Qualquer recomendacao desta agenda deve ser filtrada por um criterio adicional: - -- priorizar solucoes que melhorem custo sem multiplicar buffers; -- tratar memoria extra como recurso escasso de primeira classe; -- preferir flags, metadados pequenos e estruturas incrementais a caches grandes; -- evitar solucoes cuja performance dependa de assumir um host desktop mais forte do que o handheld alvo. - -Direcao atual da discussao: - -- nesta etapa, buffers extras devem ficar fora; -- o foco deve ser otimizar ao maximo o pipeline existente; -- cache intermediario ou novos buffers so entram em estudo depois que as otimizacoes de baixo custo de memoria forem medidas e esgotadas. - -## Sugestao / Recomendacao Atual - -Priorizar o estudo em camadas, nesta ordem: - -1. instrumentar o pipeline por etapa; -2. validar dirty flags por subsistema como mecanismo minimo de invalidacao; -3. testar rebuild condicional de buckets e bypass/isolamento de fades; -4. otimizar primitivas e caminhos contiguos de escrita no framebuffer atual; -5. estudar separacao world/HUD e cache de layer apenas se os dados justificarem e o teto de memoria permitir; -5. tratar copy/convert do host como frente paralela de otimizacao, nao como substituto da analise do `gfx`. - -## Achados Consolidados Ate Aqui - -1. O contrato base continua sendo framebuffer destrutivo em memoria. - Draws escrevem diretamente no buffer de trabalho e operacoes posteriores sobrescrevem o que estiver por baixo. A agenda nao aponta para migracao a um renderer tipo GPU, retained mode ou compositor sofisticado. - -2. O `present()` atual nao parece ser o problema principal. - No `gfx`, `present()` faz swap de buffers, nao copia completa de pixels. - -3. O custo suspeito esta na recomposicao e na apresentacao, nao na manutencao do estado logico. - Escritas de `TileMap`, scroll e `GfxSetSprite` sao pequenas e pontuais; o custo potencialmente dominante esta em rasterizar pixels, aplicar fades fullscreen e converter o framebuffer para o host. - -4. Os maiores suspeitos atuais de custo sao: - - rasterizacao de layers; - - rasterizacao de sprites; - - fades fullscreen; - - conversao `RGB565 -> RGBA8` no host; - - `pixels.render()` / apresentacao no host. - -5. O pipeline atual deve ser otimizado antes de considerar buffers extras. - A direcao aceita da discussao e esgotar primeiro flags pequenas, bypasses, melhorias de primitive paths e ajustes incrementais no pipeline existente. - -6. Restricao de plataforma pesa tanto quanto CPU. - Como o alvo e um handheld proprio com limitacoes de orcamento e memoria, solucoes que consumam RAM adicional significativa devem ficar fora desta fase. - -7. A instrumentacao minima desejada para retomar o estudo ficou definida. - Quando houver workload representativo, queremos medir ao menos: - - `render_all_total_us` - - `bucket_rebuild_us` - - `layer_raster_us` - - `sprite_raster_us` - - `scene_fade_us` - - `hud_raster_us` - - `hud_fade_us` - - `host_convert_us` - - `host_present_us` - -8. A retencao de metricas deve ser barata e orientada a analise posterior. - A preferencia atual e por acumuladores e ring buffer pequeno de snapshots, sem logging pesado por frame no hot path. - -9. Ring buffer no `TileMap` nao e prioridade nesta fase. - O custo suspeito nao esta na manutencao da grade logica do mapa, e sim na composicao da janela visivel no framebuffer. Mudar a estrutura do mapa so faria sentido se o gargalo principal estivesse em streaming/atualizacao estrutural do cenario, o que nao e a hipotese atual. - -10. A direcao de otimizacao mais promissora e padronizar copias massivas para o framebuffer atual. - Em vez de atacar apenas tilemaps com loops pixel a pixel, a discussao passa a favorecer um padrao geral de blit/copia massiva com: - - calculo previo de offsets e spans; - - fast paths para casos contiguos; - - trabalho por linha/chunk em vez de trabalho atomizado por pixel quando possivel; - - reaproveitamento dessa infraestrutura para tilemaps, sprites e outras operacoes de desenho destrutivo. - -11. Migrar o `back` para `RGBA8888` nao e direcao aceita neste momento. - Isso aumentaria custo de memoria e largura de banda no alvo handheld, alem de deslocar a otimizacao para o host desktop. O contrato interno continua preferencialmente em `RGB565`, mesmo considerando fades e uma futura pipeline de lights. - -## Nova Direcao Tecnica Em Estudo - -Quando esta agenda for reaberta, a linha principal de investigacao deve considerar: - -- manter o `TileMap` simples como estado logico; -- assumir que a janela visivel provavelmente continuara sendo recomposta na maior parte dos frames; -- concentrar a otimizacao em como essa recomposicao escreve no `back`; -- desenhar uma infraestrutura compartilhada de copias massivas/blits para o framebuffer atual; -- usar essa infraestrutura nao apenas para tilemaps, mas como base comum para raster de sprites e outros caminhos de desenho. - -Pergunta orientadora da reabertura: - -- como transformar o renderer de um pipeline de writes atomizados por pixel em um pipeline com fast paths de spans/chunks, sem perder a semantica de framebuffer destrutivo e sem pagar memoria extra relevante? - -## Status de Standby - -Esta agenda deve ficar em espera ate existir um game ou workload real que exercite de forma representativa: - -- tilemaps com scroll e composicao de world; -- sprites em quantidade suficiente para testar buckets e overdraw; -- HUD ativo; -- fades; -- apresentacao completa no host. - -Antes disso, qualquer conclusao sobre gargalo ou prioridade de otimizacao tende a ser prematura. - -## Proximo Gatilho Para Reabrir - -Reabrir esta agenda quando houver: - -- um game jogavel ou cena de teste que percorra o pipeline completo; -- dados de workload mais proximos do uso real; -- necessidade concreta de justificar uma rodada de instrumentacao e profiling. - -## Dependencias - -- `../specs/04-gfx-peripheral.md` -- `../specs/11-portability-and-cross-platform-execution.md` - -## Criterio de Saida Desta Agenda - -Pode virar PR quando houver decisao escrita sobre: - -- filosofia explicita de framebuffer destrutivo como contrato base; -- plano de instrumentacao para localizar o custo dominante do pipeline; -- nivel minimo de invalidacao no v1; -- politica de rebuild de buckets de sprites; -- bypass/cache de fade e HUD; -- politica para otimizar primitivas sem mudar a semantica observavel; -- meta de custo para o render pipeline. diff --git a/discussion/workflow/decisions/DEC-0034-async-work-lane-and-asset-backlog-contract.md b/discussion/workflow/decisions/DEC-0034-async-work-lane-and-asset-backlog-contract.md new file mode 100644 index 00000000..2095c005 --- /dev/null +++ b/discussion/workflow/decisions/DEC-0034-async-work-lane-and-asset-backlog-contract.md @@ -0,0 +1,336 @@ +--- +id: DEC-0034 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Async Work Lane and Asset Backlog Contract +status: accepted +created: 2026-06-28 +accepted: 2026-06-28 +ref_agenda: AGD-0008 +plans: [PLN-0123, PLN-0124, PLN-0125, PLN-0126, PLN-0127, PLN-0128] +tags: [perf, asset, fs, async, scheduler, runtime] +--- + +# DEC-0034 - Async Work Lane and Asset Backlog Contract + +## Status + +Accepted. + +## Contexto + +`asset.load()` currently creates an OS thread per non-resident asset request. +That behavior does not match the target runtime model: the machine should have +explicit execution lanes, bounded ownership, observable backpressure, and no +uncontrolled thread creation from guest-visible operations. + +Render worker concurrency is already handled by separate render decisions. This +decision covers the third runtime lane for asynchronous IO/decode/persistence +work: + +```text +Lane 1: VM, firmware, SystemOS, logical frame +Lane 2: render worker / asynchronous rasterization +Lane 3: async work lane for asset IO/decode and game persistence work +``` + +The concrete first consumer is asset loading. Game persistence through memcard +may also consume this lane. FS is allowed to consume this lane, but public FS API +shape belongs to the app-home filesystem agenda. + +## Decisao + +Prometeu MUST introduce a third async work lane as the runtime-owned execution +place for asset IO/decode work and compatible persistence IO work. + +The async work lane MUST be serial: it has exactly one active job at a time. +The runtime MUST NOT create one OS thread per `asset.load` request in the normal +path. + +Asset loading MUST use a backlog keyed by target `bank_type/slot`. A target can +have at most one current request. A newer request for the same `bank_type/slot` +MUST supersede the previous request for that target. + +Asset handles MUST represent stable bank slots, not transient worker threads. +A handle MUST remain queryable even when the slot has no valid loaded asset and +even when there is no active request for that slot. The handle state MUST +separate resident slot state from request/backlog state. + +Asset IO/read and decode/materialization MUST run on the async work lane. +Asset install/commit into resident banks MUST happen on the main runtime lane at +predictable ownership points. + +Memcard work MAY consume the same async work lane and has higher priority than +ordinary asset loads when contention exists. FS MAY consume the same lane for +IO-style work, but this decision does not define the public FS API. + +## Rationale + +A serial lane maps cleanly to the intended third-core mental model and avoids a +desktop-biased generic thread pool. It gives the runtime one place to observe +IO/decode backlog, progress, priority, cancellation, and telemetry. + +Keying requests by `bank_type/slot` makes the backlog naturally bounded by the +sum of bank slots. There is no need for a guest-visible `queue_full` state: +there cannot be unbounded distinct pending targets if each target has at most +one current request. + +Keeping commit/install on the main lane preserves deterministic publication +semantics and avoids letting the async lane mutate resident graphics/audio/scene +state while the VM, render handoff, or frame boundary is observing it. + +Separating `slot_state` and `request_state` inside the handle avoids ambiguity: +callers can inspect the slot at any time, while mutating operations still remain +protected by request generation. + +## Invariantes / Contrato + +### Async Work Lane + +- The async work lane MUST be runtime-owned. +- The async work lane MUST be separate from the render worker. +- The async work lane MUST execute at most one active job at a time. +- The runtime MUST NOT use `thread::spawn` per asset request in the normal + asset loading path. +- The host may implement the lane as a fixed worker thread. Hardware targets may + map it to a dedicated core when available. +- If a physical third core is unavailable, the implementation MUST preserve the + same logical lane contract. + +### Asset Backlog + +- Asset requests MUST target a concrete `bank_type/slot`. +- The backlog is ordered and serial. +- Each `bank_type/slot` MUST have at most one current request. +- A newer request for the same `bank_type/slot` MUST supersede any earlier + pending request for that target. +- If the target already contains the requested `asset_id` as a valid resident + asset, `asset.load` MUST return a handle in ready state without adding a job + to the backlog. +- The effective backlog size is bounded by the sum of targetable bank slots, + even though the contract does not expose a fixed queue limit. +- Implementations MUST collect stale metadata for canceled or superseded + requests so the handle/request history does not grow without bound. + +### Handles + +An asset handle MUST represent a stable bank slot target. The handle state MUST +include at least: + +```text +handle: + bank_type + slot + +slot_state: + loaded_asset_id + resident_state + slot_generation + +request_state: + requested_asset_id + request_generation + state + backlog_position + progress +``` + +The handle MUST be queryable when: + +- the slot is empty; +- the slot contains a valid resident asset; +- a request is queued; +- a request is active; +- a request is ready for commit; +- a request has been canceled; +- a request has been superseded; +- a request ended in error. + +Mutating operations such as commit, cancel, promote, demote, or move MUST act on +the current request generation. They MUST NOT accidentally mutate a newer +request through an older handle view. + +### Superseding + +`superseded` means a request was replaced by a newer request for the same +`bank_type/slot`. + +- Superseding is an operational state, not a fault. +- If the old request is queued, it MUST be removed from the backlog. +- If the old request is active, the lane SHOULD cancel cooperatively when the + current phase supports cheap cancellation. +- If active work cannot be interrupted cheaply, it MAY finish, but its result + MUST be discarded when its generation no longer matches the target's current + request generation. + +### Asset Backlog API Direction + +The existing asset operations remain the base surface: + +```text +asset.load(asset_id, slot) -> (status, handle) +asset.status(handle) -> status +asset.commit(handle) -> status +asset.cancel(handle) -> status +``` + +The asset backlog surface SHOULD add a small status-first API: + +```text +asset.backlog_info() + -> (status, pending_count, active_handle, active_asset_id, + active_bank_type, active_slot, active_progress) + +asset.backlog_position(handle) + -> (status, state, position, progress) + +asset.backlog_move(handle, new_position) + -> status + +asset.backlog_promote(handle) + -> status + +asset.backlog_demote(handle) + -> status + +asset.target_status(bank_type, slot) + -> (status, asset_id, handle, state, position, progress) +``` + +`asset.backlog_promote(handle)` is an official shortcut for moving a queued +request to position `1`, the first pending position after the active job. + +`asset.backlog_demote(handle)` is an official shortcut for moving a queued +request to the end of the pending backlog. + +The final ABI names and exact return shapes may be refined during planning, but +the implementation MUST preserve the capabilities above. + +### Progress + +Progress MUST be represented without floating point. The recommended scale is +`0..10000`. + +The initial phase model is: + +```text +queued -> 0 +read -> 0..4000 +decode -> 4000..9000 +stage -> 9000..10000 +ready -> 10000 +``` + +If a phase cannot report internal progress, it MUST keep the previous progress +mark and advance at phase completion. Implementations MUST NOT invent false +precision for non-linear decode phases. + +### Telemetry + +Telemetry MUST be cheap and must not add hot-path per-item decode cost. + +Minimum telemetry: + +- current backlog depth; +- target/request position; +- active job progress; +- jobs submitted; +- jobs completed; +- jobs failed; +- jobs canceled; +- jobs superseded; +- job duration; +- percentiles by `bank_type`; +- lightweight percentiles or small-window samples by `asset_id`. + +Percentiles MUST be updated when a job closes, not inside the inner decode loop. + +### Priority + +When async lane consumers contend, the initial priority order is: + +1. memcard commit/write; +2. FS write/config work; +3. asset visual/audio/scene load; +4. non-critical list/read work. + +This priority order is a lane arbitration rule. It does not define public FS +syscall semantics. + +### FS Boundary + +FS MAY consume the async work lane for IO-style work. + +This decision MUST NOT define the public FS API, app-home FS semantics, or +whether FS is exposed as request/poll or sync-appearing operations. Those +questions belong to `AGD-0006`. + +`AGD-0006` MUST be updated or interpreted with the existence of this async IO +lane in mind. + +## Impactos + +### Spec + +- `docs/specs/runtime/15-asset-management.md` must absorb the async lane, + backlog, handle, superseding, progress, and telemetry contract. +- `docs/specs/runtime/16-host-abi-and-syscalls.md` must absorb any final public + asset backlog syscall names and return shapes. +- `docs/specs/runtime/16a-syscall-policies.md` may need status catalog updates + for superseded/canceled/backend unavailable states if not already covered by + asset domain status. +- `docs/specs/runtime/09-events-and-concurrency.md` should mention the async + work lane as an implementation-side lane that does not introduce guest + callbacks. +- `AGD-0006` / future FS spec work must account for FS as a possible consumer of + the async IO lane. + +### Runtime + +- Asset loading must stop spawning a thread per request. +- AssetManager needs a serial backlog keyed by `bank_type/slot`. +- Asset handles need stable slot identity plus separated `slot_state` and + `request_state`. +- Commit/install remains a main-lane operation. +- Superseding and generation checks become required correctness mechanisms. + +### Host + +- Desktop host may implement the async work lane with one fixed worker thread. +- Host presentation/render worker remains separate and unaffected. + +### Firmware + +- No direct firmware behavior is required for asset backlog mechanics. +- Firmware flows that rely on preload or memcard may later use lane telemetry or + priority policy if needed. + +### Tooling + +- Test tools may need support for inspecting backlog state and forcing request + ordering. +- Asset fixtures should cover queued, active, ready, canceled, superseded, and + already-resident paths. + +## Referencias + +- `AGD-0008` - Async Background Work Lanes for Assets and FS. +- `docs/specs/runtime/15-asset-management.md` +- `docs/specs/runtime/16-host-abi-and-syscalls.md` +- `docs/specs/runtime/16a-syscall-policies.md` +- `docs/specs/runtime/09-events-and-concurrency.md` +- `docs/specs/runtime/08-save-memory-and-memcard.md` +- `AGD-0006` - App Home Filesystem Surface and Semantics. + +## Propagacao Necessaria + +1. Update asset management specs with the lane/backlog/handle contract. +2. Update syscall specs if backlog APIs become public ABI. +3. Update FS agenda/spec work to acknowledge the async IO lane without deciding + FS public API in this decision. +4. Plan implementation of a serial async work lane and asset backlog. +5. Plan tests for superseding, stable slot handles, immediate-ready resident + assets, progress, telemetry, and main-lane commit. + +## Revision Log + +- 2026-06-28: Initial draft from `AGD-0008`. diff --git a/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md b/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md new file mode 100644 index 00000000..f3277ed8 --- /dev/null +++ b/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md @@ -0,0 +1,118 @@ +--- +id: PLN-0123 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Async Work Lane Specification Propagation +status: open +created: 2026-06-28 +completed: +ref_decisions: [DEC-0034] +tags: [perf, asset, fs, async, scheduler, runtime, spec] +--- + +# PLN-0123 - Async Work Lane Specification Propagation + +## Briefing + +`DEC-0034` establishes a third serial async work lane, asset backlog semantics, +stable bank-slot handles, superseding, progress, telemetry, and FS/memcard lane +boundaries. The canonical specs must be updated before code work relies on the +new contract. + +## Decisions de Origem + +- `DEC-0034` - Async Work Lane and Asset Backlog Contract. + +## Alvo + +Publish the accepted async work lane and asset backlog contract in the canonical +runtime specs. + +## Escopo + +Included: + +- Update asset management spec. +- Update syscall ABI spec if public backlog APIs are named there. +- Update syscall policy/status language for operational asset states. +- Update events/concurrency spec with the async work lane as an implementation + lane that does not introduce guest callbacks. +- Add a cross-reference from FS/app-home agenda or notes to the async IO lane. + +Fora de Escopo: + +- Implementing runtime code. +- Finalizing public FS API semantics. +- Changing render worker contracts. + +## Plano de Execucao + +### Step 1 - Update Asset Management + +**What:** Add the third async lane, backlog, handle, superseding, progress, and +telemetry contract. + +**How:** Extend `docs/specs/runtime/15-asset-management.md` around the existing +`asset.load/status/commit/cancel` section. Preserve the existing +`asset.load(asset_id, slot)` identity contract. + +**Files:** `docs/specs/runtime/15-asset-management.md`. + +### Step 2 - Update Host ABI + +**What:** Add the backlog API direction if the ABI spec is the canonical place +for syscall shapes. + +**How:** Extend the asset surface section with the planned backlog operations, +marking exact numeric ids as implementation-plan work if ids are not assigned +yet. + +**Files:** `docs/specs/runtime/16-host-abi-and-syscalls.md`. + +### Step 3 - Update Syscall Policies + +**What:** Clarify that `superseded`, `canceled`, `backend_unavailable`, and +decode failures are operational statuses, not faults. + +**How:** Add asset-domain examples under status-first policy. + +**Files:** `docs/specs/runtime/16a-syscall-policies.md`. + +### Step 4 - Update Events and Concurrency + +**What:** Document that the async work lane is an implementation lane. + +**How:** Add a section next to render worker concurrency stating that asset/IO +lane work does not introduce guest callbacks or hidden guest execution. + +**Files:** `docs/specs/runtime/09-events-and-concurrency.md`. + +### Step 5 - Cross-Reference FS Work + +**What:** Record that FS may consume the async IO lane, while public FS API +semantics remain owned by the app-home filesystem discussion. + +**How:** Add a note in `AGD-0006` or its future decision input, without deciding +FS request/poll shape in this plan. + +**Files:** `discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md`. + +## Criterios de Aceite + +- [ ] Specs state that the async work lane is serial and separate from render. +- [ ] Asset spec states backlog keying by `bank_type/slot`. +- [ ] Asset spec states stable slot handles with `slot_state` and `request_state`. +- [ ] Asset spec states superseding behavior. +- [ ] Asset spec states main-lane commit/install. +- [ ] FS is cross-referenced as a permitted lane consumer without public API decisions. + +## Tests / Validacao + +- Run `discussion validate`. +- Review spec links for broken relative paths. +- No cargo tests are required for this documentation-only plan. + +## Riscos + +- Public ABI names may be refined later; the spec must avoid assigning ids too + early if implementation work has not reserved them. +- FS scope can leak into this work; keep FS public API decisions in `AGD-0006`. diff --git a/discussion/workflow/plans/PLN-0124-async-work-lane-runtime-infrastructure.md b/discussion/workflow/plans/PLN-0124-async-work-lane-runtime-infrastructure.md new file mode 100644 index 00000000..888a10ad --- /dev/null +++ b/discussion/workflow/plans/PLN-0124-async-work-lane-runtime-infrastructure.md @@ -0,0 +1,119 @@ +--- +id: PLN-0124 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Async Work Lane Runtime Infrastructure +status: open +created: 2026-06-28 +completed: +ref_decisions: [DEC-0034] +tags: [perf, asset, fs, async, scheduler, runtime] +--- + +# PLN-0124 - Async Work Lane Runtime Infrastructure + +## Briefing + +`DEC-0034` requires one runtime-owned async work lane, separate from VM/main +execution and separate from render worker execution. This plan introduces the +lane infrastructure before migrating asset loading. + +## Decisions de Origem + +- `DEC-0034` - Async Work Lane and Asset Backlog Contract. + +## Alvo + +Create a serial async work lane primitive that can execute one active job at a +time, expose observable state, and support deterministic shutdown. + +## Escopo + +Included: + +- Runtime-owned async work lane type. +- Single active job execution. +- Ordered pending job storage. +- Cooperative cancellation hook. +- Shutdown and join behavior. +- Basic lane telemetry. + +Fora de Escopo: + +- Public asset backlog syscalls. +- Asset handle model migration. +- FS public API. +- Render worker changes. + +## Plano de Execucao + +### Step 1 - Locate Runtime Service Boundary + +**What:** Choose the crate/module boundary for the async lane. + +**How:** Place generic lane infrastructure near VM runtime services, not inside +desktop host code and not inside render worker modules. + +**Files:** likely `crates/console/prometeu-system/src/services/` or +`crates/console/prometeu-system/src/services/vm_runtime/`. + +### Step 2 - Define Job and Lane Types + +**What:** Add a lane type with pending queue, active job metadata, generation, +state, progress, and cancellation flag. + +**How:** Use typed job enums or traits that do not require guest callbacks. +Start with internal asset job support but keep the lane generic enough for +memcard/FS later. + +**Files:** new module under `prometeu-system` service layer. + +### Step 3 - Implement Serial Worker + +**What:** Implement one worker loop that takes one pending job and runs it to +completion or cooperative cancellation. + +**How:** Use a fixed worker thread on host builds. Preserve a logical-lane API +so future hardware can map it to a dedicated core. + +**Files:** async lane module; lifecycle wiring in VM runtime service setup. + +### Step 4 - Implement Shutdown + +**What:** Add explicit stop/shutdown behavior. + +**How:** Wake the lane, stop accepting new jobs, report pending jobs as canceled +or shutdown-discarded, and join the worker with bounded behavior. + +**Files:** async lane module and VM runtime lifecycle modules. + +### Step 5 - Expose Internal Telemetry + +**What:** Add counters for pending depth, active job, submitted, completed, +failed, canceled, and superseded. + +**How:** Use cheap atomics or lane-owned state snapshots. Do not update +expensive percentile data inside inner decode loops. + +**Files:** async lane module; telemetry structs where runtime telemetry lives. + +## Criterios de Aceite + +- [ ] Lane has one active job maximum. +- [ ] Lane is separate from render worker code. +- [ ] Lane can be started and stopped deterministically. +- [ ] Pending jobs are observable. +- [ ] Cooperative cancellation can be signaled. +- [ ] No guest callback path is introduced. + +## Tests / Validacao + +- Unit tests for enqueue, single active execution, ordering, cancellation signal, + and shutdown. +- Concurrency tests must synchronize on real state transitions, not sleeps. +- Run targeted `cargo test` for the owning crate. + +## Riscos + +- Accidentally creating a generic pool instead of a serial lane. +- Coupling the lane to desktop host thread details. +- Reusing render worker handoff semantics where ordered backlog is required. diff --git a/discussion/workflow/plans/PLN-0125-asset-backlog-and-stable-slot-handles.md b/discussion/workflow/plans/PLN-0125-asset-backlog-and-stable-slot-handles.md new file mode 100644 index 00000000..65bb3669 --- /dev/null +++ b/discussion/workflow/plans/PLN-0125-asset-backlog-and-stable-slot-handles.md @@ -0,0 +1,127 @@ +--- +id: PLN-0125 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Asset Backlog and Stable Slot Handles +status: open +created: 2026-06-28 +completed: +ref_decisions: [DEC-0034] +tags: [perf, asset, async, runtime] +--- + +# PLN-0125 - Asset Backlog and Stable Slot Handles + +## Briefing + +`DEC-0034` changes asset loading from request-owned thread spawning to a serial +backlog keyed by `bank_type/slot`. Handles become stable bank-slot observers +with separate resident slot state and request state. + +## Decisions de Origem + +- `DEC-0034` - Async Work Lane and Asset Backlog Contract. + +## Alvo + +Refactor `AssetManager` so asset requests are tracked by target bank slot, +deduplicated/superseded by target, and observable through stable handles. + +## Escopo + +Included: + +- Stable handle structure with `bank_type`, `slot`, `slot_state`, and + `request_state`. +- Backlog keyed by `bank_type/slot`. +- Already-resident fast path. +- Superseding for queued and active requests. +- Main-lane commit/install boundary. + +Fora de Escopo: + +- Public backlog syscall wiring. +- Percentile telemetry. +- FS public API. + +## Plano de Execucao + +### Step 1 - Model Slot Handles + +**What:** Replace transient load-operation assumptions with stable bank-slot +handle state. + +**How:** Introduce structures equivalent to: + +```text +handle: bank_type, slot +slot_state: loaded_asset_id, resident_state, slot_generation +request_state: requested_asset_id, request_generation, state, position, progress +``` + +**Files:** `crates/console/prometeu-drivers/src/asset.rs` and HAL asset types if +public type definitions belong there. + +### Step 2 - Key Backlog by Target + +**What:** Ensure at most one current request per `bank_type/slot`. + +**How:** Add a target index from `(bank_type, slot)` to current request +generation/handle state. Enqueue only when the target does not already have the +same resident asset and no newer request supersedes it. + +**Files:** `crates/console/prometeu-drivers/src/asset.rs`. + +### Step 3 - Implement Already-Resident Fast Path + +**What:** Return a ready handle immediately when the requested asset already +resides in the target slot. + +**How:** Compare requested `asset_id` with the target `slot_state.loaded_asset_id` +and resident validity before creating a queued job. + +**Files:** `crates/console/prometeu-drivers/src/asset.rs`. + +### Step 4 - Implement Superseding + +**What:** New request for the same target supersedes previous queued or active +request. + +**How:** Remove queued older request immediately. For active work, update target +generation so the old result is discarded if it cannot stop cooperatively. + +**Files:** `crates/console/prometeu-drivers/src/asset.rs`; async lane module +from `PLN-0124`. + +### Step 5 - Preserve Main-Lane Commit + +**What:** Keep install/commit into resident banks on the main runtime lane. + +**How:** Async lane produces staged materialized result. `asset.commit(handle)` +installs only when the request generation is current and ready. + +**Files:** `crates/console/prometeu-drivers/src/asset.rs`; VM runtime dispatch if +commit behavior changes. + +## Criterios de Aceite + +- [ ] No asset load spawns an OS thread per request. +- [ ] At most one request exists per `bank_type/slot`. +- [ ] Same-target newer request supersedes older queued request. +- [ ] Same-target newer request prevents active stale result from installing. +- [ ] Already-resident target returns ready handle without backlog entry. +- [ ] Handles remain queryable for empty, queued, active, ready, committed, + canceled, superseded, and error states. + +## Tests / Validacao + +- Unit tests for handle state transitions. +- Unit tests for target-keyed superseding. +- Unit tests for already-resident fast path. +- Integration test for stale active result discard. +- Existing asset load/commit tests must continue passing. + +## Riscos + +- Confusing handle identity with request generation. +- Installing a stale async result after superseding. +- Losing compatibility with existing `asset.status/commit/cancel` behavior. diff --git a/discussion/workflow/plans/PLN-0126-asset-backlog-public-api-and-status-surface.md b/discussion/workflow/plans/PLN-0126-asset-backlog-public-api-and-status-surface.md new file mode 100644 index 00000000..8ea105e7 --- /dev/null +++ b/discussion/workflow/plans/PLN-0126-asset-backlog-public-api-and-status-surface.md @@ -0,0 +1,124 @@ +--- +id: PLN-0126 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Asset Backlog Public API and Status Surface +status: open +created: 2026-06-28 +completed: +ref_decisions: [DEC-0034] +tags: [perf, asset, async, syscall, runtime] +--- + +# PLN-0126 - Asset Backlog Public API and Status Surface + +## Briefing + +`DEC-0034` keeps `asset.load/status/commit/cancel` and adds a small status-first +backlog inspection and ordering surface. This plan wires that surface through +HAL syscall metadata and runtime dispatch. + +## Decisions de Origem + +- `DEC-0034` - Async Work Lane and Asset Backlog Contract. + +## Alvo + +Expose asset backlog inspection and reordering while preserving status-first +semantics and existing asset load identity. + +## Escopo + +Included: + +- Syscall registry additions for backlog APIs. +- Runtime dispatch handlers. +- Status enum additions or normalization. +- ABI/spec alignment with `PLN-0123`. + +Fora de Escopo: + +- Async lane internals. +- Asset decode refactor. +- FS API. + +## Plano de Execucao + +### Step 1 - Finalize Syscall Shapes + +**What:** Choose final names, arity, and return slots for backlog operations. + +**How:** Start from `DEC-0034`: + +```text +asset.backlog_info() +asset.backlog_position(handle) +asset.backlog_move(handle, new_position) +asset.backlog_promote(handle) +asset.backlog_demote(handle) +asset.target_status(bank_type, slot) +``` + +**Files:** `crates/console/prometeu-hal/src/syscalls/domains/asset.rs`, +`docs/specs/runtime/16-host-abi-and-syscalls.md`. + +### Step 2 - Add Status Values + +**What:** Represent `empty`, `invalid`, `queued`, `active`, `ready`, +`committed`, `canceled`, `superseded`, `error`, and `backend_unavailable`. + +**How:** Extend or map existing `LoadStatus` and `AssetOpStatus` without +turning operational states into traps. + +**Files:** HAL asset status types and `crates/console/prometeu-drivers/src/asset.rs`. + +### Step 3 - Add Dispatch + +**What:** Wire new syscalls through VM runtime dispatch. + +**How:** Add match arms near existing `AssetLoad`, `AssetStatus`, +`AssetCommit`, and `AssetCancel` arms. Return status-first values. + +**Files:** `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs`. + +### Step 4 - Implement API Methods + +**What:** Add AssetManager methods backing each public operation. + +**How:** Methods must read stable handle/target state and reorder only queued +requests. `promote` and `demote` are shortcuts over move semantics. + +**Files:** `crates/console/prometeu-drivers/src/asset.rs`. + +### Step 5 - Validate ABI Metadata + +**What:** Ensure registry ids, args, returns, capability, cost, and +non-deterministic flags are correct. + +**How:** Update syscall metadata tests and any declared PBS/runtime syscall +tables. + +**Files:** `crates/console/prometeu-hal/src/syscalls/tests.rs` and related +registry files. + +## Criterios de Aceite + +- [ ] Public backlog operations are registered with stable metadata. +- [ ] All backlog operations are status-first. +- [ ] `promote` is equivalent to moving to first pending position. +- [ ] `demote` moves to the end of pending backlog. +- [ ] Reordering a non-queued request returns operational status. +- [ ] Structural ABI misuse remains Trap. + +## Tests / Validacao + +- HAL syscall metadata tests. +- VM runtime dispatch tests for each new syscall. +- AssetManager unit tests for move/promote/demote. +- Tests for `target_status` on empty, queued, active, ready, and superseded + targets. + +## Riscos + +- Adding too many public ABI details before spec propagation is accepted. +- Returning payload values that are ambiguous when status is not success. +- Treating known but non-mutable states as traps instead of statuses. diff --git a/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md b/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md new file mode 100644 index 00000000..d04bae32 --- /dev/null +++ b/discussion/workflow/plans/PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md @@ -0,0 +1,120 @@ +--- +id: PLN-0127 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Async Lane Memcard and FS Integration Boundaries +status: open +created: 2026-06-28 +completed: +ref_decisions: [DEC-0034] +tags: [perf, fs, memcard, async, runtime] +--- + +# PLN-0127 - Async Lane Memcard and FS Integration Boundaries + +## Briefing + +`DEC-0034` allows memcard and FS work to consume the async IO lane but keeps +public FS API semantics out of scope. This plan establishes integration +boundaries without deciding app-home FS API shape. + +## Decisions de Origem + +- `DEC-0034` - Async Work Lane and Asset Backlog Contract. + +## Alvo + +Define and implement safe internal boundaries for memcard and FS use of the +async work lane, preserving lane priority and keeping FS public API decisions in +`AGD-0006`. + +## Escopo + +Included: + +- Memcard as a priority lane consumer candidate. +- FS as a permitted internal lane consumer. +- Priority ordering in the lane. +- Updates to `AGD-0006` to acknowledge the lane. + +Fora de Escopo: + +- Public FS request/poll design. +- App-home path/status contract. +- Full memcard async conversion if it requires public ABI changes. + +## Plano de Execucao + +### Step 1 - Update FS Agenda + +**What:** Record that an async IO lane exists and may be used by FS. + +**How:** Add a concise section to `AGD-0006` stating that FS public API remains +owned by that agenda, but implementation may use the async lane from `DEC-0034`. + +**Files:** `discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md`. + +### Step 2 - Identify Memcard Operations + +**What:** List memcard operations that can benefit from async IO. + +**How:** Inspect `crates/console/prometeu-system/src/services/memcard.rs` and +VM runtime dispatch for `mem.*` operations. Classify read, write, commit, and +clear behavior. + +**Files:** memcard service and VM runtime dispatch modules. + +### Step 3 - Define Lane Priority + +**What:** Encode the initial priority order from `DEC-0034`. + +**How:** Add priority classes: + +```text +memcard commit/write +FS write/config +asset load +non-critical list/read +``` + +**Files:** async lane module from `PLN-0124`. + +### Step 4 - Add Internal Consumer Hooks + +**What:** Provide internal APIs for memcard/FS to submit lane jobs later. + +**How:** Add types/interfaces without changing public FS syscall behavior. +No public FS request/poll operations are introduced here. + +**Files:** async lane module; FS/memcard service modules as needed. + +### Step 5 - Preserve Current Public Behavior + +**What:** Ensure existing memcard and FS syscalls keep their current observable +surface unless a later plan explicitly changes it. + +**How:** Do not alter return shapes in this plan. Add tests proving existing +status-first behavior remains intact. + +**Files:** VM runtime FS/memcard tests. + +## Criterios de Aceite + +- [ ] `AGD-0006` acknowledges the async IO lane and keeps FS public API in its + own scope. +- [ ] Async lane supports priority classes required by `DEC-0034`. +- [ ] Memcard and FS can be represented as internal lane consumers. +- [ ] Existing public memcard/FS syscall shapes are unchanged. +- [ ] No FS public request/poll API is introduced by this plan. + +## Tests / Validacao + +- Existing FS/memcard tests continue passing. +- Unit tests for lane priority ordering. +- Tests proving asset jobs do not overlap with higher-priority memcard jobs + once both are submitted through the lane. + +## Riscos + +- Accidentally deciding FS public API in the wrong discussion. +- Making memcard async in a way that changes status-first behavior. +- Starving asset loads if priority rules do not include fairness. diff --git a/discussion/workflow/plans/PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md b/discussion/workflow/plans/PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md new file mode 100644 index 00000000..7b60662b --- /dev/null +++ b/discussion/workflow/plans/PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md @@ -0,0 +1,126 @@ +--- +id: PLN-0128 +ticket: perf-async-background-work-lanes-for-assets-and-fs +title: Async Asset Pipeline Contract Tests and Telemetry +status: open +created: 2026-06-28 +completed: +ref_decisions: [DEC-0034] +tags: [perf, asset, async, telemetry, tests, runtime] +--- + +# PLN-0128 - Async Asset Pipeline Contract Tests and Telemetry + +## Briefing + +`DEC-0034` requires observable progress, backlog state, superseding, and cheap +telemetry. This plan adds the validation and telemetry evidence needed to prove +the new async asset pipeline is correct. + +## Decisions de Origem + +- `DEC-0034` - Async Work Lane and Asset Backlog Contract. + +## Alvo + +Provide contract tests and telemetry for the async asset lane so future +implementations can be changed without regressing lane semantics. + +## Escopo + +Included: + +- Contract tests for lane and asset backlog behavior. +- Progress reporting tests. +- Superseding and stale result discard tests. +- Telemetry counters and percentile updates. +- No-sleep deterministic test helpers where needed. + +Fora de Escopo: + +- Implementing public API wiring if `PLN-0126` has not run. +- Full benchmarking infrastructure. +- Host UI display of telemetry. + +## Plano de Execucao + +### Step 1 - Build Deterministic Test Hooks + +**What:** Add test hooks for blocking a lane job at read/decode/stage. + +**How:** Use channels/barriers or explicit fake job steps. Do not rely on +`thread::sleep` for correctness. + +**Files:** async lane tests; asset test support modules. + +### Step 2 - Test Backlog Ordering + +**What:** Prove queued jobs run serially and in backlog order. + +**How:** Submit multiple target slots and assert active job, pending positions, +promote, demote, and move behavior. + +**Files:** asset manager tests and lane tests. + +### Step 3 - Test Superseding + +**What:** Prove newer request for the same `bank_type/slot` supersedes older +requests. + +**How:** Cover queued superseding and active superseding. For active work, prove +stale completion cannot install. + +**Files:** asset manager tests. + +### Step 4 - Test Stable Handle Observability + +**What:** Prove handles are queryable for empty, queued, active, ready, +committed, canceled, superseded, and error states. + +**How:** Use `target_status` and handle status APIs to inspect both `slot_state` +and `request_state`. + +**Files:** asset manager tests; VM dispatch tests if public API is available. + +### Step 5 - Test Progress + +**What:** Prove progress uses `0..10000` and phase boundaries. + +**How:** Fake read/decode/stage phases with known completion points. Validate +non-linear phases do not report false internal progress. + +**Files:** asset manager tests; telemetry tests. + +### Step 6 - Add Telemetry + +**What:** Add counters and percentile aggregation. + +**How:** Update submitted/completed/failed/canceled/superseded counters when job +state closes. Update percentiles by `bank_type` and lightweight `asset_id` +window only at job completion. + +**Files:** asset manager telemetry, async lane telemetry, existing runtime +telemetry types if shared. + +## Criterios de Aceite + +- [ ] Tests prove one active job at a time. +- [ ] Tests prove backlog order and reordering. +- [ ] Tests prove queued and active superseding. +- [ ] Tests prove stale active results are discarded. +- [ ] Tests prove handles remain queryable across all required states. +- [ ] Tests prove progress phase behavior. +- [ ] Telemetry is updated at job closure, not inside inner decode loops. + +## Tests / Validacao + +- Run targeted cargo tests for asset manager, async lane, and VM runtime + dispatch. +- Run existing asset-bank tests to prove current behavior remains compatible. +- Run `discussion validate` after plan state changes. + +## Riscos + +- Tests that depend on sleeps will be flaky. +- Telemetry may become too expensive if updated inside decode loops. +- Public API tests must wait for `PLN-0126` if syscall wiring is not present.