From d9bf2decbd1535bfbdd5da4febb4cd218a09a77b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 17 Jan 2026 17:46:56 +0100 Subject: [PATCH 1/5] log from mangohud and read csv --- Cargo.lock | 40 +++++++++++++++++++++++++++++++++---- Cargo.toml | 1 + src/metrics/large_scenes.rs | 27 ++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29c15c906..18cfc8f5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,6 +396,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + [[package]] name = "darling" version = "0.20.11" @@ -1649,18 +1670,28 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1945,6 +1976,7 @@ dependencies = [ "chrono", "clap", "crossbeam", + "csv", "fs_extra", "git2", "nvml-wrapper", diff --git a/Cargo.toml b/Cargo.toml index 2a537d8dd..5dfc06468 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ nvml-wrapper = "0.11.0" regex = "1.11" git2 = "0.20" fs_extra = "1.3.0" +csv = "1.3" diff --git a/src/metrics/large_scenes.rs b/src/metrics/large_scenes.rs index fd0f3231e..b89c57394 100644 --- a/src/metrics/large_scenes.rs +++ b/src/metrics/large_scenes.rs @@ -122,9 +122,15 @@ impl Metrics for LargeScene { .into_iter() .flat_map(|f| ["--features".to_string(), f]); + let path = std::env::current_dir().unwrap(); + let _guard = sh.push_env( + "MANGOCONFIG", + format!("output_folder={},autostart_log=10", path.display()), + ); + let cmd = cmd!( sh, - "xvfb-run cargo run --release {features...} --package {scene} -- {parameters...}" + "xvfb-run mangohud cargo run --release {features...} --package {scene} -- {parameters...}" ); let mut results = HashMap::new(); @@ -157,6 +163,25 @@ impl Metrics for LargeScene { let gpu_memory = gpu_usage.iter().map(|v| v.mem as f32).collect::>(); let gpu_usage = gpu_usage.iter().map(|v| v.sm as f32).collect::>(); + let last_modified_file = std::fs::read_dir(".") + .expect("Couldn't access local directory") + .flatten() + .filter(|f| { + f.metadata().unwrap().is_file() + && f.file_name().into_string().unwrap().ends_with(".csv") + }) + .max_by_key(|x| x.metadata().unwrap().modified().unwrap()) + .unwrap(); + + println!("Most recent file: {:?}", last_modified_file); + + let mut rdr = csv::Reader::from_path(last_modified_file.path()).unwrap(); + for result in rdr.records() { + // The iterator yields Result, so we check the + // error here. + println!("{:?}", result); + } + results.insert( format!("{key}.cpu_usage.mean"), (statistical::mean(&cpu_usage) * 1000.0) as u64, From cced1110d2ffd4de1140faed3b87f90b1576774e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 17 Jan 2026 18:23:54 +0100 Subject: [PATCH 2/5] collect frame time from mangohud for large scenes --- src/metrics/large_scenes.rs | 73 +++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/src/metrics/large_scenes.rs b/src/metrics/large_scenes.rs index b89c57394..34228a8fa 100644 --- a/src/metrics/large_scenes.rs +++ b/src/metrics/large_scenes.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - io::Write, + io::{BufRead, Write}, path::{Path, PathBuf}, thread, time::{Duration, Instant}, @@ -163,25 +163,6 @@ impl Metrics for LargeScene { let gpu_memory = gpu_usage.iter().map(|v| v.mem as f32).collect::>(); let gpu_usage = gpu_usage.iter().map(|v| v.sm as f32).collect::>(); - let last_modified_file = std::fs::read_dir(".") - .expect("Couldn't access local directory") - .flatten() - .filter(|f| { - f.metadata().unwrap().is_file() - && f.file_name().into_string().unwrap().ends_with(".csv") - }) - .max_by_key(|x| x.metadata().unwrap().modified().unwrap()) - .unwrap(); - - println!("Most recent file: {:?}", last_modified_file); - - let mut rdr = csv::Reader::from_path(last_modified_file.path()).unwrap(); - for result in rdr.records() { - // The iterator yields Result, so we check the - // error here. - println!("{:?}", result); - } - results.insert( format!("{key}.cpu_usage.mean"), (statistical::mean(&cpu_usage) * 1000.0) as u64, @@ -253,6 +234,58 @@ impl Metrics for LargeScene { results.insert(format!("{key}.duration"), elapsed.as_millis() as u64); results.insert(format!("{key}.frames"), self.nb_frames as u64); + if let Some(last_modified_file) = std::fs::read_dir(".") + .expect("Couldn't access local directory") + .flatten() + .filter(|f| { + f.metadata().unwrap().is_file() + && f.file_name().into_string().unwrap().ends_with(".csv") + }) + .max_by_key(|x| x.metadata().unwrap().modified().unwrap()) + { + let csv_file = std::fs::File::open(last_modified_file.path()).unwrap(); + // Skip first two lines as they're info about system + let mut reader = std::io::BufReader::new(csv_file); + let mut tmp = String::new(); + let _ = reader.read_line(&mut tmp); + let _ = reader.read_line(&mut tmp); + let mut rdr = csv::ReaderBuilder::new().from_reader(reader); + let frame_times = rdr + .records() + .flatten() + .flat_map(|record| record.get(0).unwrap().parse::()) + .collect::>(); + + results.insert( + format!("{key}.frame_time.mean"), + (statistical::mean(&frame_times) * 1000.0) as u64, + ); + results.insert( + format!("{key}.frame_time.median"), + (statistical::median(&frame_times) * 1000.0) as u64, + ); + results.insert( + format!("{key}.frame_time.min"), + frame_times + .iter() + .map(|d| (d * 1000.0) as u64) + .min() + .unwrap(), + ); + results.insert( + format!("{key}.frame_time.max"), + frame_times + .iter() + .map(|d| (d * 1000.0) as u64) + .max() + .unwrap(), + ); + results.insert( + format!("{key}.frame_time.std_dev"), + (statistical::standard_deviation(&frame_times, None) * 1000.0) as u64, + ); + } + results } } From 23fba225961b0117dfe4ff0ac3d9bc06bf07ff80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 17 Jan 2026 18:25:20 +0100 Subject: [PATCH 3/5] fix mangohud config --- src/metrics/large_scenes.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/metrics/large_scenes.rs b/src/metrics/large_scenes.rs index 34228a8fa..8ff8e4737 100644 --- a/src/metrics/large_scenes.rs +++ b/src/metrics/large_scenes.rs @@ -122,10 +122,12 @@ impl Metrics for LargeScene { .into_iter() .flat_map(|f| ["--features".to_string(), f]); - let path = std::env::current_dir().unwrap(); let _guard = sh.push_env( - "MANGOCONFIG", - format!("output_folder={},autostart_log=10", path.display()), + "MANGOHUD_CONFIG", + format!( + "output_folder={},autostart_log=10", + std::env::current_dir().unwrap().display() + ), ); let cmd = cmd!( From 2ddfb5808d9d13d48f33b9f4eee3954c48f73746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 17 Jan 2026 18:27:47 +0100 Subject: [PATCH 4/5] also mangohud for stress tests --- src/metrics/stress_tests.rs | 62 ++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/metrics/stress_tests.rs b/src/metrics/stress_tests.rs index 6c270e92a..4dc82681a 100644 --- a/src/metrics/stress_tests.rs +++ b/src/metrics/stress_tests.rs @@ -118,9 +118,17 @@ impl Metrics for StressTest { .into_iter() .flat_map(|f| ["--features".to_string(), f]); + let _guard = sh.push_env( + "MANGOHUD_CONFIG", + format!( + "output_folder={},autostart_log=1", + std::env::current_dir().unwrap().display() + ), + ); + let cmd = cmd!( sh, - "xvfb-run cargo run --release {features...} --example {stress_tests} -- {parameters...}" + "xvfb-run mangohud cargo run --release {features...} --example {stress_tests} -- {parameters...}" ); let mut results = HashMap::new(); @@ -260,6 +268,58 @@ impl Metrics for StressTest { results.insert(format!("{key}.duration"), elapsed.as_millis() as u64); results.insert(format!("{key}.frames"), self.nb_frames as u64); + if let Some(last_modified_file) = std::fs::read_dir(".") + .expect("Couldn't access local directory") + .flatten() + .filter(|f| { + f.metadata().unwrap().is_file() + && f.file_name().into_string().unwrap().ends_with(".csv") + }) + .max_by_key(|x| x.metadata().unwrap().modified().unwrap()) + { + let csv_file = std::fs::File::open(last_modified_file.path()).unwrap(); + // Skip first two lines as they're info about system + let mut reader = std::io::BufReader::new(csv_file); + let mut tmp = String::new(); + let _ = reader.read_line(&mut tmp); + let _ = reader.read_line(&mut tmp); + let mut rdr = csv::ReaderBuilder::new().from_reader(reader); + let frame_times = rdr + .records() + .flatten() + .flat_map(|record| record.get(0).unwrap().parse::()) + .collect::>(); + + results.insert( + format!("{key}.frame_time.mean"), + (statistical::mean(&frame_times) * 1000.0) as u64, + ); + results.insert( + format!("{key}.frame_time.median"), + (statistical::median(&frame_times) * 1000.0) as u64, + ); + results.insert( + format!("{key}.frame_time.min"), + frame_times + .iter() + .map(|d| (d * 1000.0) as u64) + .min() + .unwrap(), + ); + results.insert( + format!("{key}.frame_time.max"), + frame_times + .iter() + .map(|d| (d * 1000.0) as u64) + .max() + .unwrap(), + ); + results.insert( + format!("{key}.frame_time.std_dev"), + (statistical::standard_deviation(&frame_times, None) * 1000.0) as u64, + ); + } + results } } From a69339c0278fa998d25e193ad23b25a57db030cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 17 Jan 2026 18:30:29 +0100 Subject: [PATCH 5/5] frame time, not fps --- src/metrics/large_scenes.rs | 2 +- src/metrics/stress_tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/metrics/large_scenes.rs b/src/metrics/large_scenes.rs index 8ff8e4737..aae4a9266 100644 --- a/src/metrics/large_scenes.rs +++ b/src/metrics/large_scenes.rs @@ -255,7 +255,7 @@ impl Metrics for LargeScene { let frame_times = rdr .records() .flatten() - .flat_map(|record| record.get(0).unwrap().parse::()) + .flat_map(|record| record.get(1).unwrap().parse::()) .collect::>(); results.insert( diff --git a/src/metrics/stress_tests.rs b/src/metrics/stress_tests.rs index 4dc82681a..0e760e8eb 100644 --- a/src/metrics/stress_tests.rs +++ b/src/metrics/stress_tests.rs @@ -287,7 +287,7 @@ impl Metrics for StressTest { let frame_times = rdr .records() .flatten() - .flat_map(|record| record.get(0).unwrap().parse::()) + .flat_map(|record| record.get(1).unwrap().parse::()) .collect::>(); results.insert(