From b150c4ed47b5e22a70e713473cfcceae4b69d9e3 Mon Sep 17 00:00:00 2001 From: IceSentry Date: Tue, 13 Sep 2022 20:38:11 -0400 Subject: [PATCH 01/12] add time wrapping to Time --- crates/bevy_time/src/time.rs | 64 +++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 1835f841c93a7..21fbb65bfaf86 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -13,6 +13,10 @@ pub struct Time { seconds_since_startup: f64, time_since_startup: Duration, startup: Instant, + duration_since_last_wrapping_period: Duration, + /// The maximum period of time in seconds before seconds_since_last_wrapping_period wraps back to 0.0 + pub max_wrapping_period: Duration, + last_wrapping_period: Instant, } impl Default for Time { @@ -25,6 +29,9 @@ impl Default for Time { seconds_since_startup: 0.0, time_since_startup: Duration::from_secs(0), delta_seconds: 0.0, + duration_since_last_wrapping_period: Duration::from_secs(0), + max_wrapping_period: Duration::from_secs(60 * 60), // 1 hour + last_wrapping_period: Instant::now(), } } } @@ -101,6 +108,11 @@ impl Time { self.time_since_startup = instant - self.startup; self.seconds_since_startup = self.time_since_startup.as_secs_f64(); self.last_update = Some(instant); + + self.duration_since_last_wrapping_period = instant - self.last_wrapping_period; + if self.duration_since_last_wrapping_period >= self.max_wrapping_period { + self.last_wrapping_period = instant; + } } /// The delta between the current tick and last tick as a [`Duration`] @@ -127,6 +139,18 @@ impl Time { self.seconds_since_startup } + /// The time from the last wrap period to the last update in seconds + /// + /// When used in shaders, the time is limited to f32 which can introduce floating point precision issues + /// fairly quickly if the app is left open for a while. This will wrap the value to 0.0 as soon as it + /// reaches the wrapping period. + /// + /// Defaults to wrapping to 0.0 every hour + #[inline] + pub fn seconds_since_last_wrapping_period(&self) -> f32 { + self.duration_since_last_wrapping_period.as_secs_f32() + } + /// The [`Instant`] the app was started #[inline] pub fn startup(&self) -> Instant { @@ -159,6 +183,7 @@ mod tests { // Create a `Time` for testing let mut time = Time { startup: start_instant, + last_wrapping_period: start_instant, ..Default::default() }; @@ -170,6 +195,7 @@ mod tests { assert_eq!(time.seconds_since_startup(), 0.0); assert_eq!(time.time_since_startup(), Duration::from_secs(0)); assert_eq!(time.delta_seconds(), 0.0); + assert_eq!(time.seconds_since_last_wrapping_period(), 0.0); // Update `time` and check results let first_update_instant = Instant::now(); @@ -188,7 +214,11 @@ mod tests { time.time_since_startup(), (first_update_instant - start_instant) ); - assert_eq!(time.delta_seconds, 0.0); + assert_eq!(time.delta_seconds(), 0.0); + assert_float_eq( + time.seconds_since_last_wrapping_period(), + time.seconds_since_startup() as f32, + ); // Update `time` again and check results let second_update_instant = Instant::now(); @@ -210,5 +240,37 @@ mod tests { (second_update_instant - start_instant) ); assert_eq!(time.delta_seconds(), time.delta().as_secs_f32()); + assert_float_eq( + time.seconds_since_last_wrapping_period(), + time.seconds_since_startup() as f32, + ); + } + + #[test] + fn update_wrapping() { + let start_instant = Instant::now(); + + let mut time = Time { + startup: start_instant, + last_wrapping_period: start_instant, + max_wrapping_period: Duration::from_secs(2), + ..Default::default() + }; + + assert_eq!(time.seconds_since_last_wrapping_period(), 0.0); + + time.update_with_instant(start_instant + Duration::from_secs(1)); + assert_float_eq(time.seconds_since_last_wrapping_period(), 1.0); + + time.update_with_instant(start_instant + Duration::from_secs(2)); + assert_float_eq(time.seconds_since_last_wrapping_period(), 2.0); + + // wraps on next update + time.update_with_instant(start_instant + Duration::from_secs(3)); + assert_float_eq(time.seconds_since_last_wrapping_period(), 1.0); + } + + fn assert_float_eq(a: f32, b: f32) { + assert!((a - b) <= f32::EPSILON, "{a} != {b}"); } } From c76218472ce5c7bcfb599c79f43e1fd262ed568d Mon Sep 17 00:00:00 2001 From: IceSentry Date: Tue, 13 Sep 2022 20:52:25 -0400 Subject: [PATCH 02/12] mod seconds since startup --- crates/bevy_time/src/time.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 21fbb65bfaf86..6b27cd86c6172 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -13,10 +13,8 @@ pub struct Time { seconds_since_startup: f64, time_since_startup: Duration, startup: Instant, - duration_since_last_wrapping_period: Duration, - /// The maximum period of time in seconds before seconds_since_last_wrapping_period wraps back to 0.0 + /// The maximum duration before `Self::seconds_since_last_wrapping_period()` wraps back to 0.0 pub max_wrapping_period: Duration, - last_wrapping_period: Instant, } impl Default for Time { @@ -29,9 +27,7 @@ impl Default for Time { seconds_since_startup: 0.0, time_since_startup: Duration::from_secs(0), delta_seconds: 0.0, - duration_since_last_wrapping_period: Duration::from_secs(0), max_wrapping_period: Duration::from_secs(60 * 60), // 1 hour - last_wrapping_period: Instant::now(), } } } @@ -108,11 +104,6 @@ impl Time { self.time_since_startup = instant - self.startup; self.seconds_since_startup = self.time_since_startup.as_secs_f64(); self.last_update = Some(instant); - - self.duration_since_last_wrapping_period = instant - self.last_wrapping_period; - if self.duration_since_last_wrapping_period >= self.max_wrapping_period { - self.last_wrapping_period = instant; - } } /// The delta between the current tick and last tick as a [`Duration`] @@ -142,13 +133,13 @@ impl Time { /// The time from the last wrap period to the last update in seconds /// /// When used in shaders, the time is limited to f32 which can introduce floating point precision issues - /// fairly quickly if the app is left open for a while. This will wrap the value to 0.0 as soon as it - /// reaches the wrapping period. + /// fairly quickly if the app is left open for a while. This will wrap the value to 0.0 on the update after + /// the `Self::max_wrapping_period` /// - /// Defaults to wrapping to 0.0 every hour + /// Defaults to wrapping every hour #[inline] pub fn seconds_since_last_wrapping_period(&self) -> f32 { - self.duration_since_last_wrapping_period.as_secs_f32() + (self.seconds_since_startup % self.max_wrapping_period.as_secs_f64()) as f32 } /// The [`Instant`] the app was started @@ -183,7 +174,6 @@ mod tests { // Create a `Time` for testing let mut time = Time { startup: start_instant, - last_wrapping_period: start_instant, ..Default::default() }; @@ -252,7 +242,6 @@ mod tests { let mut time = Time { startup: start_instant, - last_wrapping_period: start_instant, max_wrapping_period: Duration::from_secs(2), ..Default::default() }; From 59be689aecdba364e289e57dbb4715a3f358f321 Mon Sep 17 00:00:00 2001 From: Charles Date: Tue, 13 Sep 2022 21:02:38 -0400 Subject: [PATCH 03/12] Update crates/bevy_time/src/time.rs Co-authored-by: Alice Cecile --- crates/bevy_time/src/time.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 6b27cd86c6172..4c56c887872f5 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -13,7 +13,8 @@ pub struct Time { seconds_since_startup: f64, time_since_startup: Duration, startup: Instant, - /// The maximum duration before `Self::seconds_since_last_wrapping_period()` wraps back to 0.0 + /// The maximum duration before `Self::seconds_since_last_wrapping_period()` wraps back to 0.0. + /// Defaults to one hour. pub max_wrapping_period: Duration, } From f946029929431f6297f67efb5f0e01764c305963 Mon Sep 17 00:00:00 2001 From: IceSentry Date: Tue, 13 Sep 2022 23:31:22 -0400 Subject: [PATCH 04/12] better docs --- crates/bevy_time/src/time.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 4c56c887872f5..1c5b04cf5db97 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -126,6 +126,9 @@ impl Time { } /// The time from startup to the last update in seconds + /// + /// If you need an f32 value, it is highly recommended to use [`Time::seconds_since_startup_f32_wrapped`] + /// instead of casting it. Casting to an f32 can cause floating point precision issues pretty fast. #[inline] pub fn seconds_since_startup(&self) -> f64 { self.seconds_since_startup @@ -139,7 +142,7 @@ impl Time { /// /// Defaults to wrapping every hour #[inline] - pub fn seconds_since_last_wrapping_period(&self) -> f32 { + pub fn seconds_since_startup_f32_wrapped(&self) -> f32 { (self.seconds_since_startup % self.max_wrapping_period.as_secs_f64()) as f32 } @@ -186,7 +189,7 @@ mod tests { assert_eq!(time.seconds_since_startup(), 0.0); assert_eq!(time.time_since_startup(), Duration::from_secs(0)); assert_eq!(time.delta_seconds(), 0.0); - assert_eq!(time.seconds_since_last_wrapping_period(), 0.0); + assert_eq!(time.seconds_since_startup_f32_wrapped(), 0.0); // Update `time` and check results let first_update_instant = Instant::now(); @@ -207,7 +210,7 @@ mod tests { ); assert_eq!(time.delta_seconds(), 0.0); assert_float_eq( - time.seconds_since_last_wrapping_period(), + time.seconds_since_startup_f32_wrapped(), time.seconds_since_startup() as f32, ); @@ -232,7 +235,7 @@ mod tests { ); assert_eq!(time.delta_seconds(), time.delta().as_secs_f32()); assert_float_eq( - time.seconds_since_last_wrapping_period(), + time.seconds_since_startup_f32_wrapped(), time.seconds_since_startup() as f32, ); } @@ -247,17 +250,17 @@ mod tests { ..Default::default() }; - assert_eq!(time.seconds_since_last_wrapping_period(), 0.0); + assert_eq!(time.seconds_since_startup_f32_wrapped(), 0.0); time.update_with_instant(start_instant + Duration::from_secs(1)); - assert_float_eq(time.seconds_since_last_wrapping_period(), 1.0); + assert_float_eq(time.seconds_since_startup_f32_wrapped(), 1.0); time.update_with_instant(start_instant + Duration::from_secs(2)); - assert_float_eq(time.seconds_since_last_wrapping_period(), 2.0); + assert_float_eq(time.seconds_since_startup_f32_wrapped(), 2.0); // wraps on next update time.update_with_instant(start_instant + Duration::from_secs(3)); - assert_float_eq(time.seconds_since_last_wrapping_period(), 1.0); + assert_float_eq(time.seconds_since_startup_f32_wrapped(), 1.0); } fn assert_float_eq(a: f32, b: f32) { From 1ecba4a830275f497c11421204c71e4c2704c29b Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 14 Sep 2022 09:50:46 -0400 Subject: [PATCH 05/12] Apply suggestions from code review Co-authored-by: Afonso Lage --- crates/bevy_time/src/time.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 1c5b04cf5db97..537df8436b217 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -127,20 +127,20 @@ impl Time { /// The time from startup to the last update in seconds /// - /// If you need an f32 value, it is highly recommended to use [`Time::seconds_since_startup_f32_wrapped`] - /// instead of casting it. Casting to an f32 can cause floating point precision issues pretty fast. + /// If you need an `f32` value, it is highly recommended to use [`Time::seconds_since_startup_f32_wrapped`] + /// instead of casting it. Casting to an `f32` can cause floating point precision issues pretty fast. #[inline] pub fn seconds_since_startup(&self) -> f64 { self.seconds_since_startup } - /// The time from the last wrap period to the last update in seconds + /// The time from the last wrap period to the last update in seconds. /// - /// When used in shaders, the time is limited to f32 which can introduce floating point precision issues - /// fairly quickly if the app is left open for a while. This will wrap the value to 0.0 on the update after - /// the `Self::max_wrapping_period` + /// When used in shaders, the time is limited to `f32` which can introduce floating point precision issues + /// fairly quickly if the app is left open for a while. + /// This will wrap the value to 0.0 on the update after the `Self::max_wrapping_period`. /// - /// Defaults to wrapping every hour + /// Defaults to wrapping every hour. #[inline] pub fn seconds_since_startup_f32_wrapped(&self) -> f32 { (self.seconds_since_startup % self.max_wrapping_period.as_secs_f64()) as f32 @@ -264,6 +264,6 @@ mod tests { } fn assert_float_eq(a: f32, b: f32) { - assert!((a - b) <= f32::EPSILON, "{a} != {b}"); + assert!((a - b).abs() <= f32::EPSILON, "{a} != {b}"); } } From 7c4739fd682e1a2b4f47ad43e6f3d27bce4e4c0c Mon Sep 17 00:00:00 2001 From: IceSentry Date: Wed, 14 Sep 2022 09:55:37 -0400 Subject: [PATCH 06/12] fmt --- crates/bevy_time/src/time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 537df8436b217..58d3ba543e45f 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -137,7 +137,7 @@ impl Time { /// The time from the last wrap period to the last update in seconds. /// /// When used in shaders, the time is limited to `f32` which can introduce floating point precision issues - /// fairly quickly if the app is left open for a while. + /// fairly quickly if the app is left open for a while. /// This will wrap the value to 0.0 on the update after the `Self::max_wrapping_period`. /// /// Defaults to wrapping every hour. From ceba0447304a58c7197f3911a0e90d9b1dd300ff Mon Sep 17 00:00:00 2001 From: IceSentry Date: Wed, 14 Sep 2022 10:10:08 -0400 Subject: [PATCH 07/12] add WrapDuration enum --- crates/bevy_time/src/time.rs | 52 +++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 58d3ba543e45f..f080e7d58503d 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -2,6 +2,23 @@ use bevy_ecs::{reflect::ReflectResource, system::Resource}; use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{Duration, Instant}; +#[derive(Debug, Clone, Copy)] +pub enum WrapDuration { + Default, + Custom(Duration), + Max, +} + +impl From for Duration { + fn from(val: WrapDuration) -> Self { + match val { + WrapDuration::Default => Duration::from_secs(60 * 60), // 1 hour + WrapDuration::Custom(duration) => duration, + WrapDuration::Max => Duration::from_secs(60 * 60 * 24), // 1 day + } + } +} + /// Tracks elapsed time since the last update and since the App has started #[derive(Resource, Reflect, FromReflect, Debug, Clone)] #[reflect(Resource)] @@ -13,9 +30,6 @@ pub struct Time { seconds_since_startup: f64, time_since_startup: Duration, startup: Instant, - /// The maximum duration before `Self::seconds_since_last_wrapping_period()` wraps back to 0.0. - /// Defaults to one hour. - pub max_wrapping_period: Duration, } impl Default for Time { @@ -28,7 +42,6 @@ impl Default for Time { seconds_since_startup: 0.0, time_since_startup: Duration::from_secs(0), delta_seconds: 0.0, - max_wrapping_period: Duration::from_secs(60 * 60), // 1 hour } } } @@ -142,8 +155,9 @@ impl Time { /// /// Defaults to wrapping every hour. #[inline] - pub fn seconds_since_startup_f32_wrapped(&self) -> f32 { - (self.seconds_since_startup % self.max_wrapping_period.as_secs_f64()) as f32 + pub fn seconds_since_startup_f32_wrapped(&self, duration: WrapDuration) -> f32 { + let duration: Duration = duration.into(); + (self.seconds_since_startup % duration.as_secs_f64()) as f32 } /// The [`Instant`] the app was started @@ -168,7 +182,7 @@ impl Time { #[cfg(test)] #[allow(clippy::float_cmp)] mod tests { - use super::Time; + use super::{Time, WrapDuration}; use bevy_utils::{Duration, Instant}; #[test] @@ -189,7 +203,10 @@ mod tests { assert_eq!(time.seconds_since_startup(), 0.0); assert_eq!(time.time_since_startup(), Duration::from_secs(0)); assert_eq!(time.delta_seconds(), 0.0); - assert_eq!(time.seconds_since_startup_f32_wrapped(), 0.0); + assert_eq!( + time.seconds_since_startup_f32_wrapped(WrapDuration::Default), + 0.0 + ); // Update `time` and check results let first_update_instant = Instant::now(); @@ -210,7 +227,7 @@ mod tests { ); assert_eq!(time.delta_seconds(), 0.0); assert_float_eq( - time.seconds_since_startup_f32_wrapped(), + time.seconds_since_startup_f32_wrapped(WrapDuration::Default), time.seconds_since_startup() as f32, ); @@ -235,7 +252,7 @@ mod tests { ); assert_eq!(time.delta_seconds(), time.delta().as_secs_f32()); assert_float_eq( - time.seconds_since_startup_f32_wrapped(), + time.seconds_since_startup_f32_wrapped(WrapDuration::Default), time.seconds_since_startup() as f32, ); } @@ -246,21 +263,24 @@ mod tests { let mut time = Time { startup: start_instant, - max_wrapping_period: Duration::from_secs(2), ..Default::default() }; - assert_eq!(time.seconds_since_startup_f32_wrapped(), 0.0); + let wrap_duration = WrapDuration::Custom(Duration::from_secs(3)); + + assert_eq!(time.seconds_since_startup_f32_wrapped(wrap_duration), 0.0); time.update_with_instant(start_instant + Duration::from_secs(1)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(), 1.0); + assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 1.0); time.update_with_instant(start_instant + Duration::from_secs(2)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(), 2.0); + assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 2.0); - // wraps on next update time.update_with_instant(start_instant + Duration::from_secs(3)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(), 1.0); + assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 0.0); + + time.update_with_instant(start_instant + Duration::from_secs(4)); + assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 1.0); } fn assert_float_eq(a: f32, b: f32) { From 0859563e6ac323b5426cda70806b83383c61162c Mon Sep 17 00:00:00 2001 From: IceSentry Date: Wed, 14 Sep 2022 10:21:07 -0400 Subject: [PATCH 08/12] docs --- crates/bevy_time/src/time.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index f080e7d58503d..27673620b8f0b 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -2,10 +2,19 @@ use bevy_ecs::{reflect::ReflectResource, system::Resource}; use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{Duration, Instant}; +/// The duration after which the time will go wrap back to 0 #[derive(Debug, Clone, Copy)] pub enum WrapDuration { + /// Will wrap after 1 hour or 3600 seconds Default, + /// Used to provide any duration to use as the period. + /// + /// It is highly recommended to not go above the maximum value of a day Custom(Duration), + /// Will wrap after 1 day or 86400 seconds. + /// + /// `f32`'s have about 6-7 significant numbers and a day is 86400 seconds, + /// add a few decimal places for millis and you will start to get precision errors. Max, } From 1d7e63532368f528c4e17dc1b8ba9d5710088f23 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 14 Sep 2022 14:13:09 -0400 Subject: [PATCH 09/12] Apply suggestions from code review Co-authored-by: Cameron <51241057+maniwani@users.noreply.github.com> --- crates/bevy_time/src/time.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 27673620b8f0b..3dec4fda214d5 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -149,20 +149,22 @@ impl Time { /// The time from startup to the last update in seconds /// - /// If you need an `f32` value, it is highly recommended to use [`Time::seconds_since_startup_f32_wrapped`] - /// instead of casting it. Casting to an `f32` can cause floating point precision issues pretty fast. + /// If you intend to cast this to an `f32` value, note that this value is monotonically increasing, + /// and that its precision as an `f32` will noticeably degrade over time (in a matter of hours). + /// If that precision loss is unacceptable, you should use [`Time::seconds_since_startup_f32_wrapped`], + /// which will return the time from startup modulo a wrapping period. #[inline] pub fn seconds_since_startup(&self) -> f64 { self.seconds_since_startup } - /// The time from the last wrap period to the last update in seconds. + /// The time from startup to the last update, modulo the given wrapping period, in seconds. /// - /// When used in shaders, the time is limited to `f32` which can introduce floating point precision issues - /// fairly quickly if the app is left open for a while. - /// This will wrap the value to 0.0 on the update after the `Self::max_wrapping_period`. + /// Time from startup is a monotonically increasing value and so its precision when read as an `f32` + /// will noticeably degrade over time, which causes issues for some uses, e.g. shaders. + /// This method avoids noticeable degradation by limiting the values to a much smaller range. /// - /// Defaults to wrapping every hour. + /// The default wrapping period is one hour. #[inline] pub fn seconds_since_startup_f32_wrapped(&self, duration: WrapDuration) -> f32 { let duration: Duration = duration.into(); From 788beffc460f22682a04beaeee2cd55445d77ebb Mon Sep 17 00:00:00 2001 From: IceSentry Date: Wed, 14 Sep 2022 14:17:52 -0400 Subject: [PATCH 10/12] fmt --- crates/bevy_time/src/time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 3dec4fda214d5..629e9af50daef 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -151,7 +151,7 @@ impl Time { /// /// If you intend to cast this to an `f32` value, note that this value is monotonically increasing, /// and that its precision as an `f32` will noticeably degrade over time (in a matter of hours). - /// If that precision loss is unacceptable, you should use [`Time::seconds_since_startup_f32_wrapped`], + /// If that precision loss is unacceptable, you should use [`Time::seconds_since_startup_f32_wrapped`], /// which will return the time from startup modulo a wrapping period. #[inline] pub fn seconds_since_startup(&self) -> f64 { From 3ab4975c6698cf85c45462d8575f36312ae73038 Mon Sep 17 00:00:00 2001 From: IceSentry Date: Sat, 17 Sep 2022 14:25:13 -0400 Subject: [PATCH 11/12] use global period on Time --- crates/bevy_time/src/time.rs | 65 +++++++++++------------------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 629e9af50daef..cd42c92770b6a 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -2,32 +2,7 @@ use bevy_ecs::{reflect::ReflectResource, system::Resource}; use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{Duration, Instant}; -/// The duration after which the time will go wrap back to 0 -#[derive(Debug, Clone, Copy)] -pub enum WrapDuration { - /// Will wrap after 1 hour or 3600 seconds - Default, - /// Used to provide any duration to use as the period. - /// - /// It is highly recommended to not go above the maximum value of a day - Custom(Duration), - /// Will wrap after 1 day or 86400 seconds. - /// - /// `f32`'s have about 6-7 significant numbers and a day is 86400 seconds, - /// add a few decimal places for millis and you will start to get precision errors. - Max, -} - -impl From for Duration { - fn from(val: WrapDuration) -> Self { - match val { - WrapDuration::Default => Duration::from_secs(60 * 60), // 1 hour - WrapDuration::Custom(duration) => duration, - WrapDuration::Max => Duration::from_secs(60 * 60 * 24), // 1 day - } - } -} - +const SECONDS_PER_HOUR: u64 = 60 * 60; /// Tracks elapsed time since the last update and since the App has started #[derive(Resource, Reflect, FromReflect, Debug, Clone)] #[reflect(Resource)] @@ -39,6 +14,10 @@ pub struct Time { seconds_since_startup: f64, time_since_startup: Duration, startup: Instant, + /// The maximum period before [`Time::seconds_since_startup_wrapped_f32`] wraps to 0 + /// + /// Defaults to 1 hour + pub wrap_period: Duration, } impl Default for Time { @@ -51,6 +30,7 @@ impl Default for Time { seconds_since_startup: 0.0, time_since_startup: Duration::from_secs(0), delta_seconds: 0.0, + wrap_period: Duration::from_secs(SECONDS_PER_HOUR), } } } @@ -151,14 +131,14 @@ impl Time { /// /// If you intend to cast this to an `f32` value, note that this value is monotonically increasing, /// and that its precision as an `f32` will noticeably degrade over time (in a matter of hours). - /// If that precision loss is unacceptable, you should use [`Time::seconds_since_startup_f32_wrapped`], + /// If that precision loss is unacceptable, you should use [`Time::seconds_since_startup_wrapped_f32`], /// which will return the time from startup modulo a wrapping period. #[inline] pub fn seconds_since_startup(&self) -> f64 { self.seconds_since_startup } - /// The time from startup to the last update, modulo the given wrapping period, in seconds. + /// The time from startup to the last update, modulo the [`Time::wrap_period`], in seconds. /// /// Time from startup is a monotonically increasing value and so its precision when read as an `f32` /// will noticeably degrade over time, which causes issues for some uses, e.g. shaders. @@ -166,9 +146,8 @@ impl Time { /// /// The default wrapping period is one hour. #[inline] - pub fn seconds_since_startup_f32_wrapped(&self, duration: WrapDuration) -> f32 { - let duration: Duration = duration.into(); - (self.seconds_since_startup % duration.as_secs_f64()) as f32 + pub fn seconds_since_startup_wrapped_f32(&self) -> f32 { + (self.seconds_since_startup % self.wrap_period.as_secs_f64()) as f32 } /// The [`Instant`] the app was started @@ -193,7 +172,7 @@ impl Time { #[cfg(test)] #[allow(clippy::float_cmp)] mod tests { - use super::{Time, WrapDuration}; + use super::Time; use bevy_utils::{Duration, Instant}; #[test] @@ -214,10 +193,7 @@ mod tests { assert_eq!(time.seconds_since_startup(), 0.0); assert_eq!(time.time_since_startup(), Duration::from_secs(0)); assert_eq!(time.delta_seconds(), 0.0); - assert_eq!( - time.seconds_since_startup_f32_wrapped(WrapDuration::Default), - 0.0 - ); + assert_eq!(time.seconds_since_startup_wrapped_f32(), 0.0); // Update `time` and check results let first_update_instant = Instant::now(); @@ -238,7 +214,7 @@ mod tests { ); assert_eq!(time.delta_seconds(), 0.0); assert_float_eq( - time.seconds_since_startup_f32_wrapped(WrapDuration::Default), + time.seconds_since_startup_wrapped_f32(), time.seconds_since_startup() as f32, ); @@ -263,7 +239,7 @@ mod tests { ); assert_eq!(time.delta_seconds(), time.delta().as_secs_f32()); assert_float_eq( - time.seconds_since_startup_f32_wrapped(WrapDuration::Default), + time.seconds_since_startup_wrapped_f32(), time.seconds_since_startup() as f32, ); } @@ -274,24 +250,23 @@ mod tests { let mut time = Time { startup: start_instant, + wrap_period: Duration::from_secs(3), ..Default::default() }; - let wrap_duration = WrapDuration::Custom(Duration::from_secs(3)); - - assert_eq!(time.seconds_since_startup_f32_wrapped(wrap_duration), 0.0); + assert_eq!(time.seconds_since_startup_wrapped_f32(), 0.0); time.update_with_instant(start_instant + Duration::from_secs(1)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 1.0); + assert_float_eq(time.seconds_since_startup_wrapped_f32(), 1.0); time.update_with_instant(start_instant + Duration::from_secs(2)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 2.0); + assert_float_eq(time.seconds_since_startup_wrapped_f32(), 2.0); time.update_with_instant(start_instant + Duration::from_secs(3)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 0.0); + assert_float_eq(time.seconds_since_startup_wrapped_f32(), 0.0); time.update_with_instant(start_instant + Duration::from_secs(4)); - assert_float_eq(time.seconds_since_startup_f32_wrapped(wrap_duration), 1.0); + assert_float_eq(time.seconds_since_startup_wrapped_f32(), 1.0); } fn assert_float_eq(a: f32, b: f32) { From c9ab7d2e0235b5362e4379bba5f5c798792adf9d Mon Sep 17 00:00:00 2001 From: IceSentry Date: Sat, 17 Sep 2022 22:26:06 -0400 Subject: [PATCH 12/12] newline --- crates/bevy_time/src/time.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index cd42c92770b6a..58e4fa1c78353 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -3,6 +3,7 @@ use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{Duration, Instant}; const SECONDS_PER_HOUR: u64 = 60 * 60; + /// Tracks elapsed time since the last update and since the App has started #[derive(Resource, Reflect, FromReflect, Debug, Clone)] #[reflect(Resource)]