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:
Zachary Harrold 2024-12-18 08:40:36 +11:00 committed by GitHub
parent 6fd6ce1367
commit 1f2d0e6308
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
63 changed files with 520 additions and 180 deletions

View File

@ -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"

View File

@ -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);

View File

@ -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,

View File

@ -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")]

View File

@ -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

View File

@ -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},

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -1,4 +1,5 @@
use crate as bevy_ecs;
use alloc::vec::Vec;
use bevy_ecs::{
change_detection::{DetectChangesMut, MutUntyped},
component::{ComponentId, Tick},

View File

@ -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 {

View File

@ -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;

View File

@ -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)]

View File

@ -17,6 +17,7 @@ use crate::{
system::IntoObserverSystem,
world::{DeferredWorld, *},
};
use alloc::vec::Vec;
use bevy_ptr::Ptr;
use bevy_utils::HashMap;
use core::{

View File

@ -1,3 +1,4 @@
use alloc::{boxed::Box, vec, vec::Vec};
use core::any::Any;
use crate::{

View File

@ -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 = ()> {

View File

@ -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;

View File

@ -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()
}
}
}

View File

@ -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

View File

@ -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>(),

View File

@ -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::{

View File

@ -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;

View File

@ -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};

View File

@ -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>;

View File

@ -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.

View File

@ -1,3 +1,4 @@
use alloc::{boxed::Box, vec, vec::Vec};
use variadics_please::all_tuples;
use crate::{

View File

@ -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>,

View File

@ -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;

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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()),
}
}

View File

@ -1,3 +1,4 @@
use alloc::boxed::Box;
use core::{
any::TypeId,
fmt::Debug,

View File

@ -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

View File

@ -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())

View File

@ -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;

View File

@ -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.

View File

@ -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::*;

View File

@ -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},

View File

@ -1,4 +1,4 @@
use alloc::borrow::Cow;
use alloc::{borrow::Cow, vec::Vec};
use super::{IntoSystem, ReadOnlySystem, System};
use crate::{

View File

@ -1,3 +1,4 @@
use alloc::{boxed::Box, vec::Vec};
use bevy_utils::synccell::SyncCell;
use variadics_please::all_tuples;

View File

@ -1,4 +1,4 @@
use alloc::borrow::Cow;
use alloc::{borrow::Cow, format, vec::Vec};
use core::marker::PhantomData;
use crate::{

View File

@ -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`].

View File

@ -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;

View File

@ -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>()

View File

@ -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, ()>));

View File

@ -1,4 +1,4 @@
use alloc::borrow::Cow;
use alloc::{borrow::Cow, vec::Vec};
use core::any::TypeId;
use crate::{

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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>()
);

View File

@ -1,3 +1,4 @@
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use crate::{

View File

@ -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")]

View File

@ -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();

View File

@ -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)]

View File

@ -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);

View File

@ -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};

View File

@ -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`].

View File

@ -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",

View File

@ -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)*) => {

View File

@ -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
}
}