Simplify bevy_utils Features (#19090)

# Objective

Now that `bevy_platform::cfg` is merged, we can start tidying up
features. This PR starts with `bevy_utils`.

## Solution

- Removed `serde` and `critical-section` features (they were just
re-exports of `bevy_platform` anyway)
- Removed `std`, `alloc` features, relying on `bevy_platform::cfg` to
check for availability.
- Added `parallel` feature to provide access to the `Parallel` type.
- Moved the `HashMap` type aliases into `map.rs` for better
organisation.

## Testing

- CI
This commit is contained in:
Zachary Harrold 2025-05-24 11:46:11 +10:00 committed by GitHub
parent d7cab93495
commit 3d3746e5d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 124 additions and 157 deletions

View File

@ -47,7 +47,6 @@ std = [
"bevy_ecs/std",
"dep:ctrlc",
"downcast-rs/std",
"bevy_utils/std",
"bevy_tasks/std",
"bevy_platform/std",
]
@ -77,9 +76,7 @@ web = [
bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, optional = true }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false }

View File

@ -18,7 +18,6 @@ serialize = [
"dep:serde",
"bevy_ecs/serialize",
"bevy_time/serialize",
"bevy_utils/serde",
"bevy_platform/serialize",
]
@ -39,7 +38,6 @@ std = [
"bevy_app/std",
"bevy_platform/std",
"bevy_time/std",
"bevy_utils/std",
"bevy_tasks/std",
]
@ -50,7 +48,6 @@ critical-section = [
"bevy_app/critical-section",
"bevy_platform/critical-section",
"bevy_time/critical-section",
"bevy_utils/critical-section",
"bevy_tasks/critical-section",
]
@ -59,9 +56,7 @@ critical-section = [
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_time = { path = "../bevy_time", version = "0.16.0-dev", default-features = false }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [
"alloc",

View File

@ -20,12 +20,7 @@ default = ["std", "bevy_reflect", "async_executor", "backtrace"]
multi_threaded = ["bevy_tasks/multi_threaded", "dep:arrayvec"]
## Adds serialization support through `serde`.
serialize = [
"dep:serde",
"bevy_utils/serde",
"bevy_platform/serialize",
"indexmap/serde",
]
serialize = ["dep:serde", "bevy_platform/serialize", "indexmap/serde"]
## Adds runtime reflection support using `bevy_reflect`.
bevy_reflect = ["dep:bevy_reflect"]
@ -67,7 +62,7 @@ async_executor = ["std", "bevy_tasks/async_executor"]
std = [
"bevy_reflect?/std",
"bevy_tasks/std",
"bevy_utils/std",
"bevy_utils/parallel",
"bitflags/std",
"concurrent-queue/std",
"disqualified/alloc",
@ -94,9 +89,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
"smallvec",
], default-features = false, optional = true }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false }
bevy_ecs_macros = { path = "macros", version = "0.16.0-dev" }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [
"alloc",

View File

@ -32,7 +32,7 @@ qoi = ["image/qoi"]
tga = ["image/tga"]
tiff = ["image/tiff"]
webp = ["image/webp"]
serialize = ["bevy_reflect", "bevy_platform/serialize", "bevy_utils/serde"]
serialize = ["bevy_reflect", "bevy_platform/serialize"]
# For ktx2 supercompression
zlib = ["flate2"]

View File

@ -42,7 +42,6 @@ std = [
"bevy_app/std",
"bevy_ecs/std",
"bevy_math/std",
"bevy_utils/std",
"bevy_reflect/std",
"bevy_platform/std",
]

View File

@ -296,7 +296,6 @@ std = [
"bevy_state?/std",
"bevy_time/std",
"bevy_transform/std",
"bevy_utils/std",
"bevy_tasks/std",
"bevy_window?/std",
]
@ -314,7 +313,6 @@ critical-section = [
"bevy_reflect/critical-section",
"bevy_state?/critical-section",
"bevy_time/critical-section",
"bevy_utils/critical-section",
"bevy_tasks/critical-section",
]
@ -377,9 +375,7 @@ bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev", default-f
"bevy-support",
"bevy_reflect",
] }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false }
# bevy (std required)

View File

@ -54,7 +54,6 @@ wgpu-types = ["dep:wgpu-types"]
## on `no_std` targets, but provides access to certain additional features on
## supported platforms.
std = [
"bevy_utils/std",
"erased-serde/std",
"downcast-rs/std",
"serde/std",
@ -67,10 +66,7 @@ std = [
## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = [
"bevy_platform/critical-section",
"bevy_utils/critical-section",
]
critical-section = ["bevy_platform/critical-section"]
## Enables use of browser APIs.
## Note this is currently only applicable on `wasm32` architectures.
@ -79,9 +75,7 @@ web = ["bevy_platform/web", "uuid?/js"]
[dependencies]
# bevy
bevy_reflect_derive = { path = "derive", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false }
bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [
"alloc",

View File

@ -30,7 +30,6 @@ bevy_app = ["dep:bevy_app"]
## supported platforms.
std = [
"bevy_ecs/std",
"bevy_utils/std",
"bevy_reflect?/std",
"bevy_app?/std",
"bevy_platform/std",
@ -40,7 +39,6 @@ std = [
## on all platforms, including `no_std`.
critical-section = [
"bevy_ecs/critical-section",
"bevy_utils/critical-section",
"bevy_app?/critical-section",
"bevy_reflect?/critical-section",
"bevy_platform/critical-section",

View File

@ -74,7 +74,7 @@ std = [
"bevy_math/std",
"bevy_reflect?/std",
"bevy_tasks/std",
"bevy_utils/std",
"bevy_utils/parallel",
"serde?/std",
]

View File

@ -9,26 +9,10 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
default = ["std", "serde"]
default = ["parallel"]
# Functionality
## Adds serialization support through `serde`.
serde = ["bevy_platform/serialize"]
# Platform Compatibility
## Allows access to the `std` crate. Enabling this feature will prevent compilation
## on `no_std` targets, but provides access to certain additional features on
## supported platforms.
std = ["alloc", "bevy_platform/std", "dep:thread_local"]
## Allows access to the `alloc` crate.
alloc = ["bevy_platform/alloc"]
## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = ["bevy_platform/critical-section"]
# Provides access to the `Parallel` type.
parallel = ["bevy_platform/std", "dep:thread_local"]
[dependencies]
bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false }

View File

@ -9,11 +9,35 @@
//!
//! [Bevy]: https://bevyengine.org/
#[cfg(feature = "std")]
extern crate std;
/// Configuration information for this crate.
pub mod cfg {
pub(crate) use bevy_platform::cfg::*;
#[cfg(feature = "alloc")]
extern crate alloc;
pub use bevy_platform::cfg::{alloc, std};
define_alias! {
#[cfg(feature = "parallel")] => {
/// Indicates the `Parallel` type is available.
parallel
}
}
}
cfg::std! {
extern crate std;
}
cfg::alloc! {
extern crate alloc;
mod map;
pub use map::*;
}
cfg::parallel! {
mod parallel_queue;
pub use parallel_queue::*;
}
/// The utilities prelude.
///
@ -27,65 +51,14 @@ pub mod syncunsafecell;
mod default;
mod once;
#[cfg(feature = "std")]
mod parallel_queue;
#[doc(hidden)]
pub use once::OnceFlag;
pub use default::default;
#[cfg(feature = "std")]
pub use parallel_queue::*;
use core::mem::ManuallyDrop;
#[cfg(feature = "alloc")]
use {
bevy_platform::{
collections::HashMap,
hash::{Hashed, NoOpHash, PassHash},
},
core::{any::TypeId, hash::Hash},
};
/// A [`HashMap`] pre-configured to use [`Hashed`] keys and [`PassHash`] passthrough hashing.
/// Iteration order only depends on the order of insertions and deletions.
#[cfg(feature = "alloc")]
pub type PreHashMap<K, V> = HashMap<Hashed<K>, V, PassHash>;
/// Extension methods intended to add functionality to [`PreHashMap`].
#[cfg(feature = "alloc")]
pub trait PreHashMapExt<K, V> {
/// Tries to get or insert the value for the given `key` using the pre-computed hash first.
/// If the [`PreHashMap`] does not already contain the `key`, it will clone it and insert
/// the value returned by `func`.
fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V;
}
#[cfg(feature = "alloc")]
impl<K: Hash + Eq + PartialEq + Clone, V> PreHashMapExt<K, V> for PreHashMap<K, V> {
#[inline]
fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V {
use bevy_platform::collections::hash_map::RawEntryMut;
let entry = self
.raw_entry_mut()
.from_key_hashed_nocheck(key.hash(), key);
match entry {
RawEntryMut::Occupied(entry) => entry.into_mut(),
RawEntryMut::Vacant(entry) => {
let (_, value) = entry.insert_hashed_nocheck(key.hash(), key.clone(), func());
value
}
}
}
}
/// A specialized hashmap type with Key of [`TypeId`]
/// Iteration order only depends on the order of insertions and deletions.
#[cfg(feature = "alloc")]
pub type TypeIdMap<V> = HashMap<TypeId, V, NoOpHash>;
/// A type which calls a function when dropped.
/// This can be used to ensure that cleanup code is run even in case of a panic.
///
@ -144,46 +117,3 @@ impl<F: FnOnce()> Drop for OnDrop<F> {
callback();
}
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::assert_impl_all;
// Check that the HashMaps are Clone if the key/values are Clone
assert_impl_all!(PreHashMap::<u64, usize>: Clone);
#[test]
fn fast_typeid_hash() {
struct Hasher;
impl core::hash::Hasher for Hasher {
fn finish(&self) -> u64 {
0
}
fn write(&mut self, _: &[u8]) {
panic!("Hashing of core::any::TypeId changed");
}
fn write_u64(&mut self, _: u64) {}
}
Hash::hash(&TypeId::of::<()>(), &mut Hasher);
}
#[cfg(feature = "alloc")]
#[test]
fn stable_hash_within_same_program_execution() {
use alloc::vec::Vec;
let mut map_1 = <HashMap<_, _>>::default();
let mut map_2 = <HashMap<_, _>>::default();
for i in 1..10 {
map_1.insert(i, i);
map_2.insert(i, i);
}
assert_eq!(
map_1.iter().collect::<Vec<_>>(),
map_2.iter().collect::<Vec<_>>()
);
}
}

View File

@ -0,0 +1,83 @@
use core::{any::TypeId, hash::Hash};
use bevy_platform::{
collections::HashMap,
hash::{Hashed, NoOpHash, PassHash},
};
/// A [`HashMap`] pre-configured to use [`Hashed`] keys and [`PassHash`] passthrough hashing.
/// Iteration order only depends on the order of insertions and deletions.
pub type PreHashMap<K, V> = HashMap<Hashed<K>, V, PassHash>;
/// Extension methods intended to add functionality to [`PreHashMap`].
pub trait PreHashMapExt<K, V> {
/// Tries to get or insert the value for the given `key` using the pre-computed hash first.
/// If the [`PreHashMap`] does not already contain the `key`, it will clone it and insert
/// the value returned by `func`.
fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V;
}
impl<K: Hash + Eq + PartialEq + Clone, V> PreHashMapExt<K, V> for PreHashMap<K, V> {
#[inline]
fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V {
use bevy_platform::collections::hash_map::RawEntryMut;
let entry = self
.raw_entry_mut()
.from_key_hashed_nocheck(key.hash(), key);
match entry {
RawEntryMut::Occupied(entry) => entry.into_mut(),
RawEntryMut::Vacant(entry) => {
let (_, value) = entry.insert_hashed_nocheck(key.hash(), key.clone(), func());
value
}
}
}
}
/// A specialized hashmap type with Key of [`TypeId`]
/// Iteration order only depends on the order of insertions and deletions.
pub type TypeIdMap<V> = HashMap<TypeId, V, NoOpHash>;
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::assert_impl_all;
// Check that the HashMaps are Clone if the key/values are Clone
assert_impl_all!(PreHashMap::<u64, usize>: Clone);
#[test]
fn fast_typeid_hash() {
struct Hasher;
impl core::hash::Hasher for Hasher {
fn finish(&self) -> u64 {
0
}
fn write(&mut self, _: &[u8]) {
panic!("Hashing of core::any::TypeId changed");
}
fn write_u64(&mut self, _: u64) {}
}
Hash::hash(&TypeId::of::<()>(), &mut Hasher);
}
crate::cfg::alloc! {
#[test]
fn stable_hash_within_same_program_execution() {
use alloc::vec::Vec;
let mut map_1 = <HashMap<_, _>>::default();
let mut map_2 = <HashMap<_, _>>::default();
for i in 1..10 {
map_1.insert(i, i);
map_2.insert(i, i);
}
assert_eq!(
map_1.iter().collect::<Vec<_>>(),
map_2.iter().collect::<Vec<_>>()
);
}
}
}

View File

@ -10,7 +10,6 @@ pub struct Parallel<T: Send> {
locals: ThreadLocal<RefCell<T>>,
}
/// A scope guard of a `Parallel`, when this struct is dropped ,the value will writeback to its `Parallel`
impl<T: Send> Parallel<T> {
/// Gets a mutable iterator over all of the per-thread queues.
pub fn iter_mut(&mut self) -> impl Iterator<Item = &'_ mut T> {
@ -56,7 +55,6 @@ where
}
}
#[cfg(feature = "alloc")]
impl<T: Send> Parallel<Vec<T>> {
/// Collect all enqueued items from all threads and appends them to the end of a
/// single Vec.