From f622387f4a378d0724ae4f28a843e38641abbe2f Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 20 Apr 2026 09:36:04 +0100 Subject: [PATCH 1/4] [PERF] Host Desktop Frame Pacing and Presentation --- discussion/index.ndjson | 4 +- ...t-desktop-frame-pacing-and-presentation.md | 17 +- ...t-desktop-frame-pacing-and-presentation.md | 141 +++++++++++++++ ...t-desktop-frame-pacing-and-presentation.md | 170 ++++++++++++++++++ 4 files changed, 327 insertions(+), 5 deletions(-) create mode 100644 discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md create mode 100644 discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index fa97b69b..e82c3ea4 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":19,"PLN":36,"LSN":36,"CLSN":1}} +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":20,"PLN":37,"LSN":36,"CLSN":1}} {"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} @@ -12,7 +12,7 @@ {"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":"workflow/agendas/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-04-10","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0005","file":"workflow/decisions/DEC-0005-perf-push-based-telemetry-model.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0005","file":"workflow/plans/PLN-0005-perf-push-based-telemetry-implementation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0026","file":"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":"workflow/agendas/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-0010","status":"open","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-03-27","tags":[],"agendas":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0010","status":"in_progress","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":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"in_progress","created_at":"2026-03-27","updated_at":"2026-04-20"}],"decisions":[{"id":"DEC-0019","file":"DEC-0019-host-desktop-frame-pacing-and-presentation.md","status":"accepted","created_at":"2026-04-20","updated_at":"2026-04-20","ref_agenda":"AGD-0009"}],"plans":[{"id":"PLN-0036","file":"PLN-0036-host-desktop-frame-pacing-and-presentation.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0019"]}],"lessons":[]} {"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":"workflow/agendas/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-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":"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":"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"}]} diff --git a/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md index e2cf62f3..4ee79cfd 100644 --- a/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md +++ b/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md @@ -2,10 +2,10 @@ id: AGD-0009 ticket: perf-host-desktop-frame-pacing-and-presentation title: Agenda - [PERF] Host Desktop Frame Pacing and Presentation -status: open +status: in_progress created: 2026-03-27 -resolved: -decision: +resolved: 2026-04-20 +decision: DEC-0019 tags: [] --- @@ -60,8 +60,19 @@ Fechar uma politica de pacing/apresentacao host-driven que nao desperdice CPU qu ## Open Questions de Arquitetura 1. O host desktop deve ser referencia conservadora de energia ou apenas shell de desenvolvimento? + R: O desktop eh shell de desenvolvimento, mas pode tb ser usado para jogar em um pc. Deve oferecer controle de energia como um handheld, especialmente para desenvolvimento. 2. O runtime precisa expor um sinal explicito de "novo frame disponivel" para o host? + R: Atualmente fazemos o swap do buffer somente quando o frame logico eh pronto, esse eh o "ponto de entrada" para o host. Eh ali que a conversao de RGB565 -> RGBA8 pode ser realizada e mostrada. Se o host precisar saber quando o frame eh pronto, pode ser exposto por um sinal explicito. 3. Existe necessidade real de redraw continuo quando o overlay esta desligado? + R: Nao + +## Perguntas em Aberto + +Nenhuma. As perguntas de arquitetura desta agenda foram respondidas e o tema esta pronto para cristalizacao em decisao. + +## Resolucao + +A agenda fecha com a direcao de que o host desktop nao deve manter redraw continuo por padrao. O host deve operar com pacing orientado por frame logico pronto e por eventos externos relevantes, preservando um comportamento energeticamente conservador sem perder utilidade como shell de desenvolvimento. Se o host precisar consumir esse momento de forma explicita, o runtime pode expor um sinal canonico de "novo frame disponivel" no ponto em que o frame logico eh concluido. ## Dependencias diff --git a/discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md new file mode 100644 index 00000000..0d9a3145 --- /dev/null +++ b/discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md @@ -0,0 +1,141 @@ +--- +id: DEC-0019 +ticket: perf-host-desktop-frame-pacing-and-presentation +title: Decision - [PERF] Host Desktop Frame Pacing and Presentation +status: accepted +created: 2026-04-20 +accepted: 2026-04-20 +agenda: AGD-0009 +plans: [PLN-0036] +tags: [perf, host, desktop, frame-pacing, presentation, debug] +--- + +# Status + +Accepted. This decision is now normatively locked and is ready to drive planning and execution. + +## Contexto + +O host desktop atual usa `ControlFlow::Poll`, pede redraw continuamente e reapresenta o framebuffer mesmo quando a maquina nao publicou um novo frame logico. + +Esse comportamento conflita com o modelo temporal de `docs/specs/runtime/01-time-model-and-cycles.md`, onde a maquina opera em frames logicos de 60 Hz, e com o contrato de portabilidade e isolamento de debug em `docs/specs/runtime/10-debug-inspection-and-profiling.md` e `docs/specs/runtime/11-portability-and-cross-platform-execution.md`, onde apresentacao e overlay pertencem ao host, mas nao devem redefinir a semantica logica da maquina. + +O problema a ser fechado aqui nao eh o desenho final do backend grafico. O problema eh definir quando o host desktop tem permissao para acordar, converter e apresentar pixels sem transformar a shell desktop em um loop de polling agressivo. + +## Decisao + +O host desktop SHALL adotar pacing orientado por frame logico publicado e por eventos externos relevantes, e MUST NOT manter redraw continuo por padrao. + +Normas desta decisao: + +1. Gatilho canonico de redraw + - O host MUST considerar um novo frame logico publicado como gatilho principal de apresentacao. + - O host MAY solicitar redraw adicional quando houver evento externo relevante que altere apenas a superficie host-owned, incluindo resize da janela, expose/invalidate da janela, toggle de overlay, atualizacao visivel do debugger ou mudanca de estado local da shell. + - O host MUST NOT solicitar redraw continuo apenas para "ver se existe frame novo". + +2. Politica do event loop + - O host desktop MUST sair de `ControlFlow::Poll` como politica padrao. + - O event loop SHOULD operar em modo de espera dirigida por deadline ou evento, usando `WaitUntil` para o proximo slice logico quando houver trabalho temporal agendado e `Wait` quando nao houver deadline imediato. + - `Poll` MAY existir apenas em modo explicitamente opt-in de profiling/diagnostico host-owned e MUST NOT ser o comportamento normal de execucao. + +3. Conversao de framebuffer + - A conversao RGB565 -> RGBA8 permanece full-frame nesta fase. + - Essa conversao MUST acontecer apenas quando houver novo frame logico publicado ou quando um evento host-owned exigir recomposicao/apresentacao da superficie visivel. + - Dirty-region conversion e offload para shader/GPU ficam explicitamente fora do contrato desta decisao e poderao ser avaliados em discussao separada se o full-frame on-demand ainda for insuficiente. + +4. Modo ocioso, pausa e debug + - Quando a VM estiver pausada, em breakpoint, esperando `start`, ou sem cart carregado, o host MUST preservar o ultimo frame visivel valido e MUST NOT ficar redesenhando continuamente. + - Nesses estados, redraw adicional so pode ocorrer por evento host-owned relevante, como overlay/debug data visivel, resize/expose ou transicao explicita de estado. + - Buffers logicos da maquina MUST NOT ser trocados durante pausa apenas para sustentar HUD host-owned. + +5. Sinal de "novo frame disponivel" + - O runtime/host boundary MUST ter um ponto canonico de observacao do "frame pronto", alinhado ao momento em que o frame logico e publicado para apresentacao. + - A implementacao inicial MAY usar um sinal explicito, contador monotonicamente crescente, dirty flag ou observacao do ponto de swap/publicacao, desde que a semantica seja unica e nao ambigua. + - Essa decisao MUST NOT introduzir uma ABI guest-visible nova. O sinal pertence ao boundary interno runtime-host e as superficies host-owned de inspecao. + +## Rationale + +`Poll` com redraw continuo mascara custo de apresentacao, consome CPU sem beneficio visual e reduz a utilidade do desktop como referencia de comportamento energetico razoavel. + +Ao mesmo tempo, a shell desktop nao deve ser tratada como uma plataforma "ultra-conservadora" a ponto de perder ergonomia de debug. Por isso a decisao nao proibe redraw extra; ela apenas restringe redraw extra a eventos com causa concreta e visivel. + +Manter a conversao full-frame por enquanto evita reabrir a arquitetura de renderizacao durante um trabalho cujo alvo principal eh pacing. O ganho imediato vem de parar de converter e apresentar quando nada mudou. Se isso nao bastar, dirty regions ou shader path podem ser discutidos depois com evidencias. + +Separar "frame logico publicado" de "overlay host-owned mudou" preserva o contrato de portabilidade: o host pode mostrar mais informacao tecnica sem forcar a VM a produzir frames adicionais nem adulterar o framebuffer logico. + +## Invariantes / Contrato + +- A maquina continua definindo frames logicos em 60 Hz; o host nao redefine esse relogio. +- O host apresenta o ultimo frame logico publicado; ele nao inventa frames intermediarios. +- Overlay e debugger sao host-owned e MAY compor sobre uma superficie de apresentacao host-only. +- Overlay/debug MUST NOT exigir redraw continuo quando seu conteudo estiver estavel. +- Conversao/presentacao do framebuffer MUST ser dirigida por mudanca observavel, nao por polling agressivo. +- O boundary runtime-host MUST expor um criterio unico para detectar publicacao de frame logico. +- Esta decisao nao promove dirty regions nem GPU offload a contrato normativo. +- Esta decisao nao altera semantica guest-visible de `DRAW`, buffers logicos, ciclos, ou telemetria de certificacao. + +## Alternativas Descartadas + +### Manter `Poll` permanente no desktop + +Descartado porque esconde custo real de apresentacao, desperdiça CPU e cria uma shell cuja estabilidade aparente depende de trabalho inutil. + +### Redraw orientado apenas por vsync fisico + +Descartado como contrato canonico porque o modelo PROMETEU e dirigido por frame logico e a portabilidade nao depende de sincronizar a semantica da maquina a uma frequencia fisica do host. + +### Reabrir o problema com dirty regions ou shader path como pre-condicao + +Descartado nesta etapa porque mistura otimizacao secundaria com o contrato principal de pacing. O problema imediato pode e deve ser resolvido sem nova arquitetura de render backend. + +## Impactos + +### Spec + +- `docs/specs/runtime/10-debug-inspection-and-profiling.md` deve refletir que a apresentacao host-owned observa frame publicado e nao requer redraw continuo para HUD/overlay estavel. +- `docs/specs/runtime/11-portability-and-cross-platform-execution.md` deve explicitar que a superficie host de apresentacao pode recompor overlay por evento relevante, sem transformar isso em polling permanente. + +### Runtime + +- O runtime pode precisar expor ou estabilizar um ponto interno de publicacao de frame pronto para o host desktop consumir de forma canonica. +- Nenhuma nova ABI guest-visible deve ser criada por este trabalho. + +### Host Desktop + +- O loop winit deve migrar de `Poll` para espera dirigida por deadline/evento. +- `request_redraw()` deixa de ser incondicional no ciclo de idle. +- A apresentacao passa a depender de novo frame logico ou evento host-owned relevante. + +### Firmware + +- Sem mudanca de semantica guest-visible. +- Estados de pausa/breakpoint continuam impedindo avanco de tempo logico e swap de buffers logicos. + +### Tooling + +- Overlay/debugger continuam host-owned, mas precisam declarar quando realmente invalidam a superficie visivel. +- Profiling host-only pode manter um modo opt-in de polling agressivo, separado do comportamento normal. + +## Referencias + +- [AGD-0009]() +- [docs/specs/runtime/01-time-model-and-cycles.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/docs/specs/runtime/01-time-model-and-cycles.md) +- [docs/specs/runtime/10-debug-inspection-and-profiling.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/docs/specs/runtime/10-debug-inspection-and-profiling.md) +- [docs/specs/runtime/11-portability-and-cross-platform-execution.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/docs/specs/runtime/11-portability-and-cross-platform-execution.md) +- [runner.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/host/prometeu-host-desktop-winit/src/runner.rs:252) + +## Propagacao Necessaria + +- Escrever um `PLN` para implementar a mudanca no host desktop. +- Atualizar as specs normativas citadas para refletir este contrato. +- Ajustar o host desktop para consumir um gatilho canonico de frame publicado. +- Adicionar testes para: + - ausencia de redraw continuo sem frame novo; + - redraw em evento host-owned relevante; + - preservacao do ultimo frame em pausa/breakpoint/no-cart; + - modo opt-in de polling apenas quando explicitamente habilitado. + +## Revision Log + +- 2026-04-20: Initial draft from AGD-0009. +- 2026-04-20: Accepted and linked to PLN-0036. diff --git a/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md new file mode 100644 index 00000000..effb618d --- /dev/null +++ b/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md @@ -0,0 +1,170 @@ +--- +id: PLN-0036 +ticket: perf-host-desktop-frame-pacing-and-presentation +title: Plan - Host Desktop Frame Pacing and Presentation +status: review +created: 2026-04-20 +completed: +tags: [perf, host, desktop, frame-pacing, presentation, debug] +--- + +## Briefing + +Implement `DEC-0019` by removing unconditional desktop redraw polling, introducing a canonical host-side frame-publication trigger, preserving the last visible frame during paused or idle states, and updating the normative specs to reflect the locked contract. + +## Decisions de Origem + +- `DEC-0019` - Host Desktop Frame Pacing and Presentation + +## Alvo + +Deliver a host desktop execution path where: + +- the winit loop no longer uses continuous polling by default; +- redraw happens only after logical frame publication or explicit host-owned invalidation events; +- RGB565 to RGBA8 conversion remains full-frame but only runs on demand; +- paused, debugger-waiting, and no-cartridge states keep the last valid image without continuous redraw; +- specs and tests match the accepted decision. + +## Escopo + +- Update the desktop host loop in `crates/host/prometeu-host-desktop-winit/src/runner.rs`. +- Introduce or stabilize a canonical "new frame published" signal at the runtime-host boundary. +- Track host-owned invalidation causes for overlay, debugger, and window events. +- Update the relevant runtime specs under `docs/specs/runtime/`. +- Add or extend automated tests for redraw gating and paused-state behavior. + +## Fora de Escopo + +- Dirty-region framebuffer conversion. +- GPU/shader-based presentation paths. +- New guest-visible ABI or debug surface. +- Redesign of overlay visuals or debugger protocol beyond invalidation signaling. +- Reopening the architecture covered by `DEC-0019`. + +## Plano de Execucao + +### Step 1 - Establish the canonical frame-publication trigger + +**What:** Define the single host-consumable signal that indicates a new logical frame has been published. + +**How:** Inspect the current buffer swap/publication point in the desktop runtime path and choose the existing internal publication event, dirty flag, or monotonic frame counter that can become the canonical trigger required by `DEC-0019`. If no stable signal exists, add a host-internal publication marker that changes exactly once per newly published logical frame and is not guest-visible. + +**File(s):** +- `crates/host/prometeu-host-desktop-winit/src/runner.rs` +- any directly related runtime/firmware module discovered as the current frame-publication owner + +**Depends on:** none + +### Step 2 - Replace unconditional redraw requests with invalidation-driven scheduling + +**What:** Remove the unconditional redraw call from the idle loop and gate presentation on frame publication or host-owned invalidation. + +**How:** Refactor `about_to_wait()` and redraw scheduling in `runner.rs` so the host requests redraw only when: + +- a new logical frame was published since the last presentation; +- the window receives a visible invalidation event such as resize/expose; +- overlay state toggles or changes a visible host-owned layer; +- debugger-visible state changes require host-side recomposition. + +Introduce explicit host-owned invalidation state if needed so redraw causes are tracked instead of inferred by polling. + +**File(s):** +- `crates/host/prometeu-host-desktop-winit/src/runner.rs` + +**Depends on:** Step 1 + +### Step 3 - Move the event loop from default `Poll` to deadline/event waiting + +**What:** Change the normal desktop control flow so the host waits for the next logical deadline or external event instead of spinning continuously. + +**How:** Update `resumed()` and any related loop control code in `runner.rs` to stop using `ControlFlow::Poll` as the default. Use `WaitUntil` when a next logical tick deadline is known and `Wait` when the host is effectively idle with no immediate timing deadline. Keep any aggressive polling path behind an explicit profiling-only switch rather than as baseline behavior. + +**File(s):** +- `crates/host/prometeu-host-desktop-winit/src/runner.rs` +- `crates/host/prometeu-host-desktop-winit/src/main.rs` if a profiling-only opt-in flag is wired at startup + +**Depends on:** Step 2 + +### Step 4 - Preserve the last valid frame during paused, breakpoint, and no-cart states + +**What:** Ensure non-running machine states do not cause repeated conversion/presentation work while still allowing host-owned UI recomposition when something visible changes. + +**How:** Separate "machine produced a new frame" from "host-owned layer needs redraw". Keep the last presented RGBA output available for reuse, avoid logical buffer swaps during pause-only overlay activity, and ensure debugger waiting and no-cartridge states do not schedule continuous redraws. If overlay or debugger content changes while the machine is paused, redraw only the host-owned composition layer on explicit invalidation. + +**File(s):** +- `crates/host/prometeu-host-desktop-winit/src/runner.rs` +- `crates/host/prometeu-host-desktop-winit/src/overlay.rs` +- `crates/host/prometeu-host-desktop-winit/src/debugger.rs` + +**Depends on:** Step 2 + +### Step 5 - Update the normative specs + +**What:** Propagate the accepted decision into the canonical runtime specs. + +**How:** Update the portability and debug chapters so they explicitly state that desktop presentation observes published logical frames, that host-owned overlay/debug redraw is event-driven rather than continuously polled, and that stable host HUD state does not justify perpetual redraw. Keep the text aligned with the locked decision and do not introduce new architecture. + +**File(s):** +- `docs/specs/runtime/10-debug-inspection-and-profiling.md` +- `docs/specs/runtime/11-portability-and-cross-platform-execution.md` + +**Depends on:** Step 1 + +### Step 6 - Add regression tests and evidence + +**What:** Add automated coverage for the redraw contract and document manual evidence where platform events are hard to model. + +**How:** Extend or add tests in the desktop host crate to cover: + +- no redraw request when no new frame and no host invalidation occurred; +- redraw request after a newly published logical frame; +- redraw request after overlay toggle or equivalent host-owned invalidation; +- no continuous redraw in paused/debugger-waiting/no-cart states; +- preservation of last visible frame semantics while paused. + +Where direct winit event simulation is difficult, factor logic into testable state transitions and keep any remaining platform verification in a short manual checklist. + +**File(s):** +- `crates/host/prometeu-host-desktop-winit/src/runner.rs` +- `crates/host/prometeu-host-desktop-winit/src/overlay.rs` if overlay invalidation helpers become testable +- `crates/host/prometeu-host-desktop-winit/src/debugger.rs` if debugger invalidation hooks are exposed + +**Depends on:** Steps 2, 3, 4 + +## Criterios de Aceite + +- The default desktop event loop no longer runs in continuous `ControlFlow::Poll`. +- The host requests redraw only after canonical frame publication or explicit host-owned invalidation. +- Full-frame RGB565 to RGBA8 conversion remains in place but is not executed continuously while the visible machine frame is unchanged. +- Paused, breakpoint, debugger-waiting, and no-cartridge states preserve the last valid image without continuous redraw. +- Overlay or debugger changes can still trigger targeted host-owned redraw when visually necessary. +- No guest-visible ABI is added for this feature. +- The relevant runtime specs state the same redraw and presentation contract as `DEC-0019`. +- Automated tests cover the main redraw gating paths and paused-state behavior. + +## Tests / Validacao + +### Unit Tests + +- Add focused tests for redraw scheduling state transitions in the desktop host runner. +- Add tests for any helper that tracks frame-publication state or host-owned invalidation causes. + +### Integration Tests + +- Extend host crate tests to verify frame publication and debugger/overlay invalidation do not regress redraw policy. +- Run `cargo test -p prometeu-host-desktop-winit --lib`. + +### Manual Verification + +- Launch the desktop host with overlay disabled and confirm CPU usage drops when the machine is visually stable. +- Toggle overlay on and off and confirm redraw happens on the visible transition without restoring continuous polling. +- Pause execution or wait for debugger start and confirm the last frame remains visible without repeated redraw. +- Resize or expose the window and confirm the surface is recomposed correctly. + +## Riscos + +- The actual frame-publication point may be less explicit than assumed, requiring a small host-internal contract addition before redraw gating can be made reliable. +- winit waiting semantics can introduce timing regressions if the next logical deadline is computed incorrectly. +- Overlay or debugger code may currently assume redraw is always free, which can surface hidden invalidation bugs after polling is removed. +- Manual verification may still be needed for some window-system invalidation behavior that is cumbersome to model in tests. -- 2.47.2 From f0663856c3767957b04e51bd040ce266547f4b5a Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 20 Apr 2026 09:52:59 +0100 Subject: [PATCH 2/4] [PERF] Host Desktop Frame Pacing and Presentation --- .../prometeu-host-desktop-winit/src/runner.rs | 188 +++++++++++++++++- discussion/index.ndjson | 2 +- ...t-desktop-frame-pacing-and-presentation.md | 4 +- .../10-debug-inspection-and-profiling.md | 10 + ...ortability-and-cross-platform-execution.md | 10 + 5 files changed, 206 insertions(+), 8 deletions(-) diff --git a/crates/host/prometeu-host-desktop-winit/src/runner.rs b/crates/host/prometeu-host-desktop-winit/src/runner.rs index 79f10c29..fd0468fe 100644 --- a/crates/host/prometeu-host-desktop-winit/src/runner.rs +++ b/crates/host/prometeu-host-desktop-winit/src/runner.rs @@ -20,6 +20,72 @@ use winit::event_loop::{ActiveEventLoop, ControlFlow}; use winit::keyboard::{KeyCode, PhysicalKey}; use winit::window::{Window, WindowAttributes, WindowId}; +const IDLE_HOST_POLL_DT: Duration = Duration::from_millis(100); + +#[derive(Debug, Clone)] +struct PresentationState { + latest_published_frame: u64, + last_presented_frame: Option, + host_invalidated: bool, + redraw_requested: bool, +} + +impl Default for PresentationState { + fn default() -> Self { + Self { + latest_published_frame: 0, + last_presented_frame: None, + host_invalidated: true, + redraw_requested: false, + } + } +} + +impl PresentationState { + fn note_published_frame(&mut self, frame_index: u64) { + if frame_index > self.latest_published_frame { + self.latest_published_frame = frame_index; + } + } + + fn invalidate_host_surface(&mut self) { + self.host_invalidated = true; + } + + fn needs_redraw(&self) -> bool { + self.host_invalidated || self.last_presented_frame != Some(self.latest_published_frame) + } + + fn should_request_redraw(&mut self) -> bool { + if self.needs_redraw() && !self.redraw_requested { + self.redraw_requested = true; + return true; + } + + false + } + + fn mark_presented(&mut self) { + self.redraw_requested = false; + self.host_invalidated = false; + self.last_presented_frame = Some(self.latest_published_frame); + } +} + +fn desired_control_flow( + now: Instant, + machine_running: bool, + accumulator: Duration, + frame_target_dt: Duration, +) -> ControlFlow { + if machine_running { + let remaining = frame_target_dt.saturating_sub(accumulator); + ControlFlow::WaitUntil(now + remaining) + } else { + ControlFlow::WaitUntil(now + IDLE_HOST_POLL_DT) + } +} + /// The Desktop implementation of the PROMETEU Runtime. /// /// This struct acts as the physical "chassis" of the virtual console. It is @@ -69,6 +135,8 @@ pub struct HostRunner { audio: HostAudio, /// Last known pause state to sync with audio. last_paused_state: bool, + /// Tracks whether a new logical frame or host-only surface invalidation requires presentation. + presentation: PresentationState, } impl HostRunner { @@ -105,6 +173,7 @@ impl HostRunner { overlay_enabled: false, audio: HostAudio::new(), last_paused_state: false, + presentation: PresentationState::default(), } } @@ -123,6 +192,20 @@ impl HostRunner { w.request_redraw(); } } + + fn invalidate_host_surface(&mut self) { + self.presentation.invalidate_host_surface(); + } + + fn request_redraw_if_needed(&mut self) { + if self.presentation.should_request_redraw() { + self.request_redraw(); + } + } + + fn machine_running(&self) -> bool { + !self.firmware.os.paused && !self.debugger.waiting_for_start + } } impl ApplicationHandler for HostRunner { @@ -157,7 +240,9 @@ impl ApplicationHandler for HostRunner { eprintln!("[HostAudio] Disabled: {}", err); } - event_loop.set_control_flow(ControlFlow::Poll); + self.invalidate_host_surface(); + self.request_redraw_if_needed(); + event_loop.set_control_flow(ControlFlow::Wait); } fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { @@ -168,11 +253,15 @@ impl ApplicationHandler for HostRunner { WindowEvent::Resized(size) => { self.resize_surface(size.width, size.height); + self.invalidate_host_surface(); + self.request_redraw_if_needed(); } WindowEvent::ScaleFactorChanged { .. } => { let size = self.window().inner_size(); self.resize_surface(size.width, size.height); + self.invalidate_host_surface(); + self.request_redraw_if_needed(); } WindowEvent::RedrawRequested => { @@ -198,6 +287,8 @@ impl ApplicationHandler for HostRunner { if pixels.render().is_err() { event_loop.exit(); + } else { + self.presentation.mark_presented(); } } @@ -207,11 +298,15 @@ impl ApplicationHandler for HostRunner { if is_down && code == KeyCode::KeyD && self.debugger.waiting_for_start { self.debugger.waiting_for_start = false; + self.invalidate_host_surface(); + self.request_redraw_if_needed(); println!("[Debugger] Execution started!"); } if is_down && code == KeyCode::F1 { self.overlay_enabled = !self.overlay_enabled; + self.invalidate_host_surface(); + self.request_redraw_if_needed(); } } } @@ -222,7 +317,11 @@ impl ApplicationHandler for HostRunner { /// Called by `winit` when the application is idle and ready to perform updates. /// This is where the core execution loop lives. - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + let was_debugger_connected = self.debugger.stream.is_some(); + let was_waiting_for_start = self.debugger.waiting_for_start; + let was_paused = self.firmware.os.paused; + // 1. Process pending debug commands from the network. self.debugger.check_commands(&mut self.firmware, &mut self.hardware); @@ -283,6 +382,15 @@ impl ApplicationHandler for HostRunner { self.stats.record_frame(); } + self.presentation.note_published_frame(self.firmware.os.logical_frame_index); + + if was_debugger_connected != self.debugger.stream.is_some() + || was_waiting_for_start != self.debugger.waiting_for_start + || was_paused != self.firmware.os.paused + { + self.invalidate_host_surface(); + } + // 4. Feedback and Synchronization. self.audio.update_stats(&mut self.stats); @@ -298,9 +406,15 @@ impl ApplicationHandler for HostRunner { }; self.log_sink.process_events(new_events); - // 5. Request redraw so the host surface can present the latest machine frame - // and, when enabled, compose the overlay in the host-only RGBA surface. - self.request_redraw(); + // 5. Request redraw only when a new logical frame was published or the + // host-owned presentation surface became invalid. + self.request_redraw_if_needed(); + event_loop.set_control_flow(desired_control_flow( + Instant::now(), + self.machine_running(), + self.accumulator, + self.frame_target_dt, + )); } } @@ -313,6 +427,70 @@ mod tests { use std::io::{Read, Write}; use std::net::TcpStream; + #[test] + fn presentation_state_requires_initial_redraw_only_once() { + let mut state = PresentationState::default(); + + assert!(state.should_request_redraw()); + assert!(!state.should_request_redraw()); + + state.mark_presented(); + + assert!(!state.should_request_redraw()); + } + + #[test] + fn presentation_state_requests_redraw_for_new_frame_publication() { + let mut state = PresentationState::default(); + state.mark_presented(); + + state.note_published_frame(1); + + assert!(state.should_request_redraw()); + state.mark_presented(); + assert_eq!(state.last_presented_frame, Some(1)); + } + + #[test] + fn presentation_state_requests_redraw_for_host_invalidation_without_new_frame() { + let mut state = PresentationState::default(); + state.mark_presented(); + + state.invalidate_host_surface(); + + assert!(state.should_request_redraw()); + state.mark_presented(); + assert!(!state.needs_redraw()); + } + + #[test] + fn desired_control_flow_waits_until_next_logical_deadline_while_running() { + let now = Instant::now(); + let flow = + desired_control_flow(now, true, Duration::from_millis(5), Duration::from_millis(16)); + + match flow { + ControlFlow::WaitUntil(deadline) => { + assert_eq!(deadline.duration_since(now), Duration::from_millis(11)); + } + other => panic!("unexpected control flow: {:?}", other), + } + } + + #[test] + fn desired_control_flow_uses_idle_poll_when_machine_is_not_running() { + let now = Instant::now(); + let flow = + desired_control_flow(now, false, Duration::from_millis(5), Duration::from_millis(16)); + + match flow { + ControlFlow::WaitUntil(deadline) => { + assert_eq!(deadline.duration_since(now), IDLE_HOST_POLL_DT); + } + other => panic!("unexpected control flow: {:?}", other), + } + } + #[test] fn host_debugger_maps_cert_events_from_host_owned_sources() { let telemetry = TelemetryFrame { diff --git a/discussion/index.ndjson b/discussion/index.ndjson index e82c3ea4..5790463f 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -12,7 +12,7 @@ {"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":"workflow/agendas/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-04-10","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0005","file":"workflow/decisions/DEC-0005-perf-push-based-telemetry-model.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0005","file":"workflow/plans/PLN-0005-perf-push-based-telemetry-implementation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0026","file":"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":"workflow/agendas/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-0010","status":"in_progress","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":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"in_progress","created_at":"2026-03-27","updated_at":"2026-04-20"}],"decisions":[{"id":"DEC-0019","file":"DEC-0019-host-desktop-frame-pacing-and-presentation.md","status":"accepted","created_at":"2026-04-20","updated_at":"2026-04-20","ref_agenda":"AGD-0009"}],"plans":[{"id":"PLN-0036","file":"PLN-0036-host-desktop-frame-pacing-and-presentation.md","status":"review","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0019"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0010","status":"in_progress","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":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"in_progress","created_at":"2026-03-27","updated_at":"2026-04-20"}],"decisions":[{"id":"DEC-0019","file":"DEC-0019-host-desktop-frame-pacing-and-presentation.md","status":"accepted","created_at":"2026-04-20","updated_at":"2026-04-20","ref_agenda":"AGD-0009"}],"plans":[{"id":"PLN-0036","file":"PLN-0036-host-desktop-frame-pacing-and-presentation.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0019"]}],"lessons":[]} {"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":"workflow/agendas/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-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":"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":"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"}]} diff --git a/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md index effb618d..e43ccdfb 100644 --- a/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md +++ b/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md @@ -2,9 +2,9 @@ id: PLN-0036 ticket: perf-host-desktop-frame-pacing-and-presentation title: Plan - Host Desktop Frame Pacing and Presentation -status: review +status: done created: 2026-04-20 -completed: +completed: 2026-04-20 tags: [perf, host, desktop, frame-pacing, presentation, debug] --- diff --git a/docs/specs/runtime/10-debug-inspection-and-profiling.md b/docs/specs/runtime/10-debug-inspection-and-profiling.md index 1c656380..4221ab0f 100644 --- a/docs/specs/runtime/10-debug-inspection-and-profiling.md +++ b/docs/specs/runtime/10-debug-inspection-and-profiling.md @@ -131,6 +131,16 @@ The host may: - observe buffers separately - identify excessive redraw +Host-owned overlay or inspection surfaces MUST observe the last published logical frame rather than force continuous redraw to probe for changes. + +When overlay, debugger, or other host-only diagnostics become visually stable: + +- the host MUST preserve the last valid visible image; +- the host MUST NOT keep recomposing the surface by continuous polling alone; +- additional redraw is only justified by a newly published logical frame or a host-owned invalidation event such as overlay toggle, resize, expose, or explicit debugger-visible state transition. + +Paused or breakpointed execution does not grant permission to swap logical machine buffers just to sustain a host-only HUD. + ## 6 Time Profiling (Cycles) ### 6.1 Per-Frame Measurement diff --git a/docs/specs/runtime/11-portability-and-cross-platform-execution.md b/docs/specs/runtime/11-portability-and-cross-platform-execution.md index dac986f4..82d6fe1a 100644 --- a/docs/specs/runtime/11-portability-and-cross-platform-execution.md +++ b/docs/specs/runtime/11-portability-and-cross-platform-execution.md @@ -127,6 +127,14 @@ The platform layer: - **may overlay technical HUDs without modifying the logical framebuffer** - may transport the logical framebuffer into a host presentation surface where a host-only overlay layer is composed +Host presentation SHOULD be driven by published logical frames and explicit host-owned invalidation, not by perpetual redraw polling. + +In particular: + +- a stable logical frame MAY remain visible across multiple host wakeups without recomposition; +- a host MAY redraw the same logical frame again when its own surface is invalidated by resize, expose, or host-only overlay/debug changes; +- a host MUST NOT invent intermediate logical frames or require continuous redraw merely to discover whether a new logical frame exists. + ## 9 Debug and Inspection Isolation To preserve portability and certification purity, technical inspection tools (like the Debug Overlay) are moved to the Host layer. @@ -144,6 +152,8 @@ Inspection is facilitated by a lockless, push-based atomic interface: 2. **Asynchronous Observation:** The Host layer reads snapshots of these counters at its own display frequency. 3. **Loop Purity:** This ensures that the VM execution loop remains deterministic and free from synchronization overhead (locks) that could vary across host architectures. +Reading host-owned telemetry does not imply a perpetual presentation loop. The host may wake on its own schedule for inspection purposes while still presenting the logical framebuffer only when a published frame or host-owned invalidation requires it. + ## 10 File System and Persistence PROMETEU defines a **sandbox logical filesystem**: -- 2.47.2 From 193fe78b5eeb302a6b17e4438cf762417a5aaefd Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 20 Apr 2026 09:54:05 +0100 Subject: [PATCH 3/4] [PERF] Host Desktop Frame Pacing and Presentation --- discussion/index.ndjson | 4 +- ...t-desktop-frame-pacing-and-presentation.md | 90 ---------- ...t-desktop-frame-pacing-and-presentation.md | 141 --------------- ...t-desktop-frame-pacing-and-presentation.md | 170 ------------------ 4 files changed, 2 insertions(+), 403 deletions(-) delete mode 100644 discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md delete mode 100644 discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md delete mode 100644 discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 5790463f..9d953d86 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":20,"PLN":37,"LSN":36,"CLSN":1}} +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":20,"PLN":37,"LSN":37,"CLSN":1}} {"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} @@ -12,7 +12,7 @@ {"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":"workflow/agendas/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-04-10","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0005","file":"workflow/decisions/DEC-0005-perf-push-based-telemetry-model.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0005","file":"workflow/plans/PLN-0005-perf-push-based-telemetry-implementation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0026","file":"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":"workflow/agendas/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-0010","status":"in_progress","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":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"in_progress","created_at":"2026-03-27","updated_at":"2026-04-20"}],"decisions":[{"id":"DEC-0019","file":"DEC-0019-host-desktop-frame-pacing-and-presentation.md","status":"accepted","created_at":"2026-04-20","updated_at":"2026-04-20","ref_agenda":"AGD-0009"}],"plans":[{"id":"PLN-0036","file":"PLN-0036-host-desktop-frame-pacing-and-presentation.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20","ref_decisions":["DEC-0019"]}],"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":"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":"workflow/agendas/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-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":"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":"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"}]} diff --git a/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md deleted file mode 100644 index 4ee79cfd..00000000 --- a/discussion/workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -id: AGD-0009 -ticket: perf-host-desktop-frame-pacing-and-presentation -title: Agenda - [PERF] Host Desktop Frame Pacing and Presentation -status: in_progress -created: 2026-03-27 -resolved: 2026-04-20 -decision: DEC-0019 -tags: [] ---- - -# Agenda - [PERF] Host Desktop Frame Pacing and Presentation - -## Problema - -O host desktop ainda roda em modo agressivo de polling e apresentacao continua. - -Hoje o loop usa `ControlFlow::Poll`, pede redraw incondicionalmente e converte o framebuffer inteiro de RGB565 para RGBA8 a cada `RedrawRequested`, mesmo quando nao ha novo frame logico. - -## Dor - -- CPU fica ocupada sem ganho visual. -- port de referencia no desktop mascara problemas de pacing em hardware barato. -- a conta de energia/temperatura piora mesmo quando a VM esta ociosa. - -## Hotspots Atuais - -- [runner.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/host/prometeu-host-desktop-winit/src/runner.rs#L252) -- [runner.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/host/prometeu-host-desktop-winit/src/runner.rs#L270) -- [runner.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/host/prometeu-host-desktop-winit/src/runner.rs#L390) - -## Alvo da Discussao - -Fechar uma politica de pacing/apresentacao host-driven que nao desperdice CPU quando nao existe frame novo. - -## O Que Precisa Ser Definido - -1. Gatilho de redraw. - Decidir se redraw acontece: - - apenas com logical frame pronto; - - por deadline de vsync; - - por dirty flag do front buffer; - - por evento externo relevante. - -2. Politica do event loop. - Decidir entre: - - `Wait`; - - `WaitUntil`; - - `Poll` apenas em modo debug/profiling. - -3. Conversao de framebuffer. - Definir se a conversao RGB565 -> RGBA8: - - continua full-frame; - - passa a ser dirty-region; - - sai da CPU e vai para shader/path especifico do host. - -4. Modo ocioso. - Delimitar comportamento quando VM esta pausada, em breakpoint ou sem cart. - -## Open Questions de Arquitetura - -1. O host desktop deve ser referencia conservadora de energia ou apenas shell de desenvolvimento? - R: O desktop eh shell de desenvolvimento, mas pode tb ser usado para jogar em um pc. Deve oferecer controle de energia como um handheld, especialmente para desenvolvimento. -2. O runtime precisa expor um sinal explicito de "novo frame disponivel" para o host? - R: Atualmente fazemos o swap do buffer somente quando o frame logico eh pronto, esse eh o "ponto de entrada" para o host. Eh ali que a conversao de RGB565 -> RGBA8 pode ser realizada e mostrada. Se o host precisar saber quando o frame eh pronto, pode ser exposto por um sinal explicito. -3. Existe necessidade real de redraw continuo quando o overlay esta desligado? - R: Nao - -## Perguntas em Aberto - -Nenhuma. As perguntas de arquitetura desta agenda foram respondidas e o tema esta pronto para cristalizacao em decisao. - -## Resolucao - -A agenda fecha com a direcao de que o host desktop nao deve manter redraw continuo por padrao. O host deve operar com pacing orientado por frame logico pronto e por eventos externos relevantes, preservando um comportamento energeticamente conservador sem perder utilidade como shell de desenvolvimento. Se o host precisar consumir esse momento de forma explicita, o runtime pode expor um sinal canonico de "novo frame disponivel" no ponto em que o frame logico eh concluido. - -## Dependencias - -- `../specs/01-time-model-and-cycles.md` -- `../specs/10-debug-inspection-and-profiling.md` -- `../specs/11-portability-and-cross-platform-execution.md` - -## Criterio de Saida Desta Agenda - -Pode virar PR quando houver decisao escrita sobre: - -- politica de control flow do host; -- criterio canonico de redraw; -- estrategia de conversao/apresentacao de framebuffer; -- comportamento de idle/pause/debug. diff --git a/discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md deleted file mode 100644 index 0d9a3145..00000000 --- a/discussion/workflow/decisions/DEC-0019-host-desktop-frame-pacing-and-presentation.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -id: DEC-0019 -ticket: perf-host-desktop-frame-pacing-and-presentation -title: Decision - [PERF] Host Desktop Frame Pacing and Presentation -status: accepted -created: 2026-04-20 -accepted: 2026-04-20 -agenda: AGD-0009 -plans: [PLN-0036] -tags: [perf, host, desktop, frame-pacing, presentation, debug] ---- - -# Status - -Accepted. This decision is now normatively locked and is ready to drive planning and execution. - -## Contexto - -O host desktop atual usa `ControlFlow::Poll`, pede redraw continuamente e reapresenta o framebuffer mesmo quando a maquina nao publicou um novo frame logico. - -Esse comportamento conflita com o modelo temporal de `docs/specs/runtime/01-time-model-and-cycles.md`, onde a maquina opera em frames logicos de 60 Hz, e com o contrato de portabilidade e isolamento de debug em `docs/specs/runtime/10-debug-inspection-and-profiling.md` e `docs/specs/runtime/11-portability-and-cross-platform-execution.md`, onde apresentacao e overlay pertencem ao host, mas nao devem redefinir a semantica logica da maquina. - -O problema a ser fechado aqui nao eh o desenho final do backend grafico. O problema eh definir quando o host desktop tem permissao para acordar, converter e apresentar pixels sem transformar a shell desktop em um loop de polling agressivo. - -## Decisao - -O host desktop SHALL adotar pacing orientado por frame logico publicado e por eventos externos relevantes, e MUST NOT manter redraw continuo por padrao. - -Normas desta decisao: - -1. Gatilho canonico de redraw - - O host MUST considerar um novo frame logico publicado como gatilho principal de apresentacao. - - O host MAY solicitar redraw adicional quando houver evento externo relevante que altere apenas a superficie host-owned, incluindo resize da janela, expose/invalidate da janela, toggle de overlay, atualizacao visivel do debugger ou mudanca de estado local da shell. - - O host MUST NOT solicitar redraw continuo apenas para "ver se existe frame novo". - -2. Politica do event loop - - O host desktop MUST sair de `ControlFlow::Poll` como politica padrao. - - O event loop SHOULD operar em modo de espera dirigida por deadline ou evento, usando `WaitUntil` para o proximo slice logico quando houver trabalho temporal agendado e `Wait` quando nao houver deadline imediato. - - `Poll` MAY existir apenas em modo explicitamente opt-in de profiling/diagnostico host-owned e MUST NOT ser o comportamento normal de execucao. - -3. Conversao de framebuffer - - A conversao RGB565 -> RGBA8 permanece full-frame nesta fase. - - Essa conversao MUST acontecer apenas quando houver novo frame logico publicado ou quando um evento host-owned exigir recomposicao/apresentacao da superficie visivel. - - Dirty-region conversion e offload para shader/GPU ficam explicitamente fora do contrato desta decisao e poderao ser avaliados em discussao separada se o full-frame on-demand ainda for insuficiente. - -4. Modo ocioso, pausa e debug - - Quando a VM estiver pausada, em breakpoint, esperando `start`, ou sem cart carregado, o host MUST preservar o ultimo frame visivel valido e MUST NOT ficar redesenhando continuamente. - - Nesses estados, redraw adicional so pode ocorrer por evento host-owned relevante, como overlay/debug data visivel, resize/expose ou transicao explicita de estado. - - Buffers logicos da maquina MUST NOT ser trocados durante pausa apenas para sustentar HUD host-owned. - -5. Sinal de "novo frame disponivel" - - O runtime/host boundary MUST ter um ponto canonico de observacao do "frame pronto", alinhado ao momento em que o frame logico e publicado para apresentacao. - - A implementacao inicial MAY usar um sinal explicito, contador monotonicamente crescente, dirty flag ou observacao do ponto de swap/publicacao, desde que a semantica seja unica e nao ambigua. - - Essa decisao MUST NOT introduzir uma ABI guest-visible nova. O sinal pertence ao boundary interno runtime-host e as superficies host-owned de inspecao. - -## Rationale - -`Poll` com redraw continuo mascara custo de apresentacao, consome CPU sem beneficio visual e reduz a utilidade do desktop como referencia de comportamento energetico razoavel. - -Ao mesmo tempo, a shell desktop nao deve ser tratada como uma plataforma "ultra-conservadora" a ponto de perder ergonomia de debug. Por isso a decisao nao proibe redraw extra; ela apenas restringe redraw extra a eventos com causa concreta e visivel. - -Manter a conversao full-frame por enquanto evita reabrir a arquitetura de renderizacao durante um trabalho cujo alvo principal eh pacing. O ganho imediato vem de parar de converter e apresentar quando nada mudou. Se isso nao bastar, dirty regions ou shader path podem ser discutidos depois com evidencias. - -Separar "frame logico publicado" de "overlay host-owned mudou" preserva o contrato de portabilidade: o host pode mostrar mais informacao tecnica sem forcar a VM a produzir frames adicionais nem adulterar o framebuffer logico. - -## Invariantes / Contrato - -- A maquina continua definindo frames logicos em 60 Hz; o host nao redefine esse relogio. -- O host apresenta o ultimo frame logico publicado; ele nao inventa frames intermediarios. -- Overlay e debugger sao host-owned e MAY compor sobre uma superficie de apresentacao host-only. -- Overlay/debug MUST NOT exigir redraw continuo quando seu conteudo estiver estavel. -- Conversao/presentacao do framebuffer MUST ser dirigida por mudanca observavel, nao por polling agressivo. -- O boundary runtime-host MUST expor um criterio unico para detectar publicacao de frame logico. -- Esta decisao nao promove dirty regions nem GPU offload a contrato normativo. -- Esta decisao nao altera semantica guest-visible de `DRAW`, buffers logicos, ciclos, ou telemetria de certificacao. - -## Alternativas Descartadas - -### Manter `Poll` permanente no desktop - -Descartado porque esconde custo real de apresentacao, desperdiça CPU e cria uma shell cuja estabilidade aparente depende de trabalho inutil. - -### Redraw orientado apenas por vsync fisico - -Descartado como contrato canonico porque o modelo PROMETEU e dirigido por frame logico e a portabilidade nao depende de sincronizar a semantica da maquina a uma frequencia fisica do host. - -### Reabrir o problema com dirty regions ou shader path como pre-condicao - -Descartado nesta etapa porque mistura otimizacao secundaria com o contrato principal de pacing. O problema imediato pode e deve ser resolvido sem nova arquitetura de render backend. - -## Impactos - -### Spec - -- `docs/specs/runtime/10-debug-inspection-and-profiling.md` deve refletir que a apresentacao host-owned observa frame publicado e nao requer redraw continuo para HUD/overlay estavel. -- `docs/specs/runtime/11-portability-and-cross-platform-execution.md` deve explicitar que a superficie host de apresentacao pode recompor overlay por evento relevante, sem transformar isso em polling permanente. - -### Runtime - -- O runtime pode precisar expor ou estabilizar um ponto interno de publicacao de frame pronto para o host desktop consumir de forma canonica. -- Nenhuma nova ABI guest-visible deve ser criada por este trabalho. - -### Host Desktop - -- O loop winit deve migrar de `Poll` para espera dirigida por deadline/evento. -- `request_redraw()` deixa de ser incondicional no ciclo de idle. -- A apresentacao passa a depender de novo frame logico ou evento host-owned relevante. - -### Firmware - -- Sem mudanca de semantica guest-visible. -- Estados de pausa/breakpoint continuam impedindo avanco de tempo logico e swap de buffers logicos. - -### Tooling - -- Overlay/debugger continuam host-owned, mas precisam declarar quando realmente invalidam a superficie visivel. -- Profiling host-only pode manter um modo opt-in de polling agressivo, separado do comportamento normal. - -## Referencias - -- [AGD-0009]() -- [docs/specs/runtime/01-time-model-and-cycles.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/docs/specs/runtime/01-time-model-and-cycles.md) -- [docs/specs/runtime/10-debug-inspection-and-profiling.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/docs/specs/runtime/10-debug-inspection-and-profiling.md) -- [docs/specs/runtime/11-portability-and-cross-platform-execution.md](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/docs/specs/runtime/11-portability-and-cross-platform-execution.md) -- [runner.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/host/prometeu-host-desktop-winit/src/runner.rs:252) - -## Propagacao Necessaria - -- Escrever um `PLN` para implementar a mudanca no host desktop. -- Atualizar as specs normativas citadas para refletir este contrato. -- Ajustar o host desktop para consumir um gatilho canonico de frame publicado. -- Adicionar testes para: - - ausencia de redraw continuo sem frame novo; - - redraw em evento host-owned relevante; - - preservacao do ultimo frame em pausa/breakpoint/no-cart; - - modo opt-in de polling apenas quando explicitamente habilitado. - -## Revision Log - -- 2026-04-20: Initial draft from AGD-0009. -- 2026-04-20: Accepted and linked to PLN-0036. diff --git a/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md b/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md deleted file mode 100644 index e43ccdfb..00000000 --- a/discussion/workflow/plans/PLN-0036-host-desktop-frame-pacing-and-presentation.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -id: PLN-0036 -ticket: perf-host-desktop-frame-pacing-and-presentation -title: Plan - Host Desktop Frame Pacing and Presentation -status: done -created: 2026-04-20 -completed: 2026-04-20 -tags: [perf, host, desktop, frame-pacing, presentation, debug] ---- - -## Briefing - -Implement `DEC-0019` by removing unconditional desktop redraw polling, introducing a canonical host-side frame-publication trigger, preserving the last visible frame during paused or idle states, and updating the normative specs to reflect the locked contract. - -## Decisions de Origem - -- `DEC-0019` - Host Desktop Frame Pacing and Presentation - -## Alvo - -Deliver a host desktop execution path where: - -- the winit loop no longer uses continuous polling by default; -- redraw happens only after logical frame publication or explicit host-owned invalidation events; -- RGB565 to RGBA8 conversion remains full-frame but only runs on demand; -- paused, debugger-waiting, and no-cartridge states keep the last valid image without continuous redraw; -- specs and tests match the accepted decision. - -## Escopo - -- Update the desktop host loop in `crates/host/prometeu-host-desktop-winit/src/runner.rs`. -- Introduce or stabilize a canonical "new frame published" signal at the runtime-host boundary. -- Track host-owned invalidation causes for overlay, debugger, and window events. -- Update the relevant runtime specs under `docs/specs/runtime/`. -- Add or extend automated tests for redraw gating and paused-state behavior. - -## Fora de Escopo - -- Dirty-region framebuffer conversion. -- GPU/shader-based presentation paths. -- New guest-visible ABI or debug surface. -- Redesign of overlay visuals or debugger protocol beyond invalidation signaling. -- Reopening the architecture covered by `DEC-0019`. - -## Plano de Execucao - -### Step 1 - Establish the canonical frame-publication trigger - -**What:** Define the single host-consumable signal that indicates a new logical frame has been published. - -**How:** Inspect the current buffer swap/publication point in the desktop runtime path and choose the existing internal publication event, dirty flag, or monotonic frame counter that can become the canonical trigger required by `DEC-0019`. If no stable signal exists, add a host-internal publication marker that changes exactly once per newly published logical frame and is not guest-visible. - -**File(s):** -- `crates/host/prometeu-host-desktop-winit/src/runner.rs` -- any directly related runtime/firmware module discovered as the current frame-publication owner - -**Depends on:** none - -### Step 2 - Replace unconditional redraw requests with invalidation-driven scheduling - -**What:** Remove the unconditional redraw call from the idle loop and gate presentation on frame publication or host-owned invalidation. - -**How:** Refactor `about_to_wait()` and redraw scheduling in `runner.rs` so the host requests redraw only when: - -- a new logical frame was published since the last presentation; -- the window receives a visible invalidation event such as resize/expose; -- overlay state toggles or changes a visible host-owned layer; -- debugger-visible state changes require host-side recomposition. - -Introduce explicit host-owned invalidation state if needed so redraw causes are tracked instead of inferred by polling. - -**File(s):** -- `crates/host/prometeu-host-desktop-winit/src/runner.rs` - -**Depends on:** Step 1 - -### Step 3 - Move the event loop from default `Poll` to deadline/event waiting - -**What:** Change the normal desktop control flow so the host waits for the next logical deadline or external event instead of spinning continuously. - -**How:** Update `resumed()` and any related loop control code in `runner.rs` to stop using `ControlFlow::Poll` as the default. Use `WaitUntil` when a next logical tick deadline is known and `Wait` when the host is effectively idle with no immediate timing deadline. Keep any aggressive polling path behind an explicit profiling-only switch rather than as baseline behavior. - -**File(s):** -- `crates/host/prometeu-host-desktop-winit/src/runner.rs` -- `crates/host/prometeu-host-desktop-winit/src/main.rs` if a profiling-only opt-in flag is wired at startup - -**Depends on:** Step 2 - -### Step 4 - Preserve the last valid frame during paused, breakpoint, and no-cart states - -**What:** Ensure non-running machine states do not cause repeated conversion/presentation work while still allowing host-owned UI recomposition when something visible changes. - -**How:** Separate "machine produced a new frame" from "host-owned layer needs redraw". Keep the last presented RGBA output available for reuse, avoid logical buffer swaps during pause-only overlay activity, and ensure debugger waiting and no-cartridge states do not schedule continuous redraws. If overlay or debugger content changes while the machine is paused, redraw only the host-owned composition layer on explicit invalidation. - -**File(s):** -- `crates/host/prometeu-host-desktop-winit/src/runner.rs` -- `crates/host/prometeu-host-desktop-winit/src/overlay.rs` -- `crates/host/prometeu-host-desktop-winit/src/debugger.rs` - -**Depends on:** Step 2 - -### Step 5 - Update the normative specs - -**What:** Propagate the accepted decision into the canonical runtime specs. - -**How:** Update the portability and debug chapters so they explicitly state that desktop presentation observes published logical frames, that host-owned overlay/debug redraw is event-driven rather than continuously polled, and that stable host HUD state does not justify perpetual redraw. Keep the text aligned with the locked decision and do not introduce new architecture. - -**File(s):** -- `docs/specs/runtime/10-debug-inspection-and-profiling.md` -- `docs/specs/runtime/11-portability-and-cross-platform-execution.md` - -**Depends on:** Step 1 - -### Step 6 - Add regression tests and evidence - -**What:** Add automated coverage for the redraw contract and document manual evidence where platform events are hard to model. - -**How:** Extend or add tests in the desktop host crate to cover: - -- no redraw request when no new frame and no host invalidation occurred; -- redraw request after a newly published logical frame; -- redraw request after overlay toggle or equivalent host-owned invalidation; -- no continuous redraw in paused/debugger-waiting/no-cart states; -- preservation of last visible frame semantics while paused. - -Where direct winit event simulation is difficult, factor logic into testable state transitions and keep any remaining platform verification in a short manual checklist. - -**File(s):** -- `crates/host/prometeu-host-desktop-winit/src/runner.rs` -- `crates/host/prometeu-host-desktop-winit/src/overlay.rs` if overlay invalidation helpers become testable -- `crates/host/prometeu-host-desktop-winit/src/debugger.rs` if debugger invalidation hooks are exposed - -**Depends on:** Steps 2, 3, 4 - -## Criterios de Aceite - -- The default desktop event loop no longer runs in continuous `ControlFlow::Poll`. -- The host requests redraw only after canonical frame publication or explicit host-owned invalidation. -- Full-frame RGB565 to RGBA8 conversion remains in place but is not executed continuously while the visible machine frame is unchanged. -- Paused, breakpoint, debugger-waiting, and no-cartridge states preserve the last valid image without continuous redraw. -- Overlay or debugger changes can still trigger targeted host-owned redraw when visually necessary. -- No guest-visible ABI is added for this feature. -- The relevant runtime specs state the same redraw and presentation contract as `DEC-0019`. -- Automated tests cover the main redraw gating paths and paused-state behavior. - -## Tests / Validacao - -### Unit Tests - -- Add focused tests for redraw scheduling state transitions in the desktop host runner. -- Add tests for any helper that tracks frame-publication state or host-owned invalidation causes. - -### Integration Tests - -- Extend host crate tests to verify frame publication and debugger/overlay invalidation do not regress redraw policy. -- Run `cargo test -p prometeu-host-desktop-winit --lib`. - -### Manual Verification - -- Launch the desktop host with overlay disabled and confirm CPU usage drops when the machine is visually stable. -- Toggle overlay on and off and confirm redraw happens on the visible transition without restoring continuous polling. -- Pause execution or wait for debugger start and confirm the last frame remains visible without repeated redraw. -- Resize or expose the window and confirm the surface is recomposed correctly. - -## Riscos - -- The actual frame-publication point may be less explicit than assumed, requiring a small host-internal contract addition before redraw gating can be made reliable. -- winit waiting semantics can introduce timing regressions if the next logical deadline is computed incorrectly. -- Overlay or debugger code may currently assume redraw is always free, which can surface hidden invalidation bugs after polling is removed. -- Manual verification may still be needed for some window-system invalidation behavior that is cumbersome to model in tests. -- 2.47.2 From c78f091efefa34977ce8176bd871322ee193ba01 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 20 Apr 2026 09:54:24 +0100 Subject: [PATCH 4/4] [PERF] Host Desktop Frame Pacing and Presentation --- ...-and-host-invalidation-must-be-separate.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md diff --git a/discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md b/discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md new file mode 100644 index 00000000..62d3bde9 --- /dev/null +++ b/discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md @@ -0,0 +1,62 @@ +--- +id: LSN-0036 +ticket: perf-host-desktop-frame-pacing-and-presentation +title: Frame Publication and Host Invalidation Must Be Separate +created: 2026-04-20 +tags: [performance, host, desktop, presentation, frame-pacing, debug] +--- + +# Frame Publication and Host Invalidation Must Be Separate + +## Context + +The desktop host was waking aggressively with `ControlFlow::Poll`, requesting redraw continuously, and converting the full RGB565 framebuffer to RGBA8 on every redraw even when the machine had not published a new logical frame. + +That design hid real presentation cost, wasted CPU on stable screens, and blurred an important boundary: the runtime owns logical frame production, while the host owns window invalidation, overlays, and debugger presentation. + +## Key Decisions + +### Published logical frames are the canonical machine-side redraw trigger + +**What:** +The host presentation loop now treats "a new logical frame was published" as the primary reason to redraw the machine image. + +**Why:** +PROMETEU portability and timing depend on logical frames, not on a host polling loop. If the host redraws just to discover whether a frame changed, presentation cost stops reflecting actual machine behavior. + +**Trade-offs:** +This requires an explicit or at least stable host-side way to observe frame publication. The extra state tracking is worth it because it keeps redraw policy honest and testable. + +### Host-only invalidation is a separate redraw cause + +**What:** +Resize, expose, overlay toggle, and debugger-visible transitions remain valid redraw causes, but they are treated as host-owned invalidation rather than machine frame production. + +**Why:** +The host still has to repair or recomposite its visible surface when the OS window changes or a technical HUD becomes visible. That need is real, but it must not be confused with "the machine emitted another frame." + +**Trade-offs:** +The host needs separate invalidation bookkeeping. In return, paused or stable machine state no longer forces continuous framebuffer conversion. + +## Patterns and Algorithms + +- Use two independent signals in presentation code: + - latest published logical frame; + - host-owned surface invalidation. +- Request redraw only when one of those signals changes and only once per pending update. +- After presentation completes, mark the current logical frame as presented and clear host invalidation. +- When the machine is running, wait until the next logical deadline instead of spinning continuously. +- When the machine is paused or waiting for debugger start, allow low-frequency host wakeups or OS events, but keep the last valid image until a real invalidation occurs. + +## Pitfalls + +- Do not use redraw polling as a substitute for a missing publication signal. That only hides the architectural gap. +- Do not let host overlays imply extra logical frame production. Host HUDs may change the visible surface without changing machine state. +- Do not promote host-only invalidation into guest-visible ABI. The runtime-host handshake may need internal state, but the cartridge contract does not. +- Do not reopen render-backend architecture just to fix pacing. Dirty regions or GPU offload are separate optimization questions. + +## Takeaways + +- A stable screen is a first-class state and should not cost continuous presentation work. +- Frame production and host invalidation are different events and should remain different in code. +- Event-driven redraw policy is easier to test, cheaper to run, and more faithful to the machine contract than polling-driven presentation. -- 2.47.2