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