Skip to content

Commit 3fac947

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

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

crates/bevy_platform/src/time/fallback.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
reason = "Instant fallback requires unsafe to allow users to update the internal value"
66
)]
77

8-
use crate::sync::atomic::{AtomicPtr, Ordering};
8+
use crate::sync::atomic::{AtomicPtr, AtomicU64, Ordering};
99

1010
use core::{
1111
fmt,
@@ -15,6 +15,9 @@ use core::{
1515

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

18+
#[cfg(target_arch = "aarch64")]
19+
static CNTFRQ: AtomicU64 = AtomicU64::new(0);
20+
1821
/// Fallback implementation of `Instant` suitable for a `no_std` environment.
1922
///
2023
/// If you are on any of the following target architectures, this is a drop-in replacement:
@@ -166,15 +169,48 @@ fn unset_getter() -> Duration {
166169
}
167170
#[cfg(target_arch = "aarch64")] => {
168171
// SAFETY: standard technique for getting a nanosecond counter of aarch64
169-
let nanos = unsafe {
172+
let (freq, ticks) = unsafe {
173+
let mut freq = CNTFRQ.load(Ordering::Relaxed);
174+
if freq == 0 {
175+
core::arch::asm!("mrs {}, cntfrq_el0", out(reg) freq);
176+
if let Err(existing) = CNTFRQ.compare_exchange(0, freq, Ordering::Release, Ordering::Relaxed) {
177+
freq = existing;
178+
}
179+
}
180+
170181
let mut ticks: u64;
171182
core::arch::asm!("mrs {}, cntvct_el0", out(reg) ticks);
172-
ticks
183+
184+
(freq, ticks)
173185
};
186+
187+
#[rustfmt::skip]
188+
let nanos = (ticks / freq).saturating_mul(1_000_000_000) +
189+
(ticks % freq).saturating_mul(1_000_000_000) / freq;
190+
174191
Duration::from_nanos(nanos)
175192
}
176193
_ => {
177194
panic!("An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`")
178195
}
179196
}
180197
}
198+
199+
#[cfg(test)]
200+
mod tests {
201+
use std::thread::sleep;
202+
203+
use super::*;
204+
205+
#[test]
206+
fn test_unset_getter() {
207+
let dur1 = unset_getter();
208+
209+
sleep(Duration::from_millis(500));
210+
211+
let dur2 = unset_getter();
212+
213+
assert!(dur2 > dur1);
214+
assert!(dur2 - dur1 >= Duration::from_millis(500));
215+
}
216+
}

crates/bevy_winit/src/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use bevy_input::{
1414
};
1515
use bevy_log::{trace, warn};
1616
use bevy_math::{ivec2, DVec2, Vec2};
17-
use bevy_platform::time::Instant;
1817
#[cfg(not(target_arch = "wasm32"))]
1918
use bevy_tasks::tick_global_task_pools_on_main_thread;
2019
use core::marker::PhantomData;
20+
use std::time::Instant;
2121
#[cfg(target_arch = "wasm32")]
2222
use winit::platform::web::EventLoopExtWebSys;
2323
use winit::{

0 commit comments

Comments
 (0)