Skip to main content

grafix_toolbox/kit/policies/
profiling.rs

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