dev/perf-host-desktop-frame-pacing-and-presentation #19
@ -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<u64>,
|
||||
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 {
|
||||
|
||||
@ -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":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":"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":"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"}]}
|
||||
|
||||
@ -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.
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
id: AGD-0009
|
||||
ticket: perf-host-desktop-frame-pacing-and-presentation
|
||||
title: Agenda - [PERF] Host Desktop Frame Pacing and Presentation
|
||||
status: open
|
||||
created: 2026-03-27
|
||||
resolved:
|
||||
decision:
|
||||
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?
|
||||
2. O runtime precisa expor um sinal explicito de "novo frame disponivel" para o host?
|
||||
3. Existe necessidade real de redraw continuo quando o overlay esta desligado?
|
||||
|
||||
## 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.
|
||||
@ -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
|
||||
|
||||
@ -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**:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user