Create bevy_platform_support Crate (#17250)

# Objective

- Contributes to #16877

## Solution

- Initial creation of `bevy_platform_support` crate.
- Moved `bevy_utils::Instant` into new `bevy_platform_support` crate.
- Moved `portable-atomic`, `portable-atomic-util`, and
`critical-section` into new `bevy_platform_support` crate.

## Testing

- CI

---

## Showcase

Instead of needing code like this to import an `Arc`:

```rust
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;

#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
```

We can now use:

```rust
use bevy_platform_support::sync::Arc;
```

This applies to many other types, but the goal is overall the same:
allowing crates to use `std`-like types without the boilerplate of
conditional compilation and platform-dependencies.

## Migration Guide

- Replace imports of `bevy_utils::Instant` with
`bevy_platform_support::time::Instant`
- Replace imports of `bevy::utils::Instant` with
`bevy::platform_support::time::Instant`

## Notes

- `bevy_platform_support` hasn't been reserved on `crates.io`
- ~~`bevy_platform_support` is not re-exported from `bevy` at this time.
It may be worthwhile exporting this crate, but I am unsure of a
reasonable name to export it under (`platform_support` may be a bit
wordy for user-facing).~~
- I've included an implementation of `Instant` which is suitable for
`no_std` platforms that are not Wasm for the sake of eliminating feature
gates around its use. It may be a controversial inclusion, so I'm happy
to remove it if required.
- There are many other items (`spin`, `bevy_utils::Sync(Unsafe)Cell`,
etc.) which should be added to this crate. I have kept the initial scope
small to demonstrate utility without making this too unwieldy.

---------

Co-authored-by: TimJentzsch <TimJentzsch@users.noreply.github.com>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
This commit is contained in:
Zachary Harrold 2025-01-21 07:45:30 +11:00 committed by GitHub
parent edb34cd2dd
commit a64446b77e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 586 additions and 197 deletions

View File

@ -48,23 +48,25 @@ std = [
"downcast-rs/std", "downcast-rs/std",
"bevy_utils/std", "bevy_utils/std",
"bevy_tasks?/std", "bevy_tasks?/std",
"bevy_platform_support/std",
] ]
## `critical-section` provides the building blocks for synchronization primitives ## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`. ## on all platforms, including `no_std`.
critical-section = [ critical-section = [
"portable-atomic?/critical-section",
"bevy_tasks?/critical-section", "bevy_tasks?/critical-section",
"bevy_ecs/critical-section", "bevy_ecs/critical-section",
"bevy_platform_support/critical-section",
"bevy_reflect?/critical-section",
] ]
## `portable-atomic` provides additional platform support for atomic types and ## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support. ## operations, even on targets without native support.
portable-atomic = [ portable-atomic = [
"dep:portable-atomic",
"dep:portable-atomic-util",
"bevy_tasks?/portable-atomic", "bevy_tasks?/portable-atomic",
"bevy_ecs/portable-atomic", "bevy_ecs/portable-atomic",
"bevy_platform_support/portable-atomic",
"bevy_reflect?/portable-atomic",
] ]
[dependencies] [dependencies]
@ -76,6 +78,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features
"alloc", "alloc",
] } ] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, optional = true } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, optional = true }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false }
# other # other
downcast-rs = { version = "2", default-features = false } downcast-rs = { version = "2", default-features = false }
@ -83,12 +86,6 @@ thiserror = { version = "2", default-features = false }
variadics_please = "1.1" variadics_please = "1.1"
tracing = { version = "0.1", default-features = false, optional = true } tracing = { version = "0.1", default-features = false, optional = true }
log = { version = "0.4", default-features = false } log = { version = "0.4", default-features = false }
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
], optional = true }
portable-atomic-util = { version = "0.2.4", features = [
"alloc",
], optional = true }
[target.'cfg(any(unix, windows))'.dependencies] [target.'cfg(any(unix, windows))'.dependencies]
ctrlc = { version = "3.4.4", optional = true } ctrlc = { version = "3.4.4", optional = true }

View File

@ -3,11 +3,9 @@ use crate::{
plugin::Plugin, plugin::Plugin,
PluginsState, PluginsState,
}; };
use bevy_platform_support::time::Instant;
use core::time::Duration; use core::time::Duration;
#[cfg(any(target_arch = "wasm32", feature = "std"))]
use bevy_utils::Instant;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use { use {
alloc::{boxed::Box, rc::Rc}, alloc::{boxed::Box, rc::Rc},
@ -100,7 +98,6 @@ impl Plugin for ScheduleRunnerPlugin {
let tick = move |app: &mut App, let tick = move |app: &mut App,
_wait: Option<Duration>| _wait: Option<Duration>|
-> Result<Option<Duration>, AppExit> { -> Result<Option<Duration>, AppExit> {
#[cfg(any(target_arch = "wasm32", feature = "std"))]
let start_time = Instant::now(); let start_time = Instant::now();
app.update(); app.update();
@ -109,10 +106,8 @@ impl Plugin for ScheduleRunnerPlugin {
return Err(exit); return Err(exit);
}; };
#[cfg(any(target_arch = "wasm32", feature = "std"))]
let end_time = Instant::now(); let end_time = Instant::now();
#[cfg(any(target_arch = "wasm32", feature = "std"))]
if let Some(wait) = _wait { if let Some(wait) = _wait {
let exe_time = end_time - start_time; let exe_time = end_time - start_time;
if exe_time < wait { if exe_time < wait {

View File

@ -2,13 +2,14 @@
feature = "portable-atomic", feature = "portable-atomic",
expect( expect(
clippy::redundant_closure, clippy::redundant_closure,
reason = "portable_atomic_util::Arc has subtly different implicit behavior" reason = "bevy_platform_support::sync::Arc has subtly different implicit behavior"
) )
)] )]
use crate::{App, Plugin}; use crate::{App, Plugin};
use alloc::string::ToString; use alloc::string::ToString;
use bevy_platform_support::sync::Arc;
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder}; use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
use core::{fmt::Debug, marker::PhantomData}; use core::{fmt::Debug, marker::PhantomData};
use log::trace; use log::trace;
@ -16,12 +17,6 @@ use log::trace;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use {crate::Last, bevy_ecs::prelude::NonSend}; use {crate::Last, bevy_ecs::prelude::NonSend};
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use bevy_tasks::tick_global_task_pools_on_main_thread; use bevy_tasks::tick_global_task_pools_on_main_thread;

View File

@ -35,6 +35,9 @@ bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.16.0-dev" } bevy_math = { path = "../bevy_math", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.16.0-dev" } bevy_window = { path = "../bevy_window", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
bitflags = "2.3" bitflags = "2.3"

View File

@ -4,6 +4,7 @@ use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, Handle}; use bevy_asset::{load_internal_asset, Handle};
use bevy_ecs::{component::*, prelude::*}; use bevy_ecs::{component::*, prelude::*};
use bevy_math::UVec2; use bevy_math::UVec2;
use bevy_platform_support::time::Instant;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render::{ use bevy_render::{
camera::{Camera, ExtractedCamera}, camera::{Camera, ExtractedCamera},
@ -16,7 +17,7 @@ use bevy_render::{
view::Msaa, view::Msaa,
Render, RenderApp, RenderSet, Render, RenderApp, RenderSet,
}; };
use bevy_utils::{HashSet, Instant}; use bevy_utils::HashSet;
use bevy_window::PrimaryWindow; use bevy_window::PrimaryWindow;
use resolve::{ use resolve::{
node::{OitResolveNode, OitResolvePass}, node::{OitResolveNode, OitResolvePass},

View File

@ -21,6 +21,9 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.16.0-dev" } bevy_time = { path = "../bevy_time", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
# other # other
const-fnv1a-hash = "1.1.0" const-fnv1a-hash = "1.1.0"

View File

@ -6,7 +6,8 @@ use core::{
use bevy_app::{App, SubApp}; use bevy_app::{App, SubApp};
use bevy_ecs::system::{Deferred, Res, Resource, SystemBuffer, SystemParam}; use bevy_ecs::system::{Deferred, Res, Resource, SystemBuffer, SystemParam};
use bevy_utils::{HashMap, Instant, PassHash}; use bevy_platform_support::time::Instant;
use bevy_utils::{HashMap, PassHash};
use const_fnv1a_hash::fnv1a_hash_str_64; use const_fnv1a_hash::fnv1a_hash_str_64;
use crate::DEFAULT_MAX_HISTORY_LENGTH; use crate::DEFAULT_MAX_HISTORY_LENGTH;

View File

@ -76,24 +76,25 @@ std = [
"nonmax/std", "nonmax/std",
"arrayvec?/std", "arrayvec?/std",
"log/std", "log/std",
"bevy_platform_support/std",
] ]
## `critical-section` provides the building blocks for synchronization primitives ## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`. ## on all platforms, including `no_std`.
critical-section = [ critical-section = [
"dep:critical-section",
"bevy_tasks?/critical-section", "bevy_tasks?/critical-section",
"portable-atomic?/critical-section", "bevy_platform_support/critical-section",
"bevy_reflect?/critical-section",
] ]
## `portable-atomic` provides additional platform support for atomic types and ## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support. ## operations, even on targets without native support.
portable-atomic = [ portable-atomic = [
"dep:portable-atomic",
"dep:portable-atomic-util",
"bevy_tasks?/portable-atomic", "bevy_tasks?/portable-atomic",
"bevy_platform_support/portable-atomic",
"concurrent-queue/portable-atomic", "concurrent-queue/portable-atomic",
"spin/portable_atomic", "spin/portable_atomic",
"bevy_reflect?/portable-atomic",
] ]
[dependencies] [dependencies]
@ -104,6 +105,9 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features
"alloc", "alloc",
] } ] }
bevy_ecs_macros = { path = "macros", version = "0.16.0-dev" } bevy_ecs_macros = { path = "macros", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bitflags = { version = "2.3", default-features = false } bitflags = { version = "2.3", default-features = false }
concurrent-queue = { version = "2.5.0", default-features = false } concurrent-queue = { version = "2.5.0", default-features = false }
@ -124,13 +128,6 @@ arrayvec = { version = "0.7.4", default-features = false, optional = true }
smallvec = { version = "1", features = ["union"] } smallvec = { version = "1", features = ["union"] }
indexmap = { version = "2.5.0", default-features = false } indexmap = { version = "2.5.0", default-features = false }
variadics_please = { version = "1.1", default-features = false } variadics_please = { version = "1.1", default-features = false }
critical-section = { version = "1.2.0", optional = true }
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
], optional = true }
portable-atomic-util = { version = "0.2.4", features = [
"alloc",
], optional = true }
spin = { version = "0.9.8", default-features = false, features = [ spin = { version = "0.9.8", default-features = false, features = [
"spin_mutex", "spin_mutex",
"rwlock", "rwlock",

View File

@ -15,6 +15,7 @@ use crate::{
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::{borrow::Cow, format, vec::Vec}; use alloc::{borrow::Cow, format, vec::Vec};
pub use bevy_ecs_macros::Component; pub use bevy_ecs_macros::Component;
use bevy_platform_support::sync::Arc;
use bevy_ptr::{OwningPtr, UnsafeCellDeref}; use bevy_ptr::{OwningPtr, UnsafeCellDeref};
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
@ -32,12 +33,6 @@ use core::{
use disqualified::ShortName; use disqualified::ShortName;
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
pub use bevy_ecs_macros::require; pub use bevy_ecs_macros::require;
/// A data type that can be used to store data for an [entity]. /// A data type that can be used to store data for an [entity].

View File

@ -1,19 +1,13 @@
use alloc::{borrow::ToOwned, vec::Vec}; use alloc::{borrow::ToOwned, vec::Vec};
use bevy_platform_support::sync::Arc;
use bevy_ptr::{Ptr, PtrMut}; use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::{HashMap, HashSet};
use bumpalo::Bump; use bumpalo::Bump;
use core::{any::TypeId, ptr::NonNull}; use core::{any::TypeId, ptr::NonNull};
use bevy_utils::{HashMap, HashSet};
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use alloc::boxed::Box; use alloc::boxed::Box;
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
use crate::{ use crate::{
bundle::Bundle, bundle::Bundle,
component::{Component, ComponentCloneHandler, ComponentId, ComponentInfo, Components}, component::{Component, ComponentCloneHandler, ComponentId, ComponentInfo, Components},

View File

@ -13,11 +13,7 @@ use core::{
use super::Entity; use super::Entity;
#[cfg(feature = "portable-atomic")] use bevy_platform_support::sync::Arc;
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
/// A trait for entity borrows. /// A trait for entity borrows.
/// ///

View File

@ -70,6 +70,7 @@ use crate::{
storage::{SparseSetIndex, TableId, TableRow}, storage::{SparseSetIndex, TableId, TableRow},
}; };
use alloc::vec::Vec; use alloc::vec::Vec;
use bevy_platform_support::sync::atomic::Ordering;
use core::{fmt, hash::Hash, mem, num::NonZero}; use core::{fmt, hash::Hash, mem, num::NonZero};
use log::warn; use log::warn;
@ -79,26 +80,16 @@ use core::panic::Location;
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(not(feature = "portable-atomic"))] #[cfg(target_has_atomic = "64")]
use core::sync::atomic::Ordering; use bevy_platform_support::sync::atomic::AtomicI64 as AtomicIdCursor;
#[cfg(feature = "portable-atomic")]
use portable_atomic::Ordering;
#[cfg(all(target_has_atomic = "64", not(feature = "portable-atomic")))]
use core::sync::atomic::AtomicI64 as AtomicIdCursor;
#[cfg(all(target_has_atomic = "64", feature = "portable-atomic"))]
use portable_atomic::AtomicI64 as AtomicIdCursor;
#[cfg(target_has_atomic = "64")] #[cfg(target_has_atomic = "64")]
type IdCursor = i64; type IdCursor = i64;
/// Most modern platforms support 64-bit atomics, but some less-common platforms /// Most modern platforms support 64-bit atomics, but some less-common platforms
/// do not. This fallback allows compilation using a 32-bit cursor instead, with /// do not. This fallback allows compilation using a 32-bit cursor instead, with
/// the caveat that some conversions may fail (and panic) at runtime. /// the caveat that some conversions may fail (and panic) at runtime.
#[cfg(all(not(target_has_atomic = "64"), not(feature = "portable-atomic")))] #[cfg(not(target_has_atomic = "64"))]
use core::sync::atomic::AtomicIsize as AtomicIdCursor; use bevy_platform_support::sync::atomic::AtomicIsize as AtomicIdCursor;
#[cfg(all(not(target_has_atomic = "64"), feature = "portable-atomic"))]
use portable_atomic::AtomicIsize as AtomicIdCursor;
#[cfg(not(target_has_atomic = "64"))] #[cfg(not(target_has_atomic = "64"))]
type IdCursor = isize; type IdCursor = isize;

View File

@ -1,4 +1,5 @@
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use bevy_platform_support::sync::Arc;
use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor}; use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor};
use bevy_utils::{default, syncunsafecell::SyncUnsafeCell}; use bevy_utils::{default, syncunsafecell::SyncUnsafeCell};
use concurrent_queue::ConcurrentQueue; use concurrent_queue::ConcurrentQueue;
@ -12,12 +13,6 @@ use std::{
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use tracing::{info_span, Span}; use tracing::{info_span, Span};
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
use crate::{ use crate::{
archetype::ArchetypeComponentId, archetype::ArchetypeComponentId,
prelude::Resource, prelude::Resource,

View File

@ -4,12 +4,7 @@ use crate::{
system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam},
world::{FromWorld, World}, world::{FromWorld, World},
}; };
use bevy_platform_support::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "portable-atomic")]
use portable_atomic::{AtomicUsize, Ordering};
use super::unsafe_world_cell::UnsafeWorldCell; use super::unsafe_world_cell::UnsafeWorldCell;

View File

@ -53,23 +53,15 @@ use crate::{
}, },
}; };
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use bevy_platform_support::sync::atomic::{AtomicU32, Ordering};
use bevy_ptr::{OwningPtr, Ptr}; use bevy_ptr::{OwningPtr, Ptr};
use core::{any::TypeId, fmt}; use core::{any::TypeId, fmt, panic::Location};
use log::warn; use log::warn;
use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell};
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic::{AtomicU32, Ordering};
#[cfg(feature = "portable-atomic")]
use portable_atomic::{AtomicU32, Ordering};
#[cfg(feature = "track_location")] #[cfg(feature = "track_location")]
use bevy_ptr::UnsafeCellDeref; use bevy_ptr::UnsafeCellDeref;
use core::panic::Location;
use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell};
/// Stores and exposes operations on [entities](Entity), [components](Component), resources, /// Stores and exposes operations on [entities](Entity), [components](Component), resources,
/// and their associated metadata. /// and their associated metadata.
/// ///

View File

@ -15,19 +15,13 @@ use crate::{
system::Resource, system::Resource,
world::RawCommandQueue, world::RawCommandQueue,
}; };
use bevy_platform_support::sync::atomic::Ordering;
use bevy_ptr::Ptr; use bevy_ptr::Ptr;
#[cfg(feature = "track_location")]
use bevy_ptr::UnsafeCellDeref;
#[cfg(feature = "track_location")]
use core::panic::Location;
use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, ptr}; use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, ptr};
use thiserror::Error; use thiserror::Error;
#[cfg(not(feature = "portable-atomic"))] #[cfg(feature = "track_location")]
use core::sync::atomic::Ordering; use {bevy_ptr::UnsafeCellDeref, core::panic::Location};
#[cfg(feature = "portable-atomic")]
use portable_atomic::Ordering;
/// Variant of the [`World`] where resource and component accesses take `&self`, and the responsibility to avoid /// Variant of the [`World`] where resource and component accesses take `&self`, and the responsibility to avoid
/// aliasing violations are given to the caller instead of being checked at compile-time by rust's unique XOR shared rule. /// aliasing violations are given to the caller instead of being checked at compile-time by rust's unique XOR shared rule.

View File

@ -47,11 +47,19 @@ std = [
## `critical-section` provides the building blocks for synchronization primitives ## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`. ## on all platforms, including `no_std`.
critical-section = ["bevy_app/critical-section", "bevy_ecs/critical-section"] critical-section = [
"bevy_app/critical-section",
"bevy_ecs/critical-section",
"bevy_reflect?/critical-section",
]
## `portable-atomic` provides additional platform support for atomic types and ## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support. ## operations, even on targets without native support.
portable-atomic = ["bevy_app/portable-atomic", "bevy_ecs/portable-atomic"] portable-atomic = [
"bevy_app/portable-atomic",
"bevy_ecs/portable-atomic",
"bevy_reflect?/portable-atomic",
]
## Uses the `libm` maths library instead of the one provided in `std` and `core`. ## Uses the `libm` maths library instead of the one provided in `std` and `core`.
libm = ["bevy_math/libm"] libm = ["bevy_math/libm"]

View File

@ -278,6 +278,7 @@ bevy_log = { path = "../bevy_log", version = "0.16.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.16.0-dev", features = [ bevy_math = { path = "../bevy_math", version = "0.16.0-dev", features = [
"bevy_reflect", "bevy_reflect",
] } ] }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev" }
bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" } bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
"bevy", "bevy",

View File

@ -46,6 +46,7 @@ pub use bevy_math as math;
pub use bevy_pbr as pbr; pub use bevy_pbr as pbr;
#[cfg(feature = "bevy_picking")] #[cfg(feature = "bevy_picking")]
pub use bevy_picking as picking; pub use bevy_picking as picking;
pub use bevy_platform_support as platform_support;
pub use bevy_ptr as ptr; pub use bevy_ptr as ptr;
pub use bevy_reflect as reflect; pub use bevy_reflect as reflect;
#[cfg(feature = "bevy_remote")] #[cfg(feature = "bevy_remote")]

View File

@ -26,6 +26,9 @@ bevy_time = { path = "../bevy_time", version = "0.16.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.16.0-dev" } bevy_window = { path = "../bevy_window", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
# other # other
crossbeam-channel = { version = "0.5", optional = true } crossbeam-channel = { version = "0.5", optional = true }

View File

@ -41,9 +41,10 @@ use core::{fmt::Debug, time::Duration};
use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal}; use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal};
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_platform_support::time::Instant;
use bevy_reflect::prelude::*; use bevy_reflect::prelude::*;
use bevy_render::camera::NormalizedRenderTarget; use bevy_render::camera::NormalizedRenderTarget;
use bevy_utils::{HashMap, Instant}; use bevy_utils::HashMap;
use bevy_window::Window; use bevy_window::Window;
use tracing::debug; use tracing::debug;

View File

@ -0,0 +1,52 @@
[package]
name = "bevy_platform_support"
version = "0.16.0-dev"
edition = "2021"
description = "Platform compatibility support for Bevy Engine"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
default = ["std"]
# 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 = [
"alloc",
"critical-section?/std",
"portable-atomic?/std",
"portable-atomic-util?/std",
]
alloc = ["portable-atomic-util?/alloc"]
## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = ["dep:critical-section", "portable-atomic?/critical-section"]
## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = ["dep:portable-atomic", "dep:portable-atomic-util"]
[dependencies]
critical-section = { version = "1.2.0", default-features = false, optional = true }
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
], optional = true }
portable-atomic-util = { version = "0.2.4", default-features = false, optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-time = { version = "1.1", default-features = false }
getrandom = { version = "0.2.0", default-features = false, features = ["js"] }
[lints]
workspace = true
[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
all-features = true

View File

@ -0,0 +1,56 @@
# Bevy Platform Support
[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
[![Crates.io](https://img.shields.io/crates/v/bevy_platform_support.svg)](https://crates.io/crates/bevy_platform_support)
[![Downloads](https://img.shields.io/crates/d/bevy_platform_support.svg)](https://crates.io/crates/bevy_platform_support)
[![Docs](https://docs.rs/bevy_platform_support/badge.svg)](https://docs.rs/bevy_platform_support/latest/bevy_platform_support/)
[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy)
Rust is a fantastic multi-platform language with extensive support for modern targets through its [standard library](https://doc.rust-lang.org/stable/std/).
However, some items within the standard library have alternatives that are better suited for [Bevy](https://crates.io/crates/bevy) and game engines in general.
Additionally, to support embedded and other esoteric platforms, it's often necessary to shed reliance on `std`, making your crate [`no_std`](https://docs.rust-embedded.org/book/intro/no-std.html).
These needs are handled by this crate, `bevy_platform_support`.
The goal of this crate is to provide alternatives and extensions to the Rust standard library which minimize friction when developing with and for Bevy across multiple platforms.
## Getting Started
Like any dependency from [crates.io](https://crates.io/), use `cargo` to add it to your `Cargo.toml` file:
```sh
cargo add bevy_platform_support
```
Now, instead of importing from `std` you can use `bevy_platform_support` for items it has alternative for.
See the documentation for what items are available, and explanations for _why_ you may want to use them.
## `no_std` Support
By default, `bevy_platform_support` will activate the `std` feature, requiring access to the `std` crate for whichever platforms you're targeting.
To use this crate on `no_std` platforms, disable default features:
```toml
bevy_platform_support = { version = "x.y.z", default-features = false }
```
## Features
### `std` (_default_)
Enables usage of the standard library. Note that where this crate has alternatives to the standard library that it considers _better_ than what's provided, it will provide the alternative even when `std` is enabled.
This is explicitly incompatible with `no_std` targets.
### `alloc` (_default_)
Enables usage of the [`alloc`](https://doc.rust-lang.org/stable/alloc/) crate. Note that this feature is automatically enabled when enabling `std`.
This is compatible with most `no_std` targets, but not all.
### `portable-atomic`
Switches to using [`portable-atomic`](https://docs.rs/portable-atomic/latest/portable_atomic/) as a backend for atomic types, such as `Arc`, `AtomicU8`, etc.
You may need to enable this feature on platforms without full support for atomic types or certain operations, such as [atomic CAS](https://en.wikipedia.org/wiki/Compare-and-swap).
### `critical-section`
Switches to using [`critical-section`](https://docs.rs/critical-section/latest/critical_section/) as a backend for synchronization.
You may need to enable this feature on platforms with little to no support for atomic operations, and is often paired with the `portable-atomic` feature.

View File

@ -0,0 +1,19 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
)]
#![no_std]
//! Platform compatibility support for first-party [Bevy] engine crates.
//!
//! [Bevy]: https://bevyengine.org/
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod sync;
pub mod time;

View File

@ -0,0 +1,30 @@
//! Provides various synchronization alternatives to language primitives.
#[cfg(feature = "alloc")]
pub use arc::{Arc, Weak};
pub mod atomic {
//! Provides various atomic alternatives to language primitives.
//!
//! Certain platforms lack complete atomic support, requiring the use of a fallback
//! such as `portable-atomic`.
//! Using these types will ensure the correct atomic provider is used without the need for
//! feature gates in your own code.
pub use atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16,
AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering,
};
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic;
#[cfg(feature = "portable-atomic")]
use portable_atomic as atomic;
}
#[cfg(all(feature = "alloc", feature = "portable-atomic"))]
use portable_atomic_util as arc;
#[cfg(all(feature = "alloc", not(feature = "portable-atomic")))]
use alloc::sync as arc;

View File

@ -0,0 +1,194 @@
//! Provides `Instant` for all platforms.
pub use time::Instant;
// TODO: Create a `web` feature to enable WASI compatibility.
// See https://github.com/bevyengine/bevy/issues/4906
#[cfg(target_arch = "wasm32")]
use web_time as time;
#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
use std::time;
#[cfg(all(not(target_arch = "wasm32"), not(feature = "std")))]
use fallback as time;
#[cfg(all(not(target_arch = "wasm32"), not(feature = "std")))]
mod fallback {
//! Provides a fallback implementation of `Instant` from the standard library.
#![expect(
unsafe_code,
reason = "Instant fallback requires unsafe to allow users to update the internal value"
)]
use crate::sync::atomic::{AtomicPtr, Ordering};
use core::{
fmt,
ops::{Add, AddAssign, Sub, SubAssign},
time::Duration,
};
static ELAPSED_GETTER: AtomicPtr<fn() -> Duration> = AtomicPtr::new(unset_getter as *mut _);
/// Fallback implementation of `Instant` suitable for a `no_std` environment.
///
/// If you are on any of the following target architectures, this is a drop-in replacement:
///
/// - `x86`
/// - `x86_64`
/// - `aarch64`
///
/// On any other architecture, you must call [`Instant::set_elapsed`], providing a method
/// which when called supplies a monotonically increasing count of elapsed nanoseconds relative
/// to some arbitrary point in time.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(Duration);
impl Instant {
/// Returns an instant corresponding to "now".
#[must_use]
pub fn now() -> Instant {
let getter = ELAPSED_GETTER.load(Ordering::Acquire);
// SAFETY: Function pointer is always valid
let getter = unsafe { *getter };
Self((getter)())
}
/// Provides a function returning the amount of time that has elapsed since execution began.
/// The getter provided to this method will be used by [`now`](Instant::now).
///
/// # Safety
///
/// - The function provided must accurately represent the elapsed time.
/// - The function must preserve all invariants of the [`Instant`] type.
/// - The pointer to the function must be valid whenever [`Instant::now`] is called.
pub unsafe fn set_elapsed(getter: *mut fn() -> Duration) {
ELAPSED_GETTER.store(getter, Ordering::Release);
}
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
#[must_use]
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.saturating_duration_since(earlier)
}
/// Returns the amount of time elapsed from another instant to this one,
/// or None if that instant is later than this one.
///
/// Due to monotonicity bugs, even under correct logical ordering of the passed `Instant`s,
/// this method can return `None`.
#[must_use]
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
self.0.checked_sub(earlier.0)
}
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
#[must_use]
pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
self.0.saturating_sub(earlier.0)
}
/// Returns the amount of time elapsed since this instant.
#[must_use]
pub fn elapsed(&self) -> Duration {
self.saturating_duration_since(Instant::now())
}
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
self.0.checked_add(duration).map(Instant)
}
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
self.0.checked_sub(duration).map(Instant)
}
}
impl Add<Duration> for Instant {
type Output = Instant;
/// # Panics
///
/// This function may panic if the resulting point in time cannot be represented by the
/// underlying data structure. See [`Instant::checked_add`] for a version without panic.
fn add(self, other: Duration) -> Instant {
self.checked_add(other)
.expect("overflow when adding duration to instant")
}
}
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, other: Duration) -> Instant {
self.checked_sub(other)
.expect("overflow when subtracting duration from instant")
}
}
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
fn sub(self, other: Instant) -> Duration {
self.duration_since(other)
}
}
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn unset_getter() -> Duration {
let _nanos: u64;
#[cfg(target_arch = "x86")]
unsafe {
_nanos = core::arch::x86::_rdtsc();
}
#[cfg(target_arch = "x86_64")]
unsafe {
_nanos = core::arch::x86_64::_rdtsc();
}
#[cfg(target_arch = "aarch64")]
unsafe {
let mut ticks: u64;
core::arch::asm!("mrs {}, cntvct_el0", out(reg) ticks);
_nanos = ticks;
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
panic!("An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`");
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
return Duration::from_nanos(_nanos);
}
}

View File

@ -11,6 +11,48 @@ rust-version = "1.81.0"
[features] [features]
default = ["std", "smallvec", "debug"] default = ["std", "smallvec", "debug"]
# Features
## When enabled, allows documentation comments to be accessed via reflection
documentation = ["bevy_reflect_derive/documentation"]
## Enables function reflection
functions = ["bevy_reflect_derive/functions"]
# When enabled, provides Bevy-related reflection implementations
bevy = ["smallvec", "smol_str"]
# Debugging Features
## Enables features useful for debugging reflection
debug = ["debug_stack"]
## When enabled, keeps track of the current serialization/deserialization context for better error messages
debug_stack = []
# Integrations
## Adds reflection support to `glam` types.
glam = ["dep:glam"]
## Adds reflection support to `petgraph` types.
petgraph = ["dep:petgraph", "std"]
## Adds reflection support to `smallvec` types.
smallvec = ["dep:smallvec"]
## Adds reflection support to `uuid` types.
uuid = ["dep:uuid"]
## Adds reflection support to `wgpu-types` types.
wgpu-types = ["dep:wgpu-types", "std"]
# 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 = [ std = [
"bevy_utils/std", "bevy_utils/std",
"erased-serde/std", "erased-serde/std",
@ -20,22 +62,23 @@ std = [
"glam?/std", "glam?/std",
"smol_str?/std", "smol_str?/std",
"uuid?/std", "uuid?/std",
"bevy_platform_support/std",
]
## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = [
"bevy_platform_support/critical-section",
"bevy_utils/critical-section",
]
## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_platform_support/portable-atomic",
"spin/portable_atomic",
"bevy_utils/portable-atomic",
] ]
# When enabled, provides Bevy-related reflection implementations
bevy = ["smallvec", "smol_str"]
glam = ["dep:glam"]
petgraph = ["dep:petgraph", "std"]
smallvec = ["dep:smallvec"]
uuid = ["dep:uuid"]
wgpu-types = ["dep:wgpu-types", "std"]
# Enables features useful for debugging reflection
debug = ["debug_stack"]
# When enabled, keeps track of the current serialization/deserialization context for better error messages
debug_stack = []
# When enabled, allows documentation comments to be accessed via reflection
documentation = ["bevy_reflect_derive/documentation"]
# Enables function reflection
functions = ["bevy_reflect_derive/functions"]
[dependencies] [dependencies]
# bevy # bevy
@ -44,6 +87,9 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features
"alloc", "alloc",
] } ] }
bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" } bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
# used by bevy-utils, but it also needs reflect impls # used by bevy-utils, but it also needs reflect impls
foldhash = { version = "0.1.3", default-features = false } foldhash = { version = "0.1.3", default-features = false }
@ -62,7 +108,6 @@ spin = { version = "0.9.8", default-features = false, features = [
"rwlock", "rwlock",
] } ] }
assert_type_match = "0.1.1" assert_type_match = "0.1.1"
smallvec = { version = "1.11", default-features = false, optional = true } smallvec = { version = "1.11", default-features = false, optional = true }
glam = { version = "0.29", default-features = false, features = [ glam = { version = "0.29", default-features = false, features = [
"serde", "serde",

View File

@ -4,7 +4,8 @@ use crate::{
type_info::impl_type_methods, type_info::impl_type_methods,
DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType, DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType,
}; };
use alloc::{boxed::Box, format, string::String, sync::Arc}; use alloc::{boxed::Box, format, string::String};
use bevy_platform_support::sync::Arc;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use core::slice::Iter; use core::slice::Iter;

View File

@ -3,7 +3,7 @@ use crate::{
NamedField, UnnamedField, NamedField, UnnamedField,
}; };
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::sync::Arc; use bevy_platform_support::sync::Arc;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use core::slice::Iter; use core::slice::Iter;
use thiserror::Error; use thiserror::Error;

View File

@ -3,7 +3,7 @@ use crate::{
type_info::impl_type_methods, type_info::impl_type_methods,
MaybeTyped, PartialReflect, Type, TypeInfo, TypePath, MaybeTyped, PartialReflect, Type, TypeInfo, TypePath,
}; };
use alloc::sync::Arc; use bevy_platform_support::sync::Arc;
/// The named field of a reflected struct. /// The named field of a reflected struct.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

@ -11,7 +11,8 @@ use crate::{
ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
ReflectRef, TypeInfo, TypePath, ReflectRef, TypeInfo, TypePath,
}; };
use alloc::{borrow::Cow, boxed::Box, sync::Arc}; use alloc::{borrow::Cow, boxed::Box};
use bevy_platform_support::sync::Arc;
use bevy_reflect_derive::impl_type_path; use bevy_reflect_derive::impl_type_path;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
@ -92,8 +93,21 @@ impl<'env> DynamicFunction<'env> {
func: F, func: F,
info: impl TryInto<FunctionInfo, Error: Debug>, info: impl TryInto<FunctionInfo, Error: Debug>,
) -> Self { ) -> Self {
let arc = Arc::new(func);
#[cfg(feature = "portable-atomic")]
#[expect(
unsafe_code,
reason = "unsized coercion is an unstable feature for non-std types"
)]
// SAFETY:
// - Coercion from `T` to `dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env`
// is valid as `T: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env`
// - `Arc::from_raw` receives a valid pointer from a previous call to `Arc::into_raw`
let arc = unsafe { ArcFn::<'env>::from_raw(Arc::into_raw(arc) as *const _) };
Self { Self {
internal: DynamicFunctionInternal::new(Arc::new(func), info.try_into().unwrap()), internal: DynamicFunctionInternal::new(arc, info.try_into().unwrap()),
} }
} }

View File

@ -1,4 +1,5 @@
use alloc::{borrow::Cow, boxed::Box, sync::Arc}; use alloc::{borrow::Cow, boxed::Box};
use bevy_platform_support::sync::Arc;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
use crate::func::{ use crate::func::{

View File

@ -1,9 +1,9 @@
use alloc::{borrow::Cow, sync::Arc}; use alloc::borrow::Cow;
use bevy_platform_support::sync::Arc;
use bevy_utils::HashMap;
use core::fmt::Debug; use core::fmt::Debug;
use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
use bevy_utils::HashMap;
use crate::func::{ use crate::func::{
ArgList, DynamicFunction, FunctionRegistrationError, FunctionResult, IntoFunction, ArgList, DynamicFunction, FunctionRegistrationError, FunctionResult, IntoFunction,
}; };

View File

@ -1,6 +1,7 @@
use crate::type_info::impl_type_methods; use crate::type_info::impl_type_methods;
use crate::{Reflect, Type, TypePath}; use crate::{Reflect, Type, TypePath};
use alloc::{borrow::Cow, boxed::Box, sync::Arc}; use alloc::{borrow::Cow, boxed::Box};
use bevy_platform_support::sync::Arc;
use core::ops::Deref; use core::ops::Deref;
use derive_more::derive::From; use derive_more::derive::From;
@ -180,7 +181,19 @@ impl ConstParamInfo {
/// Sets the default value for the parameter. /// Sets the default value for the parameter.
pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self { pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self {
self.default = Some(Arc::new(default)); let arc = Arc::new(default);
#[cfg(feature = "portable-atomic")]
#[expect(
unsafe_code,
reason = "unsized coercion is an unstable feature for non-std types"
)]
// SAFETY:
// - Coercion from `T` to `dyn Reflect` is valid as `T: Reflect + 'static`
// - `Arc::from_raw` receives a valid pointer from a previous call to `Arc::into_raw`
let arc = unsafe { Arc::from_raw(Arc::into_raw(arc) as *const dyn Reflect) };
self.default = Some(arc);
self self
} }

View File

@ -127,7 +127,9 @@ impl_reflect_opaque!(::core::time::Duration(
Default Default
)); ));
#[cfg(any(target_arch = "wasm32", feature = "std"))] #[cfg(any(target_arch = "wasm32", feature = "std"))]
impl_reflect_opaque!(::bevy_utils::Instant(Debug, Hash, PartialEq)); impl_reflect_opaque!(::bevy_platform_support::time::Instant(
Debug, Hash, PartialEq
));
impl_reflect_opaque!(::core::num::NonZeroI128( impl_reflect_opaque!(::core::num::NonZeroI128(
Debug, Debug,
Hash, Hash,
@ -214,6 +216,11 @@ impl_reflect_opaque!(::core::num::NonZeroI8(
)); ));
impl_reflect_opaque!(::core::num::Wrapping<T: Clone + Send + Sync>()); impl_reflect_opaque!(::core::num::Wrapping<T: Clone + Send + Sync>());
impl_reflect_opaque!(::core::num::Saturating<T: Clone + Send + Sync>()); impl_reflect_opaque!(::core::num::Saturating<T: Clone + Send + Sync>());
impl_reflect_opaque!(::bevy_platform_support::sync::Arc<T: Send + Sync + ?Sized>);
// We check despite `portable-atomic` being enabled, if the standard library `Arc` is
// also available, and implement Reflect for it.
#[cfg(all(feature = "portable-atomic", target_has_atomic = "ptr"))]
impl_reflect_opaque!(::alloc::sync::Arc<T: Send + Sync + ?Sized>); impl_reflect_opaque!(::alloc::sync::Arc<T: Send + Sync + ?Sized>);
// `Serialize` and `Deserialize` only for platforms supported by serde: // `Serialize` and `Deserialize` only for platforms supported by serde:
@ -2432,7 +2439,8 @@ mod tests {
TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType, TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType,
}; };
use alloc::{collections::BTreeMap, string::String, vec}; use alloc::{collections::BTreeMap, string::String, vec};
use bevy_utils::{HashMap, Instant}; use bevy_platform_support::time::Instant;
use bevy_utils::HashMap;
use core::{ use core::{
f32::consts::{PI, TAU}, f32::consts::{PI, TAU},
time::Duration, time::Duration,

View File

@ -189,7 +189,8 @@ mod tests {
use crate::serde::{DeserializeWithRegistry, ReflectDeserializeWithRegistry}; use crate::serde::{DeserializeWithRegistry, ReflectDeserializeWithRegistry};
use crate::serde::{ReflectSerializeWithRegistry, SerializeWithRegistry}; use crate::serde::{ReflectSerializeWithRegistry, SerializeWithRegistry};
use crate::{ReflectFromReflect, TypePath}; use crate::{ReflectFromReflect, TypePath};
use alloc::{format, string::String, sync::Arc, vec, vec::Vec}; use alloc::{format, string::String, vec, vec::Vec};
use bevy_platform_support::sync::Arc;
use bevy_reflect_derive::reflect_trait; use bevy_reflect_derive::reflect_trait;
use core::any::TypeId; use core::any::TypeId;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
@ -336,6 +337,22 @@ mod tests {
registry registry
} }
fn create_arc_dyn_enemy<T: Enemy>(enemy: T) -> Arc<dyn Enemy> {
let arc = Arc::new(enemy);
#[cfg(feature = "portable-atomic")]
#[expect(
unsafe_code,
reason = "unsized coercion is an unstable feature for non-std types"
)]
// SAFETY:
// - Coercion from `T` to `dyn Enemy` is valid as `T: Enemy + 'static`
// - `Arc::from_raw` receives a valid pointer from a previous call to `Arc::into_raw`
let arc = unsafe { Arc::from_raw(Arc::into_raw(arc) as *const dyn Enemy) };
arc
}
#[test] #[test]
fn should_serialize_with_serialize_with_registry() { fn should_serialize_with_serialize_with_registry() {
let registry = create_registry(); let registry = create_registry();
@ -343,8 +360,8 @@ mod tests {
let level = Level { let level = Level {
name: String::from("Level 1"), name: String::from("Level 1"),
enemies: EnemyList(vec![ enemies: EnemyList(vec![
Arc::new(Skeleton(10)), create_arc_dyn_enemy(Skeleton(10)),
Arc::new(Zombie { create_arc_dyn_enemy(Zombie {
health: 20, health: 20,
walk_speed: 0.5, walk_speed: 0.5,
}), }),
@ -374,8 +391,8 @@ mod tests {
let expected = Level { let expected = Level {
name: String::from("Level 1"), name: String::from("Level 1"),
enemies: EnemyList(vec![ enemies: EnemyList(vec![
Arc::new(Skeleton(10)), create_arc_dyn_enemy(Skeleton(10)),
Arc::new(Zombie { create_arc_dyn_enemy(Zombie {
health: 20, health: 20,
walk_speed: 0.5, walk_speed: 0.5,
}), }),
@ -388,8 +405,8 @@ mod tests {
let unexpected = Level { let unexpected = Level {
name: String::from("Level 1"), name: String::from("Level 1"),
enemies: EnemyList(vec![ enemies: EnemyList(vec![
Arc::new(Skeleton(20)), create_arc_dyn_enemy(Skeleton(20)),
Arc::new(Zombie { create_arc_dyn_enemy(Zombie {
health: 20, health: 20,
walk_speed: 5.0, walk_speed: 5.0,
}), }),

View File

@ -6,7 +6,8 @@ use crate::{
ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut, ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut,
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
}; };
use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec}; use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use bevy_platform_support::sync::Arc;
use bevy_reflect_derive::impl_type_path; use bevy_reflect_derive::impl_type_path;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use core::{ use core::{

View File

@ -8,7 +8,8 @@ use crate::{
ApplyError, DynamicTuple, Generics, PartialReflect, Reflect, ReflectKind, ReflectMut, ApplyError, DynamicTuple, Generics, PartialReflect, Reflect, ReflectKind, ReflectMut,
ReflectOwned, ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField, ReflectOwned, ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField,
}; };
use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use bevy_platform_support::sync::Arc;
use core::{ use core::{
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
slice::Iter, slice::Iter,

View File

@ -1,6 +1,6 @@
use crate::{serde::Serializable, FromReflect, Reflect, TypeInfo, TypePath, Typed}; use crate::{serde::Serializable, FromReflect, Reflect, TypeInfo, TypePath, Typed};
use alloc::sync::Arc;
use alloc::{boxed::Box, string::String}; use alloc::{boxed::Box, string::String};
use bevy_platform_support::sync::Arc;
use bevy_ptr::{Ptr, PtrMut}; use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::{HashMap, HashSet, TypeIdMap}; use bevy_utils::{HashMap, HashSet, TypeIdMap};
use core::{ use core::{

View File

@ -56,6 +56,9 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.16.0-dev" } bevy_image = { path = "../bevy_image", version = "0.16.0-dev" }
bevy_mesh = { path = "../bevy_mesh", version = "0.16.0-dev" } bevy_mesh = { path = "../bevy_mesh", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
# rendering # rendering
image = { version = "0.25.2", default-features = false } image = { version = "0.25.2", default-features = false }

View File

@ -7,7 +7,7 @@ use std::thread::{self, ThreadId};
use bevy_diagnostic::{Diagnostic, DiagnosticMeasurement, DiagnosticPath, DiagnosticsStore}; use bevy_diagnostic::{Diagnostic, DiagnosticMeasurement, DiagnosticPath, DiagnosticsStore};
use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_utils::Instant; use bevy_platform_support::time::Instant;
use std::sync::Mutex; use std::sync::Mutex;
use wgpu::{ use wgpu::{
Buffer, BufferDescriptor, BufferUsages, CommandEncoder, ComputePass, Features, MapMode, Buffer, BufferDescriptor, BufferUsages, CommandEncoder, ComputePass, Features, MapMode,

View File

@ -18,8 +18,8 @@ use crate::{
}; };
use alloc::sync::Arc; use alloc::sync::Arc;
use bevy_ecs::{prelude::*, system::SystemState}; use bevy_ecs::{prelude::*, system::SystemState};
use bevy_platform_support::time::Instant;
use bevy_time::TimeSender; use bevy_time::TimeSender;
use bevy_utils::Instant;
use wgpu::{ use wgpu::{
Adapter, AdapterInfo, CommandBuffer, CommandEncoder, DeviceType, Instance, Queue, Adapter, AdapterInfo, CommandBuffer, CommandEncoder, DeviceType, Instance, Queue,
RequestAdapterOptions, RequestAdapterOptions,

View File

@ -36,6 +36,7 @@ critical-section = [
"bevy_ecs/critical-section", "bevy_ecs/critical-section",
"bevy_utils/critical-section", "bevy_utils/critical-section",
"bevy_app?/critical-section", "bevy_app?/critical-section",
"bevy_reflect?/critical-section",
] ]
## `portable-atomic` provides additional platform support for atomic types and ## `portable-atomic` provides additional platform support for atomic types and
@ -44,6 +45,7 @@ portable-atomic = [
"bevy_ecs/portable-atomic", "bevy_ecs/portable-atomic",
"bevy_utils/portable-atomic", "bevy_utils/portable-atomic",
"bevy_app?/portable-atomic", "bevy_app?/portable-atomic",
"bevy_reflect?/portable-atomic",
] ]
[dependencies] [dependencies]

View File

@ -15,25 +15,27 @@ std = [
"async-task/std", "async-task/std",
"spin/std", "spin/std",
"edge-executor?/std", "edge-executor?/std",
"portable-atomic-util?/std", "bevy_platform_support/std",
] ]
multi_threaded = ["std", "dep:async-channel", "dep:concurrent-queue"] multi_threaded = ["std", "dep:async-channel", "dep:concurrent-queue"]
async_executor = ["std", "dep:async-executor"] async_executor = ["std", "dep:async-executor"]
edge_executor = ["dep:edge-executor"] edge_executor = ["dep:edge-executor"]
critical-section = [ critical-section = [
"dep:critical-section", "bevy_platform_support/critical-section",
"edge-executor?/critical-section", "edge-executor?/critical-section",
"portable-atomic?/critical-section",
] ]
portable-atomic = [ portable-atomic = [
"dep:portable-atomic", "bevy_platform_support/portable-atomic",
"dep:portable-atomic-util",
"edge-executor?/portable-atomic", "edge-executor?/portable-atomic",
"async-task/portable-atomic", "async-task/portable-atomic",
"spin/portable_atomic", "spin/portable_atomic",
] ]
[dependencies] [dependencies]
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
futures-lite = { version = "2.0.1", default-features = false, features = [ futures-lite = { version = "2.0.1", default-features = false, features = [
"alloc", "alloc",
] } ] }
@ -53,13 +55,6 @@ edge-executor = { version = "0.4.1", default-features = false, optional = true }
async-channel = { version = "2.3.0", optional = true } async-channel = { version = "2.3.0", optional = true }
async-io = { version = "2.0.0", optional = true } async-io = { version = "2.0.0", optional = true }
concurrent-queue = { version = "2.0.0", optional = true } concurrent-queue = { version = "2.0.0", optional = true }
critical-section = { version = "1.2.0", optional = true }
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
], optional = true }
portable-atomic-util = { version = "0.2.4", features = [
"alloc",
], optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"

View File

@ -1,4 +1,5 @@
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use bevy_platform_support::sync::Arc;
use core::{cell::RefCell, future::Future, marker::PhantomData, mem}; use core::{cell::RefCell, future::Future, marker::PhantomData, mem};
use crate::Task; use crate::Task;
@ -6,12 +7,6 @@ use crate::Task;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::thread_local; use std::thread_local;
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
#[cfg(all( #[cfg(all(
feature = "std", feature = "std",
any(feature = "async_executor", feature = "edge_executor") any(feature = "async_executor", feature = "edge_executor")

View File

@ -6,15 +6,10 @@ use std::{
}; };
use crate::executor::FallibleTask; use crate::executor::FallibleTask;
use bevy_platform_support::sync::Arc;
use concurrent_queue::ConcurrentQueue; use concurrent_queue::ConcurrentQueue;
use futures_lite::FutureExt; use futures_lite::FutureExt;
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
use crate::{ use crate::{
block_on, block_on,
thread_executor::{ThreadExecutor, ThreadExecutorTicker}, thread_executor::{ThreadExecutor, ThreadExecutorTicker},

View File

@ -21,7 +21,9 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
"bevy", "bevy",
], optional = true } ], optional = true }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
# other # other
crossbeam-channel = "0.5.0" crossbeam-channel = "0.5.0"

View File

@ -35,7 +35,7 @@ use bevy_ecs::{
event::{event_update_system, signal_event_update_system, EventRegistry, ShouldUpdateEvents}, event::{event_update_system, signal_event_update_system, EventRegistry, ShouldUpdateEvents},
prelude::*, prelude::*,
}; };
use bevy_utils::Instant; use bevy_platform_support::time::Instant;
use core::time::Duration; use core::time::Duration;
pub use crossbeam_channel::TrySendError; pub use crossbeam_channel::TrySendError;
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};

View File

@ -1,6 +1,6 @@
use bevy_platform_support::time::Instant;
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::Instant;
use core::time::Duration; use core::time::Duration;
use crate::time::Time; use crate::time::Time;

View File

@ -160,7 +160,7 @@ use {
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*; /// # use bevy_time::prelude::*;
/// # use bevy_utils::Instant; /// # use bevy_platform_support::time::Instant;
/// # /// #
/// #[derive(Debug)] /// #[derive(Debug)]
/// struct Custom { /// struct Custom {

View File

@ -21,37 +21,32 @@ serde = ["hashbrown/serde"]
## Allows access to the `std` crate. Enabling this feature will prevent compilation ## Allows access to the `std` crate. Enabling this feature will prevent compilation
## on `no_std` targets, but provides access to certain additional features on ## on `no_std` targets, but provides access to certain additional features on
## supported platforms. ## supported platforms.
std = ["alloc", "foldhash/std", "dep:thread_local"] std = ["alloc", "bevy_platform_support/std", "foldhash/std", "dep:thread_local"]
## Allows access to the `alloc` crate. ## Allows access to the `alloc` crate.
alloc = ["hashbrown"] alloc = ["bevy_platform_support/alloc", "hashbrown"]
## `critical-section` provides the building blocks for synchronization primitives ## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`. ## on all platforms, including `no_std`.
critical-section = ["portable-atomic?/critical-section"] critical-section = ["bevy_platform_support/critical-section"]
## `portable-atomic` provides additional platform support for atomic types and ## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support. ## operations, even on targets without native support.
portable-atomic = ["dep:portable-atomic"] portable-atomic = ["bevy_platform_support/portable-atomic"]
[dependencies] [dependencies]
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false }
foldhash = { version = "0.1.3", default-features = false } foldhash = { version = "0.1.3", default-features = false }
hashbrown = { version = "0.15.1", features = [ hashbrown = { version = "0.15.1", features = [
"equivalent", "equivalent",
"raw-entry", "raw-entry",
], optional = true, default-features = false } ], optional = true, default-features = false }
thread_local = { version = "1.0", optional = true } thread_local = { version = "1.0", optional = true }
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
], optional = true }
[dev-dependencies] [dev-dependencies]
static_assertions = "1.1.0" static_assertions = "1.1.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.0", features = ["js"] }
web-time = { version = "1.1" }
[lints] [lints]
workspace = true workspace = true

View File

@ -29,7 +29,6 @@ mod default;
mod once; mod once;
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod parallel_queue; mod parallel_queue;
mod time;
#[doc(hidden)] #[doc(hidden)]
pub use once::OnceFlag; pub use once::OnceFlag;
@ -59,8 +58,6 @@ pub use foldhash::fast::{FixedState, FoldHasher as DefaultHasher, RandomState};
pub use hashbrown; pub use hashbrown;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use parallel_queue::*; pub use parallel_queue::*;
#[cfg(any(feature = "std", target_arch = "wasm32"))]
pub use time::*;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use core::any::TypeId; use core::any::TypeId;

View File

@ -1,8 +1,4 @@
#[cfg(feature = "portable-atomic")] use bevy_platform_support::sync::atomic::{AtomicBool, Ordering};
use portable_atomic::{AtomicBool, Ordering};
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic::{AtomicBool, Ordering};
/// Wrapper around an [`AtomicBool`], abstracting the backing implementation and /// Wrapper around an [`AtomicBool`], abstracting the backing implementation and
/// ordering considerations. /// ordering considerations.

View File

@ -1,5 +0,0 @@
#[cfg(target_arch = "wasm32")]
pub use web_time::Instant;
#[cfg(all(not(target_arch = "wasm32"), feature = "std"))]
pub use std::time::Instant;

View File

@ -34,6 +34,9 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.16.0-dev" } bevy_window = { path = "../bevy_window", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
# bevy optional # bevy optional
bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev", optional = true } bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev", optional = true }

View File

@ -20,11 +20,11 @@ use bevy_log::{error, trace, warn};
#[cfg(feature = "custom_cursor")] #[cfg(feature = "custom_cursor")]
use bevy_math::URect; use bevy_math::URect;
use bevy_math::{ivec2, DVec2, Vec2}; use bevy_math::{ivec2, DVec2, Vec2};
use bevy_platform_support::time::Instant;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use bevy_tasks::tick_global_task_pools_on_main_thread; use bevy_tasks::tick_global_task_pools_on_main_thread;
#[cfg(feature = "custom_cursor")] #[cfg(feature = "custom_cursor")]
use bevy_utils::HashMap; use bevy_utils::HashMap;
use bevy_utils::Instant;
use core::marker::PhantomData; use core::marker::PhantomData;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use winit::platform::web::EventLoopExtWebSys; use winit::platform::web::EventLoopExtWebSys;

View File

@ -1,5 +1,6 @@
# if crate A depends on crate B, B must come before A in this list # if crate A depends on crate B, B must come before A in this list
crates=( crates=(
bevy_platform_support
bevy_utils bevy_utils
bevy_ptr bevy_ptr
bevy_macro_utils bevy_macro_utils