|
110 | 110 | // Rustdoc lints
|
111 | 111 | #![deny(rustdoc::broken_intra_doc_links)]
|
112 | 112 | // Rustc lints
|
113 |
| -#![deny(missing_docs, unused_imports)] |
| 113 | +#![deny( |
| 114 | + missing_debug_implementations, |
| 115 | + missing_docs, |
| 116 | + rust_2018_idioms, |
| 117 | + unreachable_pub, |
| 118 | + unused_imports |
| 119 | +)] |
114 | 120 | // adds `#[no_std]` attribute if the `std` feature is not enabled
|
115 | 121 | #![cfg_attr(not(feature = "std"), no_std)]
|
| 122 | +#![cfg_attr(docsrs, feature(doc_cfg))] |
116 | 123 |
|
117 |
| -use embedded_time::{duration::Seconds, Clock, Instant}; |
118 |
| -use holodeque::ArrayDeque; |
| 124 | +pub use fps::{FPS, Error}; |
| 125 | + |
| 126 | +mod fps; |
119 | 127 |
|
120 | 128 | #[cfg(feature = "std")]
|
121 | 129 | #[doc(inline)]
|
122 | 130 | pub use std_clock::StdClock;
|
123 | 131 |
|
124 |
| -/// Measures Frames Per Second (FPS). |
125 |
| -/// |
126 |
| -/// `MAX_FPS` - Defines the maximum FPS that you expect to measure. |
127 |
| -#[derive(Debug, Clone)] |
128 |
| -pub struct FPS<const MAX_FPS: usize, C: Clock> { |
129 |
| - /// The last registered frames. |
130 |
| - last_second_frames: ArrayDeque<Option<Instant<C>>, MAX_FPS>, |
131 |
| - /// The embedded [`Clock`] that will be used to track the passed second. |
132 |
| - clock: C, |
133 |
| -} |
134 |
| - |
135 |
| -impl<const MAX_FPS: usize, C: Clock> FPS<MAX_FPS, C> { |
136 |
| - /// Creates a new Frames Per Second counter. |
137 |
| - pub fn new(clock: C) -> FPS<MAX_FPS, C> { |
138 |
| - FPS { |
139 |
| - last_second_frames: ArrayDeque::<_, MAX_FPS>::new(), |
140 |
| - clock, |
141 |
| - } |
142 |
| - } |
143 |
| - |
144 |
| - /// Adds another frame tick and returns the current Frames Pre Second. |
145 |
| - /// |
146 |
| - /// # Panics |
147 |
| - /// |
148 |
| - /// When [`Clock::try_now()`] returns an error or if the `MAX_FPS` is reached. |
149 |
| - pub fn tick(&mut self) -> usize { |
150 |
| - self.try_tick().unwrap() |
151 |
| - } |
152 |
| - |
153 |
| - /// Adds another frame tick and returns the current Frames Pre Second. |
154 |
| - /// |
155 |
| - /// This method will not panic if the `MAX_FPS` is reached, |
156 |
| - /// instead it will just return the `MAX_FPS` value (capping it in a nutshell). |
157 |
| - /// |
158 |
| - /// # Panics |
159 |
| - /// |
160 |
| - /// If [`Clock::try_now()`] returns an error. |
161 |
| - pub fn tick_max(&mut self) -> usize { |
162 |
| - self.try_tick_max().unwrap() |
163 |
| - } |
164 |
| - |
165 |
| - /// Adds another frame tick and returns the current Frames Pre Second. |
166 |
| - /// |
167 |
| - /// This method will not return an error if the `MAX_FPS` is reached, |
168 |
| - /// instead it will just return the `MAX_FPS` value (capping it in a nutshell). |
169 |
| - pub fn try_tick_max(&mut self) -> Result<usize, Error> { |
170 |
| - match self.try_tick() { |
171 |
| - Ok(fps) => Ok(fps), |
172 |
| - Err(Error::MaxFPS(_)) => Ok(MAX_FPS), |
173 |
| - Err(err) => Err(err), |
174 |
| - } |
175 |
| - } |
176 |
| - |
177 |
| - /// Adds another frame tick and returns the current Frames Pre Second. |
178 |
| - /// |
179 |
| - /// # Panics |
180 |
| - /// |
181 |
| - /// When [`Clock::try_now()`] returns an error or if the `MAX_FPS` is reached. |
182 |
| - pub fn try_tick(&mut self) -> Result<usize, Error> { |
183 |
| - let now = self.clock.try_now().map_err(Error::Clock)?; |
184 |
| - let a_second_ago = now - Seconds(1); |
185 |
| - |
186 |
| - while self |
187 |
| - .last_second_frames |
188 |
| - .front() |
189 |
| - .copied() |
190 |
| - .flatten() |
191 |
| - .map_or(false, |tick| tick < a_second_ago) |
192 |
| - { |
193 |
| - self.last_second_frames.pop_front(); |
194 |
| - } |
195 |
| - |
196 |
| - self.last_second_frames |
197 |
| - .push_back(Some(now)) |
198 |
| - .map_err(|_cap_err| Error::MaxFPS(MAX_FPS))?; |
199 |
| - |
200 |
| - // return the frames per second |
201 |
| - Ok(self.last_second_frames.len()) |
202 |
| - } |
203 |
| -} |
204 |
| - |
205 |
| -impl<const MAX_FPS: usize, C> Default for FPS<MAX_FPS, C> |
206 |
| -where |
207 |
| - C: Clock + Default, |
208 |
| -{ |
209 |
| - fn default() -> Self { |
210 |
| - Self::new(C::default()) |
211 |
| - } |
212 |
| -} |
213 |
| - |
214 |
| -#[derive(Debug)] |
215 |
| -/// The errors that [`FPS`] can return. |
216 |
| -/// |
217 |
| -/// Keep in mind that [`Error::MaxFPS`] will trigger panic on [`FPS::tick`] |
218 |
| -/// or be returned as an error on [`FPS::try_tick`]. |
219 |
| -pub enum Error { |
220 |
| - /// The clock returned an error when calling [`Clock::try_now`]. |
221 |
| - Clock(embedded_time::clock::Error), |
222 |
| - /// The maximum reading of Frames per second was reached |
223 |
| - /// The internal deque reached it's capacity. |
224 |
| - /// |
225 |
| - /// Increase the `MAX_FPS` to avoid this problem. |
226 |
| - MaxFPS(usize), |
227 |
| -} |
228 |
| - |
229 | 132 | #[cfg(feature = "std")]
|
230 |
| -mod std_clock { |
231 |
| - use std::time::Instant as StdInstant; |
232 |
| - |
233 |
| - use embedded_time::{clock::Error, rate::Fraction, Clock, Instant as EmbeddedInstant}; |
234 |
| - |
235 |
| - /// A Standard clock based on [`std`]. |
236 |
| - /// |
237 |
| - /// It takes the [`Instant::elapsed()`] time and uses nanoseconds converted to [`u64`]. |
238 |
| - /// This still leaves us with ~594 years of representable time |
239 |
| - /// |
240 |
| - /// [`Instant::elapsed()`]: std::time::Instant::elapsed() |
241 |
| - #[derive(Debug, Clone, Copy)] |
242 |
| - pub struct StdClock(StdInstant); |
243 |
| - |
244 |
| - impl Default for StdClock { |
245 |
| - fn default() -> Self { |
246 |
| - Self::new() |
247 |
| - } |
248 |
| - } |
249 |
| - |
250 |
| - impl StdClock { |
251 |
| - /// Creates a new [`StdClock`]. |
252 |
| - /// Internally it calls [`Instant::now()`]. |
253 |
| - /// |
254 |
| - /// [`Instant::now()`]: std::time::Instant::now() |
255 |
| - pub fn new() -> Self { |
256 |
| - Self(StdInstant::now()) |
257 |
| - } |
258 |
| - } |
259 |
| - |
260 |
| - impl Clock for StdClock { |
261 |
| - type T = u64; |
262 |
| - |
263 |
| - const SCALING_FACTOR: Fraction = Fraction::new(1, 1_000_000_000); |
264 |
| - |
265 |
| - fn try_now(&self) -> Result<EmbeddedInstant<Self>, Error> { |
266 |
| - // discarding the upper u64 still leaves us with ~594 years of representable time |
267 |
| - Ok(EmbeddedInstant::new(self.0.elapsed().as_nanos() as u64)) |
268 |
| - } |
269 |
| - } |
270 |
| - |
271 |
| - #[cfg(test)] |
272 |
| - mod tests { |
273 |
| - use std::thread::sleep; |
274 |
| - |
275 |
| - use embedded_time::{ |
276 |
| - duration::{Extensions, Milliseconds}, |
277 |
| - Clock, |
278 |
| - }; |
279 |
| - |
280 |
| - use super::StdClock; |
281 |
| - |
282 |
| - #[test] |
283 |
| - fn it_creates_std_instant_from_milliseconds_clock() { |
284 |
| - let clock = StdClock::new(); |
285 |
| - |
286 |
| - sleep(std::time::Duration::from_millis(400)); |
287 |
| - |
288 |
| - let start = clock.try_now().unwrap(); |
289 |
| - // wait 1.5 seconds |
290 |
| - sleep(std::time::Duration::from_millis(1_600)); |
291 |
| - let end = clock.try_now().unwrap(); |
292 |
| - |
293 |
| - let elapsed = Milliseconds::<u64>::try_from(end - start).unwrap(); |
294 |
| - |
295 |
| - let lower_bound = Milliseconds::<u64>::try_from(1_599_u32.milliseconds()).unwrap(); |
296 |
| - assert!(elapsed > lower_bound); |
297 |
| - |
298 |
| - let upper_bound = Milliseconds::<u64>::try_from(2_000_u32.milliseconds()).unwrap(); |
299 |
| - assert!(elapsed < upper_bound); |
300 |
| - } |
301 |
| - } |
302 |
| -} |
| 133 | +mod std_clock; |
0 commit comments