bevy_core/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![forbid(unsafe_code)]
3#![doc(
4    html_logo_url = "https://bevyengine.org/assets/icon.png",
5    html_favicon_url = "https://bevyengine.org/assets/icon.png"
6)]
7
8//! This crate provides core functionality for Bevy Engine.
9
10extern crate alloc;
11
12mod name;
13#[cfg(feature = "serialize")]
14mod serde;
15mod task_pool_options;
16
17use bevy_ecs::system::Resource;
18pub use name::*;
19pub use task_pool_options::*;
20
21/// The core prelude.
22///
23/// This includes the most common types in this crate, re-exported for your convenience.
24pub mod prelude {
25    #[doc(hidden)]
26    pub use crate::{
27        FrameCountPlugin, Name, NameOrEntity, TaskPoolOptions, TaskPoolPlugin,
28        TypeRegistrationPlugin,
29    };
30}
31
32use bevy_app::prelude::*;
33use bevy_ecs::prelude::*;
34use core::marker::PhantomData;
35
36#[cfg(not(target_arch = "wasm32"))]
37use bevy_tasks::tick_global_task_pools_on_main_thread;
38
39/// Registration of default types to the [`TypeRegistry`](bevy_reflect::TypeRegistry) resource.
40#[derive(Default)]
41pub struct TypeRegistrationPlugin;
42
43impl Plugin for TypeRegistrationPlugin {
44    #[cfg_attr(not(feature = "bevy_reflect"), allow(unused_variables))]
45    fn build(&self, app: &mut App) {
46        #[cfg(feature = "bevy_reflect")]
47        app.register_type::<Name>();
48    }
49}
50
51/// Setup of default task pools: [`AsyncComputeTaskPool`](bevy_tasks::AsyncComputeTaskPool),
52/// [`ComputeTaskPool`](bevy_tasks::ComputeTaskPool), [`IoTaskPool`](bevy_tasks::IoTaskPool).
53#[derive(Default)]
54pub struct TaskPoolPlugin {
55    /// Options for the [`TaskPool`](bevy_tasks::TaskPool) created at application start.
56    pub task_pool_options: TaskPoolOptions,
57}
58
59impl Plugin for TaskPoolPlugin {
60    fn build(&self, _app: &mut App) {
61        // Setup the default bevy task pools
62        self.task_pool_options.create_default_pools();
63
64        #[cfg(not(target_arch = "wasm32"))]
65        _app.add_systems(Last, tick_global_task_pools);
66    }
67}
68/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread.
69pub struct NonSendMarker(PhantomData<*mut ()>);
70
71/// A system used to check and advanced our task pools.
72///
73/// Calls [`tick_global_task_pools_on_main_thread`],
74/// and uses [`NonSendMarker`] to ensure that this system runs on the main thread
75#[cfg(not(target_arch = "wasm32"))]
76fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
77    tick_global_task_pools_on_main_thread();
78}
79
80/// Maintains a count of frames rendered since the start of the application.
81///
82/// [`FrameCount`] is incremented during [`Last`], providing predictable
83/// behavior: it will be 0 during the first update, 1 during the next, and so forth.
84///
85/// # Overflows
86///
87/// [`FrameCount`] will wrap to 0 after exceeding [`u32::MAX`]. Within reasonable
88/// assumptions, one may exploit wrapping arithmetic to determine the number of frames
89/// that have elapsed between two observations – see [`u32::wrapping_sub()`].
90#[derive(Debug, Default, Resource, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
91pub struct FrameCount(pub u32);
92
93/// Adds frame counting functionality to Apps.
94#[derive(Default)]
95pub struct FrameCountPlugin;
96
97impl Plugin for FrameCountPlugin {
98    fn build(&self, app: &mut App) {
99        app.init_resource::<FrameCount>();
100        app.add_systems(Last, update_frame_count);
101    }
102}
103
104/// A system used to increment [`FrameCount`] with wrapping addition.
105///
106/// See [`FrameCount`] for more details.
107pub fn update_frame_count(mut frame_count: ResMut<FrameCount>) {
108    frame_count.0 = frame_count.0.wrapping_add(1);
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use bevy_tasks::prelude::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool};
115
116    #[test]
117    fn runs_spawn_local_tasks() {
118        let mut app = App::new();
119        app.add_plugins((TaskPoolPlugin::default(), TypeRegistrationPlugin));
120
121        let (async_tx, async_rx) = crossbeam_channel::unbounded();
122        AsyncComputeTaskPool::get()
123            .spawn_local(async move {
124                async_tx.send(()).unwrap();
125            })
126            .detach();
127
128        let (compute_tx, compute_rx) = crossbeam_channel::unbounded();
129        ComputeTaskPool::get()
130            .spawn_local(async move {
131                compute_tx.send(()).unwrap();
132            })
133            .detach();
134
135        let (io_tx, io_rx) = crossbeam_channel::unbounded();
136        IoTaskPool::get()
137            .spawn_local(async move {
138                io_tx.send(()).unwrap();
139            })
140            .detach();
141
142        app.run();
143
144        async_rx.try_recv().unwrap();
145        compute_rx.try_recv().unwrap();
146        io_rx.try_recv().unwrap();
147    }
148
149    #[test]
150    fn frame_counter_update() {
151        let mut app = App::new();
152        app.add_plugins((
153            TaskPoolPlugin::default(),
154            TypeRegistrationPlugin,
155            FrameCountPlugin,
156        ));
157        app.update();
158
159        let frame_count = app.world().resource::<FrameCount>();
160        assert_eq!(1, frame_count.0);
161    }
162}