#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub(crate) struct GpuWait {
micros: u32,
}
impl GpuWait {
pub(crate) const fn none() -> Self {
Self { micros: 0 }
}
pub(crate) fn measure<T>(&mut self, f: impl FnOnce() -> T) -> T {
let start = std::time::Instant::now();
let value = f();
let elapsed = start.elapsed().as_micros().min(u32::MAX as u128) as u32;
self.micros = self.micros.saturating_add(elapsed);
value
}
pub(crate) const fn micros(self) -> u32 {
self.micros
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_frame_that_never_waits_reports_nothing() {
assert_eq!(GpuWait::none().micros(), 0);
}
#[test]
fn measure_returns_the_inner_value_and_accumulates() {
let mut wait = GpuWait::none();
let a = wait.measure(|| 7u32);
let b = wait.measure(|| {
std::thread::sleep(std::time::Duration::from_millis(2));
"slot"
});
assert_eq!((a, b), (7, "slot"));
assert!(wait.micros() >= 1_000, "{} us", wait.micros());
}
#[test]
fn the_total_saturates_instead_of_wrapping() {
let mut wait = GpuWait {
micros: u32::MAX - 1,
};
wait.measure(|| std::thread::sleep(std::time::Duration::from_millis(1)));
assert_eq!(wait.micros(), u32::MAX);
}
}