grafix_toolbox/kit/policies/
profiling.rs1use 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}