Skip to content

Commit c8747b6

Browse files
committed
fix(bevy_platform): no_std aarch64 wrong nanoseconds counter
1 parent 4679505 commit c8747b6

File tree

1 file changed

+41
-2
lines changed

1 file changed

+41
-2
lines changed

crates/bevy_platform/src/time/fallback.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
use crate::sync::atomic::{AtomicPtr, Ordering};
99

10+
#[cfg(target_arch = "aarch64")]
11+
use crate::sync::atomic::AtomicU64;
12+
1013
use core::{
1114
fmt,
1215
ops::{Add, AddAssign, Sub, SubAssign},
@@ -15,6 +18,9 @@ use core::{
1518

1619
static ELAPSED_GETTER: AtomicPtr<()> = AtomicPtr::new(unset_getter as *mut _);
1720

21+
#[cfg(target_arch = "aarch64")]
22+
static CNTFRQ: AtomicU64 = AtomicU64::new(0);
23+
1824
/// Fallback implementation of `Instant` suitable for a `no_std` environment.
1925
///
2026
/// If you are on any of the following target architectures, this is a drop-in replacement:
@@ -166,15 +172,48 @@ fn unset_getter() -> Duration {
166172
}
167173
#[cfg(target_arch = "aarch64")] => {
168174
// SAFETY: standard technique for getting a nanosecond counter of aarch64
169-
let nanos = unsafe {
175+
let (freq, ticks) = unsafe {
176+
let mut freq = CNTFRQ.load(Ordering::Relaxed);
177+
if freq == 0 {
178+
core::arch::asm!("mrs {}, cntfrq_el0", out(reg) freq);
179+
if let Err(existing) = CNTFRQ.compare_exchange(0, freq, Ordering::Release, Ordering::Relaxed) {
180+
freq = existing;
181+
}
182+
}
183+
170184
let mut ticks: u64;
171185
core::arch::asm!("mrs {}, cntvct_el0", out(reg) ticks);
172-
ticks
186+
187+
(freq, ticks)
173188
};
189+
190+
#[rustfmt::skip]
191+
let nanos = (ticks / freq).saturating_mul(1_000_000_000) +
192+
(ticks % freq).saturating_mul(1_000_000_000) / freq;
193+
174194
Duration::from_nanos(nanos)
175195
}
176196
_ => {
177197
panic!("An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`")
178198
}
179199
}
180200
}
201+
202+
#[cfg(test)]
203+
mod tests {
204+
use std::thread::sleep;
205+
206+
use super::*;
207+
208+
#[test]
209+
fn test_unset_getter() {
210+
let dur1 = unset_getter();
211+
212+
sleep(Duration::from_millis(500));
213+
214+
let dur2 = unset_getter();
215+
216+
assert!(dur2 > dur1);
217+
assert!(dur2 - dur1 >= Duration::from_millis(500));
218+
}
219+
}

0 commit comments

Comments
 (0)