Skip to content

Commit

Permalink
Add no_std Support to bevy_time (#17491)
Browse files Browse the repository at this point in the history
# Objective

- Contributes to #15460

## Solution

- Switched `tracing` for `log` for the atomically challenged platforms
- Setup feature flags as required
- Added to `compile-check-no-std` CI task
- Made `crossbeam-channel` optional depending on `std`.

## Testing

- CI

---

## Notes

- `crossbeam-channel` provides a MPMC channel type which isn't readily
replicable in `no_std`, and is only used for a `bevy_render`
integration. As such, I've feature-gated the `TimeReceiver` and
`TimeSender` types.

---------

Co-authored-by: Alice Cecile <[email protected]>
  • Loading branch information
bushrat011899 and alice-i-cecile authored Jan 22, 2025
1 parent f32a6fb commit d921fdc
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 35 deletions.
1 change: 0 additions & 1 deletion crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ impl_reflect_opaque!(::core::time::Duration(
Deserialize,
Default
));
#[cfg(any(target_arch = "wasm32", feature = "std"))]
impl_reflect_opaque!(::bevy_platform_support::time::Instant(
Debug, Hash, PartialEq
));
Expand Down
69 changes: 56 additions & 13 deletions crates/bevy_time/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,69 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]

[features]
default = ["bevy_reflect"]
serialize = ["serde"]
default = ["std", "bevy_reflect", "bevy_app/default"]

# Functionality

## Adds runtime reflection support using `bevy_reflect`.
bevy_reflect = [
"dep:bevy_reflect",
"bevy_ecs/bevy_reflect",
"bevy_app/bevy_reflect",
]

## Adds serialization support through `serde`.
serialize = ["dep:serde", "bevy_ecs/serialize"]

# Platform Compatibility

## Allows access to the `std` crate. Enabling this feature will prevent compilation
## on `no_std` targets, but provides access to certain additional features on
## supported platforms.
std = [
"serde?/std",
"bevy_reflect?/std",
"bevy_ecs/std",
"bevy_app/std",
"bevy_platform_support/std",
"dep:crossbeam-channel",
]

## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = [
"bevy_ecs/critical-section",
"bevy_platform_support/critical-section",
"bevy_reflect?/critical-section",
"bevy_app/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_ecs/portable-atomic",
"bevy_platform_support/portable-atomic",
"bevy_reflect?/portable-atomic",
"bevy_app/portable-atomic",
]

[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [
"bevy_reflect",
] }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, features = [
"bevy",
], optional = true }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false }

# other
crossbeam-channel = "0.5.0"
serde = { version = "1", features = ["derive"], optional = true }
tracing = { version = "0.1", default-features = false, features = ["std"] }
crossbeam-channel = { version = "0.5.0", default-features = false, features = [
"std",
], optional = true }
serde = { version = "1", features = [
"derive",
], default-features = false, optional = true }
log = { version = "0.4", default-features = false }

[lints]
workspace = true
Expand Down
60 changes: 40 additions & 20 deletions crates/bevy_time/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
)]
#![no_std]

#[cfg(feature = "std")]
extern crate std;

extern crate alloc;

/// Common run conditions
pub mod common_conditions;
Expand Down Expand Up @@ -37,9 +43,12 @@ use bevy_ecs::{
};
use bevy_platform_support::time::Instant;
use core::time::Duration;

#[cfg(feature = "std")]
pub use crossbeam_channel::TrySendError;

#[cfg(feature = "std")]
use crossbeam_channel::{Receiver, Sender};
use tracing::warn;

/// Adds time functionality to Apps.
#[derive(Default)]
Expand Down Expand Up @@ -92,8 +101,9 @@ impl Plugin for TimePlugin {
/// networking or similar, you may prefer to set the next [`Time`] value manually.
#[derive(Resource, Default)]
pub enum TimeUpdateStrategy {
/// [`Time`] will be automatically updated each frame using an [`Instant`] sent from the render world via a [`TimeSender`].
/// [`Time`] will be automatically updated each frame using an [`Instant`] sent from the render world.
/// If nothing is sent, the system clock will be used instead.
#[cfg_attr(feature = "std", doc = "See [`TimeSender`] for more details.")]
#[default]
Automatic,
/// [`Time`] will be updated to the specified [`Instant`] value each frame.
Expand All @@ -106,14 +116,17 @@ pub enum TimeUpdateStrategy {
}

/// Channel resource used to receive time from the render world.
#[cfg(feature = "std")]
#[derive(Resource)]
pub struct TimeReceiver(pub Receiver<Instant>);

/// Channel resource used to send time from the render world.
#[cfg(feature = "std")]
#[derive(Resource)]
pub struct TimeSender(pub Sender<Instant>);

/// Creates channels used for sending time between the render world and the main world.
#[cfg(feature = "std")]
pub fn create_time_channels() -> (TimeSender, TimeReceiver) {
// bound the channel to 2 since when pipelined the render phase can finish before
// the time system runs.
Expand All @@ -128,26 +141,32 @@ pub fn time_system(
mut virtual_time: ResMut<Time<Virtual>>,
mut time: ResMut<Time>,
update_strategy: Res<TimeUpdateStrategy>,
time_recv: Option<Res<TimeReceiver>>,
mut has_received_time: Local<bool>,
#[cfg(feature = "std")] time_recv: Option<Res<TimeReceiver>>,
#[cfg(feature = "std")] mut has_received_time: Local<bool>,
) {
let new_time = if let Some(time_recv) = time_recv {
// TODO: Figure out how to handle this when using pipelined rendering.
if let Ok(new_time) = time_recv.0.try_recv() {
*has_received_time = true;
new_time
} else {
if *has_received_time {
warn!("time_system did not receive the time from the render world! Calculations depending on the time may be incorrect.");
}
Instant::now()
}
} else {
Instant::now()
};

match update_strategy.as_ref() {
TimeUpdateStrategy::Automatic => real_time.update_with_instant(new_time),
TimeUpdateStrategy::Automatic => {
#[cfg(feature = "std")]
let new_time = if let Some(time_recv) = time_recv {
// TODO: Figure out how to handle this when using pipelined rendering.
if let Ok(new_time) = time_recv.0.try_recv() {
*has_received_time = true;
new_time
} else {
if *has_received_time {
log::warn!("time_system did not receive the time from the render world! Calculations depending on the time may be incorrect.");
}
Instant::now()
}
} else {
Instant::now()
};

#[cfg(not(feature = "std"))]
let new_time = Instant::now();

real_time.update_with_instant(new_time);
}
TimeUpdateStrategy::ManualInstant(instant) => real_time.update_with_instant(*instant),
TimeUpdateStrategy::ManualDuration(duration) => real_time.update_with_duration(*duration),
}
Expand All @@ -166,6 +185,7 @@ mod tests {
};
use core::error::Error;
use core::time::Duration;
use std::println;

#[derive(Event)]
struct TestEvent<T: Default> {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_time/src/virt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use core::time::Duration;
use tracing::debug;
use log::debug;

use crate::{real::Real, time::Time};

Expand Down
8 changes: 8 additions & 0 deletions tools/ci/src/commands/compile_check_no_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ impl Prepare for CompileCheckNoStdCommand {
"Please fix compiler errors in output above for bevy_transform no_std compatibility.",
));

commands.push(PreparedCommand::new::<Self>(
cmd!(
sh,
"cargo check -p bevy_time --no-default-features --features bevy_reflect,serialize --target {target}"
),
"Please fix compiler errors in output above for bevy_transform no_std compatibility.",
));

commands.push(PreparedCommand::new::<Self>(
cmd!(
sh,
Expand Down

0 comments on commit d921fdc

Please sign in to comment.