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