Mercurial > core / rust/lib/util/src/time.rs
changeset 698: |
96958d3eb5b0 |
parent: |
4f49127c9048
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 //! Utilities for working with time 2 pub use chrono::{DateTime, TimeZone, Utc}; 3 use std::time::{Duration, Instant}; 5 /// Returns the number of non-leap milliseconds since January 1, 1970 0:00:00 6 /// UTC (UNIX timestamp). 7 pub fn unix_epoch_ms() -> u64 { 8 let now: DateTime<Utc> = Utc::now(); 10 now.timestamp_millis() as u64 13 /// Frame timing values. 14 #[derive(Clone, Copy, Debug, PartialEq)] 16 /// Time elapsed since the last frame in seconds. 18 /// Time elapsed since the last frame. 20 /// Time elapsed since the last frame in seconds ignoring the time speed 22 delta_real_seconds: f32, 23 /// Time elapsed since the last frame ignoring the time speed multiplier. 24 delta_real_time: Duration, 25 /// Rate at which `State::fixed_update` is called in seconds. 27 /// Rate at which `State::fixed_update` is called. 29 /// The total number of frames that have been played in this session. 31 ///Time elapsed since game start, ignoring the speed multipler. 32 absolute_real_time: Duration, 33 ///Time elapsed since game start, taking the speed multiplier into account. 34 absolute_time: Duration, 35 ///Time multiplier. Affects returned delta_seconds, delta_time and 38 /// Fixed timestep accumulator. 39 fixed_time_accumulator: f32, 40 /// Fixed update interpolation alpha 41 interpolation_alpha: f32, 45 /// Gets the time difference between frames in seconds. 47 /// This function should not be used during `fixed_update`s, use 48 /// `fixed_seconds` instead. 49 pub fn delta_seconds(&self) -> f32 { 53 /// Gets the time difference between frames. 55 /// This function should not be used during `fixed_update`s, use 56 /// `fixed_time` instead. 57 pub fn delta_time(&self) -> Duration { 61 /// Gets the time difference between frames in seconds ignoring the time 64 /// This function should not be used during `fixed_update`s. 65 pub fn delta_real_seconds(&self) -> f32 { 66 self.delta_real_seconds 69 /// Gets the time difference between frames ignoring the time speed 71 pub fn delta_real_time(&self) -> Duration { 75 /// Gets the fixed time step in seconds. 76 pub fn fixed_seconds(&self) -> f32 { 80 /// Gets the fixed time step. 81 pub fn fixed_time(&self) -> Duration { 85 /// Gets the current frame number. This increments by 1 every frame. There 87 pub fn frame_number(&self) -> u64 { 91 /// Gets the time since the start of the game, taking into account the speed 93 pub fn absolute_time(&self) -> Duration { 97 /// Gets the time since the start of the game as seconds, taking into 98 /// account the speed multiplier. 99 pub fn absolute_time_seconds(&self) -> f64 { 100 duration_to_secs_f64(self.absolute_time) 103 /// Gets the time since the start of the game, ignoring the speed 105 pub fn absolute_real_time(&self) -> Duration { 106 self.absolute_real_time 109 /// Gets the time since the start of the game as seconds, ignoring the speed 111 pub fn absolute_real_time_seconds(&self) -> f64 { 112 duration_to_secs_f64(self.absolute_real_time) 115 /// Gets the current time speed multiplier. 116 pub fn time_scale(&self) -> f32 { 120 /// Gets the current interpolation alpha factor. 121 pub fn interpolation_alpha(&self) -> f32 { 122 self.interpolation_alpha 125 /// Sets both `delta_seconds` and `delta_time` based on the seconds given. 127 /// This should only be called by the engine. Bad things might happen if 128 /// you call this in your game. 129 pub fn set_delta_seconds(&mut self, secs: f32) { 130 self.delta_seconds = secs * self.time_scale; 131 self.delta_time = secs_to_duration(secs * self.time_scale); 132 self.delta_real_seconds = secs; 133 self.delta_real_time = secs_to_duration(secs); 135 self.absolute_time += self.delta_time; 136 self.absolute_real_time += self.delta_real_time; 139 /// Sets both `delta_time` and `delta_seconds` based on the duration given. 141 /// This should only be called by the engine. Bad things might happen if 142 /// you call this in your game. 143 pub fn set_delta_time(&mut self, time: Duration) { 144 self.delta_seconds = duration_to_secs(time) * self.time_scale; 146 secs_to_duration(duration_to_secs(time) * self.time_scale); 147 self.delta_real_seconds = duration_to_secs(time); 148 self.delta_real_time = time; 150 self.absolute_time += self.delta_time; 151 self.absolute_real_time += self.delta_real_time; 154 /// Sets both `fixed_seconds` and `fixed_time` based on the seconds given. 155 pub fn set_fixed_seconds(&mut self, secs: f32) { 156 self.fixed_seconds = secs; 157 self.fixed_time = secs_to_duration(secs); 160 /// Sets both `fixed_time` and `fixed_seconds` based on the duration given. 161 pub fn set_fixed_time(&mut self, time: Duration) { 162 self.fixed_seconds = duration_to_secs(time); 163 self.fixed_time = time; 166 /// Increments the current frame number by 1. 168 /// This should only be called by the engine. Bad things might happen if 169 /// you call this in your game. 170 pub fn increment_frame_number(&mut self) { 171 self.frame_number += 1; 174 /// Sets the time multiplier that affects how time values are computed, 175 /// effectively slowing or speeding up your game. 178 /// This will panic if multiplier is NaN, Infinity, or less than 0. 179 pub fn set_time_scale(&mut self, multiplier: f32) { 180 use std::f32::INFINITY; 181 assert!(multiplier >= 0.0); 182 assert!(multiplier != INFINITY); 183 self.time_scale = multiplier; 186 /// Restarts the internal fixed update accumulator to the desired fixed 187 /// update delta time. 189 /// This should only be called by the engine. Bad things might happen if 190 /// you call this in your game. 191 pub fn start_fixed_update(&mut self) { 192 self.fixed_time_accumulator += self.delta_real_seconds; 195 /// Checks to see if we should perform another fixed update iteration, and 196 /// if so, returns true and reduces the accumulator. 198 /// This should only be called by the engine. Bad things might happen if 199 /// you call this in your game. 200 pub fn step_fixed_update(&mut self) -> bool { 201 if self.fixed_time_accumulator >= self.fixed_seconds { 202 self.fixed_time_accumulator -= self.fixed_seconds; 209 /// Updates the interpolation alpha factor given the current fixed update 210 /// rate and accumulator. 212 /// This should only be called by the engine. Bad things might happen if 213 /// you call this in your game. 214 pub fn finish_fixed_update(&mut self) { 215 self.interpolation_alpha = self.fixed_time_accumulator / self.fixed_seconds; 219 impl Default for Time { 220 fn default() -> Time { 223 delta_time: Duration::from_secs(0), 224 delta_real_seconds: 0.0, 225 delta_real_time: Duration::from_secs(0), 226 fixed_seconds: duration_to_secs(Duration::new(0, 16_666_666)), 227 fixed_time: Duration::new(0, 16_666_666), 228 fixed_time_accumulator: 0.0, 230 interpolation_alpha: 0.0, 231 absolute_real_time: Duration::default(), 232 absolute_time: Duration::default(), 238 /// A stopwatch which accurately measures elapsed time. 239 #[derive(Clone, Debug, Eq, PartialEq, Default)] 241 /// Initial state with an elapsed time value of 0 seconds. 244 /// Stopwatch has started counting the elapsed time since this `Instant` 245 /// and accumuluated time from previous start/stop cycles `Duration`. 246 Started(Duration, Instant), 247 /// Stopwatch has been stopped and reports the elapsed time `Duration`. 252 /// Creates a new stopwatch. 253 pub fn new() -> Stopwatch { 257 /// Retrieves the elapsed time. 258 pub fn elapsed(&self) -> Duration { 260 Stopwatch::Waiting => Duration::new(0, 0), 261 Stopwatch::Started(dur, start) => dur + start.elapsed(), 262 Stopwatch::Ended(dur) => dur, 266 /// Stops, resets, and starts the stopwatch again. 267 pub fn restart(&mut self) { 268 *self = Stopwatch::Started(Duration::new(0, 0), Instant::now()); 271 /// Starts, or resumes, measuring elapsed time. If the stopwatch has been 272 /// started and stopped before, the new results are compounded onto the 273 /// existing elapsed time value. 275 /// Note: Starting an already running stopwatch will do nothing. 276 pub fn start(&mut self) { 278 Stopwatch::Waiting => self.restart(), 279 Stopwatch::Ended(dur) => { 280 *self = Stopwatch::Started(dur, Instant::now()); 286 /// Stops measuring elapsed time. 288 /// Note: Stopping a stopwatch that isn't running will do nothing. 289 pub fn stop(&mut self) { 290 if let Stopwatch::Started(dur, start) = *self { 291 *self = Stopwatch::Ended(dur + start.elapsed()); 295 /// Clears the current elapsed time value. 296 pub fn reset(&mut self) { 297 *self = Stopwatch::Waiting; 301 /// Converts a Duration to the time in seconds. 302 pub fn duration_to_secs(duration: Duration) -> f32 { 303 duration.as_secs() as f32 + (duration.subsec_nanos() as f32 / 1.0e9) 306 /// Converts a Duration to the time in seconds in an f64. 307 pub fn duration_to_secs_f64(duration: Duration) -> f64 { 308 duration.as_secs() as f64 + (f64::from(duration.subsec_nanos()) / 1.0e9) 311 /// Converts a time in seconds to a duration 312 pub fn secs_to_duration(secs: f32) -> Duration { 313 Duration::new(secs as u64, ((secs % 1.0) * 1.0e9) as u32) 316 /// Converts a Duration to nanoseconds 317 pub fn duration_to_nanos(duration: Duration) -> u64 { 318 (duration.as_secs() * 1_000_000_000) + u64::from(duration.subsec_nanos()) 321 /// Converts nanoseconds to a Duration 322 pub fn nanos_to_duration(nanos: u64) -> Duration { 323 Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32) 329 use std::{thread, time::Duration}; 331 use super::Stopwatch; 333 // Timing varies more on macOS CI 334 fn get_uncertainty() -> u32 { 335 let is_macos = !std::env::var("MACOS").unwrap_or_default().is_empty(); 336 let is_ci = std::env::var("CI").is_ok(); 337 if is_macos && is_ci { 346 const DURATION: u64 = 1; // in seconds. 347 let mut watch = Stopwatch::new(); 348 let uncertainty = get_uncertainty(); 351 thread::sleep(Duration::from_secs(DURATION)); 354 // check that elapsed time was DURATION sec +/- UNCERTAINTY% 355 let elapsed = watch.elapsed(); 356 let duration = Duration::new(DURATION, 0); 357 let lower = duration / 100 * (100 - uncertainty); 358 let upper = duration / 100 * (100 + uncertainty); 360 elapsed < upper && elapsed > lower, 361 "expected {} +- {}% seconds, got {:?}", 370 const DURATION: u64 = 2; // in seconds. 371 let mut watch = Stopwatch::new(); 374 thread::sleep(Duration::from_secs(DURATION)); 378 assert_eq!(0, watch.elapsed().subsec_nanos()); 383 const DURATION0: u64 = 2; // in seconds. 384 const DURATION: u64 = 1; // in seconds. 385 let uncertainty = get_uncertainty(); // in percents. 386 let mut watch = Stopwatch::new(); 389 thread::sleep(Duration::from_secs(DURATION0)); 393 thread::sleep(Duration::from_secs(DURATION)); 396 // check that elapsed time was DURATION sec +/- UNCERTAINTY% 397 let elapsed = watch.elapsed(); 398 let duration = Duration::new(DURATION, 0); 399 let lower = duration / 100 * (100 - uncertainty); 400 let upper = duration / 100 * (100 + uncertainty); 402 elapsed < upper && elapsed > lower, 403 "expected {} +- {}% seconds, got {:?}", 410 // test that multiple start-stop cycles are cumulative 413 const DURATION: u64 = 3; // in seconds. 414 let uncertainty = get_uncertainty(); // in percents. 415 let mut watch = Stopwatch::new(); 417 for _ in 0..DURATION { 419 thread::sleep(Duration::from_secs(1)); 423 // check that elapsed time was DURATION sec +/- UNCERTAINTY% 424 let elapsed = watch.elapsed(); 425 let duration = Duration::new(DURATION, 0); 426 let lower = duration / 100 * (100 - uncertainty); 427 let upper = duration / 100 * (100 + uncertainty); 429 elapsed < upper && elapsed > lower, 430 "expected {} +- {}% seconds, got {:?}", 437 // Test that fixed_update methods accumulate and return correctly 438 // Test confirms that with a fixed update of 120fps, we run fixed update twice 439 // with the timer Runs at 10 times game speed, which shouldn't affect fixed 442 fn fixed_update_120fps() { 445 let mut time = Time::default(); 446 time.set_fixed_seconds(1.0 / 120.0); 447 time.set_time_scale(10.0); 449 let step = 1.0 / 60.0; 450 let mut fixed_count = 0; 452 time.set_delta_seconds(step); 453 time.start_fixed_update(); 455 while time.step_fixed_update() { 459 time.finish_fixed_update(); 462 assert_eq!(fixed_count, 120); 465 // Test that fixed_update methods accumulate and return correctly 466 // Test confirms that with a fixed update every 1 second, it runs every 1 469 fn fixed_update_1sec() { 472 let mut time = Time::default(); 473 time.set_fixed_seconds(1.0); 475 let step = 1.0 / 60.0; 476 let mut fixed_count = 0; 479 time.set_delta_seconds(step); 480 time.start_fixed_update(); 482 while time.step_fixed_update() { 486 time.finish_fixed_update(); 488 assert_eq!(fixed_count, 2);