diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index 28af9753943f8..813deb251e0b1 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -7,7 +7,7 @@ entities: { 4294967296: ( components: { - "bevy_core::name::Name": ( + "bevy_ecs::name::Name": ( hash: 17588334858059901562, name: "joe", ), diff --git a/crates/bevy_animation/Cargo.toml b/crates/bevy_animation/Cargo.toml index 81e662e5c38c3..b3312aa0ce2d7 100644 --- a/crates/bevy_animation/Cargo.toml +++ b/crates/bevy_animation/Cargo.toml @@ -13,7 +13,6 @@ keywords = ["bevy"] bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" } bevy_color = { path = "../bevy_color", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" } bevy_log = { path = "../bevy_log", version = "0.15.0-dev" } bevy_math = { path = "../bevy_math", version = "0.15.0-dev" } diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index fc32ff4191dad..416e8cf16553f 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -55,11 +55,20 @@ std = [ ## `critical-section` provides the building blocks for synchronization primitives ## on all platforms, including `no_std`. -critical-section = ["bevy_tasks?/critical-section", "bevy_ecs/critical-section"] +critical-section = [ + "portable-atomic?/critical-section", + "bevy_tasks?/critical-section", + "bevy_ecs/critical-section", +] ## `portable-atomic` provides additional platform support for atomic types and ## operations, even on targets without native support. -portable-atomic = ["bevy_tasks?/portable-atomic", "bevy_ecs/portable-atomic"] +portable-atomic = [ + "dep:portable-atomic", + "dep:portable-atomic-util", + "bevy_tasks?/portable-atomic", + "bevy_ecs/portable-atomic", +] [dependencies] # bevy @@ -77,6 +86,12 @@ thiserror = { version = "2", default-features = false } variadics_please = "1.1" tracing = { version = "0.1", default-features = false, optional = true } log = { version = "0.4", default-features = false } +portable-atomic = { version = "1", default-features = false, features = [ + "fallback", +], optional = true } +portable-atomic-util = { version = "0.2.4", features = [ + "alloc", +], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] ctrlc = { version = "3.4.4", optional = true } @@ -86,6 +101,9 @@ wasm-bindgen = { version = "0.2" } web-sys = { version = "0.3", features = ["Window"] } console_error_panic_hook = "0.1.6" +[dev-dependencies] +crossbeam-channel = "0.5.0" + [lints] workspace = true diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 668ebc9118ef5..a755b978ed081 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -103,7 +103,10 @@ impl Default for App { app.sub_apps.main.update_schedule = Some(Main.intern()); #[cfg(feature = "bevy_reflect")] - app.init_resource::(); + { + app.init_resource::(); + app.register_type::(); + } #[cfg(feature = "reflect_functions")] app.init_resource::(); diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index e9c1054b9e4e1..ff680e4f04ac6 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -24,6 +24,8 @@ mod plugin; mod plugin_group; mod schedule_runner; mod sub_app; +#[cfg(feature = "bevy_tasks")] +mod task_pool_plugin; #[cfg(all(not(target_arch = "wasm32"), feature = "std"))] mod terminal_ctrl_c_handler; @@ -34,6 +36,8 @@ pub use plugin::*; pub use plugin_group::*; pub use schedule_runner::*; pub use sub_app::*; +#[cfg(feature = "bevy_tasks")] +pub use task_pool_plugin::*; #[cfg(all(not(target_arch = "wasm32"), feature = "std"))] pub use terminal_ctrl_c_handler::*; @@ -52,4 +56,8 @@ pub mod prelude { sub_app::SubApp, Plugin, PluginGroup, }; + + #[cfg(feature = "bevy_tasks")] + #[doc(hidden)] + pub use crate::{NonSendMarker, TaskPoolOptions, TaskPoolPlugin}; } diff --git a/crates/bevy_core/src/task_pool_options.rs b/crates/bevy_app/src/task_pool_plugin.rs similarity index 73% rename from crates/bevy_core/src/task_pool_options.rs rename to crates/bevy_app/src/task_pool_plugin.rs index cdb0418a35338..5623371dad58f 100644 --- a/crates/bevy_core/src/task_pool_options.rs +++ b/crates/bevy_app/src/task_pool_plugin.rs @@ -1,8 +1,55 @@ +#![cfg_attr( + feature = "portable-atomic", + expect( + clippy::redundant_closure, + reason = "portable_atomic_util::Arc has subtly different implicit behavior" + ) +)] + +use crate::{App, Last, Plugin}; + +use alloc::string::ToString; +use bevy_ecs::prelude::*; use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder}; -use bevy_utils::tracing::trace; +use core::{fmt::Debug, marker::PhantomData}; +use log::trace; + +#[cfg(feature = "portable-atomic")] +use portable_atomic_util::Arc; +#[cfg(not(feature = "portable-atomic"))] use alloc::sync::Arc; -use core::fmt::Debug; + +#[cfg(not(target_arch = "wasm32"))] +use bevy_tasks::tick_global_task_pools_on_main_thread; + +/// Setup of default task pools: [`AsyncComputeTaskPool`], [`ComputeTaskPool`], [`IoTaskPool`]. +#[derive(Default)] +pub struct TaskPoolPlugin { + /// Options for the [`TaskPool`](bevy_tasks::TaskPool) created at application start. + pub task_pool_options: TaskPoolOptions, +} + +impl Plugin for TaskPoolPlugin { + fn build(&self, _app: &mut App) { + // Setup the default bevy task pools + self.task_pool_options.create_default_pools(); + + #[cfg(not(target_arch = "wasm32"))] + _app.add_systems(Last, tick_global_task_pools); + } +} +/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread. +pub struct NonSendMarker(PhantomData<*mut ()>); + +/// A system used to check and advanced our task pools. +/// +/// Calls [`tick_global_task_pools_on_main_thread`], +/// and uses [`NonSendMarker`] to ensure that this system runs on the main thread +#[cfg(not(target_arch = "wasm32"))] +fn tick_global_task_pools(_main_thread_marker: Option>) { + tick_global_task_pools_on_main_thread(); +} /// Defines a simple way to determine how many threads to use given the number of remaining cores /// and number of total cores @@ -37,7 +84,14 @@ impl TaskPoolThreadAssignmentPolicy { /// Determine the number of threads to use for this task pool fn get_number_of_threads(&self, remaining_threads: usize, total_threads: usize) -> usize { assert!(self.percent >= 0.0); - let mut desired = (total_threads as f32 * self.percent).round() as usize; + let proportion = total_threads as f32 * self.percent; + let mut desired = proportion as usize; + + // Equivalent to round() for positive floats without libm requirement for + // no_std compatibility + if proportion - desired as f32 >= 0.5 { + desired += 1; + } // Limit ourselves to the number of cores available desired = desired.min(remaining_threads); @@ -50,7 +104,7 @@ impl TaskPoolThreadAssignmentPolicy { } /// Helper for configuring and creating the default task pools. For end-users who want full control, -/// set up [`TaskPoolPlugin`](super::TaskPoolPlugin) +/// set up [`TaskPoolPlugin`] #[derive(Clone, Debug)] pub struct TaskPoolOptions { /// If the number of physical cores is less than `min_total_threads`, force using @@ -208,3 +262,42 @@ impl TaskPoolOptions { } } } + +#[cfg(test)] +mod tests { + use super::*; + use bevy_tasks::prelude::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}; + + #[test] + fn runs_spawn_local_tasks() { + let mut app = App::new(); + app.add_plugins(TaskPoolPlugin::default()); + + let (async_tx, async_rx) = crossbeam_channel::unbounded(); + AsyncComputeTaskPool::get() + .spawn_local(async move { + async_tx.send(()).unwrap(); + }) + .detach(); + + let (compute_tx, compute_rx) = crossbeam_channel::unbounded(); + ComputeTaskPool::get() + .spawn_local(async move { + compute_tx.send(()).unwrap(); + }) + .detach(); + + let (io_tx, io_rx) = crossbeam_channel::unbounded(); + IoTaskPool::get() + .spawn_local(async move { + io_tx.send(()).unwrap(); + }) + .detach(); + + app.run(); + + async_rx.try_recv().unwrap(); + compute_rx.try_recv().unwrap(); + io_rx.try_recv().unwrap(); + } +} diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index e1d62ad2a2169..de7094a63663e 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -65,7 +65,6 @@ js-sys = "0.3" notify-debouncer-full = { version = "0.4.0", optional = true } [dev-dependencies] -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_log = { path = "../bevy_log", version = "0.15.0-dev" } [lints] diff --git a/crates/bevy_asset/src/asset_changed.rs b/crates/bevy_asset/src/asset_changed.rs index c202a57f1f9f7..b7902587d420b 100644 --- a/crates/bevy_asset/src/asset_changed.rs +++ b/crates/bevy_asset/src/asset_changed.rs @@ -296,8 +296,7 @@ mod tests { use core::num::NonZero; use crate::{AssetApp, Assets}; - use bevy_app::{App, AppExit, Last, Startup, Update}; - use bevy_core::TaskPoolPlugin; + use bevy_app::{App, AppExit, Last, Startup, TaskPoolPlugin, Update}; use bevy_ecs::schedule::IntoSystemConfigs; use bevy_ecs::{ component::Component, diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index afd41d1d18a8f..cb14a190a335f 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -633,8 +633,7 @@ mod tests { AssetPlugin, AssetServer, Assets, }; use alloc::sync::Arc; - use bevy_app::{App, Update}; - use bevy_core::TaskPoolPlugin; + use bevy_app::{App, TaskPoolPlugin, Update}; use bevy_ecs::{ event::EventCursor, prelude::*, diff --git a/crates/bevy_core/Cargo.toml b/crates/bevy_core/Cargo.toml deleted file mode 100644 index 92dd933656052..0000000000000 --- a/crates/bevy_core/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "bevy_core" -version = "0.15.0-dev" -edition = "2021" -description = "Provides core functionality for Bevy Engine" -homepage = "https://bevyengine.org" -repository = "https://github.com/bevyengine/bevy" -license = "MIT OR Apache-2.0" -keywords = ["bevy"] - -[dependencies] -# bevy -bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false } -bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false } -bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [ - "bevy", -], optional = true } -bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev" } -bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" } - -# other -serde = { version = "1.0", optional = true } - -[features] -default = ["bevy_reflect"] -bevy_reflect = [ - "dep:bevy_reflect", - "bevy_app/bevy_reflect", - "bevy_ecs/bevy_reflect", -] -serialize = ["dep:serde"] - -[dev-dependencies] -crossbeam-channel = "0.5.0" -serde_test = "1.0" - -[lints] -workspace = true - -[package.metadata.docs.rs] -rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"] -all-features = true diff --git a/crates/bevy_core/README.md b/crates/bevy_core/README.md deleted file mode 100644 index b1953a959aebf..0000000000000 --- a/crates/bevy_core/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Bevy Core - -[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license) -[![Crates.io](https://img.shields.io/crates/v/bevy_core.svg)](https://crates.io/crates/bevy_core) -[![Downloads](https://img.shields.io/crates/d/bevy_core.svg)](https://crates.io/crates/bevy_core) -[![Docs](https://docs.rs/bevy_core/badge.svg)](https://docs.rs/bevy_core/latest/bevy_core/) -[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs deleted file mode 100644 index f5c37d1daaffc..0000000000000 --- a/crates/bevy_core/src/lib.rs +++ /dev/null @@ -1,157 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![forbid(unsafe_code)] -#![doc( - html_logo_url = "https://bevyengine.org/assets/icon.png", - html_favicon_url = "https://bevyengine.org/assets/icon.png" -)] - -//! This crate provides core functionality for Bevy Engine. - -extern crate alloc; - -#[cfg(feature = "serialize")] -mod serde; -mod task_pool_options; - -use bevy_ecs::system::Resource; -pub use task_pool_options::*; - -/// The core prelude. -/// -/// This includes the most common types in this crate, re-exported for your convenience. -pub mod prelude { - #[doc(hidden)] - pub use crate::{FrameCountPlugin, TaskPoolOptions, TaskPoolPlugin, TypeRegistrationPlugin}; -} - -use bevy_app::prelude::*; -use bevy_ecs::prelude::*; -use core::marker::PhantomData; - -#[cfg(not(target_arch = "wasm32"))] -use bevy_tasks::tick_global_task_pools_on_main_thread; - -/// Registration of default types to the [`TypeRegistry`](bevy_reflect::TypeRegistry) resource. -#[derive(Default)] -pub struct TypeRegistrationPlugin; - -impl Plugin for TypeRegistrationPlugin { - #[cfg_attr(not(feature = "bevy_reflect"), allow(unused_variables))] - fn build(&self, app: &mut App) { - #[cfg(feature = "bevy_reflect")] - app.register_type::(); - } -} - -/// Setup of default task pools: [`AsyncComputeTaskPool`](bevy_tasks::AsyncComputeTaskPool), -/// [`ComputeTaskPool`](bevy_tasks::ComputeTaskPool), [`IoTaskPool`](bevy_tasks::IoTaskPool). -#[derive(Default)] -pub struct TaskPoolPlugin { - /// Options for the [`TaskPool`](bevy_tasks::TaskPool) created at application start. - pub task_pool_options: TaskPoolOptions, -} - -impl Plugin for TaskPoolPlugin { - fn build(&self, _app: &mut App) { - // Setup the default bevy task pools - self.task_pool_options.create_default_pools(); - - #[cfg(not(target_arch = "wasm32"))] - _app.add_systems(Last, tick_global_task_pools); - } -} -/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread. -pub struct NonSendMarker(PhantomData<*mut ()>); - -/// A system used to check and advanced our task pools. -/// -/// Calls [`tick_global_task_pools_on_main_thread`], -/// and uses [`NonSendMarker`] to ensure that this system runs on the main thread -#[cfg(not(target_arch = "wasm32"))] -fn tick_global_task_pools(_main_thread_marker: Option>) { - tick_global_task_pools_on_main_thread(); -} - -/// Maintains a count of frames rendered since the start of the application. -/// -/// [`FrameCount`] is incremented during [`Last`], providing predictable -/// behavior: it will be 0 during the first update, 1 during the next, and so forth. -/// -/// # Overflows -/// -/// [`FrameCount`] will wrap to 0 after exceeding [`u32::MAX`]. Within reasonable -/// assumptions, one may exploit wrapping arithmetic to determine the number of frames -/// that have elapsed between two observations – see [`u32::wrapping_sub()`]. -#[derive(Debug, Default, Resource, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct FrameCount(pub u32); - -/// Adds frame counting functionality to Apps. -#[derive(Default)] -pub struct FrameCountPlugin; - -impl Plugin for FrameCountPlugin { - fn build(&self, app: &mut App) { - app.init_resource::(); - app.add_systems(Last, update_frame_count); - } -} - -/// A system used to increment [`FrameCount`] with wrapping addition. -/// -/// See [`FrameCount`] for more details. -pub fn update_frame_count(mut frame_count: ResMut) { - frame_count.0 = frame_count.0.wrapping_add(1); -} - -#[cfg(test)] -mod tests { - use super::*; - use bevy_tasks::prelude::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool}; - - #[test] - fn runs_spawn_local_tasks() { - let mut app = App::new(); - app.add_plugins((TaskPoolPlugin::default(), TypeRegistrationPlugin)); - - let (async_tx, async_rx) = crossbeam_channel::unbounded(); - AsyncComputeTaskPool::get() - .spawn_local(async move { - async_tx.send(()).unwrap(); - }) - .detach(); - - let (compute_tx, compute_rx) = crossbeam_channel::unbounded(); - ComputeTaskPool::get() - .spawn_local(async move { - compute_tx.send(()).unwrap(); - }) - .detach(); - - let (io_tx, io_rx) = crossbeam_channel::unbounded(); - IoTaskPool::get() - .spawn_local(async move { - io_tx.send(()).unwrap(); - }) - .detach(); - - app.run(); - - async_rx.try_recv().unwrap(); - compute_rx.try_recv().unwrap(); - io_rx.try_recv().unwrap(); - } - - #[test] - fn frame_counter_update() { - let mut app = App::new(); - app.add_plugins(( - TaskPoolPlugin::default(), - TypeRegistrationPlugin, - FrameCountPlugin, - )); - app.update(); - - let frame_count = app.world().resource::(); - assert_eq!(1, frame_count.0); - } -} diff --git a/crates/bevy_core/src/serde.rs b/crates/bevy_core/src/serde.rs deleted file mode 100644 index 3e6a2ed4e8b31..0000000000000 --- a/crates/bevy_core/src/serde.rs +++ /dev/null @@ -1,54 +0,0 @@ -use core::{ - any, - fmt::{self, Formatter}, -}; - -use serde::{ - de::{Error, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; - -use super::FrameCount; - -// Manually implementing serialize/deserialize allows us to use a more compact representation as simple integers -impl Serialize for FrameCount { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_u32(self.0) - } -} - -impl<'de> Deserialize<'de> for FrameCount { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_u32(FrameVisitor) - } -} - -struct FrameVisitor; - -impl<'de> Visitor<'de> for FrameVisitor { - type Value = FrameCount; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str(any::type_name::()) - } - - fn visit_u32(self, v: u32) -> Result - where - E: Error, - { - Ok(FrameCount(v)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use serde_test::{assert_tokens, Token}; - - #[test] - fn test_serde_frame_count() { - let frame_count = FrameCount(100); - assert_tokens(&frame_count, &[Token::U32(100)]); - } -} diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index 6298abb35d759..3993b031e9e70 100644 --- a/crates/bevy_core_pipeline/Cargo.toml +++ b/crates/bevy_core_pipeline/Cargo.toml @@ -24,9 +24,9 @@ smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"] # bevy bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_color = { path = "../bevy_color", version = "0.15.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" } +bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } bevy_image = { path = "../bevy_image", version = "0.15.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" } diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index 67422b4a7221a..559ce4e3a55bc 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -8,7 +8,7 @@ use crate::{ }; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; -use bevy_core::FrameCount; +use bevy_diagnostic::FrameCount; use bevy_ecs::{ prelude::{require, Bundle, Component, Entity, ReflectComponent}, query::{QueryItem, With}, diff --git a/crates/bevy_diagnostic/Cargo.toml b/crates/bevy_diagnostic/Cargo.toml index ac16a7567bc46..5d45853997de9 100644 --- a/crates/bevy_diagnostic/Cargo.toml +++ b/crates/bevy_diagnostic/Cargo.toml @@ -12,17 +12,18 @@ keywords = ["bevy"] # Disables diagnostics that are unsupported when Bevy is dynamically linked dynamic_linking = [] sysinfo_plugin = ["sysinfo"] +serialize = ["dep:serde"] [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } bevy_time = { path = "../bevy_time", version = "0.15.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev" } const-fnv1a-hash = "1.1.0" +serde = { version = "1.0", optional = true } # macOS [target.'cfg(all(target_os="macos"))'.dependencies] @@ -38,6 +39,9 @@ sysinfo = { version = "0.33.0", optional = true, default-features = false, featu "system", ] } +[dev-dependencies] +serde_test = "1.0" + [lints] workspace = true diff --git a/crates/bevy_diagnostic/src/frame_count_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_count_diagnostics_plugin.rs new file mode 100644 index 0000000000000..12a45ddd00251 --- /dev/null +++ b/crates/bevy_diagnostic/src/frame_count_diagnostics_plugin.rs @@ -0,0 +1,101 @@ +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; + +#[cfg(feature = "serialize")] +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; + +/// Maintains a count of frames rendered since the start of the application. +/// +/// [`FrameCount`] is incremented during [`Last`], providing predictable +/// behavior: it will be 0 during the first update, 1 during the next, and so forth. +/// +/// # Overflows +/// +/// [`FrameCount`] will wrap to 0 after exceeding [`u32::MAX`]. Within reasonable +/// assumptions, one may exploit wrapping arithmetic to determine the number of frames +/// that have elapsed between two observations – see [`u32::wrapping_sub()`]. +#[derive(Debug, Default, Resource, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct FrameCount(pub u32); + +/// Adds frame counting functionality to Apps. +#[derive(Default)] +pub struct FrameCountPlugin; + +impl Plugin for FrameCountPlugin { + fn build(&self, app: &mut App) { + app.init_resource::(); + app.add_systems(Last, update_frame_count); + } +} + +/// A system used to increment [`FrameCount`] with wrapping addition. +/// +/// See [`FrameCount`] for more details. +pub fn update_frame_count(mut frame_count: ResMut) { + frame_count.0 = frame_count.0.wrapping_add(1); +} + +#[cfg(feature = "serialize")] +// Manually implementing serialize/deserialize allows us to use a more compact representation as simple integers +impl Serialize for FrameCount { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_u32(self.0) + } +} + +#[cfg(feature = "serialize")] +impl<'de> Deserialize<'de> for FrameCount { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_u32(FrameVisitor) + } +} + +#[cfg(feature = "serialize")] +struct FrameVisitor; + +#[cfg(feature = "serialize")] +impl<'de> Visitor<'de> for FrameVisitor { + type Value = FrameCount; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str(core::any::type_name::()) + } + + fn visit_u32(self, v: u32) -> Result + where + E: Error, + { + Ok(FrameCount(v)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn frame_counter_update() { + let mut app = App::new(); + app.add_plugins(FrameCountPlugin); + app.update(); + + let frame_count = app.world().resource::(); + assert_eq!(1, frame_count.0); + } +} + +#[cfg(all(test, feature = "serialize"))] +mod serde_tests { + use super::*; + + use serde_test::{assert_tokens, Token}; + + #[test] + fn test_serde_frame_count() { + let frame_count = FrameCount(100); + assert_tokens(&frame_count, &[Token::U32(100)]); + } +} diff --git a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs index 040a05773c830..03cb08f85312a 100644 --- a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs @@ -1,6 +1,5 @@ -use crate::{Diagnostic, DiagnosticPath, Diagnostics, RegisterDiagnostic}; +use crate::{Diagnostic, DiagnosticPath, Diagnostics, FrameCount, RegisterDiagnostic}; use bevy_app::prelude::*; -use bevy_core::FrameCount; use bevy_ecs::prelude::*; use bevy_time::{Real, Time}; diff --git a/crates/bevy_diagnostic/src/lib.rs b/crates/bevy_diagnostic/src/lib.rs index e030dde797044..970fc5ba07ae3 100644 --- a/crates/bevy_diagnostic/src/lib.rs +++ b/crates/bevy_diagnostic/src/lib.rs @@ -14,6 +14,7 @@ extern crate alloc; mod diagnostic; mod entity_count_diagnostics_plugin; +mod frame_count_diagnostics_plugin; mod frame_time_diagnostics_plugin; mod log_diagnostics_plugin; #[cfg(feature = "sysinfo_plugin")] @@ -22,6 +23,7 @@ mod system_information_diagnostics_plugin; pub use diagnostic::*; pub use entity_count_diagnostics_plugin::EntityCountDiagnosticsPlugin; +pub use frame_count_diagnostics_plugin::{update_frame_count, FrameCount, FrameCountPlugin}; pub use frame_time_diagnostics_plugin::FrameTimeDiagnosticsPlugin; pub use log_diagnostics_plugin::LogDiagnosticsPlugin; #[cfg(feature = "sysinfo_plugin")] diff --git a/crates/bevy_ecs/src/entity/entity_set.rs b/crates/bevy_ecs/src/entity/entity_set.rs index cc81fa4607cd0..34e48551fde7b 100644 --- a/crates/bevy_ecs/src/entity/entity_set.rs +++ b/crates/bevy_ecs/src/entity/entity_set.rs @@ -2,7 +2,6 @@ use alloc::{ boxed::Box, collections::{btree_map, btree_set}, rc::Rc, - sync::Arc, }; use core::{ @@ -14,6 +13,12 @@ use core::{ use super::Entity; +#[cfg(feature = "portable-atomic")] +use portable_atomic_util::Arc; + +#[cfg(not(feature = "portable-atomic"))] +use alloc::sync::Arc; + /// A trait for entity borrows. /// /// This trait can be thought of as `Borrow`, but yielding `Entity` directly. diff --git a/crates/bevy_ecs/src/name.rs b/crates/bevy_ecs/src/name.rs index a6b8b961ea302..3ae68798a17bf 100644 --- a/crates/bevy_ecs/src/name.rs +++ b/crates/bevy_ecs/src/name.rs @@ -45,7 +45,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; reflect(Deserialize, Serialize) )] pub struct Name { - hash: u64, // Won't be serialized (see: `bevy_core::serde` module) + hash: u64, // Won't be serialized name: Cow<'static, str>, } diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index 2d85a79ec53bc..2d687ed510833 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -1,15 +1,19 @@ -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use core::any::Any; -use std::sync::{Mutex, MutexGuard}; - +use alloc::{boxed::Box, vec::Vec}; use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor}; use bevy_utils::{default, syncunsafecell::SyncUnsafeCell}; -use core::panic::AssertUnwindSafe; +use concurrent_queue::ConcurrentQueue; +use core::{any::Any, panic::AssertUnwindSafe}; +use fixedbitset::FixedBitSet; +use std::sync::{Mutex, MutexGuard}; + #[cfg(feature = "trace")] use tracing::{info_span, Span}; -use concurrent_queue::ConcurrentQueue; -use fixedbitset::FixedBitSet; +#[cfg(feature = "portable-atomic")] +use portable_atomic_util::Arc; + +#[cfg(not(feature = "portable-atomic"))] +use alloc::sync::Arc; use crate::{ archetype::ArchetypeComponentId, diff --git a/crates/bevy_gltf/Cargo.toml b/crates/bevy_gltf/Cargo.toml index db5e6e53d0ee4..9bc1ca3e7d047 100644 --- a/crates/bevy_gltf/Cargo.toml +++ b/crates/bevy_gltf/Cargo.toml @@ -22,7 +22,6 @@ bevy_animation = { path = "../bevy_animation", version = "0.15.0-dev", optional bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" } bevy_color = { path = "../bevy_color", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" } diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 2b8c2b75bf58a..c1f6a5d2eaa43 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -2260,7 +2260,7 @@ mod test { use std::path::Path; use crate::{Gltf, GltfAssetLabel, GltfNode, GltfSkin}; - use bevy_app::App; + use bevy_app::{App, TaskPoolPlugin}; use bevy_asset::{ io::{ memory::{Dir, MemoryAssetReader}, @@ -2268,7 +2268,6 @@ mod test { }, AssetApp, AssetPlugin, AssetServer, Assets, Handle, LoadState, }; - use bevy_core::TaskPoolPlugin; use bevy_ecs::{system::Resource, world::World}; use bevy_log::LogPlugin; use bevy_render::mesh::{skinning::SkinnedMeshInverseBindposes, MeshPlugin}; diff --git a/crates/bevy_hierarchy/Cargo.toml b/crates/bevy_hierarchy/Cargo.toml index 68c32ab2eefe5..093f5f7f88c3a 100644 --- a/crates/bevy_hierarchy/Cargo.toml +++ b/crates/bevy_hierarchy/Cargo.toml @@ -11,13 +11,12 @@ keywords = ["bevy"] [features] default = ["bevy_app"] trace = [] -bevy_app = ["reflect", "dep:bevy_app", "bevy_core"] +bevy_app = ["reflect", "dep:bevy_app"] reflect = ["bevy_ecs/bevy_reflect", "bevy_reflect"] [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.15.0-dev", optional = true } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev", optional = true } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false } bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [ "bevy", diff --git a/crates/bevy_input/Cargo.toml b/crates/bevy_input/Cargo.toml index adb3021d5b3ae..fa185ea5feed9 100644 --- a/crates/bevy_input/Cargo.toml +++ b/crates/bevy_input/Cargo.toml @@ -21,7 +21,6 @@ serialize = ["serde", "smol_str/serde"] [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false, features = [ "serialize", ] } diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 3acc57d16cbf0..b00b9f6bb4a16 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -88,7 +88,6 @@ shader_format_spirv = ["bevy_render/shader_format_spirv"] serialize = [ "bevy_color?/serialize", - "bevy_core/serialize", "bevy_ecs/serialize", "bevy_input/serialize", "bevy_math/serialize", @@ -264,7 +263,6 @@ ghost_nodes = ["bevy_ui/ghost_nodes"] # bevy bevy_a11y = { path = "../bevy_a11y", version = "0.15.0-dev", optional = true } bevy_app = { path = "../bevy_app", version = "0.15.0-dev" } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index dee5e5960f3d2..bbfa4b1a566bc 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -5,9 +5,8 @@ plugin_group! { pub struct DefaultPlugins { bevy_app:::PanicHandlerPlugin, bevy_log:::LogPlugin, - bevy_core:::TaskPoolPlugin, - bevy_core:::TypeRegistrationPlugin, - bevy_core:::FrameCountPlugin, + bevy_app:::TaskPoolPlugin, + bevy_diagnostic:::FrameCountPlugin, bevy_time:::TimePlugin, bevy_transform:::TransformPlugin, bevy_hierarchy:::HierarchyPlugin, @@ -107,9 +106,8 @@ impl Plugin for IgnoreAmbiguitiesPlugin { plugin_group! { /// This plugin group will add the minimal plugins for a *Bevy* application: pub struct MinimalPlugins { - bevy_core:::TaskPoolPlugin, - bevy_core:::TypeRegistrationPlugin, - bevy_core:::FrameCountPlugin, + bevy_app:::TaskPoolPlugin, + bevy_diagnostic:::FrameCountPlugin, bevy_time:::TimePlugin, bevy_app:::ScheduleRunnerPlugin, #[cfg(feature = "bevy_ci_testing")] diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index 34b308cd972bc..e9d25adbdf8bb 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -24,7 +24,6 @@ pub use bevy_asset as asset; pub use bevy_audio as audio; #[cfg(feature = "bevy_color")] pub use bevy_color as color; -pub use bevy_core as core; #[cfg(feature = "bevy_core_pipeline")] pub use bevy_core_pipeline as core_pipeline; #[cfg(feature = "bevy_dev_tools")] diff --git a/crates/bevy_internal/src/prelude.rs b/crates/bevy_internal/src/prelude.rs index a523488f65309..42c84f134a124 100644 --- a/crates/bevy_internal/src/prelude.rs +++ b/crates/bevy_internal/src/prelude.rs @@ -1,8 +1,8 @@ #[doc(hidden)] pub use crate::{ - app::prelude::*, core::prelude::*, ecs::prelude::*, hierarchy::prelude::*, input::prelude::*, - log::prelude::*, math::prelude::*, reflect::prelude::*, time::prelude::*, - transform::prelude::*, utils::prelude::*, DefaultPlugins, MinimalPlugins, + app::prelude::*, ecs::prelude::*, hierarchy::prelude::*, input::prelude::*, log::prelude::*, + math::prelude::*, reflect::prelude::*, time::prelude::*, transform::prelude::*, + utils::prelude::*, DefaultPlugins, MinimalPlugins, }; #[doc(hidden)] diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index d584a4bc044ff..6e1407f326cbf 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -40,7 +40,6 @@ bevy_color = { path = "../bevy_color", version = "0.15.0-dev", features = [ "serialize", "wgpu-types", ] } -bevy_core = { path = "../bevy_core", version = "0.15.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } diff --git a/crates/bevy_render/src/globals.rs b/crates/bevy_render/src/globals.rs index eabaaad05ee35..65d5bbc28fd7f 100644 --- a/crates/bevy_render/src/globals.rs +++ b/crates/bevy_render/src/globals.rs @@ -7,7 +7,7 @@ use crate::{ }; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; -use bevy_core::FrameCount; +use bevy_diagnostic::FrameCount; use bevy_ecs::prelude::*; use bevy_reflect::prelude::*; use bevy_time::Time; diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index 2e282bfcab048..14c0fe45befc7 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -308,7 +308,7 @@ pub fn create_surfaces( // By accessing a NonSend resource, we tell the scheduler to put this system on the main thread, // which is necessary for some OS's #[cfg(any(target_os = "macos", target_os = "ios"))] _marker: Option< - NonSend, + NonSend, >, windows: Res, mut window_surfaces: ResMut, diff --git a/crates/bevy_tasks/src/task_pool.rs b/crates/bevy_tasks/src/task_pool.rs index 6a4e000864232..215981215f2f7 100644 --- a/crates/bevy_tasks/src/task_pool.rs +++ b/crates/bevy_tasks/src/task_pool.rs @@ -1,4 +1,3 @@ -use alloc::sync::Arc; use core::{future::Future, marker::PhantomData, mem, panic::AssertUnwindSafe}; use std::thread::{self, JoinHandle}; @@ -6,6 +5,12 @@ use crate::executor::FallibleTask; use concurrent_queue::ConcurrentQueue; use futures_lite::FutureExt; +#[cfg(feature = "portable-atomic")] +use {alloc::boxed::Box, portable_atomic_util::Arc}; + +#[cfg(not(feature = "portable-atomic"))] +use alloc::sync::Arc; + use crate::{ block_on, thread_executor::{ThreadExecutor, ThreadExecutorTicker}, @@ -70,7 +75,17 @@ impl TaskPoolBuilder { /// This is called on the thread itself and has access to all thread-local storage. /// This will block running async tasks on the thread until the callback completes. pub fn on_thread_spawn(mut self, f: impl Fn() + Send + Sync + 'static) -> Self { - self.on_thread_spawn = Some(Arc::new(f)); + #[cfg(feature = "portable-atomic")] + let arc = { + let boxed = Box::new(f); + let boxed: Box = boxed; + Arc::from(boxed) + }; + + #[cfg(not(feature = "portable-atomic"))] + let arc = Arc::new(f); + + self.on_thread_spawn = Some(arc); self } @@ -79,7 +94,17 @@ impl TaskPoolBuilder { /// This is called on the thread itself and has access to all thread-local storage. /// This will block thread termination until the callback completes. pub fn on_thread_destroy(mut self, f: impl Fn() + Send + Sync + 'static) -> Self { - self.on_thread_destroy = Some(Arc::new(f)); + #[cfg(feature = "portable-atomic")] + let arc = { + let boxed = Box::new(f); + let boxed: Box = boxed; + Arc::from(boxed) + }; + + #[cfg(not(feature = "portable-atomic"))] + let arc = Arc::new(f); + + self.on_thread_destroy = Some(arc); self } diff --git a/crates/bevy_tasks/src/usages.rs b/crates/bevy_tasks/src/usages.rs index 5be44f3b9e690..385b30fdb402d 100644 --- a/crates/bevy_tasks/src/usages.rs +++ b/crates/bevy_tasks/src/usages.rs @@ -92,7 +92,7 @@ taskpool! { (IO_TASK_POOL, IoTaskPool) } -/// A function used by `bevy_core` to tick the global tasks pools on the main thread. +/// A function used by `bevy_app` to tick the global tasks pools on the main thread. /// This will run a maximum of 100 local tasks per executor per call to this function. /// /// # Warning diff --git a/examples/ecs/send_and_receive_events.rs b/examples/ecs/send_and_receive_events.rs index df03b500f2cf6..32d18bc8dd1f6 100644 --- a/examples/ecs/send_and_receive_events.rs +++ b/examples/ecs/send_and_receive_events.rs @@ -19,7 +19,7 @@ //! //! Let's look at an example of each. -use bevy::{core::FrameCount, ecs::event::EventCursor, prelude::*}; +use bevy::{diagnostic::FrameCount, ecs::event::EventCursor, prelude::*}; fn main() { let mut app = App::new(); diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index c70d39a307e97..07efc26df97c3 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -4,8 +4,7 @@ #[cfg(feature = "custom_cursor")] use bevy::winit::cursor::CustomCursor; use bevy::{ - core::FrameCount, - diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + diagnostic::{FrameCount, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, prelude::*, window::{CursorGrabMode, PresentMode, SystemCursorIcon, WindowLevel, WindowTheme}, winit::cursor::CursorIcon, diff --git a/tests/window/minimizing.rs b/tests/window/minimizing.rs index cbfe236574b7d..8ba5d79fdd75c 100644 --- a/tests/window/minimizing.rs +++ b/tests/window/minimizing.rs @@ -1,6 +1,6 @@ //! A test to confirm that `bevy` allows minimizing the window //! This is run in CI to ensure that this doesn't regress again. -use bevy::{core::FrameCount, prelude::*}; +use bevy::{diagnostic::FrameCount, prelude::*}; fn main() { // TODO: Combine this with `resizing` once multiple_windows is simpler than diff --git a/tools/publish.sh b/tools/publish.sh index 830a8da9f5e68..ae6e869c98616 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -19,7 +19,6 @@ crates=( bevy_asset/macros bevy_asset bevy_audio - bevy_core bevy_diagnostic bevy_hierarchy bevy_transform