diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 09b92d5cbc9ac..e4fb9748c7ebc 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -48,23 +48,25 @@ std = [ "downcast-rs/std", "bevy_utils/std", "bevy_tasks?/std", + "bevy_platform_support/std", ] ## `critical-section` provides the building blocks for synchronization primitives ## on all platforms, including `no_std`. critical-section = [ - "portable-atomic?/critical-section", "bevy_tasks?/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 ## operations, even on targets without native support. portable-atomic = [ - "dep:portable-atomic", - "dep:portable-atomic-util", "bevy_tasks?/portable-atomic", "bevy_ecs/portable-atomic", + "bevy_platform_support/portable-atomic", + "bevy_reflect?/portable-atomic", ] [dependencies] @@ -76,6 +78,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features "alloc", ] } 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 downcast-rs = { version = "2", default-features = false } @@ -83,12 +86,6 @@ thiserror = { version = "2", default-features = false } variadics_please = "1.1" tracing = { version = "0.1", default-features = false, optional = true } 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] ctrlc = { version = "3.4.4", optional = true } diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index 21ff6669ef710..2a0e1f966a49e 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -3,11 +3,9 @@ use crate::{ plugin::Plugin, PluginsState, }; +use bevy_platform_support::time::Instant; use core::time::Duration; -#[cfg(any(target_arch = "wasm32", feature = "std"))] -use bevy_utils::Instant; - #[cfg(target_arch = "wasm32")] use { alloc::{boxed::Box, rc::Rc}, @@ -100,7 +98,6 @@ impl Plugin for ScheduleRunnerPlugin { let tick = move |app: &mut App, _wait: Option| -> Result, AppExit> { - #[cfg(any(target_arch = "wasm32", feature = "std"))] let start_time = Instant::now(); app.update(); @@ -109,10 +106,8 @@ impl Plugin for ScheduleRunnerPlugin { return Err(exit); }; - #[cfg(any(target_arch = "wasm32", feature = "std"))] let end_time = Instant::now(); - #[cfg(any(target_arch = "wasm32", feature = "std"))] if let Some(wait) = _wait { let exe_time = end_time - start_time; if exe_time < wait { diff --git a/crates/bevy_app/src/task_pool_plugin.rs b/crates/bevy_app/src/task_pool_plugin.rs index a4bf9d12dc9a8..d2146d9a65a46 100644 --- a/crates/bevy_app/src/task_pool_plugin.rs +++ b/crates/bevy_app/src/task_pool_plugin.rs @@ -2,13 +2,14 @@ feature = "portable-atomic", expect( 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 alloc::string::ToString; +use bevy_platform_support::sync::Arc; use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder}; use core::{fmt::Debug, marker::PhantomData}; use log::trace; @@ -16,12 +17,6 @@ use log::trace; #[cfg(not(target_arch = "wasm32"))] 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"))] use bevy_tasks::tick_global_task_pools_on_main_thread; diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index 0042b77180deb..657f308ac0561 100644 --- a/crates/bevy_core_pipeline/Cargo.toml +++ b/crates/bevy_core_pipeline/Cargo.toml @@ -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_utils = { path = "../bevy_utils", 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"] } bitflags = "2.3" diff --git a/crates/bevy_core_pipeline/src/oit/mod.rs b/crates/bevy_core_pipeline/src/oit/mod.rs index 29c5660785f6d..94774ddf52822 100644 --- a/crates/bevy_core_pipeline/src/oit/mod.rs +++ b/crates/bevy_core_pipeline/src/oit/mod.rs @@ -4,6 +4,7 @@ use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, Handle}; use bevy_ecs::{component::*, prelude::*}; use bevy_math::UVec2; +use bevy_platform_support::time::Instant; use bevy_reflect::Reflect; use bevy_render::{ camera::{Camera, ExtractedCamera}, @@ -16,7 +17,7 @@ use bevy_render::{ view::Msaa, Render, RenderApp, RenderSet, }; -use bevy_utils::{HashSet, Instant}; +use bevy_utils::HashSet; use bevy_window::PrimaryWindow; use resolve::{ node::{OitResolveNode, OitResolvePass}, diff --git a/crates/bevy_diagnostic/Cargo.toml b/crates/bevy_diagnostic/Cargo.toml index 9423b08880aff..6c17064ecada1 100644 --- a/crates/bevy_diagnostic/Cargo.toml +++ b/crates/bevy_diagnostic/Cargo.toml @@ -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_utils = { path = "../bevy_utils", 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 const-fnv1a-hash = "1.1.0" diff --git a/crates/bevy_diagnostic/src/diagnostic.rs b/crates/bevy_diagnostic/src/diagnostic.rs index 1c641c88a837a..88b1f650e33a5 100644 --- a/crates/bevy_diagnostic/src/diagnostic.rs +++ b/crates/bevy_diagnostic/src/diagnostic.rs @@ -6,7 +6,8 @@ use core::{ use bevy_app::{App, SubApp}; 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 crate::DEFAULT_MAX_HISTORY_LENGTH; diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 3f52188d1cee6..66d2643124a5b 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -76,24 +76,25 @@ std = [ "nonmax/std", "arrayvec?/std", "log/std", + "bevy_platform_support/std", ] ## `critical-section` provides the building blocks for synchronization primitives ## on all platforms, including `no_std`. critical-section = [ - "dep: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 ## operations, even on targets without native support. portable-atomic = [ - "dep:portable-atomic", - "dep:portable-atomic-util", "bevy_tasks?/portable-atomic", + "bevy_platform_support/portable-atomic", "concurrent-queue/portable-atomic", "spin/portable_atomic", + "bevy_reflect?/portable-atomic", ] [dependencies] @@ -104,6 +105,9 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features "alloc", ] } 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 } 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"] } indexmap = { version = "2.5.0", 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_mutex", "rwlock", diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 66d5db98f8fcb..27d090fe6ccd4 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -15,6 +15,7 @@ use crate::{ use alloc::boxed::Box; use alloc::{borrow::Cow, format, vec::Vec}; pub use bevy_ecs_macros::Component; +use bevy_platform_support::sync::Arc; use bevy_ptr::{OwningPtr, UnsafeCellDeref}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; @@ -32,12 +33,6 @@ use core::{ use disqualified::ShortName; 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; /// A data type that can be used to store data for an [entity]. diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 2b51c1f937b34..a06520717ba7f 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,19 +1,13 @@ use alloc::{borrow::ToOwned, vec::Vec}; +use bevy_platform_support::sync::Arc; use bevy_ptr::{Ptr, PtrMut}; +use bevy_utils::{HashMap, HashSet}; use bumpalo::Bump; use core::{any::TypeId, ptr::NonNull}; -use bevy_utils::{HashMap, HashSet}; - #[cfg(feature = "bevy_reflect")] use alloc::boxed::Box; -#[cfg(feature = "portable-atomic")] -use portable_atomic_util::Arc; - -#[cfg(not(feature = "portable-atomic"))] -use alloc::sync::Arc; - use crate::{ bundle::Bundle, component::{Component, ComponentCloneHandler, ComponentId, ComponentInfo, Components}, diff --git a/crates/bevy_ecs/src/entity/entity_set.rs b/crates/bevy_ecs/src/entity/entity_set.rs index e2d77a98fd46a..8b1f47779ef86 100644 --- a/crates/bevy_ecs/src/entity/entity_set.rs +++ b/crates/bevy_ecs/src/entity/entity_set.rs @@ -13,11 +13,7 @@ use core::{ use super::Entity; -#[cfg(feature = "portable-atomic")] -use portable_atomic_util::Arc; - -#[cfg(not(feature = "portable-atomic"))] -use alloc::sync::Arc; +use bevy_platform_support::sync::Arc; /// A trait for entity borrows. /// diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 946b6821a4e40..fd9b4a39bf549 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -70,6 +70,7 @@ use crate::{ storage::{SparseSetIndex, TableId, TableRow}, }; use alloc::vec::Vec; +use bevy_platform_support::sync::atomic::Ordering; use core::{fmt, hash::Hash, mem, num::NonZero}; use log::warn; @@ -79,26 +80,16 @@ use core::panic::Location; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; -#[cfg(not(feature = "portable-atomic"))] -use core::sync::atomic::Ordering; - -#[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")] +use bevy_platform_support::sync::atomic::AtomicI64 as AtomicIdCursor; #[cfg(target_has_atomic = "64")] type IdCursor = i64; /// 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 /// the caveat that some conversions may fail (and panic) at runtime. -#[cfg(all(not(target_has_atomic = "64"), not(feature = "portable-atomic")))] -use core::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"))] +use bevy_platform_support::sync::atomic::AtomicIsize as AtomicIdCursor; #[cfg(not(target_has_atomic = "64"))] type IdCursor = isize; diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index a9eaea7311789..cfa3dc8398431 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -1,4 +1,5 @@ use alloc::{boxed::Box, vec::Vec}; +use bevy_platform_support::sync::Arc; use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor}; use bevy_utils::{default, syncunsafecell::SyncUnsafeCell}; use concurrent_queue::ConcurrentQueue; @@ -12,12 +13,6 @@ use std::{ #[cfg(feature = "trace")] 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::{ archetype::ArchetypeComponentId, prelude::Resource, diff --git a/crates/bevy_ecs/src/world/identifier.rs b/crates/bevy_ecs/src/world/identifier.rs index 4ab38a2cdbb93..221ddd8210919 100644 --- a/crates/bevy_ecs/src/world/identifier.rs +++ b/crates/bevy_ecs/src/world/identifier.rs @@ -4,12 +4,7 @@ use crate::{ system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, world::{FromWorld, World}, }; - -#[cfg(not(feature = "portable-atomic"))] -use core::sync::atomic::{AtomicUsize, Ordering}; - -#[cfg(feature = "portable-atomic")] -use portable_atomic::{AtomicUsize, Ordering}; +use bevy_platform_support::sync::atomic::{AtomicUsize, Ordering}; use super::unsafe_world_cell::UnsafeWorldCell; diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 4b8be6cc1f3e1..82a82d407234c 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -53,23 +53,15 @@ use crate::{ }, }; use alloc::{boxed::Box, vec::Vec}; +use bevy_platform_support::sync::atomic::{AtomicU32, Ordering}; use bevy_ptr::{OwningPtr, Ptr}; -use core::{any::TypeId, fmt}; +use core::{any::TypeId, fmt, panic::Location}; use log::warn; - -#[cfg(not(feature = "portable-atomic"))] -use core::sync::atomic::{AtomicU32, Ordering}; - -#[cfg(feature = "portable-atomic")] -use portable_atomic::{AtomicU32, Ordering}; +use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}; #[cfg(feature = "track_location")] use bevy_ptr::UnsafeCellDeref; -use core::panic::Location; - -use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}; - /// Stores and exposes operations on [entities](Entity), [components](Component), resources, /// and their associated metadata. /// diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 94d36a3dc17fd..7bf9e9f53a4d0 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -15,19 +15,13 @@ use crate::{ system::Resource, world::RawCommandQueue, }; +use bevy_platform_support::sync::atomic::Ordering; 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 thiserror::Error; -#[cfg(not(feature = "portable-atomic"))] -use core::sync::atomic::Ordering; - -#[cfg(feature = "portable-atomic")] -use portable_atomic::Ordering; +#[cfg(feature = "track_location")] +use {bevy_ptr::UnsafeCellDeref, core::panic::Location}; /// 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. diff --git a/crates/bevy_input/Cargo.toml b/crates/bevy_input/Cargo.toml index aa868911e6ac8..eebed09a0a5d4 100644 --- a/crates/bevy_input/Cargo.toml +++ b/crates/bevy_input/Cargo.toml @@ -47,11 +47,19 @@ std = [ ## `critical-section` provides the building blocks for synchronization primitives ## 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 ## 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`. libm = ["bevy_math/libm"] diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index d3c66ee4c5291..9b4bdebd0138f 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -280,6 +280,7 @@ bevy_log = { path = "../bevy_log", version = "0.16.0-dev" } bevy_math = { path = "../bevy_math", version = "0.16.0-dev", features = [ "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_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [ "bevy", diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index e9d25adbdf8bb..0c826a7abaf95 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -47,6 +47,7 @@ pub use bevy_math as math; pub use bevy_pbr as pbr; #[cfg(feature = "bevy_picking")] pub use bevy_picking as picking; +pub use bevy_platform_support as platform_support; pub use bevy_ptr as ptr; pub use bevy_reflect as reflect; #[cfg(feature = "bevy_remote")] diff --git a/crates/bevy_picking/Cargo.toml b/crates/bevy_picking/Cargo.toml index 011f55c995080..71cc375c7132d 100644 --- a/crates/bevy_picking/Cargo.toml +++ b/crates/bevy_picking/Cargo.toml @@ -27,6 +27,9 @@ bevy_time = { path = "../bevy_time", 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_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 crossbeam-channel = { version = "0.5", optional = true } diff --git a/crates/bevy_picking/src/events.rs b/crates/bevy_picking/src/events.rs index ae3d879e9a47d..81fd633edf9a8 100644 --- a/crates/bevy_picking/src/events.rs +++ b/crates/bevy_picking/src/events.rs @@ -42,9 +42,10 @@ use core::{fmt::Debug, time::Duration}; use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal}; use bevy_hierarchy::Parent; use bevy_math::Vec2; +use bevy_platform_support::time::Instant; use bevy_reflect::prelude::*; use bevy_render::camera::NormalizedRenderTarget; -use bevy_utils::{HashMap, Instant}; +use bevy_utils::HashMap; use bevy_window::Window; use tracing::debug; diff --git a/crates/bevy_platform_support/Cargo.toml b/crates/bevy_platform_support/Cargo.toml new file mode 100644 index 0000000000000..91a7f4f6ca77f --- /dev/null +++ b/crates/bevy_platform_support/Cargo.toml @@ -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 diff --git a/crates/bevy_platform_support/README.md b/crates/bevy_platform_support/README.md new file mode 100644 index 0000000000000..7daf5afa5097e --- /dev/null +++ b/crates/bevy_platform_support/README.md @@ -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. diff --git a/crates/bevy_platform_support/src/lib.rs b/crates/bevy_platform_support/src/lib.rs new file mode 100644 index 0000000000000..d93295a28c96d --- /dev/null +++ b/crates/bevy_platform_support/src/lib.rs @@ -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; diff --git a/crates/bevy_platform_support/src/sync.rs b/crates/bevy_platform_support/src/sync.rs new file mode 100644 index 0000000000000..8fd47989da4be --- /dev/null +++ b/crates/bevy_platform_support/src/sync.rs @@ -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; diff --git a/crates/bevy_platform_support/src/time.rs b/crates/bevy_platform_support/src/time.rs new file mode 100644 index 0000000000000..5bf7678fd1e48 --- /dev/null +++ b/crates/bevy_platform_support/src/time.rs @@ -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 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 { + 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 { + 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 { + self.0.checked_sub(duration).map(Instant) + } + } + + impl Add 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 for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } + } + + impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other) + .expect("overflow when subtracting duration from instant") + } + } + + impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } + } + + impl Sub 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); + } +} diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 751a0cc825ff5..0ca295a4af9e6 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -11,6 +11,48 @@ rust-version = "1.81.0" [features] 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 = [ "bevy_utils/std", "erased-serde/std", @@ -20,22 +62,23 @@ std = [ "glam?/std", "smol_str?/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] # bevy @@ -44,6 +87,9 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features "alloc", ] } 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 foldhash = { version = "0.1.3", default-features = false } @@ -62,7 +108,6 @@ spin = { version = "0.9.8", default-features = false, features = [ "rwlock", ] } assert_type_match = "0.1.1" - smallvec = { version = "1.11", default-features = false, optional = true } glam = { version = "0.29", default-features = false, features = [ "serde", diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 696f389f6e5d1..7efd408286332 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -4,7 +4,8 @@ use crate::{ type_info::impl_type_methods, 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 core::slice::Iter; diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index 3397df50ddc92..e8c0d51b74082 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -3,7 +3,7 @@ use crate::{ NamedField, UnnamedField, }; use alloc::boxed::Box; -use alloc::sync::Arc; +use bevy_platform_support::sync::Arc; use bevy_utils::HashMap; use core::slice::Iter; use thiserror::Error; diff --git a/crates/bevy_reflect/src/fields.rs b/crates/bevy_reflect/src/fields.rs index ab7c3cd34db84..3a521c21ccbee 100644 --- a/crates/bevy_reflect/src/fields.rs +++ b/crates/bevy_reflect/src/fields.rs @@ -3,7 +3,7 @@ use crate::{ type_info::impl_type_methods, MaybeTyped, PartialReflect, Type, TypeInfo, TypePath, }; -use alloc::sync::Arc; +use bevy_platform_support::sync::Arc; /// The named field of a reflected struct. #[derive(Clone, Debug)] diff --git a/crates/bevy_reflect/src/func/dynamic_function.rs b/crates/bevy_reflect/src/func/dynamic_function.rs index 408cdda640015..3ed406fc2a017 100644 --- a/crates/bevy_reflect/src/func/dynamic_function.rs +++ b/crates/bevy_reflect/src/func/dynamic_function.rs @@ -11,7 +11,8 @@ use crate::{ ApplyError, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, 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 core::fmt::{Debug, Formatter}; @@ -92,8 +93,21 @@ impl<'env> DynamicFunction<'env> { func: F, info: impl TryInto, ) -> 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 { - internal: DynamicFunctionInternal::new(Arc::new(func), info.try_into().unwrap()), + internal: DynamicFunctionInternal::new(arc, info.try_into().unwrap()), } } diff --git a/crates/bevy_reflect/src/func/dynamic_function_mut.rs b/crates/bevy_reflect/src/func/dynamic_function_mut.rs index c9615f2d91ee0..5e167c5449d8c 100644 --- a/crates/bevy_reflect/src/func/dynamic_function_mut.rs +++ b/crates/bevy_reflect/src/func/dynamic_function_mut.rs @@ -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 crate::func::{ diff --git a/crates/bevy_reflect/src/func/registry.rs b/crates/bevy_reflect/src/func/registry.rs index 4bb38603fbea5..73455be0ed15f 100644 --- a/crates/bevy_reflect/src/func/registry.rs +++ b/crates/bevy_reflect/src/func/registry.rs @@ -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 std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use bevy_utils::HashMap; - use crate::func::{ ArgList, DynamicFunction, FunctionRegistrationError, FunctionResult, IntoFunction, }; diff --git a/crates/bevy_reflect/src/generics.rs b/crates/bevy_reflect/src/generics.rs index f64cefe2d9ed2..0f413d653dd3c 100644 --- a/crates/bevy_reflect/src/generics.rs +++ b/crates/bevy_reflect/src/generics.rs @@ -1,6 +1,7 @@ use crate::type_info::impl_type_methods; 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 derive_more::derive::From; @@ -180,7 +181,19 @@ impl ConstParamInfo { /// Sets the default value for the parameter. pub fn with_default(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 } diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index a765dbd4bf7b5..e201d20932cf8 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -127,7 +127,9 @@ impl_reflect_opaque!(::core::time::Duration( Default )); #[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( Debug, Hash, @@ -214,6 +216,11 @@ impl_reflect_opaque!(::core::num::NonZeroI8( )); impl_reflect_opaque!(::core::num::Wrapping()); impl_reflect_opaque!(::core::num::Saturating()); +impl_reflect_opaque!(::bevy_platform_support::sync::Arc); + +// 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); // `Serialize` and `Deserialize` only for platforms supported by serde: @@ -2432,7 +2439,8 @@ mod tests { TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType, }; use alloc::{collections::BTreeMap, string::String, vec}; - use bevy_utils::{HashMap, Instant}; + use bevy_platform_support::time::Instant; + use bevy_utils::HashMap; use core::{ f32::consts::{PI, TAU}, time::Duration, diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index dcc38c3cc5987..0fcc5854a6725 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -189,7 +189,8 @@ mod tests { use crate::serde::{DeserializeWithRegistry, ReflectDeserializeWithRegistry}; use crate::serde::{ReflectSerializeWithRegistry, SerializeWithRegistry}; 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 core::any::TypeId; use core::fmt::{Debug, Formatter}; @@ -336,6 +337,22 @@ mod tests { registry } + fn create_arc_dyn_enemy(enemy: T) -> Arc { + 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] fn should_serialize_with_serialize_with_registry() { let registry = create_registry(); @@ -343,8 +360,8 @@ mod tests { let level = Level { name: String::from("Level 1"), enemies: EnemyList(vec![ - Arc::new(Skeleton(10)), - Arc::new(Zombie { + create_arc_dyn_enemy(Skeleton(10)), + create_arc_dyn_enemy(Zombie { health: 20, walk_speed: 0.5, }), @@ -374,8 +391,8 @@ mod tests { let expected = Level { name: String::from("Level 1"), enemies: EnemyList(vec![ - Arc::new(Skeleton(10)), - Arc::new(Zombie { + create_arc_dyn_enemy(Skeleton(10)), + create_arc_dyn_enemy(Zombie { health: 20, walk_speed: 0.5, }), @@ -388,8 +405,8 @@ mod tests { let unexpected = Level { name: String::from("Level 1"), enemies: EnemyList(vec![ - Arc::new(Skeleton(20)), - Arc::new(Zombie { + create_arc_dyn_enemy(Skeleton(20)), + create_arc_dyn_enemy(Zombie { health: 20, walk_speed: 5.0, }), diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 9ee7c11d47a86..6fd207fc8f01e 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -6,7 +6,8 @@ use crate::{ ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut, 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_utils::HashMap; use core::{ diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 3445bfb6c8b3f..57cc7acdb377a 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -8,7 +8,8 @@ use crate::{ ApplyError, DynamicTuple, Generics, PartialReflect, Reflect, ReflectKind, ReflectMut, 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::{ fmt::{Debug, Formatter}, slice::Iter, diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 6c97825bccca3..aedff72c1e769 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,6 +1,6 @@ use crate::{serde::Serializable, FromReflect, Reflect, TypeInfo, TypePath, Typed}; -use alloc::sync::Arc; use alloc::{boxed::Box, string::String}; +use bevy_platform_support::sync::Arc; use bevy_ptr::{Ptr, PtrMut}; use bevy_utils::{HashMap, HashSet, TypeIdMap}; use core::{ diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 577ff685bb4c1..b540bec3c1a90 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -57,6 +57,9 @@ bevy_utils = { path = "../bevy_utils", 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_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 image = { version = "0.25.2", default-features = false } diff --git a/crates/bevy_render/src/diagnostic/internal.rs b/crates/bevy_render/src/diagnostic/internal.rs index ba15a95dde55a..190471f1a265f 100644 --- a/crates/bevy_render/src/diagnostic/internal.rs +++ b/crates/bevy_render/src/diagnostic/internal.rs @@ -7,7 +7,7 @@ use std::thread::{self, ThreadId}; use bevy_diagnostic::{Diagnostic, DiagnosticMeasurement, DiagnosticPath, DiagnosticsStore}; use bevy_ecs::system::{Res, ResMut, Resource}; -use bevy_utils::Instant; +use bevy_platform_support::time::Instant; use std::sync::Mutex; use wgpu::{ Buffer, BufferDescriptor, BufferUsages, CommandEncoder, ComputePass, Features, MapMode, diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 1cd17e8a02a86..0f46506801ed8 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -18,8 +18,8 @@ use crate::{ }; use alloc::sync::Arc; use bevy_ecs::{prelude::*, system::SystemState}; +use bevy_platform_support::time::Instant; use bevy_time::TimeSender; -use bevy_utils::Instant; use wgpu::{ Adapter, AdapterInfo, CommandBuffer, CommandEncoder, DeviceType, Instance, Queue, RequestAdapterOptions, diff --git a/crates/bevy_state/Cargo.toml b/crates/bevy_state/Cargo.toml index bc32d18f6dd6f..5aa6fff505bbd 100644 --- a/crates/bevy_state/Cargo.toml +++ b/crates/bevy_state/Cargo.toml @@ -46,6 +46,7 @@ critical-section = [ "bevy_ecs/critical-section", "bevy_utils/critical-section", "bevy_app?/critical-section", + "bevy_reflect?/critical-section", ] ## `portable-atomic` provides additional platform support for atomic types and @@ -54,6 +55,7 @@ portable-atomic = [ "bevy_ecs/portable-atomic", "bevy_utils/portable-atomic", "bevy_app?/portable-atomic", + "bevy_reflect?/portable-atomic", ] [dependencies] diff --git a/crates/bevy_tasks/Cargo.toml b/crates/bevy_tasks/Cargo.toml index e0a61ecb1debd..3e0c9720c9ba7 100644 --- a/crates/bevy_tasks/Cargo.toml +++ b/crates/bevy_tasks/Cargo.toml @@ -15,25 +15,27 @@ std = [ "async-task/std", "spin/std", "edge-executor?/std", - "portable-atomic-util?/std", + "bevy_platform_support/std", ] multi_threaded = ["std", "dep:async-channel", "dep:concurrent-queue"] async_executor = ["std", "dep:async-executor"] edge_executor = ["dep:edge-executor"] critical-section = [ - "dep:critical-section", + "bevy_platform_support/critical-section", "edge-executor?/critical-section", - "portable-atomic?/critical-section", ] portable-atomic = [ - "dep:portable-atomic", - "dep:portable-atomic-util", + "bevy_platform_support/portable-atomic", "edge-executor?/portable-atomic", "async-task/portable-atomic", "spin/portable_atomic", ] [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 = [ "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-io = { 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] wasm-bindgen-futures = "0.4" diff --git a/crates/bevy_tasks/src/single_threaded_task_pool.rs b/crates/bevy_tasks/src/single_threaded_task_pool.rs index 598c8f9f11689..b928fa9b214b4 100644 --- a/crates/bevy_tasks/src/single_threaded_task_pool.rs +++ b/crates/bevy_tasks/src/single_threaded_task_pool.rs @@ -1,4 +1,5 @@ use alloc::{string::String, vec::Vec}; +use bevy_platform_support::sync::Arc; use core::{cell::RefCell, future::Future, marker::PhantomData, mem}; use crate::Task; @@ -6,12 +7,6 @@ use crate::Task; #[cfg(feature = "std")] use std::thread_local; -#[cfg(feature = "portable-atomic")] -use portable_atomic_util::Arc; - -#[cfg(not(feature = "portable-atomic"))] -use alloc::sync::Arc; - #[cfg(all( feature = "std", any(feature = "async_executor", feature = "edge_executor") diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index c16aeca355a67..b9c0e9f98261a 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -6,15 +6,10 @@ use std::{ }; use crate::executor::FallibleTask; +use bevy_platform_support::sync::Arc; use concurrent_queue::ConcurrentQueue; use futures_lite::FutureExt; -#[cfg(feature = "portable-atomic")] -use portable_atomic_util::Arc; - -#[cfg(not(feature = "portable-atomic"))] -use alloc::sync::Arc; - use crate::{ block_on, thread_executor::{ThreadExecutor, ThreadExecutorTicker}, diff --git a/crates/bevy_time/Cargo.toml b/crates/bevy_time/Cargo.toml index 1738cdcdff795..257f7da8e51e8 100644 --- a/crates/bevy_time/Cargo.toml +++ b/crates/bevy_time/Cargo.toml @@ -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", ], 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 crossbeam-channel = "0.5.0" diff --git a/crates/bevy_time/src/lib.rs b/crates/bevy_time/src/lib.rs index c72e13982430e..dc3883132a0a6 100644 --- a/crates/bevy_time/src/lib.rs +++ b/crates/bevy_time/src/lib.rs @@ -35,7 +35,7 @@ use bevy_ecs::{ event::{event_update_system, signal_event_update_system, EventRegistry, ShouldUpdateEvents}, prelude::*, }; -use bevy_utils::Instant; +use bevy_platform_support::time::Instant; use core::time::Duration; pub use crossbeam_channel::TrySendError; use crossbeam_channel::{Receiver, Sender}; diff --git a/crates/bevy_time/src/real.rs b/crates/bevy_time/src/real.rs index ab87d7a40670f..862822b2cb30b 100644 --- a/crates/bevy_time/src/real.rs +++ b/crates/bevy_time/src/real.rs @@ -1,6 +1,6 @@ +use bevy_platform_support::time::Instant; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; -use bevy_utils::Instant; use core::time::Duration; use crate::time::Time; diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index 8ddce5fa8a732..fa0bf8a6cb2ac 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -160,7 +160,7 @@ use { /// ``` /// # use bevy_ecs::prelude::*; /// # use bevy_time::prelude::*; -/// # use bevy_utils::Instant; +/// # use bevy_platform_support::time::Instant; /// # /// #[derive(Debug)] /// struct Custom { diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 5efc75ec2d5e6..a031693e3b12e 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -21,37 +21,32 @@ serde = ["hashbrown/serde"] ## 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", "foldhash/std", "dep:thread_local"] +std = ["alloc", "bevy_platform_support/std", "foldhash/std", "dep:thread_local"] ## Allows access to the `alloc` crate. -alloc = ["hashbrown"] +alloc = ["bevy_platform_support/alloc", "hashbrown"] ## `critical-section` provides the building blocks for synchronization primitives ## 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 ## operations, even on targets without native support. -portable-atomic = ["dep:portable-atomic"] +portable-atomic = ["bevy_platform_support/portable-atomic"] [dependencies] +bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false } + foldhash = { version = "0.1.3", default-features = false } hashbrown = { version = "0.15.1", features = [ "equivalent", "raw-entry", ], optional = true, default-features = false } thread_local = { version = "1.0", optional = true } -portable-atomic = { version = "1", default-features = false, features = [ - "fallback", -], optional = true } [dev-dependencies] static_assertions = "1.1.0" -[target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2.0", features = ["js"] } -web-time = { version = "1.1" } - [lints] workspace = true diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 0fd3fa6a7e0f3..d4c01e0573b1a 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -29,7 +29,6 @@ mod default; mod once; #[cfg(feature = "std")] mod parallel_queue; -mod time; #[doc(hidden)] pub use once::OnceFlag; @@ -59,8 +58,6 @@ pub use foldhash::fast::{FixedState, FoldHasher as DefaultHasher, RandomState}; pub use hashbrown; #[cfg(feature = "std")] pub use parallel_queue::*; -#[cfg(any(feature = "std", target_arch = "wasm32"))] -pub use time::*; #[cfg(feature = "alloc")] use core::any::TypeId; diff --git a/crates/bevy_utils/src/once.rs b/crates/bevy_utils/src/once.rs index cb5cd086e9e1d..674926ec53575 100644 --- a/crates/bevy_utils/src/once.rs +++ b/crates/bevy_utils/src/once.rs @@ -1,8 +1,4 @@ -#[cfg(feature = "portable-atomic")] -use portable_atomic::{AtomicBool, Ordering}; - -#[cfg(not(feature = "portable-atomic"))] -use core::sync::atomic::{AtomicBool, Ordering}; +use bevy_platform_support::sync::atomic::{AtomicBool, Ordering}; /// Wrapper around an [`AtomicBool`], abstracting the backing implementation and /// ordering considerations. diff --git a/crates/bevy_utils/src/time.rs b/crates/bevy_utils/src/time.rs deleted file mode 100644 index 239f8073c8905..0000000000000 --- a/crates/bevy_utils/src/time.rs +++ /dev/null @@ -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; diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index 602063990c4aa..808852901ea7a 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -35,6 +35,9 @@ bevy_reflect = { path = "../bevy_reflect", 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_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_asset = { path = "../bevy_asset", version = "0.16.0-dev", optional = true } diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index 370b4a87e37c0..97c376428e7ae 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -20,11 +20,11 @@ use bevy_log::{error, trace, warn}; #[cfg(feature = "custom_cursor")] use bevy_math::URect; use bevy_math::{ivec2, DVec2, Vec2}; +use bevy_platform_support::time::Instant; #[cfg(not(target_arch = "wasm32"))] use bevy_tasks::tick_global_task_pools_on_main_thread; #[cfg(feature = "custom_cursor")] use bevy_utils::HashMap; -use bevy_utils::Instant; use core::marker::PhantomData; #[cfg(target_arch = "wasm32")] use winit::platform::web::EventLoopExtWebSys; diff --git a/tools/publish.sh b/tools/publish.sh index ae6e869c98616..1aa2ddffe1369 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -1,5 +1,6 @@ # if crate A depends on crate B, B must come before A in this list crates=( + bevy_platform_support bevy_utils bevy_ptr bevy_macro_utils