From e035fb9d27cdd5bef158bc4891288373aee32f83 Mon Sep 17 00:00:00 2001 From: Aleksander Date: Sat, 14 Mar 2026 15:12:14 +0100 Subject: [PATCH] separate debug graphs by session ID, selectable session ID list --- .../gui/tab/monado_tab_debug_timings.xml | 12 +- dash-frontend/src/tab/monado.rs | 288 ++++++++++++------ wayvr/src/overlays/dashboard.rs | 3 +- 3 files changed, 215 insertions(+), 88 deletions(-) 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 d167b75e..94dfb5d7 100644 --- a/dash-frontend/assets/gui/tab/monado_tab_debug_timings.xml +++ b/dash-frontend/assets/gui/tab/monado_tab_debug_timings.xml @@ -9,9 +9,19 @@ + + + - + + +
+
+
\ No newline at end of file diff --git a/dash-frontend/src/tab/monado.rs b/dash-frontend/src/tab/monado.rs index 4235464f..5215e784 100644 --- a/dash-frontend/src/tab/monado.rs +++ b/dash-frontend/src/tab/monado.rs @@ -4,6 +4,7 @@ use wgui::{ assets::AssetPath, components::{ bar_graph::{ComponentBarGraph, ValueCell}, + button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider, tabs::ComponentTabs, @@ -46,6 +47,10 @@ enum Task { // `ProcessList` tab ProcessListRefresh, ProcessListFocusClient(String), + + // `DebugTimings` tab + DebugTimingsRefreshSessionList, + DebugTimingsSetSessionId(i64), } struct SubtabProcessList { @@ -65,7 +70,14 @@ struct DebugGraph { data: ParserData, } +struct DebugSessionList { + buttons: Vec>, + #[allow(dead_code)] + data_vec: Vec, +} + struct TimingsSession { + resolved_name: Option, last_frame: MonadoDumpSessionFrame, } @@ -83,13 +95,20 @@ struct Graphs { when_gpu_done: DebugGraph, } +type SessionsMap = HashMap; + struct SubtabDebugTimings { #[allow(dead_code)] state: ParserState, - graphs: Graphs, + graphs: Option, + session_list: DebugSessionList, + selected_session_id: Option, - sessions: HashMap, + id_sessions_list_parent: WidgetID, + id_timings_parent: WidgetID, + + sessions: SessionsMap, } #[allow(dead_code)] @@ -131,6 +150,16 @@ impl Tab for TabMonado { process_list.focus_client(frontend, data, client_name, &self.tasks)?; } } + Task::DebugTimingsRefreshSessionList => { + if let Subtab::DebugTimings(tab) = &mut self.subtab { + tab.refresh_session_list(&mut frontend.layout, &self.tasks)?; + } + } + Task::DebugTimingsSetSessionId(session_id) => { + if let Subtab::DebugTimings(tab) = &mut self.subtab { + tab.set_session_id(&mut frontend.layout, session_id)?; + } + } Task::SetBrightness(brightness) => self.set_brightness(frontend, data, brightness), Task::SetTab(tab) => { frontend.layout.remove_children(self.id_content); @@ -148,7 +177,7 @@ impl Tab for TabMonado { self.subtab = Subtab::ProcessList(SubtabProcessList::new(self.id_content, frontend)?) } TabNameEnum::DebugTimings => { - self.subtab = Subtab::DebugTimings(SubtabDebugTimings::new(self.id_content, frontend)?) + self.subtab = Subtab::DebugTimings(SubtabDebugTimings::new(self.id_content, frontend, &self.tasks)?) } } } @@ -167,7 +196,7 @@ impl Tab for TabMonado { } } Subtab::DebugTimings(timings) => { - timings.update(data, frontend); + timings.update(&self.tasks, data, frontend); } } @@ -249,21 +278,71 @@ impl SubtabGeneralSettings { } } +fn mount_sessions_list( + state: &mut ParserState, + layout: &mut Layout, + tasks: &Tasks, + id_parent: WidgetID, + sessions: &SessionsMap, +) -> anyhow::Result { + let mut buttons = Vec::new(); + let mut data_vec = Vec::new(); + let globals = layout.state.globals.clone(); + layout.remove_children(id_parent); + + for (session_id, session) in sessions { + let mut params = HashMap::new(); + + params.insert( + Rc::from("text"), + Rc::from(format!( + "{} (ID {})", + session.resolved_name.as_ref().map_or("Unknown", |s| s.as_str()), + session_id, + )), + ); + + let data = state.parse_template( + &doc_params_tab_debug_timings(&globals), + "SessionButton", + layout, + id_parent, + params, + )?; + + let button = data.fetch_component_as::("button")?; + + button.on_click({ + let tasks = tasks.clone(); + let session_id = *session_id; + Rc::new(move |_, _| { + tasks.push(Task::DebugTimingsSetSessionId(session_id)); + Ok(()) + }) + }); + + buttons.push(button); + data_vec.push(data); + } + + Ok(DebugSessionList { buttons, data_vec }) +} + fn mount_graph( state: &mut ParserState, - globals: &WguiGlobals, layout: &mut Layout, id_parent: WidgetID, name: &'static str, limits: (f32, f32), ) -> anyhow::Result { + let globals = layout.state.globals.clone(); 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), + &doc_params_tab_debug_timings(&globals), "DebugGraph", layout, id_parent, @@ -279,28 +358,46 @@ fn ns_to_ms(ns: i64) -> f32 { } impl SubtabDebugTimings { - fn new(parent_id: WidgetID, frontend: &mut Frontend) -> anyhow::Result { + fn new(parent_id: WidgetID, frontend: &mut Frontend, tasks: &Tasks) -> anyhow::Result { let mut state = wgui::parser::parse_from_assets( &doc_params_tab_debug_timings(&frontend.globals), &mut frontend.layout, parent_id, )?; - let id_parent = state.get_widget_id("parent")?; + let id_timings_parent = state.get_widget_id("timings_parent")?; + let id_sessions_list_parent = state.get_widget_id("session_list_parent")?; + + let sessions = Default::default(); + + let session_list = mount_sessions_list( + &mut state, + &mut frontend.layout, + tasks, + id_sessions_list_parent, + &sessions, + )?; + + Ok(Self { + state, + graphs: None, + session_list, + id_sessions_list_parent, + id_timings_parent, + sessions, + selected_session_id: None, + }) + } + + fn set_session_id(&mut self, layout: &mut Layout, session_id: i64) -> anyhow::Result<()> { + layout.remove_children(self.id_timings_parent); let mut graph = |name: &'static str, limits: (f32, f32)| -> anyhow::Result { - mount_graph( - &mut state, - &frontend.globals, - &mut frontend.layout, - id_parent, - name, - limits, - ) + mount_graph(&mut self.state, layout, self.id_timings_parent, name, limits) }; // populate graphs - let graphs = Graphs { + self.graphs = Some(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))?, @@ -311,17 +408,26 @@ impl SubtabDebugTimings { 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))?, - }; + when_gpu_done: graph("When GPU done", (0.0, 30.0))?, + }); - Ok(Self { - state, - graphs, - sessions: Default::default(), - }) + self.selected_session_id = Some(session_id); + + Ok(()) } - fn update(&mut self, data: &mut T, frontend: &mut Frontend) { + fn refresh_session_list(&mut self, layout: &mut Layout, tasks: &Tasks) -> anyhow::Result<()> { + self.session_list = mount_sessions_list( + &mut self.state, + layout, + tasks, + self.id_sessions_list_parent, + &self.sessions, + )?; + Ok(()) + } + + fn update(&mut self, tasks: &Tasks, data: &mut T, frontend: &mut Frontend) { if !frontend.interface.monado_metrics_set_enabled(data, true) { return; } @@ -334,85 +440,95 @@ impl SubtabDebugTimings { let col_green = Color::new(0.0, 1.0, 0.0, 1.0); for frame in frames { - log::info!("{:?}", frame); + //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); + if let Some(graphs) = &mut self.graphs + && let Some(selected_session_id) = self.selected_session_id + && selected_session_id == frame.session_id + { + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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, - }); + 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 }); + self.sessions.insert( + frame.session_id, + TimingsSession { + last_frame: frame, + resolved_name: None, + }, + ); + tasks.push(Task::DebugTimingsRefreshSessionList); } } } diff --git a/wayvr/src/overlays/dashboard.rs b/wayvr/src/overlays/dashboard.rs index 8f6b0a34..1db82735 100644 --- a/wayvr/src/overlays/dashboard.rs +++ b/wayvr/src/overlays/dashboard.rs @@ -609,8 +609,9 @@ impl DashInterface for DashInterfaceLive { } #[cfg(not(feature = "openxr"))] - fn monado_metrics_set_enabled(&mut self, _: &mut AppState, _enabled: bool) { + fn monado_metrics_set_enabled(&mut self, _: &mut AppState, _enabled: bool) -> bool { // not supported in this build + false } #[cfg(not(feature = "openxr"))]