From e4e5c6e55b0669138f0dc995232431b82ed30f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Tue, 10 Nov 2020 13:30:01 +0100 Subject: [PATCH] massivly switch clock algorithm. - before we had a Clock that tried to average multiple ticks and predict the next sleep. This system is massivly bugged. a) We know exactly how long the busy time took, so we dont need to predict anything in the first place b) Preduction was totally unrealistic after a single lag spike c) When a very slow tick happens, we dont benefit from 10 fast ticks. - Instead we just try to keep the tick time exact what we expect. If we can't manage a constant tick time because we are to slow, the systems have to "catch" this via the `dt` anyway. --- CHANGELOG.md | 1 + Cargo.lock | 24 +--- chat-cli/src/main.rs | 10 +- common/Cargo.toml | 2 +- common/src/clock.rs | 177 ++++++++++++++++++++----- server-cli/src/main.rs | 17 ++- voxygen/src/audio/channel.rs | 2 +- voxygen/src/audio/fader.rs | 52 ++++---- voxygen/src/audio/mod.rs | 9 +- voxygen/src/hud/mod.rs | 2 +- voxygen/src/lib.rs | 2 +- voxygen/src/main.rs | 2 +- voxygen/src/menu/char_selection/mod.rs | 2 +- voxygen/src/menu/main/mod.rs | 2 +- voxygen/src/run.rs | 5 +- voxygen/src/session.rs | 14 +- voxygen/src/singleplayer.rs | 6 +- 17 files changed, 210 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b321e64f8..15797bbd8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Switched to a Whittaker map for better tree spawning patterns - Switched to procedural snow cover on trees - Significantly improved terrain generation performance +- Significantly stabilized the game clock, to produce more "constant" TPS ### Removed diff --git a/Cargo.lock b/Cargo.lock index 068d50c164..ecddda7a45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4593,16 +4593,7 @@ checksum = "6f70411ba0d11c4aee042b1c73e68086322a293f8038c53ac95d36319c91ec19" dependencies = [ "tracing-core", "tracing-subscriber", - "tracy-client 0.9.0", -] - -[[package]] -name = "tracy-client" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650c280019cd1a841752d50f6a834216a2c8a810aeb18bdd48054be23cacd8a6" -dependencies = [ - "tracy-client-sys 0.9.0", + "tracy-client", ] [[package]] @@ -4611,16 +4602,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb214203bffe8cefe9bc0dfc12140f85e8d59b42e1f52f696071fdf8595e066a" dependencies = [ - "tracy-client-sys 0.10.0", -] - -[[package]] -name = "tracy-client-sys" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07e4636dad390858d247cf2633eaea90e67d7e21151cd476bbfd070d321279f" -dependencies = [ - "cc", + "tracy-client-sys", ] [[package]] @@ -4907,7 +4889,7 @@ dependencies = [ "specs-idvs", "sum_type", "tracing", - "tracy-client 0.8.0", + "tracy-client", "vek 0.12.0", ] diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index 4306d18d65..d537c42e75 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -26,7 +26,7 @@ fn main() { info!("Starting chat-cli..."); // Set up an fps clock. - let mut clock = Clock::start(); + let mut clock = Clock::new(Duration::from_millis(1000 / TPS)); println!("Enter your username"); let username = read_input(); @@ -71,11 +71,7 @@ fn main() { client.send_chat(msg) } - let events = match client.tick( - comp::ControllerInputs::default(), - clock.get_last_delta(), - |_| {}, - ) { + let events = match client.tick(comp::ControllerInputs::default(), clock.dt(), |_| {}) { Ok(events) => events, Err(err) => { error!("Error: {:?}", err); @@ -104,6 +100,6 @@ fn main() { client.cleanup(); // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); + clock.tick(); } } diff --git a/common/Cargo.toml b/common/Cargo.toml index e2767bcb4a..ee17816cd4 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -42,7 +42,7 @@ num-traits = "0.2" num-derive = "0.3" # Tracy -tracy-client = { version = "0.8.0", optional = true } +tracy-client = { version = "0.9.0", optional = true } [dev-dependencies] criterion = "0.3" diff --git a/common/src/clock.rs b/common/src/clock.rs index 5fc354622f..39e0acefec 100644 --- a/common/src/clock.rs +++ b/common/src/clock.rs @@ -1,63 +1,170 @@ use crate::span; use std::{ + collections::VecDeque, thread, time::{Duration, Instant}, }; -const CLOCK_SMOOTHING: f64 = 0.9; - +/// This Clock tries to make this tick a constant time by sleeping the rest of +/// the tick +/// - if we actually took less time than we planned: sleep and return planned +/// time +/// - if we ran behind: don't sleep and return actual time +/// We DON'T do any fancy averaging of the deltas for tick for 2 reasons: +/// - all Systems have to work based on `dt` and we cannot assume that this is +/// const through all ticks +/// - when we have a slow tick, a lag, it doesn't help that we have 10 fast +/// ticks directly afterwards +/// We return a smoothed version for display only! pub struct Clock { + /// This is the dt that the Clock tries to archive with each call of tick. + target_dt: Duration, + /// last time `tick` was called last_sys_time: Instant, - last_delta: Option, - running_average_delta: f64, - compensation: f64, + /// will be calculated in `tick` returns the dt used by the next iteration + /// of the main loop + last_dt: Duration, + /// summed up `last_dt` + total_tick_time: Duration, + // Stats only + // stored as millis in u16 to save space. if it's more than u16::MAX (16s) we have other + // problems + last_dts_millis: VecDeque, + last_dts_millis_sorted: Vec, + stats: ClockStats, } +pub struct ClockStats { + /// busy_dt is the part of the last tick that we didn't sleep. + /// e.g. the total tick is 33ms, including 25ms sleeping. then this returns + /// 8ms + pub last_busy_dt: Duration, + /// avg over the last NUMBER_OF_OLD_DELTAS_KEPT ticks + pub average_tps: f64, + /// = 50% percentile + pub median_tps: f64, + /// lowest 10% of the frames + pub percentile_90_tps: f64, + /// lowest 5% of the frames + pub percentile_95_tps: f64, + /// lowest 1% of the frames + pub percentile_99_tps: f64, +} + +const NUMBER_OF_OLD_DELTAS_KEPT: usize = 100; + impl Clock { - pub fn start() -> Self { + pub fn new(target_dt: Duration) -> Self { Self { + target_dt, last_sys_time: Instant::now(), - last_delta: None, - running_average_delta: 0.0, - compensation: 1.0, + last_dt: target_dt, + total_tick_time: Duration::default(), + last_dts_millis: VecDeque::with_capacity(NUMBER_OF_OLD_DELTAS_KEPT), + last_dts_millis_sorted: Vec::with_capacity(NUMBER_OF_OLD_DELTAS_KEPT), + stats: ClockStats::new(&Vec::new(), target_dt), } } - pub fn get_tps(&self) -> f64 { 1.0 / self.running_average_delta } + pub fn set_target_dt(&mut self, target_dt: Duration) { self.target_dt = target_dt; } - pub fn get_last_delta(&self) -> Duration { - self.last_delta.unwrap_or_else(|| Duration::new(0, 0)) - } + pub fn stats(&self) -> &ClockStats { &self.stats } - pub fn get_avg_delta(&self) -> Duration { Duration::from_secs_f64(self.running_average_delta) } + pub fn dt(&self) -> Duration { self.last_dt } - pub fn tick(&mut self, tgt: Duration) { + /// Do not modify without asking @xMAC94x first! + pub fn tick(&mut self) { span!(_guard, "tick", "Clock::tick"); - let delta = Instant::now().duration_since(self.last_sys_time); + let current_sys_time = Instant::now(); + let busy_delta = current_sys_time.duration_since(self.last_sys_time); + // Maintain TPS + self.last_dts_millis_sorted = self.last_dts_millis.iter().copied().collect(); + self.last_dts_millis_sorted.sort_unstable(); + self.stats = ClockStats::new(&self.last_dts_millis_sorted, busy_delta); // Attempt to sleep to fill the gap. - if let Some(sleep_dur) = tgt.checked_sub(delta) { - if self.running_average_delta != 0.0 { - self.compensation = - (self.compensation + (tgt.as_secs_f64() / self.running_average_delta) - 1.0) - .max(0.0) - } - - let sleep_secs = sleep_dur.as_secs_f64() * self.compensation; - if sleep_secs > 0.0 { - thread::sleep(Duration::from_secs_f64(sleep_secs)); - } + if let Some(sleep_dur) = self.target_dt.checked_sub(busy_delta) { + Clock::sleep(current_sys_time, sleep_dur) } - let delta = Instant::now().duration_since(self.last_sys_time); + let after_sleep_sys_time = Instant::now(); + self.last_dt = after_sleep_sys_time.duration_since(self.last_sys_time); + if self.last_dts_millis.len() >= NUMBER_OF_OLD_DELTAS_KEPT { + self.last_dts_millis.pop_front(); + } + self.last_dts_millis + .push_back((self.last_dt.as_millis() as u16).min(std::u16::MAX)); + self.total_tick_time += self.last_dt; + self.last_sys_time = after_sleep_sys_time; + } - self.last_sys_time = Instant::now(); - self.last_delta = Some(delta); - self.running_average_delta = if self.running_average_delta == 0.0 { - delta.as_secs_f64() + /// try to do a high precision sleep. + /// When this `fn` returns it SHOULD be before+dur as accurate is possible. + /// Returns the after sleep Instant. + fn sleep(before: Instant, dur: Duration) { + const BUSY_WAITING_TRIGGER_DUR: Duration = Duration::from_millis(2); + #[cfg(not(windows))] + const BUSY_WAITING_TIME: Duration = Duration::from_nanos(125_000); + #[cfg(windows)] + const BUSY_WAITING_TIME: Duration = Duration::from_nanos(500_000); + + // If we have more than BUSY_TRIGGER_DUR on our sleep bucket, do sleep for + // (1-BUSY_WAITING_TIME) time and do some busy waiting. If we are on high load, + // don't try such fancy precision increasing + if dur > BUSY_WAITING_TRIGGER_DUR { + let first_sleep_dur = dur - BUSY_WAITING_TIME; + thread::sleep(first_sleep_dur); + let target_time = before + dur; + span!(_guard, "tick", "Clock::busy_wait"); + while target_time > Instant::now() { + //busy waiting + std::sync::atomic::spin_loop_hint(); + } } else { - CLOCK_SMOOTHING * self.running_average_delta - + (1.0 - CLOCK_SMOOTHING) * delta.as_secs_f64() - }; + thread::sleep(dur); + } + } +} + +impl ClockStats { + fn new(sorted: &[u16], last_busy_dt: Duration) -> Self { + const NANOS_PER_SEC: f64 = Duration::from_secs(1).as_nanos() as f64; + const NANOS_PER_MILLI: f64 = Duration::from_millis(1).as_nanos() as f64; + + let len = sorted.len(); + let average_millis = sorted.iter().fold(0, |a, x| a + *x as u32) / len.max(1) as u32; + + let average_tps = NANOS_PER_SEC / (average_millis as f64 * NANOS_PER_MILLI); + let (median_tps, percentile_90_tps, percentile_95_tps, percentile_99_tps) = if len + >= NUMBER_OF_OLD_DELTAS_KEPT + { + let median_millis = sorted[len / 2]; + let percentile_90_millis = sorted[(NUMBER_OF_OLD_DELTAS_KEPT as f32 * 0.1) as usize]; + let percentile_95_millis = sorted[(NUMBER_OF_OLD_DELTAS_KEPT as f32 * 0.05) as usize]; + let percentile_99_millis = sorted[(NUMBER_OF_OLD_DELTAS_KEPT as f32 * 0.01) as usize]; + + let median_tps = NANOS_PER_SEC / (median_millis as f64 * NANOS_PER_MILLI); + let percentile_90_tps = NANOS_PER_SEC / (percentile_90_millis as f64 * NANOS_PER_MILLI); + let percentile_95_tps = NANOS_PER_SEC / (percentile_95_millis as f64 * NANOS_PER_MILLI); + let percentile_99_tps = NANOS_PER_SEC / (percentile_99_millis as f64 * NANOS_PER_MILLI); + ( + median_tps, + percentile_90_tps, + percentile_95_tps, + percentile_99_tps, + ) + } else { + let avg_tps = NANOS_PER_SEC / last_busy_dt.as_nanos() as f64; + (avg_tps, avg_tps, avg_tps, avg_tps) + }; + + Self { + last_busy_dt, + average_tps, + median_tps, + percentile_90_tps, + percentile_95_tps, + percentile_99_tps, + } } } diff --git a/server-cli/src/main.rs b/server-cli/src/main.rs index 1707656261..e5a52a8648 100644 --- a/server-cli/src/main.rs +++ b/server-cli/src/main.rs @@ -140,19 +140,22 @@ fn main() -> io::Result<()> { let mut shutdown_coordinator = ShutdownCoordinator::new(Arc::clone(&sigusr1_signal)); // Set up an fps clock - let mut clock = Clock::start(); + let mut clock = Clock::new(Duration::from_millis(1000 / TPS)); // Wait for a tick so we don't start with a zero dt - // TODO: consider integrating this into Clock::start? - clock.tick(Duration::from_millis(1000 / TPS)); loop { + #[cfg(feature = "tracy")] + common::util::tracy_client::finish_continuous_frame!(); + #[cfg(feature = "tracy")] + let frame = common::util::tracy_client::start_noncontinuous_frame!("work"); + // Terminate the server if instructed to do so by the shutdown coordinator if shutdown_coordinator.check(&mut server, &settings) { break; } let events = server - .tick(Input::default(), clock.get_last_delta()) + .tick(Input::default(), clock.dt()) .expect("Failed to tick server"); for event in events { @@ -165,8 +168,6 @@ fn main() -> io::Result<()> { // Clean up the server after a tick. server.cleanup(); - #[cfg(feature = "tracy")] - common::util::tracy_client::finish_continuous_frame!(); if let Some(tui) = tui.as_ref() { match tui.msg_r.try_recv() { @@ -194,8 +195,10 @@ fn main() -> io::Result<()> { } } + #[cfg(feature = "tracy")] + drop(frame); // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); + clock.tick(); } Ok(()) diff --git a/voxygen/src/audio/channel.rs b/voxygen/src/audio/channel.rs index 5f19e42532..3bbdd70672 100644 --- a/voxygen/src/audio/channel.rs +++ b/voxygen/src/audio/channel.rs @@ -114,7 +114,7 @@ impl MusicChannel { /// Maintain the fader attached to this channel. If the channel is not /// fading, no action is taken. - pub fn maintain(&mut self, dt: f32) { + pub fn maintain(&mut self, dt: std::time::Duration) { if self.state == ChannelState::Fading { self.fader.update(dt); self.sink.set_volume(self.fader.get_volume()); diff --git a/voxygen/src/audio/fader.rs b/voxygen/src/audio/fader.rs index b2c6c8ca16..18211595e2 100644 --- a/voxygen/src/audio/fader.rs +++ b/voxygen/src/audio/fader.rs @@ -1,11 +1,13 @@ //! Controls volume transitions for Audio Channels +use std::time::Duration; + /// Faders are attached to channels with initial and target volumes as well as a /// transition time. #[derive(PartialEq, Clone, Copy)] pub struct Fader { - length: f32, - running_time: f32, + length: Duration, + running_time: Duration, volume_from: f32, volume_to: f32, is_running: bool, @@ -21,19 +23,19 @@ pub enum FadeDirection { fn lerp(t: f32, a: f32, b: f32) -> f32 { (1.0 - t) * a + t * b } impl Fader { - pub fn fade(length: f32, volume_from: f32, volume_to: f32) -> Self { + pub fn fade(length: Duration, volume_from: f32, volume_to: f32) -> Self { Self { length, - running_time: 0.0, + running_time: Duration::default(), volume_from, volume_to, is_running: true, } } - pub fn fade_in(time: f32, volume_to: f32) -> Self { Self::fade(time, 0.0, volume_to) } + pub fn fade_in(time: Duration, volume_to: f32) -> Self { Self::fade(time, 0.0, volume_to) } - pub fn fade_out(time: f32, volume_from: f32) -> Self { Self::fade(time, volume_from, 0.0) } + pub fn fade_out(time: Duration, volume_from: f32) -> Self { Self::fade(time, volume_from, 0.0) } /// Used to update the `target` volume of the fader when the max or min /// volume changes. This occurs when the player changes their in-game @@ -62,7 +64,7 @@ impl Fader { } /// Called each tick to update the volume and state - pub fn update(&mut self, dt: f32) { + pub fn update(&mut self, dt: std::time::Duration) { if self.is_running { self.running_time += dt; if self.running_time >= self.length { @@ -74,7 +76,7 @@ impl Fader { pub fn get_volume(&self) -> f32 { lerp( - self.running_time / self.length, + self.running_time.as_nanos() as f32 / self.length.as_nanos() as f32, self.volume_from, self.volume_to, ) @@ -87,8 +89,8 @@ impl Fader { impl Default for Fader { fn default() -> Self { Self { - length: 0.0, - running_time: 0.0, + length: Duration::default(), + running_time: Duration::default(), volume_from: 0.0, volume_to: 1.0, is_running: false, @@ -102,14 +104,14 @@ mod tests { #[test] fn fade_direction_in() { - let fader = Fader::fade_in(10.0, 0.0); + let fader = Fader::fade_in(Duration::from_secs(10), 0.0); assert_eq!(fader.direction(), FadeDirection::In); } #[test] fn fade_direction_out() { - let fader = Fader::fade_out(10.0, 1.0); + let fader = Fader::fade_out(Duration::from_secs(10), 1.0); assert_eq!(fader.direction(), FadeDirection::Out); } @@ -117,10 +119,10 @@ mod tests { #[test] #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn fade_out_completes() { - let mut fader = Fader::fade_out(10.0, 1.0); + let mut fader = Fader::fade_out(Duration::from_secs(10), 1.0); // Run for the full duration - fader.update(10.0); + fader.update(Duration::from_secs(10)); assert_eq!(fader.get_volume(), 0.0); assert!(fader.is_finished()); @@ -128,32 +130,32 @@ mod tests { #[test] fn update_target_volume_fading_out_when_currently_above() { - let mut fader = Fader::fade_out(20.0, 1.0); + let mut fader = Fader::fade_out(Duration::from_secs(20), 1.0); // After 0.1s, the fader should still be close to 1.0 - fader.update(0.1); + fader.update(Duration::from_millis(100)); // Reduce volume to 0.4. We are currently above that. fader.update_target_volume(0.4); // The volume should immediately reduce to < 0.4 on the next update - fader.update(0.1); + fader.update(Duration::from_millis(100)); assert!(fader.get_volume() < 0.4) } #[test] fn update_target_volume_fading_out_when_currently_below() { - let mut fader = Fader::fade_out(10.0, 0.8); + let mut fader = Fader::fade_out(Duration::from_secs(10), 0.8); // After 9s, the fader should be close to 0 - fader.update(9.0); + fader.update(Duration::from_secs(9)); // Notify of a volume increase to 1.0. We are already far below that. fader.update_target_volume(1.0); // The fader should be unaffected by the new value, and continue dropping - fader.update(0.1); + fader.update(Duration::from_millis(100)); assert!(fader.get_volume() < 0.2); } @@ -161,16 +163,16 @@ mod tests { #[test] #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn update_target_volume_fading_in_when_currently_above() { - let mut fader = Fader::fade_in(10.0, 1.0); + let mut fader = Fader::fade_in(Duration::from_secs(10), 1.0); // After 9s, the fader should be close to 1.0 - fader.update(9.0); + fader.update(Duration::from_secs(9)); // Reduce volume to 0.4. We are currently above that. fader.update_target_volume(0.4); // Run out the fader. It's volume should be 0.4 - fader.update(1.0); + fader.update(Duration::from_secs(1)); assert_eq!(fader.get_volume(), 0.4); } @@ -178,10 +180,10 @@ mod tests { #[test] #[allow(clippy::float_cmp)] // TODO: Pending review in #587 fn update_target_volume_fading_in_when_currently_below() { - let mut fader = Fader::fade_in(20.0, 1.0); + let mut fader = Fader::fade_in(Duration::from_secs(20), 1.0); // After 0.1s, the fader should still be close to 0.0 - fader.update(0.1); + fader.update(Duration::from_millis(100)); // Reduce volume to 0.4. The volume_to should be reduced accordingly. fader.update_target_volume(0.4); diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 5dab15fd4f..6e38549b4d 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -9,6 +9,7 @@ pub mod soundcache; use channel::{MusicChannel, MusicChannelTag, SfxChannel}; use fader::Fader; use soundcache::SoundCache; +use std::time::Duration; use tracing::warn; use common::assets; @@ -84,7 +85,7 @@ impl AudioFrontend { } /// Drop any unused music channels, and update their faders - pub fn maintain(&mut self, dt: f32) { + pub fn maintain(&mut self, dt: Duration) { self.music_channels.retain(|c| !c.is_done()); for channel in self.music_channels.iter_mut() { @@ -124,11 +125,13 @@ impl AudioFrontend { if existing_channel.get_tag() != next_channel_tag { // Fade the existing channel out. It will be removed when the fade completes. - existing_channel.set_fader(Fader::fade_out(2.0, self.music_volume)); + existing_channel + .set_fader(Fader::fade_out(Duration::from_secs(2), self.music_volume)); let mut next_music_channel = MusicChannel::new(&audio_device); - next_music_channel.set_fader(Fader::fade_in(12.0, self.music_volume)); + next_music_channel + .set_fader(Fader::fade_in(Duration::from_secs(12), self.music_volume)); self.music_channels.push(next_music_channel); } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 38f26f98f0..845a15c2ea 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -746,7 +746,7 @@ impl Hud { // pulse time for pulsating elements self.pulse = self.pulse + dt.as_secs_f32(); // FPS - let fps = global_state.clock.get_tps(); + let fps = global_state.clock.stats().average_tps; let version = common::util::DISPLAY_VERSION_LONG.clone(); if self.show.ingame { diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index bb9acf6d99..12a585f647 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -63,7 +63,7 @@ impl GlobalState { self.window.needs_refresh_resize(); } - pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); } + pub fn maintain(&mut self, dt: std::time::Duration) { self.audio.maintain(dt); } #[cfg(feature = "singleplayer")] pub fn paused(&self) -> bool { diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 80794f9134..ae4be0453c 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -185,7 +185,7 @@ fn main() { profile, window, settings, - clock: Clock::start(), + clock: Clock::new(std::time::Duration::from_millis(30)), info_message: None, #[cfg(feature = "singleplayer")] singleplayer: None, diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index cd3adc0229..a50dc0c604 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -155,7 +155,7 @@ impl PlayState for CharSelectionState { match self.client.borrow_mut().tick( comp::ControllerInputs::default(), - global_state.clock.get_last_delta(), + global_state.clock.dt(), |_| {}, ) { Ok(events) => { diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index a9a0d88718..7f4bb43bd0 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -211,7 +211,7 @@ impl PlayState for MainMenuState { // Maintain the UI. for event in self .main_menu_ui - .maintain(global_state, global_state.clock.get_last_delta()) + .maintain(global_state, global_state.clock.dt()) { match event { MainMenuEvent::LoginAttempt { diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index ee9004fa1d..a8eac50d09 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -168,12 +168,13 @@ fn handle_main_events_cleared( if !exit { // Wait for the next tick. span!(_guard, "Main thread sleep"); - global_state.clock.tick(Duration::from_millis( + global_state.clock.set_target_dt(Duration::from_millis( 1000 / global_state.settings.graphics.max_fps as u64, )); + global_state.clock.tick(); span!(_guard, "Maintain global state"); // Maintain global state. - global_state.maintain(global_state.clock.get_last_delta().as_secs_f32()); + global_state.maintain(global_state.clock.dt()); } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index cd46775585..c94d93071c 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -631,7 +631,7 @@ impl PlayState for SessionState { let right = self.scene.camera().right(); let dir = right * axis_right + forward * axis_up; - let dt = global_state.clock.get_last_delta().as_secs_f32(); + let dt = global_state.clock.dt().as_secs_f32(); if self.freefly_vel.magnitude_squared() > 0.01 { let new_vel = self.freefly_vel - self.freefly_vel.normalized() * (FREEFLY_DAMPING * dt); @@ -668,11 +668,7 @@ impl PlayState for SessionState { // Runs if either in a multiplayer server or the singleplayer server is unpaused if !global_state.paused() { // Perform an in-game tick. - match self.tick( - global_state.clock.get_avg_delta(), - global_state, - &mut outcomes, - ) { + match self.tick(global_state.clock.dt(), global_state, &mut outcomes) { Ok(TickAction::Continue) => {}, // Do nothing Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu Err(err) => { @@ -697,8 +693,8 @@ impl PlayState for SessionState { .gameplay .toggle_debug .then(|| DebugInfo { - tps: global_state.clock.get_tps(), - frame_time: global_state.clock.get_avg_delta(), + tps: global_state.clock.stats().average_tps, + frame_time: global_state.clock.stats().last_busy_dt, ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), coordinates: self .client @@ -741,7 +737,7 @@ impl PlayState for SessionState { global_state, &debug_info, &self.scene.camera(), - global_state.clock.get_last_delta(), + global_state.clock.dt(), HudInfo { is_aiming, is_first_person: matches!( diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index b9bd860142..0bc68f50d9 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -152,7 +152,7 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { info!("Starting server-cli..."); // Set up an fps clock - let mut clock = Clock::start(); + let mut clock = Clock::new(Duration::from_millis(1000 / TPS)); loop { // Check any event such as stopping and pausing @@ -167,7 +167,7 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { } // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); + clock.tick(); // Skip updating the server if it's paused if paused.load(Ordering::SeqCst) && server.number_of_players() < 2 { @@ -177,7 +177,7 @@ fn run_server(mut server: Server, rec: Receiver, paused: Arc) { } let events = server - .tick(Input::default(), clock.get_last_delta()) + .tick(Input::default(), clock.dt()) .expect("Failed to tick server!"); for event in events {