1use std::sync::Mutex;
7use std::sync::atomic::{AtomicUsize, Ordering};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[non_exhaustive]
13pub enum Stage {
14 ContainerParse,
16 Metadata,
18 EntropyDecode,
20 Unpack,
22 Predictor,
24 Calibration,
26 Postprocess,
28 GpuUpload,
30 GpuCompute,
32 GpuDownload,
34 Preview,
36}
37
38impl Stage {
39 pub fn label(self) -> &'static str {
41 match self {
42 Stage::ContainerParse => "Container parse",
43 Stage::Metadata => "Metadata",
44 Stage::EntropyDecode => "Entropy decode",
45 Stage::Unpack => "Pixel unpack",
46 Stage::Predictor => "Pixel reconstruction",
47 Stage::Calibration => "Calibration",
48 Stage::Postprocess => "Post-process",
49 Stage::GpuUpload => "GPU upload",
50 Stage::GpuCompute => "GPU compute",
51 Stage::GpuDownload => "GPU download",
52 Stage::Preview => "Preview",
53 }
54 }
55}
56
57static CLOCK: AtomicUsize = AtomicUsize::new(0);
58
59pub fn set_clock(f: fn() -> f64) {
61 CLOCK.store(f as usize, Ordering::Relaxed);
62}
63
64pub fn now_ms() -> f64 {
66 let p = CLOCK.load(Ordering::Relaxed);
67 if p != 0 {
68 let f: fn() -> f64 = unsafe { std::mem::transmute::<usize, fn() -> f64>(p) };
71 return f();
72 }
73 default_clock()
74}
75
76#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
77fn default_clock() -> f64 {
78 use std::sync::OnceLock;
79 use std::time::Instant;
80 static START: OnceLock<Instant> = OnceLock::new();
81 START.get_or_init(Instant::now).elapsed().as_secs_f64() * 1000.0
82}
83
84#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
85fn default_clock() -> f64 {
86 0.0
87}
88
89#[derive(Debug, Default)]
92pub struct Profiler {
93 entries: Mutex<Vec<(Stage, f64)>>,
94}
95
96impl Profiler {
97 pub fn new() -> Self {
99 Self::default()
100 }
101 pub fn record(&self, stage: Stage, ms: f64) {
103 if let Ok(mut e) = self.entries.lock() {
104 if let Some(x) = e.iter_mut().find(|(s, _)| *s == stage) {
105 x.1 += ms;
106 } else {
107 e.push((stage, ms));
108 }
109 }
110 }
111 pub fn time<T>(&self, stage: Stage, f: impl FnOnce() -> T) -> T {
113 let t = now_ms();
114 let r = f();
115 self.record(stage, now_ms() - t);
116 r
117 }
118 pub fn stages(&self) -> Vec<(Stage, f64)> {
120 let mut v = self.entries.lock().map(|e| e.clone()).unwrap_or_default();
121 v.sort_by_key(|(s, _)| *s);
122 v
123 }
124}