From c16c403966f16602538da7b2cf44cd7d20296603 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Thu, 26 Feb 2026 22:04:07 +0100 Subject: [PATCH] display debug graphs --- Cargo.lock | 214 +----------------- dash-frontend/Cargo.toml | 1 - .../gui/tab/monado_tab_debug_timings.xml | 15 +- dash-frontend/src/tab/monado.rs | 208 +++++++++++++++-- wayvr/src/backend/openxr/monado_state.rs | 21 +- wayvr/src/overlays/dashboard.rs | 77 +++++++ .../subsystem/monado_metrics/metrics_fd.rs | 20 +- wgui/Cargo.toml | 1 - wgui/src/components/bar_graph.rs | 14 +- wlx-common/src/dash_interface.rs | 21 ++ wlx-common/src/dash_interface_emulated.rs | 32 +++ 11 files changed, 367 insertions(+), 257 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8be646bd..4372aafe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,17 +910,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.0", -] - [[package]] name = "chrono" version = "0.4.42" @@ -1242,15 +1231,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -1437,7 +1417,6 @@ dependencies = [ "hyper", "keyvalues-parser", "log", - "rand 0.10.0", "rust-embed", "serde", "serde_json", @@ -2127,20 +2106,6 @@ dependencies = [ "wasip2", ] -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core 0.10.0", - "wasip2", - "wasip3", -] - [[package]] name = "gif" version = "0.14.1" @@ -2475,12 +2440,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -2616,8 +2575,6 @@ checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown 0.16.1", - "serde", - "serde_core", ] [[package]] @@ -2844,12 +2801,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lebe" version = "0.5.3" @@ -4385,17 +4336,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" -dependencies = [ - "chacha20", - "getrandom 0.4.1", - "rand_core 0.10.0", -] - [[package]] name = "rand_chacha" version = "0.2.2" @@ -4453,12 +4393,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - [[package]] name = "rand_hc" version = "0.2.0" @@ -4676,12 +4610,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "ringbuffer" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b0b88a509053cbfd535726dcaaceee631313cef981266119527a1d110f6d2b" - [[package]] name = "rodio" version = "0.21.1" @@ -5061,7 +4989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", + "cpufeatures", "digest", ] @@ -6003,12 +5931,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -6210,16 +6132,7 @@ version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen 0.46.0", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -6280,40 +6193,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.12.1", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.10.0", - "hashbrown 0.15.5", - "indexmap 2.12.1", - "semver", -] - [[package]] name = "wayland-backend" version = "0.3.12" @@ -6599,7 +6478,6 @@ dependencies = [ "parking_lot", "regex", "resvg", - "ringbuffer", "roxmltree 0.21.1", "rust-embed", "rustc-hash 2.1.1", @@ -7123,94 +7001,6 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap 2.12.1", - "prettyplease", - "syn 2.0.113", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.113", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.10.0", - "indexmap 2.12.1", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.12.1", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "wlx-capture" version = "0.6.0" diff --git a/dash-frontend/Cargo.toml b/dash-frontend/Cargo.toml index 96848392..06f1b010 100644 --- a/dash-frontend/Cargo.toml +++ b/dash-frontend/Cargo.toml @@ -28,7 +28,6 @@ hyper = { version = "1.8.1", features = ["client", "http1", "http2"] } http-body-util = "0.1.3" async-native-tls = "0.5.0" smol-hyper = "0.1.1" -rand = "0.10.0" [features] default = ["monado"] diff --git a/dash-frontend/assets/gui/tab/monado_tab_debug_timings.xml b/dash-frontend/assets/gui/tab/monado_tab_debug_timings.xml index d74db3d1..d167b75e 100644 --- a/dash-frontend/assets/gui/tab/monado_tab_debug_timings.xml +++ b/dash-frontend/assets/gui/tab/monado_tab_debug_timings.xml @@ -1,12 +1,17 @@ - - - + + - - + + + \ No newline at end of file diff --git a/dash-frontend/src/tab/monado.rs b/dash-frontend/src/tab/monado.rs index f1758a5b..5ae3b6b7 100644 --- a/dash-frontend/src/tab/monado.rs +++ b/dash-frontend/src/tab/monado.rs @@ -8,13 +8,13 @@ use wgui::{ slider::ComponentSlider, tabs::ComponentTabs, }, - drawing, + drawing::{self, Color}, globals::WguiGlobals, layout::{Layout, WidgetID}, - parser::{self, Fetchable, ParseDocumentParams, ParserState}, + parser::{self, Fetchable, ParseDocumentParams, ParserData, ParserState}, task::Tasks, }; -use wlx_common::dash_interface; +use wlx_common::dash_interface::{self, DashInterface, MonadoDumpSessionFrame}; use crate::{ frontend::Frontend, @@ -59,12 +59,37 @@ struct SubtabGeneralSettings { state: ParserState, } +struct DebugGraph { + graph: Rc, + #[allow(dead_code)] + data: ParserData, +} + +struct TimingsSession { + last_frame: MonadoDumpSessionFrame, +} + +struct Graphs { + predicted_display_time: DebugGraph, + predicted_frame_time: DebugGraph, + predicted_wake_up_time: DebugGraph, + predicted_gpu_done_time: DebugGraph, + predicted_display_period: DebugGraph, + display_time: DebugGraph, + when_predicted: DebugGraph, + when_wait_woke: DebugGraph, + when_begin: DebugGraph, + when_delivered: DebugGraph, + when_gpu_done: DebugGraph, +} + struct SubtabDebugTimings { #[allow(dead_code)] state: ParserState, - graph_first: Rc, - graph_second: Rc, + graphs: Graphs, + + sessions: HashMap, } #[allow(dead_code)] @@ -142,7 +167,7 @@ impl Tab for TabMonado { } } Subtab::DebugTimings(timings) => { - timings.update(&mut frontend.layout); + timings.update(data, frontend); } } @@ -224,36 +249,177 @@ impl SubtabGeneralSettings { } } +fn mount_graph( + state: &mut ParserState, + globals: &WguiGlobals, + layout: &mut Layout, + id_parent: WidgetID, + name: &'static str, + limits: (f32, f32), +) -> anyhow::Result { + let mut params = HashMap::new(); + params.insert(Rc::from("name"), Rc::from(name)); + params.insert(Rc::from("limit_min"), Rc::from(limits.0.to_string())); + params.insert(Rc::from("limit_max"), Rc::from(limits.1.to_string())); + + let data = state.parse_template( + &doc_params_tab_debug_timings(globals), + "DebugGraph", + layout, + id_parent, + params, + )?; + + let graph = data.fetch_component_as::("graph")?; + Ok(DebugGraph { graph, data }) +} + +fn ns_to_ms(ns: i64) -> f32 { + (ns / 1000) as f32 / 1000.0 +} + impl SubtabDebugTimings { fn new(parent_id: WidgetID, frontend: &mut Frontend) -> anyhow::Result { - let state = wgui::parser::parse_from_assets( + let mut state = wgui::parser::parse_from_assets( &doc_params_tab_debug_timings(&frontend.globals), &mut frontend.layout, parent_id, )?; - let graph_first = state.fetch_component_as::("graph_first")?; - let graph_second = state.fetch_component_as::("graph_second")?; + let id_parent = state.get_widget_id("parent")?; + + let mut graphs: Vec = Vec::new(); + + let mut graph = |name: &'static str, limits: (f32, f32)| -> anyhow::Result { + mount_graph( + &mut state, + &frontend.globals, + &mut frontend.layout, + id_parent, + name, + limits, + ) + }; + + // populate graphs + let graphs = Graphs { + predicted_display_time: graph("Predicted display time", (0.0, 30.0))?, + predicted_frame_time: graph("Predicted frame time", (0.0, 30.0))?, + predicted_wake_up_time: graph("Predicted wake-up time", (0.0, 30.0))?, + predicted_gpu_done_time: graph("Predicted GPU done time", (0.0, 30.0))?, + predicted_display_period: graph("Predicted display period", (0.0, 30.0))?, + display_time: graph("Display time", (0.0, 30.0))?, + when_predicted: graph("When predicted", (0.0, 30.0))?, + when_wait_woke: graph("When wait woke", (0.0, 30.0))?, + when_begin: graph("When begin", (0.0, 30.0))?, + when_delivered: graph("When delivered", (0.0, 30.0))?, + when_gpu_done: graph("When gpu done", (0.0, 30.0))?, + }; Ok(Self { state, - graph_first, - graph_second, + graphs, + sessions: Default::default(), }) } - fn update(&mut self, layout: &mut Layout) { - self.graph_first.push_value(ValueCell { - value: rand::random_range(0.0..50.0), - color: drawing::Color::new(rand::random_range(0.0..1.0), rand::random_range(0.0..1.0), 0.0, 1.0), - }); + fn update(&mut self, data: &mut T, frontend: &mut Frontend) { + if !frontend.interface.monado_metrics_set_enabled(data, true) { + return; + } - self.graph_second.push_value(ValueCell { - value: rand::random_range(0.0..30.0), - color: drawing::Color::new(0.0, rand::random_range(0.0..1.0), rand::random_range(0.0..1.0), 1.0), - }); + let frames = frontend.interface.monado_metrics_dump_session_frames(data); + if frames.is_empty() { + return; + } - layout.mark_redraw(); + let col_green = Color::new(0.0, 1.0, 0.0, 1.0); + + for frame in frames { + log::info!("{:?}", frame); + + match self.sessions.get_mut(&frame.session_id) { + Some(session) => { + let predicted_display_time = ns_to_ms(session.last_frame.predicted_display_time_ns as i64); + let predicted_frame_time = ns_to_ms(frame.predicted_frame_time_ns as i64); + let predicted_wake_up_time = + ns_to_ms(frame.predicted_wake_up_time_ns as i64 - session.last_frame.predicted_wake_up_time_ns as i64); + let predicted_gpu_done_time = + ns_to_ms(frame.predicted_gpu_done_time_ns as i64 - session.last_frame.predicted_gpu_done_time_ns as i64); + let predicted_display_period = ns_to_ms(session.last_frame.predicted_display_period_ns as i64); // 6.944 ms for 144Hz + let display_time = ns_to_ms(frame.display_time_ns as i64 - session.last_frame.display_time_ns as i64); + let when_predicted = ns_to_ms(frame.when_predicted_ns as i64 - session.last_frame.when_predicted_ns as i64); + let when_wait_woke = ns_to_ms(frame.when_wait_woke_ns as i64 - session.last_frame.when_wait_woke_ns as i64); + let when_begin = ns_to_ms(frame.when_begin_ns as i64 - session.last_frame.when_begin_ns as i64); + let when_delivered = ns_to_ms(frame.when_delivered_ns as i64 - session.last_frame.when_delivered_ns as i64); + let when_gpu_done = ns_to_ms(frame.when_gpu_done_ns as i64 - session.last_frame.when_gpu_done_ns as i64); + + self.graphs.predicted_display_time.graph.push_value(ValueCell { + value: predicted_display_time, + color: col_green, + }); + + self.graphs.predicted_frame_time.graph.push_value(ValueCell { + value: predicted_frame_time, + color: col_green, + }); + + self.graphs.predicted_wake_up_time.graph.push_value(ValueCell { + value: predicted_wake_up_time, + color: col_green, + }); + + self.graphs.predicted_gpu_done_time.graph.push_value(ValueCell { + value: predicted_gpu_done_time, + color: col_green, + }); + + self.graphs.predicted_display_period.graph.push_value(ValueCell { + value: predicted_display_period, + color: col_green, + }); + + self.graphs.display_time.graph.push_value(ValueCell { + value: display_time, + color: col_green, + }); + + self.graphs.when_predicted.graph.push_value(ValueCell { + value: when_predicted, + color: col_green, + }); + + self.graphs.when_wait_woke.graph.push_value(ValueCell { + value: when_wait_woke, + color: col_green, + }); + + self.graphs.when_begin.graph.push_value(ValueCell { + value: when_begin, + color: col_green, + }); + + self.graphs.when_delivered.graph.push_value(ValueCell { + value: when_delivered, + color: col_green, + }); + + self.graphs.when_gpu_done.graph.push_value(ValueCell { + value: when_gpu_done, + color: col_green, + }); + + session.last_frame = frame; + } + None => { + self + .sessions + .insert(frame.session_id, TimingsSession { last_frame: frame }); + } + } + } + + frontend.layout.mark_redraw(); } } diff --git a/wayvr/src/backend/openxr/monado_state.rs b/wayvr/src/backend/openxr/monado_state.rs index fd679f74..190d0f89 100644 --- a/wayvr/src/backend/openxr/monado_state.rs +++ b/wayvr/src/backend/openxr/monado_state.rs @@ -8,23 +8,32 @@ pub struct MonadoState { impl MonadoState { pub fn new() -> anyhow::Result { let ipc = libmonado::Monado::auto_connect().map_err(|s| anyhow::anyhow!("{s}"))?; - let mut res = Self { ipc, metrics: None }; - res.set_metrics_enabled(true)?; + let res = Self { ipc, metrics: None }; Ok(res) } pub fn update(&mut self) { if let Some(metrics) = &mut self.metrics { metrics.update(); + + if metrics.is_full() { + let _ = self.set_metrics_enabled(false); // disable metrics if they aren't used + } } } pub fn set_metrics_enabled(&mut self, enabled: bool) -> anyhow::Result<()> { - if enabled && self.metrics.is_none() { - self.metrics = Some(monado_metrics::metrics_fd::MonadoMetricsFd::new( - &mut self.ipc, - )?); + if enabled { + if self.metrics.is_none() { + log::info!("Starting Monado metrics"); + self.metrics = Some(monado_metrics::metrics_fd::MonadoMetricsFd::new( + &mut self.ipc, + )?); + } } else { + if self.metrics.is_some() { + log::info!("Stopping Monado metrics"); + } self.metrics = None; } diff --git a/wayvr/src/overlays/dashboard.rs b/wayvr/src/overlays/dashboard.rs index a1c2cb2e..8f6b0a34 100644 --- a/wayvr/src/overlays/dashboard.rs +++ b/wayvr/src/overlays/dashboard.rs @@ -524,6 +524,70 @@ impl DashInterface for DashInterfaceLive { monado_set_brightness(&mut monado.ipc, brightness).ok() } + #[cfg(feature = "openxr")] + fn monado_metrics_set_enabled(&mut self, app: &mut AppState, enabled: bool) -> bool { + let Some(monado) = &mut app.monado_state else { + return false; + }; + + if let Err(e) = monado.set_metrics_enabled(enabled) { + log::error!("failed to enable metrics: {e:?}"); + return false; + } + + true + } + + #[cfg(feature = "openxr")] + #[allow(clippy::match_same_arms)] + fn monado_metrics_dump_session_frames( + &mut self, + app: &mut AppState, + ) -> Vec { + let Some(monado) = &mut app.monado_state else { + return Vec::new(); + }; + + let Some(metrics) = &mut monado.metrics else { + return Vec::new(); // metrics not enabled or not available + }; + + metrics + .dump_records() + .iter() + .filter_map(|record| { + use crate::subsystem::monado_metrics::proto::record; + let record = record.record?; + match record { + record::Record::SessionFrame(sframe) => { + // map it to our struct + Some(dash_interface::MonadoDumpSessionFrame { + session_id: sframe.session_id, + frame_id: sframe.frame_id, + predicted_frame_time_ns: sframe.predicted_frame_time_ns, + predicted_wake_up_time_ns: sframe.predicted_wake_up_time_ns, + predicted_gpu_done_time_ns: sframe.predicted_gpu_done_time_ns, + predicted_display_time_ns: sframe.predicted_display_time_ns, + predicted_display_period_ns: sframe.predicted_display_period_ns, + display_time_ns: sframe.display_time_ns, + when_predicted_ns: sframe.when_predicted_ns, + when_wait_woke_ns: sframe.when_wait_woke_ns, + when_begin_ns: sframe.when_begin_ns, + when_delivered_ns: sframe.when_delivered_ns, + when_gpu_done_ns: sframe.when_gpu_done_ns, + discarded: sframe.discarded, + }) + } + record::Record::Version(_) => None, + record::Record::Used(_) => None, + record::Record::SystemFrame(_) => None, + record::Record::SystemGpuInfo(_) => None, + record::Record::SystemPresentInfo(_) => None, + } + }) + .collect() + } + #[cfg(not(feature = "openxr"))] fn monado_client_list( &mut self, @@ -543,6 +607,19 @@ impl DashInterface for DashInterfaceLive { fn monado_brightness_set(&mut self, _: &mut AppState, _: f32) -> Option<()> { None } + + #[cfg(not(feature = "openxr"))] + fn monado_metrics_set_enabled(&mut self, _: &mut AppState, _enabled: bool) { + // not supported in this build + } + + #[cfg(not(feature = "openxr"))] + fn monado_metrics_dump_session_frames( + &mut self, + _: &mut AppState, + ) -> Vec { + Vec::new() // not supported in this build + } } #[cfg(feature = "openxr")] diff --git a/wayvr/src/subsystem/monado_metrics/metrics_fd.rs b/wayvr/src/subsystem/monado_metrics/metrics_fd.rs index e158e93c..983a39c9 100644 --- a/wayvr/src/subsystem/monado_metrics/metrics_fd.rs +++ b/wayvr/src/subsystem/monado_metrics/metrics_fd.rs @@ -13,6 +13,8 @@ pub struct MonadoMetricsFd { records: VecDeque, } +const RECORD_QUEUE_SIZE: usize = 500; + impl MonadoMetricsFd { pub fn new(monado: &mut libmonado::Monado) -> anyhow::Result { let (stream_reader, stream_writer) = std::os::unix::net::UnixStream::pair()?; @@ -29,15 +31,21 @@ impl MonadoMetricsFd { } fn parse_message(&mut self, record: proto::Record) { - log::debug!("metrics message: {record:?}"); - - if self.records.len() < 500 { - self.records.push_back(record); - } else { - log::warn!("record queue full, discarding"); + self.records.push_back(record); + if self.records.len() >= RECORD_QUEUE_SIZE { + self.records.pop_front(); } } + pub fn dump_records(&mut self) -> Vec { + let records = std::mem::take(&mut self.records); + records.into_iter().collect() + } + + pub fn is_full(&self) -> bool { + self.records.len() >= RECORD_QUEUE_SIZE - 1 + } + // called every frame pub fn update(&mut self) { let mut buf: [u8; 1024] = [0; 1024]; diff --git a/wgui/Cargo.toml b/wgui/Cargo.toml index 4a9a8e32..3b6ab1f8 100644 --- a/wgui/Cargo.toml +++ b/wgui/Cargo.toml @@ -35,4 +35,3 @@ vulkano.workspace = true vulkano-shaders.workspace = true rust-embed.workspace = true flate2 = "1.1.5" -ringbuffer = "0.16.0" diff --git a/wgui/src/components/bar_graph.rs b/wgui/src/components/bar_graph.rs index bc56b49b..6398e4be 100644 --- a/wgui/src/components/bar_graph.rs +++ b/wgui/src/components/bar_graph.rs @@ -1,7 +1,6 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, collections::VecDeque, rc::Rc}; use glam::Vec2; -use ringbuffer::{AllocRingBuffer, RingBuffer}; use taffy::{ FlexDirection, JustifyContent, prelude::{auto, length, percent}, @@ -39,7 +38,7 @@ pub struct ValueCell { struct State { limits: (f32, f32), /* min - max */ - values: AllocRingBuffer, + values: VecDeque, } #[allow(clippy::struct_field_names)] @@ -51,6 +50,7 @@ struct Data { id_label_val_max: WidgetID, unit: String, + capacity: u32, } pub struct ComponentBarGraph { @@ -99,7 +99,10 @@ impl ComponentBarGraph { pub fn push_value(&self, cell: ValueCell) { let mut state = self.state.borrow_mut(); - state.values.enqueue(cell); + if state.values.len() > self.data.capacity as usize { + state.values.pop_front(); + } + state.values.push_back(cell); } } @@ -151,7 +154,7 @@ pub fn construct( let state = Rc::new(RefCell::new(State { limits: params.limits, - values: AllocRingBuffer::new(params.capacity.clamp(1, 1000) as usize), + values: VecDeque::new(), })); let (_, _) = ess.layout.add_child( @@ -227,6 +230,7 @@ pub fn construct( id_label_val_min: label_val_min.id, id_label_val_max: label_val_max.id, unit: params.unit, + capacity: params.capacity, }); let base = ComponentBase { diff --git a/wlx-common/src/dash_interface.rs b/wlx-common/src/dash_interface.rs index 821ab1eb..424ed2b2 100644 --- a/wlx-common/src/dash_interface.rs +++ b/wlx-common/src/dash_interface.rs @@ -16,6 +16,25 @@ pub struct MonadoClient { pub is_io_active: bool, } +// see `wayvr/src/subsystem/monado_metrics/proto.rs` for documentation +#[derive(Debug)] +pub struct MonadoDumpSessionFrame { + pub session_id: i64, + pub frame_id: i64, + pub predicted_frame_time_ns: u64, + pub predicted_wake_up_time_ns: u64, + pub predicted_gpu_done_time_ns: u64, + pub predicted_display_time_ns: u64, + pub predicted_display_period_ns: u64, + pub display_time_ns: u64, + pub when_predicted_ns: u64, + pub when_wait_woke_ns: u64, + pub when_begin_ns: u64, + pub when_delivered_ns: u64, + pub when_gpu_done_ns: u64, + pub discarded: bool, +} + #[derive(Clone, Copy)] pub enum RecenterMode { FixFloor, @@ -40,6 +59,8 @@ pub trait DashInterface { fn monado_client_focus(&mut self, data: &mut T, name: &str) -> anyhow::Result<()>; fn monado_brightness_get(&mut self, data: &mut T) -> Option; fn monado_brightness_set(&mut self, data: &mut T, brightness: f32) -> Option<()>; + fn monado_metrics_set_enabled(&mut self, data: &mut T, enabled: bool) -> bool; + fn monado_metrics_dump_session_frames(&mut self, data: &mut T) -> Vec; fn recenter_playspace(&mut self, data: &mut T, mode: RecenterMode) -> anyhow::Result<()>; fn desktop_finder<'a>(&'a mut self, data: &'a mut T) -> &'a mut DesktopFinder; fn general_config<'a>(&'a mut self, data: &'a mut T) -> &'a mut GeneralConfig; diff --git a/wlx-common/src/dash_interface_emulated.rs b/wlx-common/src/dash_interface_emulated.rs index 017e7bfb..49b0dd7e 100644 --- a/wlx-common/src/dash_interface_emulated.rs +++ b/wlx-common/src/dash_interface_emulated.rs @@ -63,6 +63,8 @@ pub struct DashInterfaceEmulated { general_config: GeneralConfig, monado_clients: Vec, brightness: f32, + debug_frame_id: u64, + debug_counter: u64, } impl DashInterfaceEmulated { @@ -121,6 +123,8 @@ impl DashInterfaceEmulated { general_config, monado_clients, brightness: 1.0, + debug_counter: 0, + debug_frame_id: 0, } } } @@ -256,4 +260,32 @@ impl DashInterface<()> for DashInterfaceEmulated { self.brightness = brightness; Some(()) } + + fn monado_metrics_set_enabled(&mut self, _: &mut (), _enabled: bool) -> bool { + true + } + + fn monado_metrics_dump_session_frames(&mut self, _: &mut ()) -> Vec { + // simulate """""typical""""" wired hmd timing graphs + self.debug_frame_id += 1; + self.debug_counter ^= self.debug_frame_id * 383; + self.debug_counter ^= 0xdeadbeefbaddcafe; + self.debug_counter >>= 1; + vec![dash_interface::MonadoDumpSessionFrame { + session_id: 123, + frame_id: self.debug_frame_id as _, + predicted_frame_time_ns: (((self.debug_frame_id) % 15) * 1000 * 1000) as _, + predicted_wake_up_time_ns: (((self.debug_frame_id * 2) % 16) * 1000 * 1000) as _, + predicted_gpu_done_time_ns: (((self.debug_frame_id * 3) % 17) * 1000 * 1000) as _, + predicted_display_time_ns: (((self.debug_frame_id * 4) % 14) * 1000 * 1000) as _, + predicted_display_period_ns: (((self.debug_frame_id * 5) % 13) * 1000 * 1000) as _, + display_time_ns: (((self.debug_frame_id * 7) % 12) * 1000 * 1000) as _, + when_predicted_ns: (((self.debug_frame_id * 53) % 11) * 1000 * 1000) as _, + when_wait_woke_ns: (((self.debug_frame_id * 23) % 10) * 1000 * 1000) as _, + when_begin_ns: (((self.debug_frame_id * 13) % 9) * 1000 * 1000) as _, + when_delivered_ns: (((self.debug_frame_id * 11) % 8) * 1000 * 1000) as _, + when_gpu_done_ns: (((self.debug_frame_id * 12) % 7) * 1000 * 1000) as _, + discarded: self.debug_frame_id.is_multiple_of(5), + }] + } }