Add no_std support to bevy_ecs (#16758)
# Objective - Contributes to #15460 ## Solution - Added the following features: - `std` (default) - `async_executor` (default) - `edge_executor` - `critical-section` - `portable-atomic` - Gated `tracing` in `bevy_utils` to allow compilation on certain platforms - Switched from `tracing` to `log` for simple message logging within `bevy_ecs`. Note that `tracing` supports capturing from `log` so this should be an uncontroversial change. - Fixed imports and added feature gates as required - Made `bevy_tasks` optional within `bevy_ecs`. Turns out it's only needed for parallel operations which are already gated behind `multi_threaded` anyway. ## Testing - Added to `compile-check-no-std` CI command - `cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section,portable-atomic --target thumbv6m-none-eabi` - `cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section` - `cargo check -p bevy_ecs --no-default-features` ## Draft Release Notes Bevy's core ECS now supports `no_std` platforms. In prior versions of Bevy, it was not possible to work with embedded or niche platforms due to our reliance on the standard library, `std`. This has blocked a number of novel use-cases for Bevy, such as an embedded database for IoT devices, or for creating games on retro consoles. With this release, `bevy_ecs` no longer requires `std`. To use Bevy on a `no_std` platform, you must disable default features and enable the new `edge_executor` and `critical-section` features. You may also need to enable `portable-atomic` and `critical-section` if your platform does not natively support all atomic types and operations used by Bevy. ```toml [dependencies] bevy_ecs = { version = "0.16", default-features = false, features = [ # Required for platforms with incomplete atomics (e.g., Raspberry Pi Pico) "portable-atomic", "critical-section", # Optional "bevy_reflect", "serialize", "bevy_debug_stepping", "edge_executor" ] } ``` Currently, this has been tested on bare-metal x86 and the Raspberry Pi Pico. If you have trouble using `bevy_ecs` on a particular platform, please reach out either through a GitHub issue or in the `no_std` working group on the Bevy Discord server. Keep an eye out for future `no_std` updates as we continue to improve the parity between `std` and `no_std`. We look forward to seeing what kinds of applications are now possible with Bevy! ## Notes - Creating PR in draft to ensure CI is passing before requesting reviews. - This implementation has no support for multithreading in `no_std`, especially due to `NonSend` being unsound if allowed in multithreading. The reason is we cannot check the `ThreadId` in `no_std`, so we have no mechanism to at-runtime determine if access is sound. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Vic <59878206+Victoronz@users.noreply.github.com>
This commit is contained in:
parent
6fd6ce1367
commit
1f2d0e6308
@ -11,27 +11,103 @@ categories = ["game-engines", "data-structures"]
|
||||
rust-version = "1.81.0"
|
||||
|
||||
[features]
|
||||
default = ["bevy_reflect"]
|
||||
trace = []
|
||||
multi_threaded = ["bevy_tasks/multi_threaded", "arrayvec"]
|
||||
bevy_debug_stepping = []
|
||||
serialize = ["dep:serde"]
|
||||
track_change_detection = []
|
||||
default = ["std", "bevy_reflect", "async_executor"]
|
||||
|
||||
# Functionality
|
||||
|
||||
## Enables multithreading support. Schedules will attempt to run systems on
|
||||
## multiple threads whenever possible.
|
||||
multi_threaded = ["bevy_tasks/multi_threaded", "dep:arrayvec"]
|
||||
|
||||
## Adds serialization support through `serde`.
|
||||
serialize = ["dep:serde", "bevy_utils/serde"]
|
||||
|
||||
## Adds runtime reflection support using `bevy_reflect`.
|
||||
bevy_reflect = ["dep:bevy_reflect"]
|
||||
|
||||
## Extends reflection support to functions.
|
||||
reflect_functions = ["bevy_reflect", "bevy_reflect/functions"]
|
||||
detailed_trace = []
|
||||
|
||||
# Debugging Features
|
||||
|
||||
## Enables `tracing` integration, allowing spans and other metrics to be reported
|
||||
## through that framework.
|
||||
trace = ["std", "dep:tracing", "bevy_utils/tracing"]
|
||||
|
||||
## Enables a more detailed set of traces which may be noisy if left on by default.
|
||||
detailed_trace = ["trace"]
|
||||
|
||||
## Provides system stepping support, allowing them to be paused, stepped, and
|
||||
## other debug operations which can help with diagnosing certain behaviors.
|
||||
bevy_debug_stepping = []
|
||||
|
||||
## Provides more detailed tracking of the cause of various effects within the ECS.
|
||||
## This will often provide more detailed error messages.
|
||||
track_change_detection = []
|
||||
|
||||
# Executor Backend
|
||||
|
||||
## Uses `async-executor` as a task execution backend.
|
||||
## This backend is incompatible with `no_std` targets.
|
||||
async_executor = ["dep:bevy_tasks", "std", "bevy_tasks/async_executor"]
|
||||
|
||||
## Uses `edge-executor` as a task execution backend.
|
||||
## Use this instead of `async-executor` if working on a `no_std` target.
|
||||
edge_executor = ["dep:bevy_tasks", "bevy_tasks/edge_executor"]
|
||||
|
||||
# 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_reflect?/std",
|
||||
"bevy_tasks/std",
|
||||
"bevy_utils/std",
|
||||
"bitflags/std",
|
||||
"concurrent-queue/std",
|
||||
"disqualified/alloc",
|
||||
"fixedbitset/std",
|
||||
"indexmap/std",
|
||||
"serde?/std",
|
||||
"nonmax/std",
|
||||
"arrayvec?/std",
|
||||
"log/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",
|
||||
]
|
||||
|
||||
## `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",
|
||||
"concurrent-queue/portable-atomic",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
bevy_ptr = { path = "../bevy_ptr", version = "0.15.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", optional = true }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", default-features = false, optional = true }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev", default-features = false, optional = true }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev", default-features = false, features = [
|
||||
"alloc",
|
||||
] }
|
||||
bevy_ecs_macros = { path = "macros", version = "0.15.0-dev" }
|
||||
|
||||
bitflags = "2.3"
|
||||
concurrent-queue = "2.5.0"
|
||||
disqualified = "1.0"
|
||||
fixedbitset = "0.5"
|
||||
serde = { version = "1", optional = true, default-features = false }
|
||||
bitflags = { version = "2.3", default-features = false }
|
||||
concurrent-queue = { version = "2.5.0", default-features = false }
|
||||
disqualified = { version = "1.0", default-features = false }
|
||||
fixedbitset = { version = "0.5", default-features = false }
|
||||
serde = { version = "1", default-features = false, features = [
|
||||
"alloc",
|
||||
], optional = true }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
derive_more = { version = "1", default-features = false, features = [
|
||||
"from",
|
||||
@ -39,11 +115,25 @@ derive_more = { version = "1", default-features = false, features = [
|
||||
"into",
|
||||
"as_ref",
|
||||
] }
|
||||
nonmax = "0.5"
|
||||
arrayvec = { version = "0.7.4", optional = true }
|
||||
nonmax = { version = "0.5", default-features = false }
|
||||
arrayvec = { version = "0.7.4", default-features = false, optional = true }
|
||||
smallvec = { version = "1", features = ["union"] }
|
||||
indexmap = { version = "2.5.0", default-features = false, features = ["std"] }
|
||||
variadics_please = "1.0"
|
||||
indexmap = { version = "2.5.0", default-features = false }
|
||||
variadics_please = { version = "1.0", 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",
|
||||
"once",
|
||||
] }
|
||||
tracing = { version = "0.1", default-features = false, optional = true }
|
||||
log = { version = "0.4", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8"
|
||||
|
||||
@ -149,7 +149,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
|
||||
storages: &mut #bevy_ecs_path::storage::Storages,
|
||||
required_components: &mut #bevy_ecs_path::component::RequiredComponents,
|
||||
inheritance_depth: u16,
|
||||
recursion_check_stack: &mut Vec<#bevy_ecs_path::component::ComponentId>
|
||||
recursion_check_stack: &mut #bevy_ecs_path::__macro_exports::Vec<#bevy_ecs_path::component::ComponentId>
|
||||
) {
|
||||
#bevy_ecs_path::component::enforce_no_required_components_recursion(components, recursion_check_stack);
|
||||
let self_id = components.register_component::<Self>(storages);
|
||||
|
||||
@ -26,6 +26,7 @@ use crate::{
|
||||
observer::Observers,
|
||||
storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow},
|
||||
};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use bevy_utils::HashMap;
|
||||
use core::{
|
||||
hash::Hash,
|
||||
|
||||
@ -20,6 +20,7 @@ use crate::{
|
||||
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, ON_ADD, ON_INSERT, ON_REPLACE},
|
||||
};
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use bevy_ptr::{ConstNonNull, OwningPtr};
|
||||
use bevy_utils::{HashMap, HashSet, TypeIdMap};
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
system::{Local, Resource, SystemParam},
|
||||
world::{DeferredWorld, FromWorld, World},
|
||||
};
|
||||
use alloc::{borrow::Cow, sync::Arc};
|
||||
use alloc::{borrow::Cow, format, vec::Vec};
|
||||
pub use bevy_ecs_macros::Component;
|
||||
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
@ -30,6 +30,12 @@ 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].
|
||||
@ -2005,7 +2011,37 @@ impl RequiredComponents {
|
||||
constructor: fn() -> C,
|
||||
inheritance_depth: u16,
|
||||
) {
|
||||
let erased: RequiredComponentConstructor = RequiredComponentConstructor(Arc::new(
|
||||
let erased: RequiredComponentConstructor = RequiredComponentConstructor({
|
||||
// `portable-atomic-util` `Arc` is not able to coerce an unsized
|
||||
// type like `std::sync::Arc` can. Creating a `Box` first does the
|
||||
// coercion.
|
||||
//
|
||||
// This would be resolved by https://github.com/rust-lang/rust/issues/123430
|
||||
|
||||
#[cfg(feature = "portable-atomic")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
type Constructor = dyn for<'a, 'b> Fn(
|
||||
&'a mut Table,
|
||||
&'b mut SparseSets,
|
||||
Tick,
|
||||
TableRow,
|
||||
Entity,
|
||||
&'static Location<'static>,
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "track_change_detection"))]
|
||||
type Constructor =
|
||||
dyn for<'a, 'b> Fn(&'a mut Table, &'b mut SparseSets, Tick, TableRow, Entity);
|
||||
|
||||
#[cfg(feature = "portable-atomic")]
|
||||
type Intermediate<T> = Box<T>;
|
||||
|
||||
#[cfg(not(feature = "portable-atomic"))]
|
||||
type Intermediate<T> = Arc<T>;
|
||||
|
||||
let boxed: Intermediate<Constructor> = Intermediate::new(
|
||||
move |table,
|
||||
sparse_sets,
|
||||
change_tick,
|
||||
@ -2033,7 +2069,11 @@ impl RequiredComponents {
|
||||
}
|
||||
});
|
||||
},
|
||||
));
|
||||
);
|
||||
|
||||
Arc::from(boxed)
|
||||
});
|
||||
|
||||
// SAFETY:
|
||||
// `component_id` matches the type initialized by the `erased` constructor above.
|
||||
// `erased` initializes a component for `component_id` in such a way that
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{borrow::ToOwned, vec::Vec};
|
||||
use core::any::TypeId;
|
||||
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
|
||||
#[cfg(feature = "portable-atomic")]
|
||||
use portable_atomic_util::Arc;
|
||||
|
||||
#[cfg(not(feature = "portable-atomic"))]
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
bundle::Bundle,
|
||||
component::{component_clone_ignore, Component, ComponentCloneHandler, ComponentId},
|
||||
|
||||
@ -49,8 +49,6 @@ pub use visit_entities::*;
|
||||
mod hash;
|
||||
pub use hash::*;
|
||||
|
||||
use bevy_utils::tracing::warn;
|
||||
|
||||
use crate::{
|
||||
archetype::{ArchetypeId, ArchetypeRow},
|
||||
identifier::{
|
||||
@ -61,22 +59,36 @@ use crate::{
|
||||
},
|
||||
storage::{SparseSetIndex, TableId, TableRow},
|
||||
};
|
||||
use alloc::{borrow::ToOwned, string::String, vec::Vec};
|
||||
use core::{fmt, hash::Hash, mem, num::NonZero};
|
||||
use log::warn;
|
||||
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
use core::panic::Location;
|
||||
use core::{fmt, hash::Hash, mem, num::NonZero, sync::atomic::Ordering};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
#[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")]
|
||||
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(not(target_has_atomic = "64"))]
|
||||
#[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"))]
|
||||
type IdCursor = isize;
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use crate as bevy_ecs;
|
||||
use alloc::vec::Vec;
|
||||
use bevy_ecs::{
|
||||
event::{Event, EventCursor, EventId, EventInstance},
|
||||
system::Resource,
|
||||
};
|
||||
use bevy_utils::detailed_trace;
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
use core::panic::Location;
|
||||
use core::{
|
||||
@ -142,7 +142,8 @@ impl<E: Event> Events<E> {
|
||||
caller,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
detailed_trace!("Events::send() -> id: {}", event_id);
|
||||
#[cfg(feature = "detailed_trace")]
|
||||
tracing::trace!("Events::send() -> id: {}", event_id);
|
||||
|
||||
let event_instance = EventInstance { event_id, event };
|
||||
|
||||
@ -318,7 +319,8 @@ impl<E: Event> Extend<E> for Events<E> {
|
||||
self.events_b.extend(events);
|
||||
|
||||
if old_count != event_count {
|
||||
detailed_trace!(
|
||||
#[cfg(feature = "detailed_trace")]
|
||||
tracing::trace!(
|
||||
"Events::extend() -> ids: ({}..{})",
|
||||
self.event_count,
|
||||
event_count
|
||||
|
||||
@ -2,7 +2,6 @@ use crate as bevy_ecs;
|
||||
#[cfg(feature = "multi_threaded")]
|
||||
use bevy_ecs::batching::BatchingStrategy;
|
||||
use bevy_ecs::event::{Event, EventCursor, EventId, EventInstance, Events};
|
||||
use bevy_utils::detailed_trace;
|
||||
use core::{iter::Chain, slice::Iter};
|
||||
|
||||
/// An iterator that yields any unread events from an [`EventReader`](super::EventReader) or [`EventCursor`].
|
||||
@ -92,7 +91,8 @@ impl<'a, E: Event> Iterator for EventIteratorWithId<'a, E> {
|
||||
.map(|instance| (&instance.event, instance.event_id))
|
||||
{
|
||||
Some(item) => {
|
||||
detailed_trace!("EventReader::iter() -> {}", item.1);
|
||||
#[cfg(feature = "detailed_trace")]
|
||||
tracing::trace!("EventReader::iter() -> {}", item.1);
|
||||
self.reader.last_event_count += 1;
|
||||
self.unread -= 1;
|
||||
Some(item)
|
||||
|
||||
@ -2,7 +2,6 @@ use crate as bevy_ecs;
|
||||
#[cfg(feature = "multi_threaded")]
|
||||
use bevy_ecs::batching::BatchingStrategy;
|
||||
use bevy_ecs::event::{Event, EventCursor, EventId, EventInstance, Events};
|
||||
use bevy_utils::detailed_trace;
|
||||
use core::{iter::Chain, slice::IterMut};
|
||||
|
||||
/// An iterator that yields any unread events from an [`EventMutator`] or [`EventCursor`].
|
||||
@ -95,7 +94,8 @@ impl<'a, E: Event> Iterator for EventMutIteratorWithId<'a, E> {
|
||||
.map(|instance| (&mut instance.event, instance.event_id))
|
||||
{
|
||||
Some(item) => {
|
||||
detailed_trace!("EventMutator::iter() -> {}", item.1);
|
||||
#[cfg(feature = "detailed_trace")]
|
||||
tracing::trace!("EventMutator::iter() -> {}", item.1);
|
||||
self.mutator.last_event_count += 1;
|
||||
self.unread -= 1;
|
||||
Some(item)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate as bevy_ecs;
|
||||
use alloc::vec::Vec;
|
||||
use bevy_ecs::{
|
||||
change_detection::{DetectChangesMut, MutUntyped},
|
||||
component::{ComponentId, Tick},
|
||||
|
||||
@ -4,9 +4,15 @@
|
||||
//! speed up code by shrinking the stack size of large types,
|
||||
//! and make comparisons for any type as fast as integers.
|
||||
|
||||
use alloc::{borrow::ToOwned, boxed::Box};
|
||||
use core::{fmt::Debug, hash::Hash, ops::Deref};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{OnceLock, PoisonError, RwLock};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use spin::{once::Once as OnceLock, rwlock::RwLock};
|
||||
|
||||
use bevy_utils::HashSet;
|
||||
|
||||
/// An interned value. Will stay valid until the end of the program and will not drop.
|
||||
@ -136,15 +142,31 @@ impl<T: Internable + ?Sized> Interner<T> {
|
||||
/// [`Interned<T>`] using the obtained static reference. Subsequent calls for the same `value`
|
||||
/// will return [`Interned<T>`] using the same static reference.
|
||||
pub fn intern(&self, value: &T) -> Interned<T> {
|
||||
#[cfg(feature = "std")]
|
||||
let lock = self.0.get_or_init(Default::default);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let lock = self.0.call_once(Default::default);
|
||||
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
let set = lock.read().unwrap_or_else(PoisonError::into_inner);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let set = lock.read();
|
||||
|
||||
if let Some(value) = set.get(value) {
|
||||
return Interned(*value);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
let mut set = lock.write().unwrap_or_else(PoisonError::into_inner);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let mut set = lock.write();
|
||||
|
||||
if let Some(value) = set.get(value) {
|
||||
Interned(*value)
|
||||
} else {
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
||||
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
||||
)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
compile_error!("bevy_ecs cannot safely compile for a 16-bit platform.");
|
||||
@ -68,9 +69,9 @@ pub mod prelude {
|
||||
},
|
||||
system::{
|
||||
Commands, Deferred, EntityCommand, EntityCommands, In, InMut, InRef, IntoSystem, Local,
|
||||
NonSend, NonSendMut, ParallelCommands, ParamSet, Populated, Query, ReadOnlySystem, Res,
|
||||
ResMut, Resource, Single, System, SystemIn, SystemInput, SystemParamBuilder,
|
||||
SystemParamFunction, WithParamWarnPolicy,
|
||||
NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem, Res, ResMut, Resource,
|
||||
Single, System, SystemIn, SystemInput, SystemParamBuilder, SystemParamFunction,
|
||||
WithParamWarnPolicy,
|
||||
},
|
||||
world::{
|
||||
Command, EntityMut, EntityRef, EntityWorldMut, FilteredResources, FilteredResourcesMut,
|
||||
@ -78,6 +79,10 @@ pub mod prelude {
|
||||
},
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "std")]
|
||||
pub use crate::system::ParallelCommands;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub use crate::reflect::{
|
||||
@ -89,6 +94,17 @@ pub mod prelude {
|
||||
pub use crate::reflect::AppFunctionRegistry;
|
||||
}
|
||||
|
||||
/// Exports used by macros.
|
||||
///
|
||||
/// These are not meant to be used directly and are subject to breaking changes.
|
||||
#[doc(hidden)]
|
||||
pub mod __macro_exports {
|
||||
// Cannot directly use `alloc::vec::Vec` in macros, as a crate may not have
|
||||
// included `extern crate alloc;`. This re-export ensures we have access
|
||||
// to `Vec` in `no_std` and `std` contexts.
|
||||
pub use alloc::vec::Vec;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate as bevy_ecs;
|
||||
|
||||
@ -4,6 +4,7 @@ use crate::{
|
||||
observer::ObserverState,
|
||||
world::{DeferredWorld, World},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to.
|
||||
#[derive(Default)]
|
||||
|
||||
@ -17,6 +17,7 @@ use crate::{
|
||||
system::IntoObserverSystem,
|
||||
world::{DeferredWorld, *},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use bevy_ptr::Ptr;
|
||||
use bevy_utils::HashMap;
|
||||
use core::{
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use core::any::Any;
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -4,6 +4,7 @@ use crate::{
|
||||
event::Event,
|
||||
world::{Command, DeferredWorld, World},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// A [`Command`] that emits a given trigger for a given set of targets.
|
||||
pub struct TriggerEvent<E, Targets: TriggerTargets = ()> {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::component::ComponentId;
|
||||
use crate::storage::SparseSetIndex;
|
||||
use crate::world::World;
|
||||
use alloc::{format, string::String, vec, vec::Vec};
|
||||
use core::{fmt, fmt::Debug, marker::PhantomData};
|
||||
use derive_more::derive::From;
|
||||
use disqualified::ShortName;
|
||||
|
||||
@ -2366,7 +2366,7 @@ impl<C: Component, T: Copy, S: Copy> StorageSwitch<C, T, S> {
|
||||
#[cfg(debug_assertions)]
|
||||
unreachable!();
|
||||
#[cfg(not(debug_assertions))]
|
||||
std::hint::unreachable_unchecked()
|
||||
core::hint::unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use super::{QueryData, QueryFilter, ReadOnlyQueryData};
|
||||
use crate::{
|
||||
archetype::{Archetype, ArchetypeEntity, Archetypes},
|
||||
component::Tick,
|
||||
@ -6,6 +7,7 @@ use crate::{
|
||||
storage::{Table, TableRow, Tables},
|
||||
world::unsafe_world_cell::UnsafeWorldCell,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
@ -15,8 +17,6 @@ use core::{
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
use super::{QueryData, QueryFilter, ReadOnlyQueryData};
|
||||
|
||||
/// An [`Iterator`] over query results of a [`Query`](crate::system::Query).
|
||||
///
|
||||
/// This struct is created by the [`Query::iter`](crate::system::Query::iter) and
|
||||
|
||||
@ -11,11 +11,12 @@ use crate::{
|
||||
storage::{SparseSetIndex, TableId},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
|
||||
};
|
||||
use bevy_utils::tracing::warn;
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::Span;
|
||||
use alloc::vec::Vec;
|
||||
use core::{borrow::Borrow, fmt, mem::MaybeUninit, ptr};
|
||||
use fixedbitset::FixedBitSet;
|
||||
use log::warn;
|
||||
#[cfg(feature = "trace")]
|
||||
use tracing::Span;
|
||||
|
||||
use super::{
|
||||
NopWorldQuery, QueryBuilder, QueryData, QueryEntityError, QueryFilter, QueryManyIter,
|
||||
@ -269,7 +270,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
matched_tables: Default::default(),
|
||||
matched_archetypes: Default::default(),
|
||||
#[cfg(feature = "trace")]
|
||||
par_iter_span: bevy_utils::tracing::info_span!(
|
||||
par_iter_span: tracing::info_span!(
|
||||
"par_for_each",
|
||||
query = core::any::type_name::<D>(),
|
||||
filter = core::any::type_name::<F>(),
|
||||
@ -295,7 +296,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
matched_tables: Default::default(),
|
||||
matched_archetypes: Default::default(),
|
||||
#[cfg(feature = "trace")]
|
||||
par_iter_span: bevy_utils::tracing::info_span!(
|
||||
par_iter_span: tracing::info_span!(
|
||||
"par_for_each",
|
||||
data = core::any::type_name::<D>(),
|
||||
filter = core::any::type_name::<F>(),
|
||||
@ -658,7 +659,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
matched_tables: self.matched_tables.clone(),
|
||||
matched_archetypes: self.matched_archetypes.clone(),
|
||||
#[cfg(feature = "trace")]
|
||||
par_iter_span: bevy_utils::tracing::info_span!(
|
||||
par_iter_span: tracing::info_span!(
|
||||
"par_for_each",
|
||||
query = core::any::type_name::<NewD>(),
|
||||
filter = core::any::type_name::<NewF>(),
|
||||
@ -780,7 +781,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
matched_tables,
|
||||
matched_archetypes,
|
||||
#[cfg(feature = "trace")]
|
||||
par_iter_span: bevy_utils::tracing::info_span!(
|
||||
par_iter_span: tracing::info_span!(
|
||||
"par_for_each",
|
||||
query = core::any::type_name::<NewD>(),
|
||||
filter = core::any::type_name::<NewF>(),
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
//! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`].
|
||||
//!
|
||||
//! Same as [`super::component`], but for bundles.
|
||||
use alloc::boxed::Box;
|
||||
use core::any::{Any, TypeId};
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
system::{EntityCommands, Resource},
|
||||
world::{Command, World},
|
||||
};
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, boxed::Box};
|
||||
use bevy_reflect::{PartialReflect, TypeRegistry};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
//!
|
||||
//! Same as [`super::component`], but for [`FromWorld`].
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use bevy_reflect::{FromType, Reflect};
|
||||
|
||||
use crate::world::{FromWorld, World};
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
//! Contains error and result helpers for use in fallible systems.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
/// A dynamic error type for use in fallible systems.
|
||||
pub type Error = Box<dyn core::error::Error + Send + Sync + 'static>;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, boxed::Box, format};
|
||||
use core::ops::Not;
|
||||
|
||||
use crate::system::{
|
||||
@ -401,6 +401,7 @@ pub mod common_conditions {
|
||||
removal_detection::RemovedComponents,
|
||||
system::{In, IntoSystem, Local, Res, Resource, System, SystemInput},
|
||||
};
|
||||
use alloc::format;
|
||||
|
||||
/// A [`Condition`]-satisfying system that returns `true`
|
||||
/// on the first time the condition is run and false every time after.
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
#[cfg(feature = "std")]
|
||||
mod multi_threaded;
|
||||
mod simple;
|
||||
mod single_threaded;
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, vec, vec::Vec};
|
||||
use core::any::TypeId;
|
||||
|
||||
pub use self::{
|
||||
multi_threaded::{MainThreadExecutor, MultiThreadedExecutor},
|
||||
simple::SimpleExecutor,
|
||||
single_threaded::SingleThreadedExecutor,
|
||||
};
|
||||
pub use self::{simple::SimpleExecutor, single_threaded::SingleThreadedExecutor};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use self::multi_threaded::{MainThreadExecutor, MultiThreadedExecutor};
|
||||
|
||||
use fixedbitset::FixedBitSet;
|
||||
|
||||
@ -53,6 +53,7 @@ pub enum ExecutorKind {
|
||||
/// immediately after running each system.
|
||||
Simple,
|
||||
/// Runs the schedule using a thread pool. Non-conflicting systems can run in parallel.
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(all(not(target_arch = "wasm32"), feature = "multi_threaded"), default)]
|
||||
MultiThreaded,
|
||||
}
|
||||
@ -72,9 +73,17 @@ pub struct SystemSchedule {
|
||||
pub(super) system_conditions: Vec<Vec<BoxedCondition>>,
|
||||
/// Indexed by system node id.
|
||||
/// Number of systems that the system immediately depends on.
|
||||
#[cfg_attr(
|
||||
not(feature = "std"),
|
||||
expect(dead_code, reason = "currently only used with the std feature")
|
||||
)]
|
||||
pub(super) system_dependencies: Vec<usize>,
|
||||
/// Indexed by system node id.
|
||||
/// List of systems that immediately depend on the system.
|
||||
#[cfg_attr(
|
||||
not(feature = "std"),
|
||||
expect(dead_code, reason = "currently only used with the std feature")
|
||||
)]
|
||||
pub(super) system_dependents: Vec<Vec<usize>>,
|
||||
/// Indexed by system node id.
|
||||
/// List of sets containing the system that have conditions
|
||||
@ -265,6 +274,10 @@ mod __rust_begin_short_backtrace {
|
||||
|
||||
/// # Safety
|
||||
/// See `ReadOnlySystem::run_unsafe`.
|
||||
#[cfg_attr(
|
||||
not(feature = "std"),
|
||||
expect(dead_code, reason = "currently only used with the std feature")
|
||||
)]
|
||||
#[inline(never)]
|
||||
pub(super) unsafe fn readonly_run_unsafe<O: 'static>(
|
||||
system: &mut dyn ReadOnlySystem<In = (), Out = O>,
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use core::any::Any;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor};
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::Span;
|
||||
use bevy_utils::{default, syncunsafecell::SyncUnsafeCell};
|
||||
use core::panic::AssertUnwindSafe;
|
||||
#[cfg(feature = "trace")]
|
||||
use tracing::{info_span, Span};
|
||||
|
||||
use concurrent_queue::ConcurrentQueue;
|
||||
use fixedbitset::FixedBitSet;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
use core::panic::AssertUnwindSafe;
|
||||
use fixedbitset::FixedBitSet;
|
||||
#[cfg(feature = "trace")]
|
||||
use tracing::info_span;
|
||||
|
||||
use crate::{
|
||||
schedule::{
|
||||
@ -100,16 +100,25 @@ impl SystemExecutor for SimpleExecutor {
|
||||
continue;
|
||||
}
|
||||
|
||||
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let f = AssertUnwindSafe(|| {
|
||||
// TODO: implement an error-handling API instead of suppressing a possible failure.
|
||||
let _ = __rust_begin_short_backtrace::run(system, world);
|
||||
}));
|
||||
if let Err(payload) = res {
|
||||
});
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
if let Err(payload) = std::panic::catch_unwind(f) {
|
||||
eprintln!("Encountered a panic in system `{}`!", &*system.name());
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
{
|
||||
(f)();
|
||||
}
|
||||
}
|
||||
|
||||
self.evaluated_sets.clear();
|
||||
self.completed_systems.clear();
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
use core::panic::AssertUnwindSafe;
|
||||
use fixedbitset::FixedBitSet;
|
||||
#[cfg(feature = "trace")]
|
||||
use tracing::info_span;
|
||||
|
||||
use crate::{
|
||||
schedule::{is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule},
|
||||
@ -107,7 +107,7 @@ impl SystemExecutor for SingleThreadedExecutor {
|
||||
continue;
|
||||
}
|
||||
|
||||
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let f = AssertUnwindSafe(|| {
|
||||
if system.is_exclusive() {
|
||||
// TODO: implement an error-handling API instead of suppressing a possible failure.
|
||||
let _ = __rust_begin_short_backtrace::run(system, world);
|
||||
@ -122,11 +122,21 @@ impl SystemExecutor for SingleThreadedExecutor {
|
||||
let _ = __rust_begin_short_backtrace::run_unsafe(system, world);
|
||||
};
|
||||
}
|
||||
}));
|
||||
if let Err(payload) = res {
|
||||
});
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
if let Err(payload) = std::panic::catch_unwind(f) {
|
||||
eprintln!("Encountered a panic in system `{}`!", &*system.name());
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
{
|
||||
(f)();
|
||||
}
|
||||
|
||||
self.unapplied_systems.insert(system_index);
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
//!
|
||||
//! [`petgraph`]: https://docs.rs/petgraph/0.6.5/petgraph/
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use bevy_utils::{hashbrown::HashSet, FixedHasher};
|
||||
use core::{
|
||||
fmt,
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
use alloc::collections::BTreeSet;
|
||||
use core::fmt::{Debug, Write};
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
use bevy_utils::{
|
||||
default,
|
||||
tracing::{error, info, warn},
|
||||
HashMap, HashSet,
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeSet,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use bevy_utils::{default, HashMap, HashSet};
|
||||
use core::fmt::{Debug, Write};
|
||||
use disqualified::ShortName;
|
||||
use fixedbitset::FixedBitSet;
|
||||
use log::{error, info, warn};
|
||||
use thiserror::Error;
|
||||
#[cfg(feature = "trace")]
|
||||
use tracing::info_span;
|
||||
|
||||
use crate::{
|
||||
self as bevy_ecs,
|
||||
@ -205,6 +208,7 @@ fn make_executor(kind: ExecutorKind) -> Box<dyn SystemExecutor> {
|
||||
match kind {
|
||||
ExecutorKind::Simple => Box::new(SimpleExecutor::new()),
|
||||
ExecutorKind::SingleThreaded => Box::new(SingleThreadedExecutor::new()),
|
||||
#[cfg(feature = "std")]
|
||||
ExecutorKind::MultiThreaded => Box::new(MultiThreadedExecutor::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
any::TypeId,
|
||||
fmt::Debug,
|
||||
|
||||
@ -1,22 +1,19 @@
|
||||
use core::any::TypeId;
|
||||
use fixedbitset::FixedBitSet;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
schedule::{InternedScheduleLabel, NodeId, Schedule, ScheduleLabel},
|
||||
system::{IntoSystem, ResMut, Resource, System},
|
||||
};
|
||||
use bevy_utils::{
|
||||
tracing::{info, warn},
|
||||
TypeIdMap,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use bevy_utils::{HashMap, TypeIdMap};
|
||||
use core::any::TypeId;
|
||||
use fixedbitset::FixedBitSet;
|
||||
use log::{info, warn};
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(not(feature = "bevy_debug_stepping"))]
|
||||
use bevy_utils::tracing::error;
|
||||
use log::error;
|
||||
|
||||
#[cfg(test)]
|
||||
use bevy_utils::tracing::debug;
|
||||
use log::debug;
|
||||
|
||||
use crate as bevy_ecs;
|
||||
|
||||
@ -423,6 +420,7 @@ impl Stepping {
|
||||
// transitions, and add debugging messages for permitted
|
||||
// transitions. Any action transition that falls through
|
||||
// this match block will be performed.
|
||||
#[expect(clippy::match_same_arms)]
|
||||
match (self.action, action) {
|
||||
// ignore non-transition updates, and prevent a call to
|
||||
// enable() from overwriting a step or continue call
|
||||
|
||||
@ -4,10 +4,13 @@ use crate::{
|
||||
component::{ComponentId, ComponentTicks, Components, Tick, TickCells},
|
||||
storage::{blob_vec::BlobVec, SparseSet},
|
||||
};
|
||||
use alloc::string::String;
|
||||
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
use core::panic::Location;
|
||||
use core::{cell::UnsafeCell, mem::ManuallyDrop};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::thread::ThreadId;
|
||||
|
||||
/// The type-erased backing storage and metadata for a single resource within a [`World`].
|
||||
@ -19,8 +22,13 @@ pub struct ResourceData<const SEND: bool> {
|
||||
data: ManuallyDrop<BlobVec>,
|
||||
added_ticks: UnsafeCell<Tick>,
|
||||
changed_ticks: UnsafeCell<Tick>,
|
||||
#[cfg_attr(
|
||||
not(feature = "std"),
|
||||
expect(dead_code, reason = "currently only used with the std feature")
|
||||
)]
|
||||
type_name: String,
|
||||
id: ArchetypeComponentId,
|
||||
#[cfg(feature = "std")]
|
||||
origin_thread_id: Option<ThreadId>,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
changed_by: UnsafeCell<&'static Location<'static>>,
|
||||
@ -35,6 +43,7 @@ impl<const SEND: bool> Drop for ResourceData<SEND> {
|
||||
// If this thread is already panicking, panicking again will cause
|
||||
// the entire process to abort. In this case we choose to avoid
|
||||
// dropping or checking this altogether and just leak the column.
|
||||
#[cfg(feature = "std")]
|
||||
if std::thread::panicking() {
|
||||
return;
|
||||
}
|
||||
@ -63,6 +72,8 @@ impl<const SEND: bool> ResourceData<SEND> {
|
||||
if SEND {
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
if self.origin_thread_id != Some(std::thread::current().id()) {
|
||||
// Panic in tests, as testing for aborting is nearly impossible
|
||||
panic!(
|
||||
@ -72,6 +83,10 @@ impl<const SEND: bool> ResourceData<SEND> {
|
||||
std::thread::current().id()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Handle no_std non-send.
|
||||
// Currently, no_std is single-threaded only, so this is safe to ignore.
|
||||
// To support no_std multithreading, an alternative will be required.
|
||||
}
|
||||
|
||||
/// Returns true if the resource is populated.
|
||||
@ -182,6 +197,7 @@ impl<const SEND: bool> ResourceData<SEND> {
|
||||
self.data.replace_unchecked(Self::ROW, value);
|
||||
}
|
||||
} else {
|
||||
#[cfg(feature = "std")]
|
||||
if !SEND {
|
||||
self.origin_thread_id = Some(std::thread::current().id());
|
||||
}
|
||||
@ -220,6 +236,7 @@ impl<const SEND: bool> ResourceData<SEND> {
|
||||
self.data.replace_unchecked(Self::ROW, value);
|
||||
}
|
||||
} else {
|
||||
#[cfg(feature = "std")]
|
||||
if !SEND {
|
||||
self.origin_thread_id = Some(std::thread::current().id());
|
||||
}
|
||||
@ -373,6 +390,7 @@ impl<const SEND: bool> Resources<SEND> {
|
||||
changed_ticks: UnsafeCell::new(Tick::new(0)),
|
||||
type_name: String::from(component_info.name()),
|
||||
id: f(),
|
||||
#[cfg(feature = "std")]
|
||||
origin_thread_id: None,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
changed_by: UnsafeCell::new(Location::caller())
|
||||
|
||||
@ -4,6 +4,7 @@ use crate::{
|
||||
entity::Entity,
|
||||
storage::{Column, TableRow},
|
||||
};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use bevy_ptr::{OwningPtr, Ptr};
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
use core::panic::Location;
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::{
|
||||
component::TickCells,
|
||||
storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use bevy_ptr::PtrMut;
|
||||
|
||||
/// Very similar to a normal [`Column`], but with the capacities and lengths cut out for performance reasons.
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::{
|
||||
query::DebugCheckedUnwrap,
|
||||
storage::{blob_vec::BlobVec, ImmutableSparseSet, SparseSet},
|
||||
};
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
|
||||
use bevy_utils::HashMap;
|
||||
pub use column::*;
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
use crate::query::DebugCheckedUnwrap;
|
||||
use alloc::alloc::{alloc, handle_alloc_error, realloc};
|
||||
use alloc::{
|
||||
alloc::{alloc, handle_alloc_error, realloc},
|
||||
boxed::Box,
|
||||
};
|
||||
use core::{
|
||||
alloc::Layout,
|
||||
mem::{needs_drop, size_of},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
|
||||
use super::{IntoSystem, ReadOnlySystem, System};
|
||||
use crate::{
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use bevy_utils::synccell::SyncCell;
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, format, vec::Vec};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#[cfg(feature = "std")]
|
||||
mod parallel_scope;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{marker::PhantomData, panic::Location};
|
||||
|
||||
use super::{
|
||||
@ -22,7 +24,9 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use bevy_ptr::OwningPtr;
|
||||
use bevy_utils::tracing::{error, info};
|
||||
use log::{error, info};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use parallel_scope::*;
|
||||
|
||||
/// A [`Command`] queue to perform structural changes to the [`World`].
|
||||
|
||||
@ -10,7 +10,7 @@ use crate::{
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World},
|
||||
};
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, vec, vec::Vec};
|
||||
use core::marker::PhantomData;
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
|
||||
@ -11,12 +11,12 @@ use crate::{
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId},
|
||||
};
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, vec, vec::Vec};
|
||||
use core::marker::PhantomData;
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::{info_span, Span};
|
||||
use tracing::{info_span, Span};
|
||||
|
||||
use super::{In, IntoSystem, ReadOnlySystem, SystemParamBuilder};
|
||||
|
||||
@ -213,7 +213,7 @@ impl ParamWarnPolicy {
|
||||
return;
|
||||
}
|
||||
|
||||
bevy_utils::tracing::warn!(
|
||||
log::warn!(
|
||||
"{0} did not run because it requested inaccessible system parameter {1}",
|
||||
name,
|
||||
disqualified::ShortName::of::<P>()
|
||||
|
||||
@ -1651,7 +1651,13 @@ mod tests {
|
||||
assert_is_system(returning::<Option<()>>.map(drop));
|
||||
assert_is_system(returning::<&str>.map(u64::from_str).map(Result::unwrap));
|
||||
assert_is_system(static_system_param);
|
||||
assert_is_system(exclusive_in_out::<(), Result<(), std::io::Error>>.map(bevy_utils::error));
|
||||
assert_is_system(
|
||||
exclusive_in_out::<(), Result<(), std::io::Error>>.map(|result| {
|
||||
if let Err(error) = result {
|
||||
log::error!("{:?}", error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
assert_is_system(exclusive_with_state);
|
||||
assert_is_system(returning::<bool>.pipe(exclusive_in_out::<bool, ()>));
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
use core::any::TypeId;
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use bevy_utils::tracing::warn;
|
||||
use core::fmt::Debug;
|
||||
use log::warn;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
|
||||
};
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
|
||||
use core::any::TypeId;
|
||||
|
||||
use super::IntoSystem;
|
||||
|
||||
@ -16,6 +16,7 @@ use crate::{
|
||||
FromWorld, World,
|
||||
},
|
||||
};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
|
||||
use bevy_ecs_macros::impl_param_set;
|
||||
pub use bevy_ecs_macros::{Resource, SystemParam};
|
||||
use bevy_ptr::UnsafeCellDeref;
|
||||
|
||||
@ -8,6 +8,7 @@ use crate::{
|
||||
system::{input::SystemInput, BoxedSystem, IntoSystem, System},
|
||||
world::{Command, World},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use bevy_ecs_macros::{Component, Resource};
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
use bevy_reflect::Reflect;
|
||||
|
||||
@ -7,8 +7,9 @@ use core::{
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use bevy_ptr::{OwningPtr, Unaligned};
|
||||
use bevy_utils::tracing::warn;
|
||||
use log::warn;
|
||||
|
||||
use crate::world::{Command, World};
|
||||
|
||||
@ -261,7 +262,7 @@ impl RawCommandQueue {
|
||||
self.bytes.as_mut().as_mut_ptr().add(local_cursor).cast(),
|
||||
))
|
||||
};
|
||||
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let f = AssertUnwindSafe(|| {
|
||||
// SAFETY: The data underneath the cursor must correspond to the type erased in metadata,
|
||||
// since they were stored next to each other by `.push()`.
|
||||
// For ZSTs, the type doesn't matter as long as the pointer is non-null.
|
||||
@ -269,7 +270,11 @@ impl RawCommandQueue {
|
||||
// At this point, it will either point to the next `CommandMeta`,
|
||||
// or the cursor will be out of bounds and the loop will end.
|
||||
unsafe { (meta.consume_command_and_get_size)(cmd, world, &mut local_cursor) };
|
||||
}));
|
||||
});
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
let result = std::panic::catch_unwind(f);
|
||||
|
||||
if let Err(payload) = result {
|
||||
// local_cursor now points to the location _after_ the panicked command.
|
||||
@ -298,6 +303,10 @@ impl RawCommandQueue {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
(f)();
|
||||
}
|
||||
|
||||
// Reset the buffer: all commands past the original `start` cursor have been applied.
|
||||
// SAFETY: we are setting the length of bytes to the original length, minus the length of the original
|
||||
// list of commands being considered. All bytes remaining in the Vec are still valid, unapplied commands.
|
||||
|
||||
@ -356,7 +356,7 @@ impl<'w> DeferredWorld<'w> {
|
||||
events: impl IntoIterator<Item = E>,
|
||||
) -> Option<SendBatchIds<E>> {
|
||||
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
||||
bevy_utils::tracing::error!(
|
||||
log::error!(
|
||||
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
||||
core::any::type_name::<E>()
|
||||
);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use alloc::vec::Vec;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::{
|
||||
|
||||
@ -12,6 +12,7 @@ use crate::{
|
||||
system::IntoObserverSystem,
|
||||
world::{error::EntityComponentError, DeferredWorld, Mut, World},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use bevy_ptr::{OwningPtr, Ptr};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use spin::once::Once as OnceLock;
|
||||
|
||||
use crate::{
|
||||
change_detection::{Mut, MutUntyped, Ref, Ticks, TicksMut},
|
||||
component::{ComponentId, Tick},
|
||||
@ -217,11 +221,21 @@ impl<'w, 's> From<&'w FilteredResourcesMut<'_, 's>> for FilteredResources<'w, 's
|
||||
impl<'w> From<&'w World> for FilteredResources<'w, 'static> {
|
||||
fn from(value: &'w World) -> Self {
|
||||
static READ_ALL_RESOURCES: OnceLock<Access<ComponentId>> = OnceLock::new();
|
||||
let access = READ_ALL_RESOURCES.get_or_init(|| {
|
||||
let access = {
|
||||
let init = || {
|
||||
let mut access = Access::new();
|
||||
access.read_all_resources();
|
||||
access
|
||||
});
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
let access = READ_ALL_RESOURCES.get_or_init(init);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let access = READ_ALL_RESOURCES.call_once(init);
|
||||
|
||||
access
|
||||
};
|
||||
|
||||
let last_run = value.last_change_tick();
|
||||
let this_run = value.read_change_tick();
|
||||
@ -494,11 +508,22 @@ impl<'w, 's> FilteredResourcesMut<'w, 's> {
|
||||
impl<'w> From<&'w mut World> for FilteredResourcesMut<'w, 'static> {
|
||||
fn from(value: &'w mut World) -> Self {
|
||||
static WRITE_ALL_RESOURCES: OnceLock<Access<ComponentId>> = OnceLock::new();
|
||||
let access = WRITE_ALL_RESOURCES.get_or_init(|| {
|
||||
|
||||
let access = {
|
||||
let init = || {
|
||||
let mut access = Access::new();
|
||||
access.write_all_resources();
|
||||
access
|
||||
});
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
let access = WRITE_ALL_RESOURCES.get_or_init(init);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
let access = WRITE_ALL_RESOURCES.call_once(init);
|
||||
|
||||
access
|
||||
};
|
||||
|
||||
let last_run = value.last_change_tick();
|
||||
let this_run = value.change_tick();
|
||||
|
||||
@ -4,8 +4,13 @@ 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 super::unsafe_world_cell::UnsafeWorldCell;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
|
||||
@ -51,13 +51,16 @@ use crate::{
|
||||
error::{EntityFetchError, TryRunScheduleError},
|
||||
},
|
||||
};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use bevy_ptr::{OwningPtr, Ptr};
|
||||
use bevy_utils::tracing::warn;
|
||||
use core::{
|
||||
any::TypeId,
|
||||
fmt,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
use core::{any::TypeId, fmt};
|
||||
use log::warn;
|
||||
|
||||
#[cfg(not(feature = "portable-atomic"))]
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[cfg(feature = "portable-atomic")]
|
||||
use portable_atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
use bevy_ptr::UnsafeCellDeref;
|
||||
@ -2764,7 +2767,7 @@ impl World {
|
||||
events: impl IntoIterator<Item = E>,
|
||||
) -> Option<SendBatchIds<E>> {
|
||||
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
||||
bevy_utils::tracing::error!(
|
||||
log::error!(
|
||||
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
||||
core::any::type_name::<E>()
|
||||
);
|
||||
@ -3093,7 +3096,7 @@ impl World {
|
||||
} = self.storages;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
let _span = bevy_utils::tracing::info_span!("check component ticks").entered();
|
||||
let _span = tracing::info_span!("check component ticks").entered();
|
||||
tables.check_change_ticks(change_tick);
|
||||
sparse_sets.check_change_ticks(change_tick);
|
||||
resources.check_change_ticks(change_tick);
|
||||
|
||||
@ -4,6 +4,7 @@ use core::any::TypeId;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use alloc::string::{String, ToString};
|
||||
use bevy_reflect::{Reflect, ReflectFromPtr};
|
||||
|
||||
use crate::{prelude::*, world::ComponentId};
|
||||
|
||||
@ -25,6 +25,12 @@ 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;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
@ -306,7 +312,7 @@ impl<'w> UnsafeWorldCell<'w> {
|
||||
let change_tick = unsafe { &self.world_metadata().change_tick };
|
||||
// NOTE: We can used a relaxed memory ordering here, since nothing
|
||||
// other than the atomic value itself is relying on atomic synchronization
|
||||
Tick::new(change_tick.fetch_add(1, core::sync::atomic::Ordering::Relaxed))
|
||||
Tick::new(change_tick.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// Provides unchecked access to the internal data stores of the [`World`].
|
||||
|
||||
@ -9,15 +9,16 @@ license = "MIT OR Apache-2.0"
|
||||
keywords = ["bevy"]
|
||||
|
||||
[features]
|
||||
default = ["std", "serde"]
|
||||
std = ["alloc", "tracing/std", "foldhash/std", "dep:thread_local"]
|
||||
default = ["std", "serde", "tracing"]
|
||||
std = ["alloc", "tracing?/std", "foldhash/std", "dep:thread_local"]
|
||||
alloc = ["hashbrown"]
|
||||
detailed_trace = []
|
||||
serde = ["hashbrown/serde"]
|
||||
tracing = ["dep:tracing"]
|
||||
|
||||
[dependencies]
|
||||
foldhash = { version = "0.1.3", default-features = false }
|
||||
tracing = { version = "0.1", default-features = false }
|
||||
tracing = { version = "0.1", default-features = false, optional = true }
|
||||
hashbrown = { version = "0.15.1", features = [
|
||||
"equivalent",
|
||||
"raw-entry",
|
||||
|
||||
@ -61,6 +61,7 @@ pub use hashbrown;
|
||||
#[cfg(feature = "std")]
|
||||
pub use parallel_queue::*;
|
||||
pub use time::*;
|
||||
#[cfg(feature = "tracing")]
|
||||
pub use tracing;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
@ -400,16 +401,19 @@ impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
}
|
||||
|
||||
/// Calls the [`tracing::info!`] macro on a value.
|
||||
#[cfg(feature = "tracing")]
|
||||
pub fn info<T: Debug>(data: T) {
|
||||
tracing::info!("{:?}", data);
|
||||
}
|
||||
|
||||
/// Calls the [`tracing::debug!`] macro on a value.
|
||||
#[cfg(feature = "tracing")]
|
||||
pub fn dbg<T: Debug>(data: T) {
|
||||
tracing::debug!("{:?}", data);
|
||||
}
|
||||
|
||||
/// Processes a [`Result`] by calling the [`tracing::warn!`] macro in case of an [`Err`] value.
|
||||
#[cfg(feature = "tracing")]
|
||||
pub fn warn<E: Debug>(result: Result<(), E>) {
|
||||
if let Err(warn) = result {
|
||||
tracing::warn!("{:?}", warn);
|
||||
@ -417,6 +421,7 @@ pub fn warn<E: Debug>(result: Result<(), E>) {
|
||||
}
|
||||
|
||||
/// Processes a [`Result`] by calling the [`tracing::error!`] macro in case of an [`Err`] value.
|
||||
#[cfg(feature = "tracing")]
|
||||
pub fn error<E: Debug>(result: Result<(), E>) {
|
||||
if let Err(error) = result {
|
||||
tracing::error!("{:?}", error);
|
||||
@ -424,6 +429,7 @@ pub fn error<E: Debug>(result: Result<(), E>) {
|
||||
}
|
||||
|
||||
/// Like [`tracing::trace`], but conditional on cargo feature `detailed_trace`.
|
||||
#[cfg(feature = "tracing")]
|
||||
#[macro_export]
|
||||
macro_rules! detailed_trace {
|
||||
($($tts:tt)*) => {
|
||||
|
||||
@ -94,6 +94,14 @@ impl Prepare for CompileCheckNoStdCommand {
|
||||
"Please fix compiler errors in output above for bevy_tasks no_std compatibility.",
|
||||
));
|
||||
|
||||
commands.push(PreparedCommand::new::<Self>(
|
||||
cmd!(
|
||||
sh,
|
||||
"cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section,bevy_debug_stepping,bevy_reflect --target {target}"
|
||||
),
|
||||
"Please fix compiler errors in output above for bevy_ecs no_std compatibility.",
|
||||
));
|
||||
|
||||
commands
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user