grafix_toolbox/kit/policies/
profiling.rs

1use crate::{lib::*, sync::*};
2
3#[cfg(not(feature = "profiling"))]
4#[macro_export]
5macro_rules! TIMER {
6	(GL, $_: ident, $b: block) => {{
7		$b
8	}};
9	($_: ident, $b: block) => {{
10		$b
11	}};
12}
13
14#[cfg(feature = "profiling")]
15#[macro_export]
16macro_rules! TIMER {
17	(GL, $n: ident, $b: block) => {{
18		profiling::GPU::Start(stringify!($n));
19		let ret = $b;
20		profiling::GPU::Stop(stringify!($n));
21		ret
22	}};
23	($n: ident, $b: block) => {{
24		profiling::CPU::Start(stringify!($n));
25		let ret = $b;
26		profiling::CPU::Stop(stringify!($n));
27		ret
28	}};
29}
30
31macro_rules! PROFILER {
32	($n: ident, $t: ty) => {
33		pub mod $n {
34			use super::*;
35			use GenericTimer as Timer;
36
37			pub fn Start(name: STR) {
38				let mut lock = map();
39				let t = lock.remove(name).unwrap_or_else(Timer::new::<$t>);
40				lock.insert(name, t.start(name));
41			}
42			pub fn Stop(name: STR) {
43				let mut lock = map();
44				let t = lock.remove(name).unwrap_or_else(Timer::new::<$t>);
45				lock.insert(name, t.stop(name));
46			}
47			pub fn Print(name: &str) {
48				let t = map().remove(name).expect(&format!("No timer {name:?}"));
49				print_impl(name, t);
50			}
51			pub fn PrintAll() {
52				let mut all = map().drain().collect_vec();
53				all.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
54				all.into_iter().for_each(|(n, t)| print_impl(n, t));
55			}
56
57			fn print_impl(name: &str, t: Timer) {
58				let (t, i) = t.get_res(name);
59				let format = move || {
60					for step in &[(1_000_000_000, " s"), (1_000_000, " ms"), (1_000, " us")] {
61						let frame = i * step.0;
62						if t >= frame {
63							return format!("{:.3} {}", f64(t) / f64(frame), step.1);
64						}
65					}
66					return format!("{:.3} ns", f64(t) / f64(i));
67				};
68				PRINT!("Timer {name:?}: {} |{i}", format());
69			}
70			fn map<'s>() -> MutexGuard<'s, HashMap<STR, Timer>> {
71				LazyStatic!(HashMap<STR, Timer>, {
72					logging::Logger::shutdown_hook(PrintAll);
73					Def()
74				})
75			}
76		}
77	};
78}
79PROFILER!(CPU, CPUTimerStopped);
80PROFILER!(GPU, GLTimer);
81
82enum GenericTimer {
83	Started(Started),
84	Done(Done),
85}
86impl GenericTimer {
87	fn new<T: New>() -> Self {
88		Self::Done(T::boxed_new())
89	}
90	fn start(self, _name: &str) -> Self {
91		use GenericTimer::*;
92		match self {
93			Done(done) => Started(done.start()),
94			Started { .. } => ASSERT!(false, "Timer {_name:?} already started"),
95		}
96	}
97	fn stop(self, _name: &str) -> Self {
98		use GenericTimer::*;
99		match self {
100			Started(started) => Done(started.stop()),
101			Done { .. } => ASSERT!(false, "Timer {_name:?} not started"),
102		}
103	}
104	fn get_res(self, _name: &str) -> (u128, u128) {
105		use GenericTimer::*;
106		match self {
107			Done(done) => done.get_res(),
108			Started(_) => ASSERT!(false, "Timer {_name:?} not stopped"),
109		}
110	}
111}
112
113type Started = Box<dyn Stop>;
114type Done = Box<dyn Start>;
115trait Start: SendStat {
116	fn start(self: Box<Self>) -> Started;
117	fn get_res(self: Box<Self>) -> (u128, u128);
118}
119trait Stop: SendStat {
120	fn stop(self: Box<Self>) -> Done;
121}
122trait New {
123	fn boxed_new() -> Done;
124}
125
126#[derive(Default)]
127struct GLTimer {
128	o: GL::Query,
129	total: i64,
130	iters: u128,
131}
132impl Start for GLTimer {
133	fn start(self: Box<Self>) -> Started {
134		crate::GL!(gl::BeginQuery(gl::TIME_ELAPSED, self.o.obj));
135		Box(*self)
136	}
137	fn get_res(self: Box<Self>) -> (u128, u128) {
138		let _ = mem::ManuallyDrop::new(self.o);
139		(u128(self.total), self.iters)
140	}
141}
142impl Stop for GLTimer {
143	fn stop(self: Box<Self>) -> Done {
144		crate::GL!(gl::EndQuery(gl::TIME_ELAPSED));
145		let mut res = 0;
146		crate::GL!(gl::GetQueryObjecti64v(self.o.obj, gl::QUERY_RESULT, &mut res));
147		let Self { o, total, iters } = *self;
148		Box(Self { o, total: total + res, iters: iters + 1 })
149	}
150}
151impl New for GLTimer {
152	fn boxed_new() -> Done {
153		Box(Self::default())
154	}
155}
156
157#[derive(Default)]
158struct CPUTimerStopped {
159	total: time::Duration,
160	iters: u128,
161}
162impl Start for CPUTimerStopped {
163	fn start(self: Box<Self>) -> Started {
164		let Self { total, iters } = *self;
165		Box(CPUTimerStarted { total, iters, stamp: time::Instant::now() })
166	}
167	fn get_res(self: Box<Self>) -> (u128, u128) {
168		(self.total.as_nanos(), self.iters)
169	}
170}
171struct CPUTimerStarted {
172	total: time::Duration,
173	iters: u128,
174	stamp: time::Instant,
175}
176impl Stop for CPUTimerStarted {
177	fn stop(self: Box<Self>) -> Done {
178		let Self { total, iters, stamp } = *self;
179		Box(CPUTimerStopped { total: total + stamp.elapsed(), iters: iters + 1 })
180	}
181}
182impl New for CPUTimerStopped {
183	fn boxed_new() -> Done {
184		Box(Self::default())
185	}
186}