--- 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.