bevy/crates/bevy_ecs/src/reflect/from_world.rs
Zachary Harrold 1f2d0e6308
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>
2024-12-17 21:40:36 +00:00

88 lines
3.3 KiB
Rust

//! Definitions for [`FromWorld`] reflection.
//! This allows creating instances of types that are known only at runtime and
//! require an `&mut World` to be initialized.
//!
//! This module exports two types: [`ReflectFromWorldFns`] and [`ReflectFromWorld`].
//!
//! Same as [`super::component`], but for [`FromWorld`].
use alloc::boxed::Box;
use bevy_reflect::{FromType, Reflect};
use crate::world::{FromWorld, World};
/// A struct used to operate on the reflected [`FromWorld`] trait of a type.
///
/// A [`ReflectFromWorld`] for type `T` can be obtained via
/// [`bevy_reflect::TypeRegistration::data`].
#[derive(Clone)]
pub struct ReflectFromWorld(ReflectFromWorldFns);
/// The raw function pointers needed to make up a [`ReflectFromWorld`].
#[derive(Clone)]
pub struct ReflectFromWorldFns {
/// Function pointer implementing [`ReflectFromWorld::from_world()`].
pub from_world: fn(&mut World) -> Box<dyn Reflect>,
}
impl ReflectFromWorldFns {
/// Get the default set of [`ReflectFromWorldFns`] for a specific type using its
/// [`FromType`] implementation.
///
/// This is useful if you want to start with the default implementation before overriding some
/// of the functions to create a custom implementation.
pub fn new<T: Reflect + FromWorld>() -> Self {
<ReflectFromWorld as FromType<T>>::from_type().0
}
}
impl ReflectFromWorld {
/// Constructs default reflected [`FromWorld`] from world using [`from_world()`](FromWorld::from_world).
pub fn from_world(&self, world: &mut World) -> Box<dyn Reflect> {
(self.0.from_world)(world)
}
/// Create a custom implementation of [`ReflectFromWorld`].
///
/// This is an advanced feature,
/// useful for scripting implementations,
/// that should not be used by most users
/// unless you know what you are doing.
///
/// Usually you should derive [`Reflect`] and add the `#[reflect(FromWorld)]` bundle
/// to generate a [`ReflectFromWorld`] implementation automatically.
///
/// See [`ReflectFromWorldFns`] for more information.
pub fn new(fns: ReflectFromWorldFns) -> Self {
Self(fns)
}
/// The underlying function pointers implementing methods on `ReflectFromWorld`.
///
/// This is useful when you want to keep track locally of an individual
/// function pointer.
///
/// Calling [`TypeRegistry::get`] followed by
/// [`TypeRegistration::data::<ReflectFromWorld>`] can be costly if done several
/// times per frame. Consider cloning [`ReflectFromWorld`] and keeping it
/// between frames, cloning a `ReflectFromWorld` is very cheap.
///
/// If you only need a subset of the methods on `ReflectFromWorld`,
/// use `fn_pointers` to get the underlying [`ReflectFromWorldFns`]
/// and copy the subset of function pointers you care about.
///
/// [`TypeRegistration::data::<ReflectFromWorld>`]: bevy_reflect::TypeRegistration::data
/// [`TypeRegistry::get`]: bevy_reflect::TypeRegistry::get
pub fn fn_pointers(&self) -> &ReflectFromWorldFns {
&self.0
}
}
impl<B: Reflect + FromWorld> FromType<B> for ReflectFromWorld {
fn from_type() -> Self {
ReflectFromWorld(ReflectFromWorldFns {
from_world: |world| Box::new(B::from_world(world)),
})
}
}