1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::timers::*;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::rc::Rc;
enum RefreshMode {
Lazy,
FullSpeed,
}
impl<'a> TryFrom<&Vec<&'a str>> for RefreshMode {
type Error = ();
fn try_from(options: &Vec<&'a str>) -> Result<Self, Self::Error> {
if options.contains(&"refresh_lazy") {
Ok(Self::Lazy)
} else if options.contains(&"refresh_full_speed") {
Ok(Self::FullSpeed)
} else {
Err(())
}
}
}
pub struct FPSCounter {
frame_times: RefCell<Vec<instant::Instant>>,
update_timer: Timer,
refresh_mode: RefreshMode,
output_console: bool,
output_overlay: bool,
}
impl FPSCounter {
pub fn new() -> Option<Rc<Self>> {
let options = match std::env::var("SIXTYFPS_DEBUG_PERFORMANCE") {
Ok(var) => var,
_ => return None,
};
let options: Vec<&str> = options.split(',').collect();
let refresh_mode = match RefreshMode::try_from(&options) {
Ok(mode) => mode,
Err(_) => {
eprintln!("Missing refresh mode in SIXTYFPS_DEBUG_PERFORMANCE. Please specify either refresh_full_speed or refresh_lazy");
return None;
}
};
let output_console = options.contains(&"console");
let output_overlay = options.contains(&"overlay");
if !output_console && !output_overlay {
eprintln!("Missing output mode in SIXTYFPS_DEBUG_PERFORMANCE. Please specify either console or overlay (or both)");
return None;
}
Some(Rc::new(Self {
frame_times: Default::default(),
update_timer: Default::default(),
refresh_mode,
output_console,
output_overlay,
}))
}
pub fn start(self: &Rc<Self>, winsys_info: &str) {
#[cfg(debug_assertions)]
let build_config = "debug";
#[cfg(not(debug_assertions))]
let build_config = "release";
eprintln!("SixtyFPS: Build config: {}; Backend: {}", build_config, winsys_info);
let this = self.clone();
self.update_timer.stop();
self.update_timer.start(TimerMode::Repeated, std::time::Duration::from_secs(1), move || {
this.trim_frame_times();
if this.output_console {
eprintln!("average frames per second: {}", this.frame_times.borrow().len());
}
})
}
fn trim_frame_times(self: &Rc<Self>) {
let mut i = 0;
let mut frame_times = self.frame_times.borrow_mut();
while i < frame_times.len() {
if frame_times[i].elapsed() > std::time::Duration::from_secs(1) {
frame_times.remove(i);
} else {
i += 1
}
}
}
pub fn measure_frame_rendered(
self: &Rc<Self>,
renderer_for_overlay: &mut dyn crate::item_rendering::ItemRenderer,
) {
self.frame_times.borrow_mut().push(instant::Instant::now());
if matches!(self.refresh_mode, RefreshMode::FullSpeed) {
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.set_has_active_animations());
}
self.trim_frame_times();
if self.output_overlay {
renderer_for_overlay.draw_string(
&format!("FPS: {}", self.frame_times.borrow().len()),
crate::Color::from_rgb_u8(0, 128, 128),
);
}
}
}