From d79c11faa847e3d2973e124ed05638fb4c5c10ad Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 25 May 2026 14:23:18 +0100 Subject: [PATCH] implements PLN-0084 remove host debug overlay --- .../prometeu-host-desktop-winit/src/lib.rs | 1 - .../src/overlay.rs | 487 ------------------ .../prometeu-host-desktop-winit/src/runner.rs | 26 +- discussion/index.ndjson | 2 +- .../PLN-0084-host-debug-overlay-removal.md | 2 +- .../10-debug-inspection-and-profiling.md | 36 +- ...ortability-and-cross-platform-execution.md | 15 +- 7 files changed, 33 insertions(+), 536 deletions(-) delete mode 100644 crates/host/prometeu-host-desktop-winit/src/overlay.rs diff --git a/crates/host/prometeu-host-desktop-winit/src/lib.rs b/crates/host/prometeu-host-desktop-winit/src/lib.rs index be84a693..a91c231f 100644 --- a/crates/host/prometeu-host-desktop-winit/src/lib.rs +++ b/crates/host/prometeu-host-desktop-winit/src/lib.rs @@ -4,7 +4,6 @@ pub mod debugger; pub mod fs_backend; pub mod input; pub mod log_sink; -pub mod overlay; pub mod runner; pub mod stats; pub mod utilities; diff --git a/crates/host/prometeu-host-desktop-winit/src/overlay.rs b/crates/host/prometeu-host-desktop-winit/src/overlay.rs deleted file mode 100644 index a18795c0..00000000 --- a/crates/host/prometeu-host-desktop-winit/src/overlay.rs +++ /dev/null @@ -1,487 +0,0 @@ -use crate::stats::HostStats; -use prometeu_firmware::Firmware; - -const PANEL_X: usize = 6; -const PANEL_Y: usize = 3; -const PANEL_WIDTH: usize = 170; -const PANEL_PADDING_X: usize = 8; -const PANEL_PADDING_Y: usize = 3; -const LINE_HEIGHT: usize = 12; -const CHAR_SCALE: usize = 1; -const BAR_WIDTH: usize = PANEL_WIDTH - (PANEL_PADDING_X * 2); -const BAR_HEIGHT: usize = 6; - -const BG: [u8; 4] = [10, 18, 32, 208]; -const BORDER: [u8; 4] = [90, 126, 170, 255]; -const TEXT: [u8; 4] = [235, 240, 255, 255]; -const DIM: [u8; 4] = [150, 168, 196, 255]; -const WARN: [u8; 4] = [255, 104, 104, 255]; -const BAR_BG: [u8; 4] = [30, 42, 61, 255]; -const BAR_FILL: [u8; 4] = [91, 184, 255, 255]; -const BAR_WARN: [u8; 4] = [255, 150, 102, 255]; -const OVERLAY_HEAP_FALLBACK_BYTES: usize = 8 * 1024 * 1024; - -#[derive(Debug, Clone)] -pub(crate) struct OverlayMetric { - label: &'static str, - value: String, - warn: bool, -} - -#[derive(Debug, Clone)] -pub(crate) struct OverlayBar { - label: &'static str, - value: String, - ratio: f32, - warn: bool, -} - -#[derive(Debug, Clone)] -pub(crate) struct OverlaySnapshot { - rows: Vec<(OverlayMetric, OverlayMetric)>, - bars: Vec, - footer: Vec, -} - -#[derive(Clone, Copy)] -struct Rect { - x: usize, - y: usize, - width: usize, - height: usize, -} - -struct FrameCanvas<'a> { - frame: &'a mut [u8], - width: usize, - height: usize, -} - -pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &mut Firmware) -> OverlaySnapshot { - let tel = firmware.os.vm().telemetry_snapshot(); - let recent_logs = firmware.os.recent_logs(10); - let violations_count = - recent_logs.iter().filter(|e| e.tag >= 0xCA01 && e.tag <= 0xCA07).count(); - - let mut footer = Vec::new(); - if violations_count > 0 - && let Some(event) = - recent_logs.into_iter().rev().find(|e| e.tag >= 0xCA01 && e.tag <= 0xCA07) - { - footer.push(OverlayMetric { - label: "CERT", - value: truncate_value(&event.msg, 28), - warn: true, - }); - } - - if let Some(report) = firmware.os.vm().last_crash_report() { - footer.push(OverlayMetric { - label: "CRASH", - value: truncate_value(&report.summary(), 28), - warn: true, - }); - } - - let cycles_ratio = ratio(tel.cycles_used, tel.cycles_budget); - let heap_total_bytes = firmware - .os - .vm() - .cert_config() - .max_heap_bytes - .or(if tel.heap_max_bytes > 0 { Some(tel.heap_max_bytes) } else { None }) - .unwrap_or(OVERLAY_HEAP_FALLBACK_BYTES); - let heap_ratio = ratio(tel.heap_used_bytes as u64, heap_total_bytes as u64); - let glyph_ratio = ratio(tel.glyph_slots_used as u64, tel.glyph_slots_total as u64); - let sound_ratio = ratio(tel.sound_slots_used as u64, tel.sound_slots_total as u64); - let scene_ratio = ratio(tel.scene_slots_used as u64, tel.scene_slots_total as u64); - - OverlaySnapshot { - rows: vec![ - ( - OverlayMetric { - label: "FPS", - value: format!("{:.1}", stats.current_fps), - warn: false, - }, - OverlayMetric { - label: "CERT", - value: violations_count.to_string(), - warn: violations_count > 0, - }, - ), - ( - OverlayMetric { - label: "HOST", - value: format!("{:.2}ms", stats.average_host_cpu_ms()), - warn: false, - }, - OverlayMetric { label: "STEPS", value: tel.vm_steps.to_string(), warn: false }, - ), - ( - OverlayMetric { label: "SYSC", value: tel.syscalls.to_string(), warn: false }, - OverlayMetric { label: "LOGS", value: tel.logs_count.to_string(), warn: false }, - ), - ], - bars: vec![ - OverlayBar { - label: "HEAP", - value: format!( - "{}K{}", - tel.heap_used_bytes.div_ceil(1024), - if heap_total_bytes > 0 { - format!(" / {}K", heap_total_bytes.div_ceil(1024)) - } else { - String::new() - } - ), - ratio: heap_ratio, - warn: tel.heap_used_bytes >= heap_total_bytes, - }, - OverlayBar { - label: "BUDGET", - value: if tel.cycles_budget > 0 { - format!( - "{:.1}K/{:.1}K", - tel.cycles_used as f64 / 1000.0, - tel.cycles_budget as f64 / 1000.0 - ) - } else { - "0.0K/0.0K".to_string() - }, - ratio: cycles_ratio, - warn: cycles_ratio >= 0.9, - }, - OverlayBar { - label: "GLYPH", - value: format!("{} / {} slots", tel.glyph_slots_used, tel.glyph_slots_total), - ratio: glyph_ratio, - warn: tel.glyph_slots_total > 0 && tel.glyph_slots_used >= tel.glyph_slots_total, - }, - OverlayBar { - label: "SOUNDS", - value: format!("{} / {} slots", tel.sound_slots_used, tel.sound_slots_total), - ratio: sound_ratio, - warn: tel.sound_slots_total > 0 && tel.sound_slots_used >= tel.sound_slots_total, - }, - OverlayBar { - label: "SCENE", - value: format!("{} / {} slots", tel.scene_slots_used, tel.scene_slots_total), - ratio: scene_ratio, - warn: tel.scene_slots_total > 0 && tel.scene_slots_used >= tel.scene_slots_total, - }, - ], - footer, - } -} - -pub(crate) fn draw_overlay( - frame: &mut [u8], - frame_width: usize, - frame_height: usize, - snapshot: &OverlaySnapshot, -) { - let mut canvas = FrameCanvas { frame, width: frame_width, height: frame_height }; - let panel_height = frame_height.saturating_sub(PANEL_Y * 2); - let panel_rect = Rect { x: PANEL_X, y: PANEL_Y, width: PANEL_WIDTH, height: panel_height }; - - fill_rect_alpha(&mut canvas, panel_rect, BG); - stroke_rect(&mut canvas, panel_rect, BORDER); - - let mut y = PANEL_Y + PANEL_PADDING_Y; - for (left, right) in &snapshot.rows { - draw_metric_pair(canvas.frame, canvas.width, canvas.height, y, left, right); - y += LINE_HEIGHT; - } - - for bar in &snapshot.bars { - let color = if bar.warn { WARN } else { TEXT }; - draw_text( - canvas.frame, - canvas.width, - canvas.height, - PANEL_X + PANEL_PADDING_X, - y, - bar.label, - DIM, - ); - draw_text(canvas.frame, canvas.width, canvas.height, PANEL_X + 48, y, &bar.value, color); - y += LINE_HEIGHT - 2; - - let bar_x = PANEL_X + PANEL_PADDING_X; - let bar_rect = Rect { x: bar_x, y, width: BAR_WIDTH, height: BAR_HEIGHT }; - fill_rect(&mut canvas, bar_rect, BAR_BG); - let fill_width = ((BAR_WIDTH as f32) * bar.ratio.clamp(0.0, 1.0)).round() as usize; - fill_rect( - &mut canvas, - Rect { x: bar_x, y, width: fill_width, height: BAR_HEIGHT }, - if bar.warn { BAR_WARN } else { BAR_FILL }, - ); - stroke_rect(&mut canvas, bar_rect, BORDER); - y += BAR_HEIGHT + 6; - } - - for line in &snapshot.footer { - let color = if line.warn { WARN } else { TEXT }; - draw_text( - canvas.frame, - canvas.width, - canvas.height, - PANEL_X + PANEL_PADDING_X, - y, - line.label, - DIM, - ); - draw_text(canvas.frame, canvas.width, canvas.height, PANEL_X + 48, y, &line.value, color); - y += LINE_HEIGHT; - } -} - -fn draw_metric_pair( - frame: &mut [u8], - frame_width: usize, - frame_height: usize, - y: usize, - left: &OverlayMetric, - right: &OverlayMetric, -) { - let left_x = PANEL_X + PANEL_PADDING_X; - let right_x = PANEL_X + 86; - - draw_metric(frame, frame_width, frame_height, left_x, y, left); - draw_metric(frame, frame_width, frame_height, right_x, y, right); -} - -fn draw_metric( - frame: &mut [u8], - frame_width: usize, - frame_height: usize, - x: usize, - y: usize, - metric: &OverlayMetric, -) { - let color = if metric.warn { WARN } else { TEXT }; - draw_text(frame, frame_width, frame_height, x, y, metric.label, DIM); - draw_text(frame, frame_width, frame_height, x + 30, y, &metric.value, color); -} - -fn ratio(value: u64, total: u64) -> f32 { - if total == 0 { 0.0 } else { (value as f32 / total as f32).clamp(0.0, 1.0) } -} - -fn truncate_value(value: &str, max_len: usize) -> String { - let mut upper = value.to_ascii_uppercase(); - if upper.len() > max_len { - upper.truncate(max_len); - } - upper -} - -fn draw_text( - frame: &mut [u8], - frame_width: usize, - frame_height: usize, - x: usize, - y: usize, - text: &str, - color: [u8; 4], -) { - let mut cursor_x = x; - for ch in text.chars() { - if ch == '\n' { - continue; - } - draw_char(frame, frame_width, frame_height, cursor_x, y, ch.to_ascii_uppercase(), color); - cursor_x += 6 * CHAR_SCALE; - } -} - -fn draw_char( - frame: &mut [u8], - frame_width: usize, - frame_height: usize, - x: usize, - y: usize, - ch: char, - color: [u8; 4], -) { - let glyph = glyph_bits(ch); - for (row, bits) in glyph.iter().enumerate() { - for col in 0..5 { - if bits & (1 << (4 - col)) != 0 { - let mut canvas = FrameCanvas { frame, width: frame_width, height: frame_height }; - fill_rect( - &mut canvas, - Rect { - x: x + col * CHAR_SCALE, - y: y + row * CHAR_SCALE, - width: CHAR_SCALE, - height: CHAR_SCALE, - }, - color, - ); - } - } - } -} - -fn fill_rect_alpha(canvas: &mut FrameCanvas<'_>, rect: Rect, color: [u8; 4]) { - let max_x = (rect.x + rect.width).min(canvas.width); - let max_y = (rect.y + rect.height).min(canvas.height); - for py in rect.y..max_y { - for px in rect.x..max_x { - blend_pixel(canvas.frame, canvas.width, px, py, color); - } - } -} - -fn fill_rect(canvas: &mut FrameCanvas<'_>, rect: Rect, color: [u8; 4]) { - let max_x = (rect.x + rect.width).min(canvas.width); - let max_y = (rect.y + rect.height).min(canvas.height); - for py in rect.y..max_y { - for px in rect.x..max_x { - write_pixel(canvas.frame, canvas.width, px, py, color); - } - } -} - -fn stroke_rect(canvas: &mut FrameCanvas<'_>, rect: Rect, color: [u8; 4]) { - if rect.width == 0 || rect.height == 0 { - return; - } - fill_rect(canvas, Rect { x: rect.x, y: rect.y, width: rect.width, height: 1 }, color); - fill_rect( - canvas, - Rect { x: rect.x, y: rect.y + rect.height.saturating_sub(1), width: rect.width, height: 1 }, - color, - ); - fill_rect(canvas, Rect { x: rect.x, y: rect.y, width: 1, height: rect.height }, color); - fill_rect( - canvas, - Rect { x: rect.x + rect.width.saturating_sub(1), y: rect.y, width: 1, height: rect.height }, - color, - ); -} - -fn blend_pixel(frame: &mut [u8], frame_width: usize, x: usize, y: usize, color: [u8; 4]) { - let idx = (y * frame_width + x) * 4; - let alpha = color[3] as f32 / 255.0; - let inv = 1.0 - alpha; - frame[idx] = (frame[idx] as f32 * inv + color[0] as f32 * alpha).round() as u8; - frame[idx + 1] = (frame[idx + 1] as f32 * inv + color[1] as f32 * alpha).round() as u8; - frame[idx + 2] = (frame[idx + 2] as f32 * inv + color[2] as f32 * alpha).round() as u8; - frame[idx + 3] = 0xFF; -} - -fn write_pixel(frame: &mut [u8], frame_width: usize, x: usize, y: usize, color: [u8; 4]) { - let idx = (y * frame_width + x) * 4; - frame[idx] = color[0]; - frame[idx + 1] = color[1]; - frame[idx + 2] = color[2]; - frame[idx + 3] = color[3]; -} - -fn glyph_bits(ch: char) -> [u8; 7] { - match ch { - 'A' => [0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11], - 'B' => [0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E], - 'C' => [0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E], - 'D' => [0x1E, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1E], - 'E' => [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F], - 'F' => [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10], - 'G' => [0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0E], - 'H' => [0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11], - 'I' => [0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E], - 'J' => [0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0E], - 'K' => [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11], - 'L' => [0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F], - 'M' => [0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11], - 'N' => [0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11], - 'O' => [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], - 'P' => [0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10], - 'Q' => [0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D], - 'R' => [0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11], - 'S' => [0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E], - 'T' => [0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04], - 'U' => [0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], - 'V' => [0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04], - 'W' => [0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A], - 'X' => [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11], - 'Y' => [0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04], - 'Z' => [0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F], - '0' => [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E], - '1' => [0x04, 0x0C, 0x14, 0x04, 0x04, 0x04, 0x1F], - '2' => [0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F], - '3' => [0x1E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0x1E], - '4' => [0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02], - '5' => [0x1F, 0x10, 0x10, 0x1E, 0x01, 0x01, 0x1E], - '6' => [0x0E, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x0E], - '7' => [0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08], - '8' => [0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E], - '9' => [0x0E, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E], - ':' => [0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00], - '.' => [0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06], - '/' => [0x01, 0x02, 0x02, 0x04, 0x08, 0x08, 0x10], - '%' => [0x19, 0x19, 0x02, 0x04, 0x08, 0x13, 0x13], - '(' => [0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02], - ')' => [0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08], - '-' => [0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00], - '_' => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F], - '+' => [0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00], - ' ' => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - '?' => [0x0E, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04], - _ => [0x0E, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04], - } -} - -#[cfg(test)] -mod tests { - use super::*; - use prometeu_drivers::hardware::Hardware; - - fn sample_snapshot() -> OverlaySnapshot { - OverlaySnapshot { - rows: vec![ - ( - OverlayMetric { label: "FPS", value: "60.0".to_string(), warn: false }, - OverlayMetric { label: "CERT", value: "2".to_string(), warn: true }, - ), - ( - OverlayMetric { label: "HOST", value: "1.23ms".to_string(), warn: false }, - OverlayMetric { label: "STEPS", value: "420".to_string(), warn: false }, - ), - ], - bars: vec![ - OverlayBar { - label: "HEAP", - value: "1024K / 8192K".to_string(), - ratio: 0.125, - warn: false, - }, - OverlayBar { - label: "BUDGET", - value: "9.0K/10.0K".to_string(), - ratio: 0.9, - warn: true, - }, - ], - footer: vec![OverlayMetric { - label: "CRASH", - value: "VM PANIC".to_string(), - warn: true, - }], - } - } - - #[test] - fn draw_overlay_writes_to_host_rgba_frame() { - let mut frame = vec![0u8; Hardware::W * Hardware::H * 4]; - draw_overlay(&mut frame, Hardware::W, Hardware::H, &sample_snapshot()); - assert!(frame.iter().any(|&byte| byte != 0)); - } - - #[test] - fn truncate_value_normalizes_and_caps() { - assert_eq!(truncate_value("panic: lowercase", 12), "PANIC: LOWER"); - } -} diff --git a/crates/host/prometeu-host-desktop-winit/src/runner.rs b/crates/host/prometeu-host-desktop-winit/src/runner.rs index a5044674..035d251b 100644 --- a/crates/host/prometeu-host-desktop-winit/src/runner.rs +++ b/crates/host/prometeu-host-desktop-winit/src/runner.rs @@ -3,7 +3,6 @@ use crate::debugger::HostDebugger; use crate::fs_backend::HostDirBackend; use crate::input::HostInputHandler; use crate::log_sink::HostConsoleSink; -use crate::overlay::{capture_snapshot, draw_overlay}; use crate::stats::HostStats; use crate::utilities::draw_rgba8888_to_rgba8; use pixels::wgpu::PresentMode; @@ -128,9 +127,6 @@ pub struct HostRunner { /// directly instead of depending on guest-visible inspection syscalls. debugger: HostDebugger, - /// Flag to enable/disable the technical telemetry display. - overlay_enabled: bool, - /// The physical audio driver. audio: HostAudio, /// Last known pause state to sync with audio. @@ -170,7 +166,6 @@ impl HostRunner { stats: HostStats::new(), debugger: HostDebugger::new(), - overlay_enabled: false, audio: HostAudio::new(), last_paused_state: false, presentation: PresentationState::default(), @@ -272,9 +267,6 @@ impl ApplicationHandler for HostRunner { } WindowEvent::RedrawRequested => { - let overlay_snapshot = - self.overlay_enabled.then(|| capture_snapshot(&self.stats, &mut self.firmware)); - // Get Pixels directly from the field (not via helper that gets the entire &mut self) let pixels = self.pixels.as_mut().expect("pixels not initialized"); @@ -286,10 +278,6 @@ impl ApplicationHandler for HostRunner { let src = self.hardware.gfx.front_buffer(); draw_rgba8888_to_rgba8(src, frame); - - if let Some(snapshot) = overlay_snapshot.as_ref() { - draw_overlay(frame, Hardware::W, Hardware::H, snapshot); - } } // <- frame borrow ends here if pixels.render().is_err() { @@ -309,12 +297,6 @@ impl ApplicationHandler for HostRunner { 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(); - } } } @@ -332,12 +314,8 @@ impl ApplicationHandler for HostRunner { // 1. Process pending debug commands from the network. self.debugger.check_commands(&mut self.firmware, &mut self.hardware); - // Sync inspection mode state. This is host-owned overlay/debugger control, - // not a guest-visible debug ABI switch. - self.firmware - .os - .vm() - .set_inspection_active(self.overlay_enabled || self.debugger.stream.is_some()); + // Sync debugger inspection state; this is not a guest-visible debug ABI switch. + self.firmware.os.vm().set_inspection_active(self.debugger.stream.is_some()); // 2. Maintain a filesystem connection if it was lost (e.g., directory removed). if let Some(root) = &self.fs_root diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 1fcf0b5c..5ef00f93 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,5 +1,5 @@ {"type":"meta","next_id":{"DSC":39,"AGD":39,"DEC":31,"PLN":87,"LSN":47,"CLSN":1}} -{"type":"discussion","id":"DSC-0038","status":"in_progress","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-05-25","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[{"id":"AGD-0038","file":"AGD-0038-renderframepacket-boundary-for-classic-2d-renderer.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25"}],"decisions":[{"id":"DEC-0030","file":"DEC-0030-logical-render-pipeline-command-boundary.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25","ref_agenda":"AGD-0038"}],"plans":[{"id":"PLN-0073","file":"PLN-0073-render-contract-specs.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0074","file":"PLN-0074-hal-render-submission-types.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0075","file":"PLN-0075-rendermanager-core.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0076","file":"PLN-0076-capabilities-and-abi-domain-split.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0077","file":"PLN-0077-composer-buffer-and-game2d-packet.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0078","file":"PLN-0078-classic2d-game-renderer-consumer.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0079","file":"PLN-0079-gfx2d-primitive-domain.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0080","file":"PLN-0080-shell-ui-packet-and-gfxui-domain.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0081","file":"PLN-0081-shell-hub-migration.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0082","file":"PLN-0082-frame-publication-and-present-boundary.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0083","file":"PLN-0083-fade-removal.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0084","file":"PLN-0084-host-debug-overlay-removal.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0085","file":"PLN-0085-pbs-stdlib-and-syscall-declarations.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0086","file":"PLN-0086-end-to-end-render-boundary-validation.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0038","status":"in_progress","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-05-25","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[{"id":"AGD-0038","file":"AGD-0038-renderframepacket-boundary-for-classic-2d-renderer.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25"}],"decisions":[{"id":"DEC-0030","file":"DEC-0030-logical-render-pipeline-command-boundary.md","status":"accepted","created_at":"2026-05-25","updated_at":"2026-05-25","ref_agenda":"AGD-0038"}],"plans":[{"id":"PLN-0073","file":"PLN-0073-render-contract-specs.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0074","file":"PLN-0074-hal-render-submission-types.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0075","file":"PLN-0075-rendermanager-core.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0076","file":"PLN-0076-capabilities-and-abi-domain-split.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0077","file":"PLN-0077-composer-buffer-and-game2d-packet.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0078","file":"PLN-0078-classic2d-game-renderer-consumer.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0079","file":"PLN-0079-gfx2d-primitive-domain.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0080","file":"PLN-0080-shell-ui-packet-and-gfxui-domain.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0081","file":"PLN-0081-shell-hub-migration.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0082","file":"PLN-0082-frame-publication-and-present-boundary.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0083","file":"PLN-0083-fade-removal.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0084","file":"PLN-0084-host-debug-overlay-removal.md","status":"done","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0085","file":"PLN-0085-pbs-stdlib-and-syscall-declarations.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]},{"id":"PLN-0086","file":"PLN-0086-end-to-end-render-boundary-validation.md","status":"open","created_at":"2026-05-25","updated_at":"2026-05-25","ref_decisions":["DEC-0030"]}],"lessons":[]} {"type":"discussion","id":"DSC-0035","status":"done","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0044","file":"discussion/lessons/DSC-0035-task-owned-shell-windows/LSN-0044-task-window-liveness-belongs-to-the-task.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"type":"discussion","id":"DSC-0034","status":"done","ticket":"system-os-domain-facades","title":"Agenda - SystemOS Domain Facades","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","services","api-surface","lifecycle","fs"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0043","file":"discussion/lessons/DSC-0034-system-os-domain-facades/LSN-0043-systemos-domain-facades.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"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":"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":"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":"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":"discussion/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"}]} diff --git a/discussion/workflow/plans/PLN-0084-host-debug-overlay-removal.md b/discussion/workflow/plans/PLN-0084-host-debug-overlay-removal.md index 0b30fe7c..31607e5a 100644 --- a/discussion/workflow/plans/PLN-0084-host-debug-overlay-removal.md +++ b/discussion/workflow/plans/PLN-0084-host-debug-overlay-removal.md @@ -2,7 +2,7 @@ id: PLN-0084 ticket: render-frame-packet-boundary title: Host Debug Overlay Removal -status: open +status: done created: 2026-05-25 ref_decisions: [DEC-0030] tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline] diff --git a/docs/specs/runtime/10-debug-inspection-and-profiling.md b/docs/specs/runtime/10-debug-inspection-and-profiling.md index 4221ab0f..4c9ed75a 100644 --- a/docs/specs/runtime/10-debug-inspection-and-profiling.md +++ b/docs/specs/runtime/10-debug-inspection-and-profiling.md @@ -18,7 +18,7 @@ It covers: - profiling; - event and fault visibility; - certification input data; -- host-side debug overlay (HUD) isolation. +- host-side debug UI isolation. ## 2 Execution Modes @@ -131,15 +131,15 @@ 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. +Host-owned 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: +When debugger or other host-only diagnostics become 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. +- additional redraw is only justified by a newly published logical frame or a host-owned invalidation event such as 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. +Paused or breakpointed execution does not grant permission to swap logical machine buffers just to sustain host-only diagnostics. ## 6 Time Profiling (Cycles) @@ -285,28 +285,32 @@ Each event has: - cost - consequence -## 11 Host-Side Debug Overlay (HUD) Isolation +## 11 Host-Side Debug UI Isolation -The visual Debug Overlay (HUD) for technical inspection is not part of the emulated machine pipeline. +The desktop host MUST NOT inject a debug overlay into Game or Shell output. Any +future visual debug UI is a separate product/domain concern and requires its own +decision before it can become part of host presentation. ### 10.1 Responsibilities 1. **Runtime:** Only exposes telemetry data via the machine diagnostics surface. It does not perform HUD rendering or string formatting. 2. **Emulated graphics contract:** Machine graphics primitives such as `fill_rect` and `draw_text` remain valid parts of the emulated graphics/syscall contract. They are not host overlay APIs. -3. **Host overlay module:** The Desktop Host owns a dedicated overlay module that performs host-side text, panel, and simple bar composition. -4. **Composition boundary:** The overlay is composed on the Host presentation surface after the emulated frame is ready. Overlay pixels must not be written back into the emulated framebuffer. -5. **Host control:** Overlay visibility and presentation policy remain under Host control. -6. **Host (Desktop):** Responsible for collecting telemetry from the runtime and rendering the HUD as a native, transparent layer. +3. **Host diagnostics:** The Desktop Host may consume telemetry for logs, + debugger streams, and non-render diagnostics, but it does not compose debug + pixels into the presentation surface. +4. **Composition boundary:** Published Game and Shell output must be presented + without host-owned debug overlay injection. ### 10.2 Principles -- **Zero Pipeline Interference:** HUD rendering must not inject pixels into the emulated framebuffer. It is applied after upscaling or as a separate display surface. -- **Zero Cycle Impact:** HUD-related processing (like formatting technical text) must occur outside the emulated machine cycles. -- **Toggle Control:** The activation of the overlay (typically via **F1**) is managed by the Host layer. +- **Zero Pipeline Interference:** Host diagnostics must not inject pixels into + the emulated framebuffer or the host presentation surface. +- **Zero Cycle Impact:** Diagnostic processing must occur outside the emulated + machine cycles. ### 10.3 Atomic Telemetry Model -To ensure zero-impact synchronization between the VM and the Host Debug Overlay, PROMETEU uses a **push-based atomic model**: +To ensure zero-impact synchronization between the VM and host diagnostics, PROMETEU uses a **push-based atomic model**: 1. **Atomic Storage:** Metrics such as cycles, syscalls, and memory usage are stored in a dedicated `AtomicTelemetry` structure using thread-safe atomic types (`AtomicU64`, `AtomicU32`, etc.). 2. **Lockless Access:** The Host (Desktop) reads these metrics asynchronously and without locks by taking a `snapshot()` of the atomic state. @@ -320,4 +324,4 @@ All machine diagnostics and profiling data: - feed the host-owned certification report; - are collected deterministically; - do not require a guest-visible debug ABI; -- are consistent regardless of whether the Host HUD is active or not. +- are consistent regardless of whether host diagnostics are active or not. 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 4b8b8a5b..bb6a9d33 100644 --- a/docs/specs/runtime/11-portability-and-cross-platform-execution.md +++ b/docs/specs/runtime/11-portability-and-cross-platform-execution.md @@ -51,7 +51,8 @@ The contract is about logical behavior, not identical physical latency or throug - audio output - physical input collection - access to the sandbox file system -- **technical inspection surfaces (Debug Overlay/HUD)** +- technical inspection and debugger integrations that do not modify presented + Game or Shell pixels The host provides realization of machine surfaces. It does not redefine cartridge semantics. @@ -123,8 +124,8 @@ The graphics system: The platform layer: - consumes closed render submissions through a render-surface implementation -- **may overlay technical HUDs without modifying the logical framebuffer** -- may transport published RGBA8888 output into a host presentation surface where a host-only overlay layer is composed +- transports published RGBA8888 output into a host presentation surface without + injecting host-owned debug overlay pixels The host presentation layer MUST treat RGBA8888 as the canonical logical framebuffer format. RGB565 conversion is not part of the normal host @@ -136,17 +137,19 @@ host-owned invalidation, not by perpetual redraw polling. In particular: - a stable published submission 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 MAY redraw the same logical frame again when its own surface is invalidated by resize, expose, or debugger-visible host 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. +To preserve portability and certification purity, technical inspection tools are host-owned and must not inject pixels into Game or Shell output. - **Host-exclusive:** These tools are only implemented where they are relevant (e.g., Desktop) and do not exist in the logical machine. - **Non-intrusive:** They must not consume machine cycles or alter memory state. - **Consistent Results:** A cartridge will produce the same logical results and certification metrics regardless of the Host's inspection capabilities. -- **Contract Boundary:** Emulated graphics primitives such as `fill_rect` and `draw_text` remain part of the machine contract, but Host overlays must not depend on them. +- **Contract Boundary:** Emulated graphics primitives such as `fill_rect` and + `draw_text` remain part of the machine contract; host diagnostics must not + depend on them for visual injection. ### 9.1 Atomic Telemetry Interface