implements PLN-0084 remove host debug overlay
This commit is contained in:
parent
295666b310
commit
d79c11faa8
@ -4,7 +4,6 @@ pub mod debugger;
|
|||||||
pub mod fs_backend;
|
pub mod fs_backend;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod log_sink;
|
pub mod log_sink;
|
||||||
pub mod overlay;
|
|
||||||
pub mod runner;
|
pub mod runner;
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
pub mod utilities;
|
pub mod utilities;
|
||||||
|
|||||||
@ -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<OverlayBar>,
|
|
||||||
footer: Vec<OverlayMetric>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,7 +3,6 @@ use crate::debugger::HostDebugger;
|
|||||||
use crate::fs_backend::HostDirBackend;
|
use crate::fs_backend::HostDirBackend;
|
||||||
use crate::input::HostInputHandler;
|
use crate::input::HostInputHandler;
|
||||||
use crate::log_sink::HostConsoleSink;
|
use crate::log_sink::HostConsoleSink;
|
||||||
use crate::overlay::{capture_snapshot, draw_overlay};
|
|
||||||
use crate::stats::HostStats;
|
use crate::stats::HostStats;
|
||||||
use crate::utilities::draw_rgba8888_to_rgba8;
|
use crate::utilities::draw_rgba8888_to_rgba8;
|
||||||
use pixels::wgpu::PresentMode;
|
use pixels::wgpu::PresentMode;
|
||||||
@ -128,9 +127,6 @@ pub struct HostRunner {
|
|||||||
/// directly instead of depending on guest-visible inspection syscalls.
|
/// directly instead of depending on guest-visible inspection syscalls.
|
||||||
debugger: HostDebugger,
|
debugger: HostDebugger,
|
||||||
|
|
||||||
/// Flag to enable/disable the technical telemetry display.
|
|
||||||
overlay_enabled: bool,
|
|
||||||
|
|
||||||
/// The physical audio driver.
|
/// The physical audio driver.
|
||||||
audio: HostAudio,
|
audio: HostAudio,
|
||||||
/// Last known pause state to sync with audio.
|
/// Last known pause state to sync with audio.
|
||||||
@ -170,7 +166,6 @@ impl HostRunner {
|
|||||||
|
|
||||||
stats: HostStats::new(),
|
stats: HostStats::new(),
|
||||||
debugger: HostDebugger::new(),
|
debugger: HostDebugger::new(),
|
||||||
overlay_enabled: false,
|
|
||||||
audio: HostAudio::new(),
|
audio: HostAudio::new(),
|
||||||
last_paused_state: false,
|
last_paused_state: false,
|
||||||
presentation: PresentationState::default(),
|
presentation: PresentationState::default(),
|
||||||
@ -272,9 +267,6 @@ impl ApplicationHandler for HostRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WindowEvent::RedrawRequested => {
|
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)
|
// 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");
|
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();
|
let src = self.hardware.gfx.front_buffer();
|
||||||
|
|
||||||
draw_rgba8888_to_rgba8(src, frame);
|
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
|
} // <- frame borrow ends here
|
||||||
|
|
||||||
if pixels.render().is_err() {
|
if pixels.render().is_err() {
|
||||||
@ -309,12 +297,6 @@ impl ApplicationHandler for HostRunner {
|
|||||||
self.request_redraw_if_needed();
|
self.request_redraw_if_needed();
|
||||||
println!("[Debugger] Execution started!");
|
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.
|
// 1. Process pending debug commands from the network.
|
||||||
self.debugger.check_commands(&mut self.firmware, &mut self.hardware);
|
self.debugger.check_commands(&mut self.firmware, &mut self.hardware);
|
||||||
|
|
||||||
// Sync inspection mode state. This is host-owned overlay/debugger control,
|
// Sync debugger inspection state; this is not a guest-visible debug ABI switch.
|
||||||
// not a guest-visible debug ABI switch.
|
self.firmware.os.vm().set_inspection_active(self.debugger.stream.is_some());
|
||||||
self.firmware
|
|
||||||
.os
|
|
||||||
.vm()
|
|
||||||
.set_inspection_active(self.overlay_enabled || self.debugger.stream.is_some());
|
|
||||||
|
|
||||||
// 2. Maintain a filesystem connection if it was lost (e.g., directory removed).
|
// 2. Maintain a filesystem connection if it was lost (e.g., directory removed).
|
||||||
if let Some(root) = &self.fs_root
|
if let Some(root) = &self.fs_root
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{"type":"meta","next_id":{"DSC":39,"AGD":39,"DEC":31,"PLN":87,"LSN":47,"CLSN":1}}
|
{"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-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-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"}]}
|
{"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"}]}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
id: PLN-0084
|
id: PLN-0084
|
||||||
ticket: render-frame-packet-boundary
|
ticket: render-frame-packet-boundary
|
||||||
title: Host Debug Overlay Removal
|
title: Host Debug Overlay Removal
|
||||||
status: open
|
status: done
|
||||||
created: 2026-05-25
|
created: 2026-05-25
|
||||||
ref_decisions: [DEC-0030]
|
ref_decisions: [DEC-0030]
|
||||||
tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline]
|
tags: [gfx, renderer, runtime, frame-composer, architecture, ui, pipeline]
|
||||||
|
|||||||
@ -18,7 +18,7 @@ It covers:
|
|||||||
- profiling;
|
- profiling;
|
||||||
- event and fault visibility;
|
- event and fault visibility;
|
||||||
- certification input data;
|
- certification input data;
|
||||||
- host-side debug overlay (HUD) isolation.
|
- host-side debug UI isolation.
|
||||||
|
|
||||||
## 2 Execution Modes
|
## 2 Execution Modes
|
||||||
|
|
||||||
@ -131,15 +131,15 @@ The host may:
|
|||||||
- observe buffers separately
|
- observe buffers separately
|
||||||
- identify excessive redraw
|
- 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 preserve the last valid visible image;
|
||||||
- the host MUST NOT keep recomposing the surface by continuous polling alone;
|
- 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)
|
## 6 Time Profiling (Cycles)
|
||||||
|
|
||||||
@ -285,28 +285,32 @@ Each event has:
|
|||||||
- cost
|
- cost
|
||||||
- consequence
|
- 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
|
### 10.1 Responsibilities
|
||||||
|
|
||||||
1. **Runtime:** Only exposes telemetry data via the machine diagnostics surface. It does not perform HUD rendering or string formatting.
|
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.
|
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.
|
3. **Host diagnostics:** The Desktop Host may consume telemetry for logs,
|
||||||
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.
|
debugger streams, and non-render diagnostics, but it does not compose debug
|
||||||
5. **Host control:** Overlay visibility and presentation policy remain under Host control.
|
pixels into the presentation surface.
|
||||||
6. **Host (Desktop):** Responsible for collecting telemetry from the runtime and rendering the HUD as a native, transparent layer.
|
4. **Composition boundary:** Published Game and Shell output must be presented
|
||||||
|
without host-owned debug overlay injection.
|
||||||
|
|
||||||
### 10.2 Principles
|
### 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 Pipeline Interference:** Host diagnostics must not inject pixels into
|
||||||
- **Zero Cycle Impact:** HUD-related processing (like formatting technical text) must occur outside the emulated machine cycles.
|
the emulated framebuffer or the host presentation surface.
|
||||||
- **Toggle Control:** The activation of the overlay (typically via **F1**) is managed by the Host layer.
|
- **Zero Cycle Impact:** Diagnostic processing must occur outside the emulated
|
||||||
|
machine cycles.
|
||||||
|
|
||||||
### 10.3 Atomic Telemetry Model
|
### 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.).
|
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.
|
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;
|
- feed the host-owned certification report;
|
||||||
- are collected deterministically;
|
- are collected deterministically;
|
||||||
- do not require a guest-visible debug ABI;
|
- 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.
|
||||||
|
|||||||
@ -51,7 +51,8 @@ The contract is about logical behavior, not identical physical latency or throug
|
|||||||
- audio output
|
- audio output
|
||||||
- physical input collection
|
- physical input collection
|
||||||
- access to the sandbox file system
|
- 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.
|
The host provides realization of machine surfaces. It does not redefine cartridge semantics.
|
||||||
|
|
||||||
@ -123,8 +124,8 @@ The graphics system:
|
|||||||
The platform layer:
|
The platform layer:
|
||||||
|
|
||||||
- consumes closed render submissions through a render-surface implementation
|
- consumes closed render submissions through a render-surface implementation
|
||||||
- **may overlay technical HUDs without modifying the logical framebuffer**
|
- transports published RGBA8888 output into a host presentation surface without
|
||||||
- may transport published RGBA8888 output into a host presentation surface where a host-only overlay layer is composed
|
injecting host-owned debug overlay pixels
|
||||||
|
|
||||||
The host presentation layer MUST treat RGBA8888 as the canonical logical
|
The host presentation layer MUST treat RGBA8888 as the canonical logical
|
||||||
framebuffer format. RGB565 conversion is not part of the normal host
|
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:
|
In particular:
|
||||||
|
|
||||||
- a stable published submission MAY remain visible across multiple host wakeups without recomposition;
|
- 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.
|
- 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
|
## 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.
|
- **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.
|
- **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.
|
- **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
|
### 9.1 Atomic Telemetry Interface
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user