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),
+ }]
+ }
}