display debug graphs

This commit is contained in:
Aleksander 2026-02-26 22:04:07 +01:00
parent d5148e2107
commit c16c403966
11 changed files with 367 additions and 257 deletions

214
Cargo.lock generated
View File

@ -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"

View File

@ -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"]

View File

@ -1,12 +1,17 @@
<layout>
<include src="../t_group_box.xml" />
<elements>
<rectangle macro="group_box">
<GroupBoxTitle src_builtin="dashboard/not_a_bug.svg" text="Debug timings" />
<!-- name, limit_min, limit_max -->
<template name="DebugGraph">
<rectangle flex_direction="column" align_items="center" gap="8" border="2" color="#ffffff11" color2="#ffffff22" gradient="vertical" border_color="#ffffff33" round="4" padding="4">
<label text="${name}" size="9" weight="bold" />
<BarGraph id="graph" width="180" height="70" unit="ms" limit_min="${limit_min}" limit_max="${limit_max}" capacity="50" />
</rectangle>
</template>
<BarGraph id="graph_first" width="200" height="100" unit="%" limit_min="0" limit_max="50" capacity="100" />
<BarGraph id="graph_second" width="300" height="200" unit="ms" capacity="50" />
<elements>
<rectangle macro="group_box" id="parent" flex_wrap="wrap" flex_direction="row">
<GroupBoxTitle src_builtin="dashboard/not_a_bug.svg" text="Debug timings" />
</rectangle>
</elements>
</layout>

View File

@ -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<ComponentBarGraph>,
#[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<ComponentBarGraph>,
graph_second: Rc<ComponentBarGraph>,
graphs: Graphs,
sessions: HashMap<i64 /* session id */, TimingsSession>,
}
#[allow(dead_code)]
@ -142,7 +167,7 @@ impl<T> Tab<T> for TabMonado<T> {
}
}
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<DebugGraph> {
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::<ComponentBarGraph>("graph")?;
Ok(DebugGraph { graph, data })
}
fn ns_to_ms(ns: i64) -> f32 {
(ns / 1000) as f32 / 1000.0
}
impl SubtabDebugTimings {
fn new<T>(parent_id: WidgetID, frontend: &mut Frontend<T>) -> anyhow::Result<Self> {
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::<ComponentBarGraph>("graph_first")?;
let graph_second = state.fetch_component_as::<ComponentBarGraph>("graph_second")?;
let id_parent = state.get_widget_id("parent")?;
let mut graphs: Vec<DebugGraph> = Vec::new();
let mut graph = |name: &'static str, limits: (f32, f32)| -> anyhow::Result<DebugGraph> {
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<T>(&mut self, data: &mut T, frontend: &mut Frontend<T>) {
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();
}
}

View File

@ -8,23 +8,32 @@ pub struct MonadoState {
impl MonadoState {
pub fn new() -> anyhow::Result<Self> {
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;
}

View File

@ -524,6 +524,70 @@ impl DashInterface<AppState> 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<dash_interface::MonadoDumpSessionFrame> {
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<AppState> 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<dash_interface::MonadoDumpSessionFrame> {
Vec::new() // not supported in this build
}
}
#[cfg(feature = "openxr")]

View File

@ -13,6 +13,8 @@ pub struct MonadoMetricsFd {
records: VecDeque<proto::Record>,
}
const RECORD_QUEUE_SIZE: usize = 500;
impl MonadoMetricsFd {
pub fn new(monado: &mut libmonado::Monado) -> anyhow::Result<Self> {
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<proto::Record> {
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];

View File

@ -35,4 +35,3 @@ vulkano.workspace = true
vulkano-shaders.workspace = true
rust-embed.workspace = true
flate2 = "1.1.5"
ringbuffer = "0.16.0"

View File

@ -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<ValueCell>,
values: VecDeque<ValueCell>,
}
#[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 {

View File

@ -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<T> {
fn monado_client_focus(&mut self, data: &mut T, name: &str) -> anyhow::Result<()>;
fn monado_brightness_get(&mut self, data: &mut T) -> Option<f32>;
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<MonadoDumpSessionFrame>;
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;

View File

@ -63,6 +63,8 @@ pub struct DashInterfaceEmulated {
general_config: GeneralConfig,
monado_clients: Vec<dash_interface::MonadoClient>,
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<dash_interface::MonadoDumpSessionFrame> {
// 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),
}]
}
}