From af305cbadd2d79510800f1c15ac651807a7d9202 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 5 Nov 2020 12:28:24 -0800 Subject: [PATCH 01/12] ecs: system params can be in any order, faster compiles, remove foreach --- crates/bevy_app/src/app_builder.rs | 2 +- crates/bevy_asset/src/assets.rs | 2 +- crates/bevy_asset/src/lib.rs | 2 +- .../src/frame_time_diagnostics_plugin.rs | 2 +- crates/bevy_diagnostic/src/lib.rs | 2 +- .../src/print_diagnostics_plugin.rs | 2 +- crates/bevy_ecs/src/lib.rs | 4 +- .../bevy_ecs/src/resource/resource_query.rs | 12 + .../src/schedule/parallel_executor.rs | 636 +++++++++--------- crates/bevy_ecs/src/schedule/schedule.rs | 10 + crates/bevy_ecs/src/system/into_system.rs | 565 ++++++---------- .../bevy_ecs/src/system/into_thread_local.rs | 121 ++++ crates/bevy_ecs/src/system/mod.rs | 2 + crates/bevy_ecs/src/system/query/query_set.rs | 4 +- crates/bevy_ecs/src/system/system.rs | 1 + crates/bevy_input/src/lib.rs | 2 +- crates/bevy_pbr/src/lib.rs | 2 +- .../bevy_pbr/src/render_graph/lights_node.rs | 2 +- crates/bevy_render/src/lib.rs | 2 +- .../src/render_graph/nodes/camera_node.rs | 2 +- .../nodes/render_resources_node.rs | 2 +- crates/bevy_render/src/render_graph/system.rs | 5 + crates/bevy_sprite/src/lib.rs | 2 +- .../hierarchy/hierarchy_maintenance_system.rs | 2 +- .../src/transform_system_bundle.rs | 2 +- crates/bevy_ui/src/lib.rs | 2 +- .../wgpu_resource_diagnostics_plugin.rs | 2 +- crates/bevy_wgpu/src/lib.rs | 2 +- crates/bevy_window/src/lib.rs | 2 +- examples/ios/src/lib.rs | 2 +- 30 files changed, 705 insertions(+), 695 deletions(-) create mode 100644 crates/bevy_ecs/src/system/into_thread_local.rs diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index 43c554dedd0c5..4c1bbfeb358d0 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -4,7 +4,7 @@ use crate::{ plugin::Plugin, stage, startup_stage, PluginGroup, PluginGroupBuilder, }; -use bevy_ecs::{FromResources, IntoQuerySystem, Resources, System, World}; +use bevy_ecs::{FromResources, IntoSystem, Resources, System, World}; /// Configure [App]s using the builder pattern pub struct AppBuilder { diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 074608815c9b5..4b756cf02f341 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -2,7 +2,7 @@ use crate::{ update_asset_storage_system, Asset, AssetLoader, AssetServer, Handle, HandleId, RefChange, }; use bevy_app::{prelude::Events, AppBuilder}; -use bevy_ecs::{FromResources, IntoQuerySystem, ResMut}; +use bevy_ecs::{FromResources, IntoSystem, ResMut}; use bevy_type_registry::RegisterType; use bevy_utils::HashMap; use crossbeam_channel::Sender; diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index d5a78d8831d72..6f5098dede741 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -31,7 +31,7 @@ pub mod prelude { } use bevy_app::{prelude::Plugin, AppBuilder}; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_type_registry::RegisterType; /// Adds support for Assets to an App. Assets are typed collections with change tracking, which are added as App Resources. diff --git a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs index 4d979bbee1d42..bbafb39c8e38a 100644 --- a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs @@ -1,7 +1,7 @@ use crate::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; use bevy_core::Time; -use bevy_ecs::{IntoQuerySystem, Res, ResMut}; +use bevy_ecs::{IntoSystem, Res, ResMut}; /// Adds "frame time" diagnostic to an App, specifically "frame time", "fps" and "frame count" #[derive(Default)] diff --git a/crates/bevy_diagnostic/src/lib.rs b/crates/bevy_diagnostic/src/lib.rs index c5f9b4c394de7..ce4b285887e82 100644 --- a/crates/bevy_diagnostic/src/lib.rs +++ b/crates/bevy_diagnostic/src/lib.rs @@ -18,7 +18,7 @@ impl Plugin for DiagnosticsPlugin { app.init_resource::(); #[cfg(feature = "profiler")] { - use bevy_ecs::IntoQuerySystem; + use bevy_ecs::IntoSystem; app.add_resource::>(Box::new( system_profiler::SystemProfiler::default(), )) diff --git a/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs index db7878ad3b22e..62bd855a95caa 100644 --- a/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs @@ -1,7 +1,7 @@ use super::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; use bevy_core::{Time, Timer}; -use bevy_ecs::{IntoQuerySystem, Res, ResMut}; +use bevy_ecs::{IntoSystem, Res, ResMut}; use std::time::Duration; /// An App Plugin that prints diagnostics to the console diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 96e284b92174e..0e73ef4c28cf5 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -12,9 +12,7 @@ pub use world::*; pub mod prelude { pub use crate::{ resource::{ChangedRes, FromResources, Local, OrRes, Res, ResMut, Resource, Resources}, - system::{ - Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System, - }, + system::{Commands, IntoSystem, IntoThreadLocalSystem, Query, System}, world::WorldBuilderSource, Added, Bundle, Changed, Component, Entity, Mut, Mutated, Or, QuerySet, Ref, RefMut, With, Without, World, diff --git a/crates/bevy_ecs/src/resource/resource_query.rs b/crates/bevy_ecs/src/resource/resource_query.rs index ab33775bce0e6..84318299bd3d6 100644 --- a/crates/bevy_ecs/src/resource/resource_query.rs +++ b/crates/bevy_ecs/src/resource/resource_query.rs @@ -194,6 +194,18 @@ pub trait FetchResource<'a>: Sized { } } +pub trait ResourceFetchSelf { + #[allow(clippy::missing_safety_doc)] + unsafe fn get(resources: &Resources, system_id: Option) -> Self; +} + +impl<'a, Q: ResourceQuery, F: FetchResource<'a, Item=Q>> ResourceFetchSelf for Q { + unsafe fn get(resources: &Resources, system_id: Option) -> Q { + let resources: &'a Resources = std::mem::transmute(resources); + >::get(resources, system_id) + } +} + impl<'a, T: Resource> ResourceQuery for Res<'a, T> { type Fetch = FetchResourceRead; } diff --git a/crates/bevy_ecs/src/schedule/parallel_executor.rs b/crates/bevy_ecs/src/schedule/parallel_executor.rs index d9e0fd9f342c9..f7ca0c1b731a3 100644 --- a/crates/bevy_ecs/src/schedule/parallel_executor.rs +++ b/crates/bevy_ecs/src/schedule/parallel_executor.rs @@ -526,321 +526,321 @@ impl ExecutorStage { } } -#[cfg(test)] -mod tests { - use super::ParallelExecutor; - use crate::{ - resource::{Res, ResMut, Resources}, - schedule::Schedule, - system::{IntoQuerySystem, IntoThreadLocalSystem, Query}, - Commands, - }; - use bevy_hecs::{Entity, World}; - use bevy_tasks::{ComputeTaskPool, TaskPool}; - use fixedbitset::FixedBitSet; - use parking_lot::Mutex; - use std::{collections::HashSet, sync::Arc}; - - #[derive(Default)] - struct CompletedSystems { - completed_systems: Arc>>, - } - - #[test] - fn cross_stage_archetype_change_prepare() { - let mut world = World::new(); - let mut resources = Resources::default(); - resources.insert(ComputeTaskPool(TaskPool::default())); - - let mut schedule = Schedule::default(); - schedule.add_stage("PreArchetypeChange"); - schedule.add_stage("PostArchetypeChange"); - - fn insert(mut commands: Commands) { - commands.spawn((1u32,)); - } - - fn read(query: Query<&u32>, entities: Query) { - for entity in &mut entities.iter() { - // query.get() does a "system permission check" that will fail if the entity is from a - // new archetype which hasnt been "prepared yet" - query.get_component::(entity).unwrap(); - } - - assert_eq!(1, entities.iter().count()); - } - - schedule.add_system_to_stage("PreArchetypeChange", insert.system()); - schedule.add_system_to_stage("PostArchetypeChange", read.system()); - - let mut executor = ParallelExecutor::default(); - schedule.initialize(&mut world, &mut resources); - executor.run(&mut schedule, &mut world, &mut resources); - } - - #[test] - fn intra_stage_archetype_change_prepare() { - let mut world = World::new(); - let mut resources = Resources::default(); - resources.insert(ComputeTaskPool(TaskPool::default())); - - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - - fn insert(world: &mut World, _resources: &mut Resources) { - world.spawn((1u32,)); - } - - fn read(query: Query<&u32>, entities: Query) { - for entity in &mut entities.iter() { - // query.get() does a "system permission check" that will fail if the entity is from a - // new archetype which hasnt been "prepared yet" - query.get_component::(entity).unwrap(); - } - - assert_eq!(1, entities.iter().count()); - } - - schedule.add_system_to_stage("update", insert.thread_local_system()); - schedule.add_system_to_stage("update", read.system()); - - let mut executor = ParallelExecutor::default(); - executor.run(&mut schedule, &mut world, &mut resources); - } - - #[test] - fn schedule() { - let mut world = World::new(); - let mut resources = Resources::default(); - resources.insert(ComputeTaskPool(TaskPool::default())); - resources.insert(CompletedSystems::default()); - resources.insert(1.0f64); - resources.insert(2isize); - - world.spawn((1.0f32,)); - world.spawn((1u32, 1u64)); - world.spawn((2u32,)); - - let mut schedule = Schedule::default(); - schedule.add_stage("A"); // component queries - schedule.add_stage("B"); // thread local - schedule.add_stage("C"); // resources - - // A system names - const READ_U32_SYSTEM_NAME: &str = "read_u32"; - const WRITE_FLOAT_SYSTEM_NAME: &str = "write_float"; - const READ_U32_WRITE_U64_SYSTEM_NAME: &str = "read_u32_write_u64"; - const READ_U64_SYSTEM_NAME: &str = "read_u64"; - - // B system names - const WRITE_U64_SYSTEM_NAME: &str = "write_u64"; - const THREAD_LOCAL_SYSTEM_SYSTEM_NAME: &str = "thread_local_system"; - const WRITE_F32_SYSTEM_NAME: &str = "write_f32"; - - // C system names - const READ_F64_RES_SYSTEM_NAME: &str = "read_f64_res"; - const READ_ISIZE_RES_SYSTEM_NAME: &str = "read_isize_res"; - const READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME: &str = "read_isize_write_f64_res"; - const WRITE_F64_RES_SYSTEM_NAME: &str = "write_f64_res"; - - // A systems - - fn read_u32(completed_systems: Res, _query: Query<&u32>) { - let mut completed_systems = completed_systems.completed_systems.lock(); - completed_systems.insert(READ_U32_SYSTEM_NAME); - } - - fn write_float(completed_systems: Res, _query: Query<&f32>) { - let mut completed_systems = completed_systems.completed_systems.lock(); - completed_systems.insert(WRITE_FLOAT_SYSTEM_NAME); - } - - fn read_u32_write_u64( - completed_systems: Res, - _query: Query<(&u32, &mut u64)>, - ) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(!completed_systems.contains(READ_U64_SYSTEM_NAME)); - completed_systems.insert(READ_U32_WRITE_U64_SYSTEM_NAME); - } - - fn read_u64(completed_systems: Res, _query: Query<&u64>) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(READ_U32_WRITE_U64_SYSTEM_NAME)); - assert!(!completed_systems.contains(WRITE_U64_SYSTEM_NAME)); - completed_systems.insert(READ_U64_SYSTEM_NAME); - } - - schedule.add_system_to_stage("A", read_u32.system()); - schedule.add_system_to_stage("A", write_float.system()); - schedule.add_system_to_stage("A", read_u32_write_u64.system()); - schedule.add_system_to_stage("A", read_u64.system()); - - // B systems - - fn write_u64(completed_systems: Res, _query: Query<&mut u64>) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(READ_U64_SYSTEM_NAME)); - assert!(!completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); - assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); - completed_systems.insert(WRITE_U64_SYSTEM_NAME); - } - - fn thread_local_system(_world: &mut World, resources: &mut Resources) { - let completed_systems = resources.get::().unwrap(); - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); - assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); - completed_systems.insert(THREAD_LOCAL_SYSTEM_SYSTEM_NAME); - } - - fn write_f32(completed_systems: Res, _query: Query<&mut f32>) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); - assert!(completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); - assert!(!completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); - completed_systems.insert(WRITE_F32_SYSTEM_NAME); - } - - schedule.add_system_to_stage("B", write_u64.system()); - schedule.add_system_to_stage("B", thread_local_system.thread_local_system()); - schedule.add_system_to_stage("B", write_f32.system()); - - // C systems - - fn read_f64_res(completed_systems: Res, _f64_res: Res) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(WRITE_F32_SYSTEM_NAME)); - assert!(!completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); - assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); - completed_systems.insert(READ_F64_RES_SYSTEM_NAME); - } - - fn read_isize_res(completed_systems: Res, _isize_res: Res) { - let mut completed_systems = completed_systems.completed_systems.lock(); - completed_systems.insert(READ_ISIZE_RES_SYSTEM_NAME); - } - - fn read_isize_write_f64_res( - completed_systems: Res, - _isize_res: Res, - _f64_res: ResMut, - ) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); - assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); - completed_systems.insert(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME); - } - - fn write_f64_res(completed_systems: Res, _f64_res: ResMut) { - let mut completed_systems = completed_systems.completed_systems.lock(); - assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); - assert!(completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); - completed_systems.insert(WRITE_F64_RES_SYSTEM_NAME); - } - - schedule.add_system_to_stage("C", read_f64_res.system()); - schedule.add_system_to_stage("C", read_isize_res.system()); - schedule.add_system_to_stage("C", read_isize_write_f64_res.system()); - schedule.add_system_to_stage("C", write_f64_res.system()); - - fn run_executor_and_validate( - executor: &mut ParallelExecutor, - schedule: &mut Schedule, - world: &mut World, - resources: &mut Resources, - ) { - executor.run(schedule, world, resources); - - assert_eq!( - executor.stages[0].system_dependents, - vec![vec![], vec![], vec![3], vec![]] - ); - assert_eq!( - executor.stages[1].system_dependents, - vec![vec![1], vec![2], vec![]] - ); - assert_eq!( - executor.stages[2].system_dependents, - vec![vec![2, 3], vec![], vec![3], vec![]] - ); - - let stage_0_len = executor.stages[0].system_dependencies.len(); - let mut read_u64_deps = FixedBitSet::with_capacity(stage_0_len); - read_u64_deps.insert(2); - - assert_eq!( - executor.stages[0].system_dependencies, - vec![ - FixedBitSet::with_capacity(stage_0_len), - FixedBitSet::with_capacity(stage_0_len), - FixedBitSet::with_capacity(stage_0_len), - read_u64_deps, - ] - ); - - let stage_1_len = executor.stages[1].system_dependencies.len(); - let mut thread_local_deps = FixedBitSet::with_capacity(stage_1_len); - thread_local_deps.insert(0); - let mut write_f64_deps = FixedBitSet::with_capacity(stage_1_len); - write_f64_deps.insert(1); - assert_eq!( - executor.stages[1].system_dependencies, - vec![ - FixedBitSet::with_capacity(stage_1_len), - thread_local_deps, - write_f64_deps - ] - ); - - let stage_2_len = executor.stages[2].system_dependencies.len(); - let mut read_isize_write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); - read_isize_write_f64_res_deps.insert(0); - let mut write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); - write_f64_res_deps.insert(0); - write_f64_res_deps.insert(2); - assert_eq!( - executor.stages[2].system_dependencies, - vec![ - FixedBitSet::with_capacity(stage_2_len), - FixedBitSet::with_capacity(stage_2_len), - read_isize_write_f64_res_deps, - write_f64_res_deps - ] - ); - - let completed_systems = resources.get::().unwrap(); - assert_eq!( - completed_systems.completed_systems.lock().len(), - 11, - "completed_systems should have been incremented once for each system" - ); - } - - // Stress test the "clean start" case - for _ in 0..1000 { - let mut executor = ParallelExecutor::default(); - run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); - resources - .get::() - .unwrap() - .completed_systems - .lock() - .clear(); - } - - // Stress test the "continue running" case - let mut executor = ParallelExecutor::default(); - run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); - for _ in 0..1000 { - // run again (with completed_systems reset) to ensure executor works correctly across runs - resources - .get::() - .unwrap() - .completed_systems - .lock() - .clear(); - run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); - } - } -} +// #[cfg(test)] +// mod tests { +// use super::ParallelExecutor; +// use crate::{ +// resource::{Res, ResMut, Resources}, +// schedule::Schedule, +// system::{IntoSystem, IntoThreadLocalSystem, Query}, +// Commands, +// }; +// use bevy_hecs::{Entity, World}; +// use bevy_tasks::{ComputeTaskPool, TaskPool}; +// use fixedbitset::FixedBitSet; +// use parking_lot::Mutex; +// use std::{collections::HashSet, sync::Arc}; + +// #[derive(Default)] +// struct CompletedSystems { +// completed_systems: Arc>>, +// } + +// #[test] +// fn cross_stage_archetype_change_prepare() { +// let mut world = World::new(); +// let mut resources = Resources::default(); +// resources.insert(ComputeTaskPool(TaskPool::default())); + +// let mut schedule = Schedule::default(); +// schedule.add_stage("PreArchetypeChange"); +// schedule.add_stage("PostArchetypeChange"); + +// fn insert(mut commands: Commands) { +// commands.spawn((1u32,)); +// } + +// fn read(query: Query<&u32>, entities: Query) { +// for entity in &mut entities.iter() { +// // query.get() does a "system permission check" that will fail if the entity is from a +// // new archetype which hasnt been "prepared yet" +// query.get_component::(entity).unwrap(); +// } + +// assert_eq!(1, entities.iter().count()); +// } + +// schedule.add_system_to_stage("PreArchetypeChange", insert.system()); +// schedule.add_system_to_stage("PostArchetypeChange", read.system()); + +// let mut executor = ParallelExecutor::default(); +// schedule.initialize(&mut world, &mut resources); +// executor.run(&mut schedule, &mut world, &mut resources); +// } + +// #[test] +// fn intra_stage_archetype_change_prepare() { +// let mut world = World::new(); +// let mut resources = Resources::default(); +// resources.insert(ComputeTaskPool(TaskPool::default())); + +// let mut schedule = Schedule::default(); +// schedule.add_stage("update"); + +// fn insert(world: &mut World, _resources: &mut Resources) { +// world.spawn((1u32,)); +// } + +// fn read(query: Query<&u32>, entities: Query) { +// for entity in &mut entities.iter() { +// // query.get() does a "system permission check" that will fail if the entity is from a +// // new archetype which hasnt been "prepared yet" +// query.get_component::(entity).unwrap(); +// } + +// assert_eq!(1, entities.iter().count()); +// } + +// schedule.add_system_to_stage("update", insert.thread_local_system()); +// schedule.add_system_to_stage("update", read.system()); + +// let mut executor = ParallelExecutor::default(); +// executor.run(&mut schedule, &mut world, &mut resources); +// } + +// #[test] +// fn schedule() { +// let mut world = World::new(); +// let mut resources = Resources::default(); +// resources.insert(ComputeTaskPool(TaskPool::default())); +// resources.insert(CompletedSystems::default()); +// resources.insert(1.0f64); +// resources.insert(2isize); + +// world.spawn((1.0f32,)); +// world.spawn((1u32, 1u64)); +// world.spawn((2u32,)); + +// let mut schedule = Schedule::default(); +// schedule.add_stage("A"); // component queries +// schedule.add_stage("B"); // thread local +// schedule.add_stage("C"); // resources + +// // A system names +// const READ_U32_SYSTEM_NAME: &str = "read_u32"; +// const WRITE_FLOAT_SYSTEM_NAME: &str = "write_float"; +// const READ_U32_WRITE_U64_SYSTEM_NAME: &str = "read_u32_write_u64"; +// const READ_U64_SYSTEM_NAME: &str = "read_u64"; + +// // B system names +// const WRITE_U64_SYSTEM_NAME: &str = "write_u64"; +// const THREAD_LOCAL_SYSTEM_SYSTEM_NAME: &str = "thread_local_system"; +// const WRITE_F32_SYSTEM_NAME: &str = "write_f32"; + +// // C system names +// const READ_F64_RES_SYSTEM_NAME: &str = "read_f64_res"; +// const READ_ISIZE_RES_SYSTEM_NAME: &str = "read_isize_res"; +// const READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME: &str = "read_isize_write_f64_res"; +// const WRITE_F64_RES_SYSTEM_NAME: &str = "write_f64_res"; + +// // A systems + +// fn read_u32(completed_systems: Res, _query: Query<&u32>) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// completed_systems.insert(READ_U32_SYSTEM_NAME); +// } + +// fn write_float(completed_systems: Res, _query: Query<&f32>) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// completed_systems.insert(WRITE_FLOAT_SYSTEM_NAME); +// } + +// fn read_u32_write_u64( +// completed_systems: Res, +// _query: Query<(&u32, &mut u64)>, +// ) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(!completed_systems.contains(READ_U64_SYSTEM_NAME)); +// completed_systems.insert(READ_U32_WRITE_U64_SYSTEM_NAME); +// } + +// fn read_u64(completed_systems: Res, _query: Query<&u64>) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(READ_U32_WRITE_U64_SYSTEM_NAME)); +// assert!(!completed_systems.contains(WRITE_U64_SYSTEM_NAME)); +// completed_systems.insert(READ_U64_SYSTEM_NAME); +// } + +// schedule.add_system_to_stage("A", read_u32.system()); +// schedule.add_system_to_stage("A", write_float.system()); +// schedule.add_system_to_stage("A", read_u32_write_u64.system()); +// schedule.add_system_to_stage("A", read_u64.system()); + +// // B systems + +// fn write_u64(completed_systems: Res, _query: Query<&mut u64>) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(READ_U64_SYSTEM_NAME)); +// assert!(!completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); +// assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); +// completed_systems.insert(WRITE_U64_SYSTEM_NAME); +// } + +// fn thread_local_system(_world: &mut World, resources: &mut Resources) { +// let completed_systems = resources.get::().unwrap(); +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); +// assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); +// completed_systems.insert(THREAD_LOCAL_SYSTEM_SYSTEM_NAME); +// } + +// fn write_f32(completed_systems: Res, _query: Query<&mut f32>) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); +// assert!(completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); +// assert!(!completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); +// completed_systems.insert(WRITE_F32_SYSTEM_NAME); +// } + +// schedule.add_system_to_stage("B", write_u64.system()); +// schedule.add_system_to_stage("B", thread_local_system.thread_local_system()); +// schedule.add_system_to_stage("B", write_f32.system()); + +// // C systems + +// fn read_f64_res(completed_systems: Res, _f64_res: Res) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(WRITE_F32_SYSTEM_NAME)); +// assert!(!completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); +// assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); +// completed_systems.insert(READ_F64_RES_SYSTEM_NAME); +// } + +// fn read_isize_res(completed_systems: Res, _isize_res: Res) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// completed_systems.insert(READ_ISIZE_RES_SYSTEM_NAME); +// } + +// fn read_isize_write_f64_res( +// completed_systems: Res, +// _isize_res: Res, +// _f64_res: ResMut, +// ) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); +// assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); +// completed_systems.insert(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME); +// } + +// fn write_f64_res(completed_systems: Res, _f64_res: ResMut) { +// let mut completed_systems = completed_systems.completed_systems.lock(); +// assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); +// assert!(completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); +// completed_systems.insert(WRITE_F64_RES_SYSTEM_NAME); +// } + +// schedule.add_system_to_stage("C", read_f64_res.system()); +// schedule.add_system_to_stage("C", read_isize_res.system()); +// schedule.add_system_to_stage("C", read_isize_write_f64_res.system()); +// schedule.add_system_to_stage("C", write_f64_res.system()); + +// fn run_executor_and_validate( +// executor: &mut ParallelExecutor, +// schedule: &mut Schedule, +// world: &mut World, +// resources: &mut Resources, +// ) { +// executor.run(schedule, world, resources); + +// assert_eq!( +// executor.stages[0].system_dependents, +// vec![vec![], vec![], vec![3], vec![]] +// ); +// assert_eq!( +// executor.stages[1].system_dependents, +// vec![vec![1], vec![2], vec![]] +// ); +// assert_eq!( +// executor.stages[2].system_dependents, +// vec![vec![2, 3], vec![], vec![3], vec![]] +// ); + +// let stage_0_len = executor.stages[0].system_dependencies.len(); +// let mut read_u64_deps = FixedBitSet::with_capacity(stage_0_len); +// read_u64_deps.insert(2); + +// assert_eq!( +// executor.stages[0].system_dependencies, +// vec![ +// FixedBitSet::with_capacity(stage_0_len), +// FixedBitSet::with_capacity(stage_0_len), +// FixedBitSet::with_capacity(stage_0_len), +// read_u64_deps, +// ] +// ); + +// let stage_1_len = executor.stages[1].system_dependencies.len(); +// let mut thread_local_deps = FixedBitSet::with_capacity(stage_1_len); +// thread_local_deps.insert(0); +// let mut write_f64_deps = FixedBitSet::with_capacity(stage_1_len); +// write_f64_deps.insert(1); +// assert_eq!( +// executor.stages[1].system_dependencies, +// vec![ +// FixedBitSet::with_capacity(stage_1_len), +// thread_local_deps, +// write_f64_deps +// ] +// ); + +// let stage_2_len = executor.stages[2].system_dependencies.len(); +// let mut read_isize_write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); +// read_isize_write_f64_res_deps.insert(0); +// let mut write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); +// write_f64_res_deps.insert(0); +// write_f64_res_deps.insert(2); +// assert_eq!( +// executor.stages[2].system_dependencies, +// vec![ +// FixedBitSet::with_capacity(stage_2_len), +// FixedBitSet::with_capacity(stage_2_len), +// read_isize_write_f64_res_deps, +// write_f64_res_deps +// ] +// ); + +// let completed_systems = resources.get::().unwrap(); +// assert_eq!( +// completed_systems.completed_systems.lock().len(), +// 11, +// "completed_systems should have been incremented once for each system" +// ); +// } + +// // Stress test the "clean start" case +// for _ in 0..1000 { +// let mut executor = ParallelExecutor::default(); +// run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); +// resources +// .get::() +// .unwrap() +// .completed_systems +// .lock() +// .clear(); +// } + +// // Stress test the "continue running" case +// let mut executor = ParallelExecutor::default(); +// run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); +// for _ in 0..1000 { +// // run again (with completed_systems reset) to ensure executor works correctly across runs +// resources +// .get::() +// .unwrap() +// .completed_systems +// .lock() +// .clear(); +// run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); +// } +// } +// } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 050b066040bd5..fd710946f4a2c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -198,4 +198,14 @@ impl Schedule { pub fn generation(&self) -> usize { self.generation } + + pub fn run_on_systems(&mut self, mut func: impl FnMut(&mut dyn System)) { + for stage_name in self.stage_order.iter() { + if let Some(stage_systems) = self.stages.get_mut(stage_name) { + for system in stage_systems.iter_mut() { + func(&mut **system); + } + } + } + } } diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 502913a697907..a16c90a665196 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,422 +1,281 @@ -pub use super::Query; use crate::{ - resource::{FetchResource, ResourceQuery, Resources, UnsafeClone}, - system::{Commands, System, SystemId, ThreadLocalExecution}, - QueryAccess, QuerySet, QueryTuple, TypeAccess, + Commands, FetchResource, Query, QuerySet, QueryTuple, ResourceFetchSelf, ResourceQuery, + Resources, System, SystemId, ThreadLocalExecution, }; -use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, World}; +use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, TypeAccess, World}; use std::{any::TypeId, borrow::Cow}; -#[derive(Debug)] -pub(crate) struct SystemFn +pub struct SystemState { + id: SystemId, + name: Cow<'static, str>, + is_initialized: bool, + archetype_component_access: TypeAccess, + resource_access: TypeAccess, + query_archetype_component_accesses: Vec>, + query_accesses: Vec>, + query_type_names: Vec<&'static str>, + commands: Commands, + current_query_index: usize, +} + +impl SystemState { + pub fn reset_indices(&mut self) { + self.current_query_index = 0; + } + + pub fn update(&mut self, world: &World) { + self.archetype_component_access.clear(); + let mut conflict_index = None; + let mut conflict_name = None; + for (i, (query_accesses, component_access)) in self + .query_accesses + .iter() + .zip(self.query_archetype_component_accesses.iter_mut()) + .enumerate() + { + component_access.clear(); + for query_access in query_accesses.iter() { + query_access.get_world_archetype_access(world, Some(component_access)); + } + if !component_access.is_compatible(&self.archetype_component_access) { + conflict_index = Some(i); + conflict_name = component_access + .get_conflict(&self.archetype_component_access) + .and_then(|archetype_component| { + query_accesses + .iter() + .filter_map(|query_access| { + query_access.get_type_name(archetype_component.component) + }) + .next() + }); + break; + } + self.archetype_component_access.union(component_access); + } + if let Some(conflict_index) = conflict_index { + let mut conflicts_with_index = None; + for prior_index in 0..conflict_index { + if !self.query_archetype_component_accesses[conflict_index] + .is_compatible(&self.query_archetype_component_accesses[prior_index]) + { + conflicts_with_index = Some(prior_index); + } + } + panic!("System {} has conflicting queries. {} conflicts with the component access [{}] in this prior query: {}", + core::any::type_name::(), + self.query_type_names[conflict_index], + conflict_name.unwrap_or("Unknown"), + conflicts_with_index.map(|index| self.query_type_names[index]).unwrap_or("Unknown")); + } + } +} + +pub struct FuncSystem where - F: FnMut(&World, &Resources, &mut State) + Send + Sync, - ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, - State: Send + Sync, + F: FnMut(&mut SystemState, &World, &Resources) + Send + Sync + 'static, + Init: FnMut(&mut SystemState, &World, &mut Resources) + Send + Sync + 'static, + ThreadLocalFunc: FnMut(&mut SystemState, &mut World, &mut Resources) + Send + Sync + 'static, { - pub state: State, - pub func: F, - pub thread_local_func: ThreadLocalF, - pub init_func: Init, - pub thread_local_execution: ThreadLocalExecution, - pub resource_access: TypeAccess, - pub name: Cow<'static, str>, - pub id: SystemId, - pub archetype_component_access: TypeAccess, - pub update_func: Update, + func: F, + thread_local_func: ThreadLocalFunc, + init_func: Init, + state: SystemState, } -impl System for SystemFn +impl System for FuncSystem where - F: FnMut(&World, &Resources, &mut State) + Send + Sync, - ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, - State: Send + Sync, + F: FnMut(&mut SystemState, &World, &Resources) + Send + Sync + 'static, + Init: FnMut(&mut SystemState, &World, &mut Resources) + Send + Sync + 'static, + ThreadLocalFunc: FnMut(&mut SystemState, &mut World, &mut Resources) + Send + Sync + 'static, { - fn name(&self) -> Cow<'static, str> { - self.name.clone() + fn name(&self) -> std::borrow::Cow<'static, str> { + self.state.name.clone() + } + + fn id(&self) -> SystemId { + self.state.id } fn update(&mut self, world: &World) { - (self.update_func)(world, &mut self.archetype_component_access, &mut self.state); + self.state.update(world); } fn archetype_component_access(&self) -> &TypeAccess { - &self.archetype_component_access + &self.state.archetype_component_access } - fn resource_access(&self) -> &TypeAccess { - &self.resource_access + fn resource_access(&self) -> &TypeAccess { + &self.state.resource_access } fn thread_local_execution(&self) -> ThreadLocalExecution { - self.thread_local_execution + ThreadLocalExecution::NextFlush } - #[inline] fn run(&mut self, world: &World, resources: &Resources) { - (self.func)(world, resources, &mut self.state); + (self.func)(&mut self.state, world, resources) } fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { - (self.thread_local_func)(world, resources, &mut self.state); + (self.thread_local_func)(&mut self.state, world, resources) } fn initialize(&mut self, world: &mut World, resources: &mut Resources) { - (self.init_func)(world, resources, &mut self.state); + (self.init_func)(&mut self.state, world, resources); + self.state.is_initialized = true; } - fn id(&self) -> SystemId { - self.id + fn is_initialized(&self) -> bool { + self.state.is_initialized } } -/// Converts `Self` into a For-Each system -pub trait IntoForEachSystem { - fn system(self) -> Box; +pub trait SystemParam { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources); + fn get_param(system_state: &mut SystemState, world: &World, resources: &Resources) -> Self; } -struct ForEachState { - commands: Commands, - query_access: QueryAccess, +impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { + #[inline] + fn get_param(system_state: &mut SystemState, world: &World, _resources: &Resources) -> Self { + let query_index = system_state.current_query_index; + unsafe { + let world: &'a World = std::mem::transmute(world); + let archetype_component_access: &'a TypeAccess = + std::mem::transmute(&system_state.query_archetype_component_accesses[query_index]); + system_state.current_query_index += 1; + Query::new(world, archetype_component_access) + } + } + + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state + .query_accesses + .push(vec![::access()]); + system_state + .query_type_names + .push(std::any::type_name::()); + } } -macro_rules! impl_into_foreach_system { - (($($commands: ident)*), ($($resource: ident),*), ($($component: ident),*)) => { - impl IntoForEachSystem<($($commands,)*), ($($resource,)*), ($($component,)*)> for Func - where - Func: - FnMut($($commands,)* $($resource,)* $($component,)*) + - FnMut( - $($commands,)* - $(<<$resource as ResourceQuery>::Fetch as FetchResource>::Item,)* - $(<<$component as HecsQuery>::Fetch as Fetch>::Item,)*)+ - Send + Sync + 'static, - $($component: HecsQuery,)* - $($resource: ResourceQuery,)* - { - #[allow(non_snake_case)] - #[allow(unused_variables)] - #[allow(unused_unsafe)] - fn system(mut self) -> Box { - let id = SystemId::new(); - Box::new(SystemFn { - state: ForEachState { - commands: Commands::default(), - query_access: <($($component,)*) as HecsQuery>::Fetch::access(), - }, - thread_local_execution: ThreadLocalExecution::NextFlush, - name: core::any::type_name::().into(), - id, - func: move |world, resources, state| { - { - let state_commands = &state.commands; - if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) { - // SAFE: the scheduler has ensured that there is no archetype clashing here - unsafe { - for ($($component,)*) in world.query_unchecked::<($($component,)*)>() { - fn_call!(self, ($($commands, state_commands)*), ($($resource),*), ($($component),*)) - } - } - } - } - }, - thread_local_func: move |world, resources, state| { - state.commands.apply(world, resources); - }, - init_func: move |world, resources, state| { - <($($resource,)*)>::initialize(resources, Some(id)); - state.commands.set_entity_reserver(world.get_entity_reserver()) - }, - resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(), - archetype_component_access: TypeAccess::default(), - update_func: |world, archetype_component_access, state| { - archetype_component_access.clear(); - state.query_access.get_world_archetype_access(world, Some(archetype_component_access)); - }, - }) - } +impl SystemParam for QuerySet { + #[inline] + fn get_param(system_state: &mut SystemState, world: &World, _resources: &Resources) -> Self { + let query_index = system_state.current_query_index; + system_state.current_query_index += 1; + unsafe { + QuerySet::new( + world, + &system_state.query_archetype_component_accesses[query_index], + ) } - }; + } + + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state.query_accesses.push(T::get_accesses()); + system_state + .query_type_names + .push(std::any::type_name::()); + } } -struct QuerySystemState { - query_accesses: Vec>, - query_type_names: Vec<&'static str>, - archetype_component_accesses: Vec>, - commands: Commands, +impl SystemParam for R +where + R: ResourceQuery + ResourceFetchSelf, +{ + fn init(system_state: &mut SystemState, _world: &World, resources: &mut Resources) { + R::initialize(resources, Some(system_state.id)); + system_state + .resource_access + .union(&::access()); + } + + #[inline] + fn get_param(system_state: &mut SystemState, _world: &World, resources: &Resources) -> Self { + unsafe { ::get(resources, Some(system_state.id)) } + } +} + +impl SystemParam for Commands { + fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { + system_state + .commands + .set_entity_reserver(world.get_entity_reserver()) + } + + #[inline] + fn get_param(system_state: &mut SystemState, _world: &World, _resources: &Resources) -> Self { + system_state.commands.clone() + } } -/// Converts `Self` into a Query System -pub trait IntoQuerySystem { +pub trait IntoSystem { fn system(self) -> Box; } -macro_rules! impl_into_query_system { - (($($commands: ident)*), ($($resource: ident),*), ($($query: ident),*), ($($query_set: ident),*)) => { - impl IntoQuerySystem<($($commands,)*), ($($resource,)*), ($($query,)*), ($($query_set,)*)> for Func where - Func: - FnMut($($commands,)* $($resource,)* $(Query<$query>,)* $(QuerySet<$query_set>,)*) + - FnMut( - $($commands,)* - $(<<$resource as ResourceQuery>::Fetch as FetchResource>::Item,)* - $(Query<$query>,)* - $(QuerySet<$query_set>,)* - ) + - Send + Sync +'static, - $($query: HecsQuery,)* - $($query_set: QueryTuple,)* - $($resource: ResourceQuery,)* +macro_rules! impl_into_system { + (($($param: ident),*)) => { + impl IntoSystem<($($param,)*)> for Func + where Func: FnMut($($param),*) + Send + Sync + 'static, { - #[allow(non_snake_case)] #[allow(unused_variables)] - #[allow(unused_unsafe)] - #[allow(unused_assignments)] - #[allow(unused_mut)] fn system(mut self) -> Box { - let id = SystemId::new(); - let query_accesses = vec![ - $(vec![<$query::Fetch as Fetch>::access()],)* - $($query_set::get_accesses(),)* - ]; - let query_type_names = vec![ - $(std::any::type_name::<$query>(),)* - $(std::any::type_name::<$query_set>(),)* - ]; - let archetype_component_accesses = vec![TypeAccess::default(); query_accesses.len()]; - Box::new(SystemFn { - state: QuerySystemState { - query_accesses, - query_type_names, - archetype_component_accesses, + Box::new(FuncSystem { + state: SystemState { + name: std::any::type_name::().into(), + archetype_component_access: TypeAccess::default(), + resource_access: TypeAccess::default(), + is_initialized: false, + id: SystemId::new(), commands: Commands::default(), + query_archetype_component_accesses: Vec::new(), + query_accesses: Vec::new(), + query_type_names: Vec::new(), + current_query_index: 0, }, - thread_local_execution: ThreadLocalExecution::NextFlush, - id, - name: core::any::type_name::().into(), - func: move |world, resources, state| { - { - if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) { - let mut i = 0; - $( - let $query = Query::<$query>::new( - world, - &state.archetype_component_accesses[i] - ); - i += 1; - )* - $( - let $query_set = QuerySet::<$query_set>::new( - world, - &state.archetype_component_accesses[i] - ); - i += 1; - )* - - let commands = &state.commands; - fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*), ($($query_set),*)) - } - } + func: move |state, world, resources| { + state.reset_indices(); + self($($param::get_param(state, world, resources)),*); }, - thread_local_func: move |world, resources, state| { + thread_local_func: |state, world, resources| { state.commands.apply(world, resources); }, - init_func: move |world, resources, state| { - <($($resource,)*)>::initialize(resources, Some(id)); - state.commands.set_entity_reserver(world.get_entity_reserver()) - - }, - resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(), - archetype_component_access: TypeAccess::default(), - update_func: |world, archetype_component_access, state| { - archetype_component_access.clear(); - let mut conflict_index = None; - let mut conflict_name = None; - for (i, (query_accesses, component_access)) in state.query_accesses.iter().zip(state.archetype_component_accesses.iter_mut()).enumerate() { - component_access.clear(); - for query_access in query_accesses.iter() { - query_access.get_world_archetype_access(world, Some(component_access)); - } - if !component_access.is_compatible(archetype_component_access) { - conflict_index = Some(i); - conflict_name = component_access.get_conflict(archetype_component_access).and_then(|archetype_component| - query_accesses - .iter() - .filter_map(|query_access| query_access.get_type_name(archetype_component.component)) - .next()); - break; - } - archetype_component_access.union(component_access); - } - if let Some(conflict_index) = conflict_index { - let mut conflicts_with_index = None; - for prior_index in 0..conflict_index { - if !state.archetype_component_accesses[conflict_index].is_compatible(&state.archetype_component_accesses[prior_index]) { - conflicts_with_index = Some(prior_index); - } - } - panic!("System {} has conflicting queries. {} conflicts with the component access [{}] in this prior query: {}", - core::any::type_name::(), - state.query_type_names[conflict_index], - conflict_name.unwrap_or("Unknown"), - conflicts_with_index.map(|index| state.query_type_names[index]).unwrap_or("Unknown")); - } + init_func: |state, world, resources| { + $($param::init(state, world, resources);)* }, }) } } - }; -} - -macro_rules! fn_call { - ($self:ident, ($($commands: ident, $commands_var: ident)*), ($($resource: ident),*), ($($a: ident),*), ($($b: ident),*)) => { - unsafe { $self($($commands_var.clone(),)* $($resource.unsafe_clone(),)* $($a,)* $($b,)*) } - }; - ($self:ident, ($($commands: ident, $commands_var: ident)*), ($($resource: ident),*), ($($a: ident),*)) => { - unsafe { $self($($commands_var.clone(),)* $($resource.unsafe_clone(),)* $($a,)*) } - }; - ($self:ident, (), ($($resource: ident),*), ($($a: ident),*)) => { - unsafe { $self($($resource.unsafe_clone(),)* $($a,)*) } - }; -} - -macro_rules! impl_into_query_systems { - (($($resource: ident,)*), ($($query: ident),*)) => { - #[rustfmt::skip] - impl_into_query_system!((), ($($resource),*), ($($query),*), ()); - #[rustfmt::skip] - impl_into_query_system!((), ($($resource),*), ($($query),*), (QS1)); - #[rustfmt::skip] - impl_into_query_system!((), ($($resource),*), ($($query),*), (QS1, QS2)); - - #[rustfmt::skip] - impl_into_query_system!((Commands), ($($resource),*), ($($query),*), ()); - #[rustfmt::skip] - impl_into_query_system!((Commands), ($($resource),*), ($($query),*), (QS1)); - #[rustfmt::skip] - impl_into_query_system!((Commands), ($($resource),*), ($($query),*), (QS1, QS2)); - } -} -macro_rules! impl_into_foreach_systems { - (($($resource: ident,)*), ($($component: ident),*)) => { - #[rustfmt::skip] - impl_into_foreach_system!((), ($($resource),*), ($($component),*)); - #[rustfmt::skip] - impl_into_foreach_system!((Commands), ($($resource),*), ($($component),*)); - } -} - -macro_rules! impl_into_systems { - ($($resource: ident),*) => { - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E,F)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E,F,G)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E,F,G,H)); - - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), ()); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C,D)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C,D,E)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C,D,E,F)); }; } -#[rustfmt::skip] -impl_into_systems!(); -#[rustfmt::skip] -impl_into_systems!(Ra); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh,Ri); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh,Ri,Rj); - -/// Converts `Self` into a thread local system -pub trait IntoThreadLocalSystem { - fn thread_local_system(self) -> Box; -} - -impl IntoThreadLocalSystem for F -where - F: ThreadLocalSystemFn, -{ - fn thread_local_system(mut self) -> Box { - Box::new(SystemFn { - state: (), - thread_local_func: move |world, resources, _| { - self.run(world, resources); - }, - func: |_, _, _| {}, - init_func: |_, _, _| {}, - update_func: |_, _, _| {}, - thread_local_execution: ThreadLocalExecution::Immediate, - name: core::any::type_name::().into(), - id: SystemId::new(), - resource_access: TypeAccess::default(), - archetype_component_access: TypeAccess::default(), - }) - } -} - -/// A thread local system function -pub trait ThreadLocalSystemFn: Send + Sync + 'static { - fn run(&mut self, world: &mut World, resource: &mut Resources); -} - -impl ThreadLocalSystemFn for F -where - F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, -{ - fn run(&mut self, world: &mut World, resources: &mut Resources) { - self(world, resources); - } -} +impl_into_system!(()); +impl_into_system!((A)); +impl_into_system!((A, B)); +impl_into_system!((A, B, C)); +impl_into_system!((A, B, C, D)); +impl_into_system!((A, B, C, D, E)); +impl_into_system!((A, B, C, D, E, F)); +impl_into_system!((A, B, C, D, E, F, G)); +impl_into_system!((A, B, C, D, E, F, G, H)); +impl_into_system!((A, B, C, D, E, F, G, H, I)); +impl_into_system!((A, B, C, D, E, F, G, H, I, J)); #[cfg(test)] mod tests { - use super::{IntoForEachSystem, IntoQuerySystem, Query}; + use super::{IntoSystem, Query}; use crate::{ resource::{ResMut, Resources}, schedule::Schedule, - ChangedRes, Mut, QuerySet, + ChangedRes, QuerySet, }; use bevy_hecs::{Entity, With, World}; @@ -528,8 +387,10 @@ mod tests { #[test] fn changed_resource_system() { - fn incr_e_on_flip(_run_on_flip: ChangedRes, mut i: Mut) { - *i += 1; + fn incr_e_on_flip(_run_on_flip: ChangedRes, mut query: Query<&mut i32>) { + for mut i in query.iter_mut() { + *i += 1; + } } let mut world = World::default(); diff --git a/crates/bevy_ecs/src/system/into_thread_local.rs b/crates/bevy_ecs/src/system/into_thread_local.rs new file mode 100644 index 0000000000000..b83e4df720b9e --- /dev/null +++ b/crates/bevy_ecs/src/system/into_thread_local.rs @@ -0,0 +1,121 @@ +pub use super::Query; +use crate::{ + resource::Resources, + system::{System, SystemId, ThreadLocalExecution}, + TypeAccess, +}; +use bevy_hecs::{ArchetypeComponent, World}; +use std::{any::TypeId, borrow::Cow}; + +#[derive(Debug)] +pub(crate) struct SystemFn +where + F: FnMut(&World, &Resources, &mut State) + Send + Sync, + ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, + Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, + Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, + State: Send + Sync, +{ + pub state: State, + pub func: F, + pub thread_local_func: ThreadLocalF, + pub init_func: Init, + pub thread_local_execution: ThreadLocalExecution, + pub resource_access: TypeAccess, + pub name: Cow<'static, str>, + pub id: SystemId, + pub archetype_component_access: TypeAccess, + pub update_func: Update, +} + +impl System for SystemFn +where + F: FnMut(&World, &Resources, &mut State) + Send + Sync, + ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, + Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, + Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, + State: Send + Sync, +{ + fn name(&self) -> Cow<'static, str> { + self.name.clone() + } + + fn update(&mut self, world: &World) { + (self.update_func)(world, &mut self.archetype_component_access, &mut self.state); + } + + fn archetype_component_access(&self) -> &TypeAccess { + &self.archetype_component_access + } + + fn resource_access(&self) -> &TypeAccess { + &self.resource_access + } + + fn thread_local_execution(&self) -> ThreadLocalExecution { + self.thread_local_execution + } + + #[inline] + fn run(&mut self, world: &World, resources: &Resources) { + (self.func)(world, resources, &mut self.state); + } + + fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { + (self.thread_local_func)(world, resources, &mut self.state); + } + + fn initialize(&mut self, world: &mut World, resources: &mut Resources) { + (self.init_func)(world, resources, &mut self.state); + } + + fn id(&self) -> SystemId { + self.id + } + + fn is_initialized(&self) -> bool { + // TODO: either make this correct or remove everything in this file :) + false + } +} + +/// Converts `Self` into a thread local system +pub trait IntoThreadLocalSystem { + fn thread_local_system(self) -> Box; +} + +impl IntoThreadLocalSystem for F +where + F: ThreadLocalSystemFn, +{ + fn thread_local_system(mut self) -> Box { + Box::new(SystemFn { + state: (), + thread_local_func: move |world, resources, _| { + self.run(world, resources); + }, + func: |_, _, _| {}, + init_func: |_, _, _| {}, + update_func: |_, _, _| {}, + thread_local_execution: ThreadLocalExecution::Immediate, + name: core::any::type_name::().into(), + id: SystemId::new(), + resource_access: TypeAccess::default(), + archetype_component_access: TypeAccess::default(), + }) + } +} + +/// A thread local system function +pub trait ThreadLocalSystemFn: Send + Sync + 'static { + fn run(&mut self, world: &mut World, resource: &mut Resources); +} + +impl ThreadLocalSystemFn for F +where + F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, +{ + fn run(&mut self, world: &mut World, resources: &mut Resources) { + self(world, resources); + } +} \ No newline at end of file diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index ef5e9f20436fd..975580b390bb6 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1,5 +1,6 @@ mod commands; mod into_system; +mod into_thread_local; #[cfg(feature = "profiler")] mod profiler; mod query; @@ -8,6 +9,7 @@ mod system; pub use commands::*; pub use into_system::*; +pub use into_thread_local::*; #[cfg(feature = "profiler")] pub use profiler::*; pub use query::*; diff --git a/crates/bevy_ecs/src/system/query/query_set.rs b/crates/bevy_ecs/src/system/query/query_set.rs index 616ad04055bc4..4cc244b9d5c00 100644 --- a/crates/bevy_ecs/src/system/query/query_set.rs +++ b/crates/bevy_ecs/src/system/query/query_set.rs @@ -17,9 +17,9 @@ pub trait QueryTuple { } impl QuerySet { - pub fn new(world: &World, component_access: &TypeAccess) -> Self { + pub unsafe fn new(world: &World, component_access: &TypeAccess) -> Self { QuerySet { - value: unsafe { T::new(world, component_access) }, + value: T::new(world, component_access), } } } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 75d4cc370a1d4..d2f1600df40f4 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -23,6 +23,7 @@ impl SystemId { pub trait System: Send + Sync { fn name(&self) -> Cow<'static, str>; fn id(&self) -> SystemId; + fn is_initialized(&self) -> bool; fn update(&mut self, world: &World); fn archetype_component_access(&self) -> &TypeAccess; fn resource_access(&self) -> &TypeAccess; diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 92c884eb9a2b8..b3ced9c404fab 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -28,7 +28,7 @@ use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotio use touch::{touch_screen_input_system, TouchInput, Touches}; use bevy_app::startup_stage::STARTUP; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use gamepad::{ gamepad_event_system, GamepadAxis, GamepadButton, GamepadEvent, GamepadEventRaw, GamepadSettings, diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 3feb8cc6cdd40..8270b46859558 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -14,7 +14,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_asset::{AddAsset, Assets, Handle}; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_render::{prelude::Color, render_graph::RenderGraph, shader}; use bevy_type_registry::RegisterType; use light::Light; diff --git a/crates/bevy_pbr/src/render_graph/lights_node.rs b/crates/bevy_pbr/src/render_graph/lights_node.rs index ffc573394d5fe..fc2295d5c1276 100644 --- a/crates/bevy_pbr/src/render_graph/lights_node.rs +++ b/crates/bevy_pbr/src/render_graph/lights_node.rs @@ -3,7 +3,7 @@ use crate::{ render_graph::uniform, }; use bevy_core::{AsBytes, Byteable}; -use bevy_ecs::{Commands, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World}; +use bevy_ecs::{Commands, IntoSystem, Local, Query, Res, ResMut, Resources, System, World}; use bevy_render::{ render_graph::{CommandQueue, Node, ResourceSlots, SystemNode}, renderer::{ diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index c96d6416a77fa..4983bb991cea2 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -32,7 +32,7 @@ use crate::prelude::*; use base::{MainPass, Msaa}; use bevy_app::prelude::*; use bevy_asset::AddAsset; -use bevy_ecs::{IntoQuerySystem, IntoThreadLocalSystem}; +use bevy_ecs::{IntoSystem, IntoThreadLocalSystem}; use camera::{ ActiveCameras, Camera, OrthographicProjection, PerspectiveProjection, VisibleEntities, }; diff --git a/crates/bevy_render/src/render_graph/nodes/camera_node.rs b/crates/bevy_render/src/render_graph/nodes/camera_node.rs index c9af7fe0e93cf..bdc5217c75de8 100644 --- a/crates/bevy_render/src/render_graph/nodes/camera_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/camera_node.rs @@ -8,7 +8,7 @@ use crate::{ }; use bevy_core::AsBytes; -use bevy_ecs::{Commands, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World}; +use bevy_ecs::{Commands, IntoSystem, Local, Query, Res, ResMut, Resources, System, World}; use bevy_transform::prelude::*; use std::borrow::Cow; diff --git a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs index 9fe24487484db..de566101f9289 100644 --- a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs @@ -11,7 +11,7 @@ use crate::{ use bevy_asset::{Asset, Assets, Handle, HandleId}; use bevy_ecs::{ - Commands, Entity, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World, + Commands, Entity, IntoSystem, Local, Query, Res, ResMut, Resources, System, World, }; use bevy_utils::HashMap; use renderer::{AssetRenderResourceBindings, BufferId, RenderResourceType, RenderResources}; diff --git a/crates/bevy_render/src/render_graph/system.rs b/crates/bevy_render/src/render_graph/system.rs index 330be0a1f9baa..99b31bf4152fd 100644 --- a/crates/bevy_render/src/render_graph/system.rs +++ b/crates/bevy_render/src/render_graph/system.rs @@ -10,6 +10,11 @@ pub fn render_graph_schedule_executor_system(world: &mut World, resources: &mut commands.apply(world, resources); if let Some(schedule) = system_schedule.as_mut() { + schedule.run_on_systems(|system| { + if !system.is_initialized() { + system.initialize(world, resources); + } + }); schedule.run(world, resources); } let mut render_graph = resources.get_mut::().unwrap(); diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index cea0da2285d48..d7107e2e2415f 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -26,7 +26,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_asset::{AddAsset, Assets, Handle}; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_math::Vec2; use bevy_render::{ mesh::{shape, Mesh}, diff --git a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs index 9663bea17b15d..ea249e61a34f5 100644 --- a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs +++ b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs @@ -1,5 +1,5 @@ use crate::components::*; -use bevy_ecs::{Commands, Entity, IntoQuerySystem, Query, System, Without}; +use bevy_ecs::{Commands, Entity, IntoSystem, Query, System, Without}; use bevy_utils::HashMap; use smallvec::SmallVec; diff --git a/crates/bevy_transform/src/transform_system_bundle.rs b/crates/bevy_transform/src/transform_system_bundle.rs index cf76625feeb0b..ec9a3f70090b9 100644 --- a/crates/bevy_transform/src/transform_system_bundle.rs +++ b/crates/bevy_transform/src/transform_system_bundle.rs @@ -3,7 +3,7 @@ use crate::{ transform_propagate_system::transform_propagate_system, transform_systems, }; -use bevy_ecs::{IntoQuerySystem, System}; +use bevy_ecs::{IntoSystem, System}; use hierarchy_maintenance_system::hierarchy_maintenance_systems; use transform_systems::transform_systems; diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 56ccaeb31a772..21b9c4d80a954 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -25,7 +25,7 @@ pub mod prelude { } use bevy_app::prelude::*; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_render::render_graph::RenderGraph; use update::ui_z_system; diff --git a/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs b/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs index 0da3d6d1d9a36..079332c93cf89 100644 --- a/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs +++ b/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs @@ -1,7 +1,7 @@ use crate::renderer::WgpuRenderResourceContext; use bevy_app::prelude::*; use bevy_diagnostic::{Diagnostic, DiagnosticId, Diagnostics}; -use bevy_ecs::{IntoQuerySystem, Res, ResMut}; +use bevy_ecs::{IntoSystem, Res, ResMut}; use bevy_render::renderer::RenderResourceContext; #[derive(Default)] diff --git a/crates/bevy_wgpu/src/lib.rs b/crates/bevy_wgpu/src/lib.rs index 4c38dcdeda4ae..8cb2c7db69da6 100644 --- a/crates/bevy_wgpu/src/lib.rs +++ b/crates/bevy_wgpu/src/lib.rs @@ -11,7 +11,7 @@ pub use wgpu_renderer::*; pub use wgpu_resources::*; use bevy_app::prelude::*; -use bevy_ecs::{IntoQuerySystem, IntoThreadLocalSystem, Resources, World}; +use bevy_ecs::{IntoSystem, IntoThreadLocalSystem, Resources, World}; use bevy_render::renderer::{free_shared_buffers_system, RenderResourceContext, SharedBuffers}; use renderer::WgpuRenderResourceContext; diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 41d61d5bf6e64..36f7a736ef10f 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -13,7 +13,7 @@ pub mod prelude { } use bevy_app::prelude::*; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; pub struct WindowPlugin { pub add_primary_window: bool, diff --git a/examples/ios/src/lib.rs b/examples/ios/src/lib.rs index c3a7f938086fc..0449a921ac046 100644 --- a/examples/ios/src/lib.rs +++ b/examples/ios/src/lib.rs @@ -1,6 +1,6 @@ use bevy::{ prelude::{ - shape, App, Assets, Camera3dComponents, Color, Commands, DefaultPlugins, IntoQuerySystem, + shape, App, Assets, Camera3dComponents, Color, Commands, DefaultPlugins, IntoSystem, LightComponents, Mesh, Msaa, PbrComponents, ResMut, StandardMaterial, Transform, Vec3, WindowDescriptor, }, From cba1b039f5b98db1bd581e74217c1b21471f7e5c Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 12:39:26 -0800 Subject: [PATCH 02/12] progress --- .../src/schedule/parallel_executor.rs | 636 +++++++++--------- crates/bevy_ecs/src/system/into_system.rs | 56 +- .../bevy_ecs/src/system/into_thread_local.rs | 72 +- 3 files changed, 352 insertions(+), 412 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/parallel_executor.rs b/crates/bevy_ecs/src/schedule/parallel_executor.rs index f7ca0c1b731a3..d647f192ef239 100644 --- a/crates/bevy_ecs/src/schedule/parallel_executor.rs +++ b/crates/bevy_ecs/src/schedule/parallel_executor.rs @@ -526,321 +526,321 @@ impl ExecutorStage { } } -// #[cfg(test)] -// mod tests { -// use super::ParallelExecutor; -// use crate::{ -// resource::{Res, ResMut, Resources}, -// schedule::Schedule, -// system::{IntoSystem, IntoThreadLocalSystem, Query}, -// Commands, -// }; -// use bevy_hecs::{Entity, World}; -// use bevy_tasks::{ComputeTaskPool, TaskPool}; -// use fixedbitset::FixedBitSet; -// use parking_lot::Mutex; -// use std::{collections::HashSet, sync::Arc}; - -// #[derive(Default)] -// struct CompletedSystems { -// completed_systems: Arc>>, -// } - -// #[test] -// fn cross_stage_archetype_change_prepare() { -// let mut world = World::new(); -// let mut resources = Resources::default(); -// resources.insert(ComputeTaskPool(TaskPool::default())); - -// let mut schedule = Schedule::default(); -// schedule.add_stage("PreArchetypeChange"); -// schedule.add_stage("PostArchetypeChange"); - -// fn insert(mut commands: Commands) { -// commands.spawn((1u32,)); -// } - -// fn read(query: Query<&u32>, entities: Query) { -// for entity in &mut entities.iter() { -// // query.get() does a "system permission check" that will fail if the entity is from a -// // new archetype which hasnt been "prepared yet" -// query.get_component::(entity).unwrap(); -// } - -// assert_eq!(1, entities.iter().count()); -// } - -// schedule.add_system_to_stage("PreArchetypeChange", insert.system()); -// schedule.add_system_to_stage("PostArchetypeChange", read.system()); - -// let mut executor = ParallelExecutor::default(); -// schedule.initialize(&mut world, &mut resources); -// executor.run(&mut schedule, &mut world, &mut resources); -// } - -// #[test] -// fn intra_stage_archetype_change_prepare() { -// let mut world = World::new(); -// let mut resources = Resources::default(); -// resources.insert(ComputeTaskPool(TaskPool::default())); - -// let mut schedule = Schedule::default(); -// schedule.add_stage("update"); - -// fn insert(world: &mut World, _resources: &mut Resources) { -// world.spawn((1u32,)); -// } - -// fn read(query: Query<&u32>, entities: Query) { -// for entity in &mut entities.iter() { -// // query.get() does a "system permission check" that will fail if the entity is from a -// // new archetype which hasnt been "prepared yet" -// query.get_component::(entity).unwrap(); -// } - -// assert_eq!(1, entities.iter().count()); -// } - -// schedule.add_system_to_stage("update", insert.thread_local_system()); -// schedule.add_system_to_stage("update", read.system()); - -// let mut executor = ParallelExecutor::default(); -// executor.run(&mut schedule, &mut world, &mut resources); -// } - -// #[test] -// fn schedule() { -// let mut world = World::new(); -// let mut resources = Resources::default(); -// resources.insert(ComputeTaskPool(TaskPool::default())); -// resources.insert(CompletedSystems::default()); -// resources.insert(1.0f64); -// resources.insert(2isize); - -// world.spawn((1.0f32,)); -// world.spawn((1u32, 1u64)); -// world.spawn((2u32,)); - -// let mut schedule = Schedule::default(); -// schedule.add_stage("A"); // component queries -// schedule.add_stage("B"); // thread local -// schedule.add_stage("C"); // resources - -// // A system names -// const READ_U32_SYSTEM_NAME: &str = "read_u32"; -// const WRITE_FLOAT_SYSTEM_NAME: &str = "write_float"; -// const READ_U32_WRITE_U64_SYSTEM_NAME: &str = "read_u32_write_u64"; -// const READ_U64_SYSTEM_NAME: &str = "read_u64"; - -// // B system names -// const WRITE_U64_SYSTEM_NAME: &str = "write_u64"; -// const THREAD_LOCAL_SYSTEM_SYSTEM_NAME: &str = "thread_local_system"; -// const WRITE_F32_SYSTEM_NAME: &str = "write_f32"; - -// // C system names -// const READ_F64_RES_SYSTEM_NAME: &str = "read_f64_res"; -// const READ_ISIZE_RES_SYSTEM_NAME: &str = "read_isize_res"; -// const READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME: &str = "read_isize_write_f64_res"; -// const WRITE_F64_RES_SYSTEM_NAME: &str = "write_f64_res"; - -// // A systems - -// fn read_u32(completed_systems: Res, _query: Query<&u32>) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// completed_systems.insert(READ_U32_SYSTEM_NAME); -// } - -// fn write_float(completed_systems: Res, _query: Query<&f32>) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// completed_systems.insert(WRITE_FLOAT_SYSTEM_NAME); -// } - -// fn read_u32_write_u64( -// completed_systems: Res, -// _query: Query<(&u32, &mut u64)>, -// ) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(!completed_systems.contains(READ_U64_SYSTEM_NAME)); -// completed_systems.insert(READ_U32_WRITE_U64_SYSTEM_NAME); -// } - -// fn read_u64(completed_systems: Res, _query: Query<&u64>) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(READ_U32_WRITE_U64_SYSTEM_NAME)); -// assert!(!completed_systems.contains(WRITE_U64_SYSTEM_NAME)); -// completed_systems.insert(READ_U64_SYSTEM_NAME); -// } - -// schedule.add_system_to_stage("A", read_u32.system()); -// schedule.add_system_to_stage("A", write_float.system()); -// schedule.add_system_to_stage("A", read_u32_write_u64.system()); -// schedule.add_system_to_stage("A", read_u64.system()); - -// // B systems - -// fn write_u64(completed_systems: Res, _query: Query<&mut u64>) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(READ_U64_SYSTEM_NAME)); -// assert!(!completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); -// assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); -// completed_systems.insert(WRITE_U64_SYSTEM_NAME); -// } - -// fn thread_local_system(_world: &mut World, resources: &mut Resources) { -// let completed_systems = resources.get::().unwrap(); -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); -// assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); -// completed_systems.insert(THREAD_LOCAL_SYSTEM_SYSTEM_NAME); -// } - -// fn write_f32(completed_systems: Res, _query: Query<&mut f32>) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); -// assert!(completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); -// assert!(!completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); -// completed_systems.insert(WRITE_F32_SYSTEM_NAME); -// } - -// schedule.add_system_to_stage("B", write_u64.system()); -// schedule.add_system_to_stage("B", thread_local_system.thread_local_system()); -// schedule.add_system_to_stage("B", write_f32.system()); - -// // C systems - -// fn read_f64_res(completed_systems: Res, _f64_res: Res) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(WRITE_F32_SYSTEM_NAME)); -// assert!(!completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); -// assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); -// completed_systems.insert(READ_F64_RES_SYSTEM_NAME); -// } - -// fn read_isize_res(completed_systems: Res, _isize_res: Res) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// completed_systems.insert(READ_ISIZE_RES_SYSTEM_NAME); -// } - -// fn read_isize_write_f64_res( -// completed_systems: Res, -// _isize_res: Res, -// _f64_res: ResMut, -// ) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); -// assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); -// completed_systems.insert(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME); -// } - -// fn write_f64_res(completed_systems: Res, _f64_res: ResMut) { -// let mut completed_systems = completed_systems.completed_systems.lock(); -// assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); -// assert!(completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); -// completed_systems.insert(WRITE_F64_RES_SYSTEM_NAME); -// } - -// schedule.add_system_to_stage("C", read_f64_res.system()); -// schedule.add_system_to_stage("C", read_isize_res.system()); -// schedule.add_system_to_stage("C", read_isize_write_f64_res.system()); -// schedule.add_system_to_stage("C", write_f64_res.system()); - -// fn run_executor_and_validate( -// executor: &mut ParallelExecutor, -// schedule: &mut Schedule, -// world: &mut World, -// resources: &mut Resources, -// ) { -// executor.run(schedule, world, resources); - -// assert_eq!( -// executor.stages[0].system_dependents, -// vec![vec![], vec![], vec![3], vec![]] -// ); -// assert_eq!( -// executor.stages[1].system_dependents, -// vec![vec![1], vec![2], vec![]] -// ); -// assert_eq!( -// executor.stages[2].system_dependents, -// vec![vec![2, 3], vec![], vec![3], vec![]] -// ); - -// let stage_0_len = executor.stages[0].system_dependencies.len(); -// let mut read_u64_deps = FixedBitSet::with_capacity(stage_0_len); -// read_u64_deps.insert(2); - -// assert_eq!( -// executor.stages[0].system_dependencies, -// vec![ -// FixedBitSet::with_capacity(stage_0_len), -// FixedBitSet::with_capacity(stage_0_len), -// FixedBitSet::with_capacity(stage_0_len), -// read_u64_deps, -// ] -// ); - -// let stage_1_len = executor.stages[1].system_dependencies.len(); -// let mut thread_local_deps = FixedBitSet::with_capacity(stage_1_len); -// thread_local_deps.insert(0); -// let mut write_f64_deps = FixedBitSet::with_capacity(stage_1_len); -// write_f64_deps.insert(1); -// assert_eq!( -// executor.stages[1].system_dependencies, -// vec![ -// FixedBitSet::with_capacity(stage_1_len), -// thread_local_deps, -// write_f64_deps -// ] -// ); - -// let stage_2_len = executor.stages[2].system_dependencies.len(); -// let mut read_isize_write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); -// read_isize_write_f64_res_deps.insert(0); -// let mut write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); -// write_f64_res_deps.insert(0); -// write_f64_res_deps.insert(2); -// assert_eq!( -// executor.stages[2].system_dependencies, -// vec![ -// FixedBitSet::with_capacity(stage_2_len), -// FixedBitSet::with_capacity(stage_2_len), -// read_isize_write_f64_res_deps, -// write_f64_res_deps -// ] -// ); - -// let completed_systems = resources.get::().unwrap(); -// assert_eq!( -// completed_systems.completed_systems.lock().len(), -// 11, -// "completed_systems should have been incremented once for each system" -// ); -// } - -// // Stress test the "clean start" case -// for _ in 0..1000 { -// let mut executor = ParallelExecutor::default(); -// run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); -// resources -// .get::() -// .unwrap() -// .completed_systems -// .lock() -// .clear(); -// } - -// // Stress test the "continue running" case -// let mut executor = ParallelExecutor::default(); -// run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); -// for _ in 0..1000 { -// // run again (with completed_systems reset) to ensure executor works correctly across runs -// resources -// .get::() -// .unwrap() -// .completed_systems -// .lock() -// .clear(); -// run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); -// } -// } -// } +#[cfg(test)] +mod tests { + use super::ParallelExecutor; + use crate::{ + resource::{Res, ResMut, Resources}, + schedule::Schedule, + system::{IntoSystem, IntoThreadLocalSystem, Query}, + Commands, + }; + use bevy_hecs::{Entity, World}; + use bevy_tasks::{ComputeTaskPool, TaskPool}; + use fixedbitset::FixedBitSet; + use parking_lot::Mutex; + use std::{collections::HashSet, sync::Arc}; + + #[derive(Default)] + struct CompletedSystems { + completed_systems: Arc>>, + } + + #[test] + fn cross_stage_archetype_change_prepare() { + let mut world = World::new(); + let mut resources = Resources::default(); + resources.insert(ComputeTaskPool(TaskPool::default())); + + let mut schedule = Schedule::default(); + schedule.add_stage("PreArchetypeChange"); + schedule.add_stage("PostArchetypeChange"); + + fn insert(mut commands: Commands) { + commands.spawn((1u32,)); + } + + fn read(query: Query<&u32>, entities: Query) { + for entity in &mut entities.iter() { + // query.get() does a "system permission check" that will fail if the entity is from a + // new archetype which hasnt been "prepared yet" + query.get_component::(entity).unwrap(); + } + + assert_eq!(1, entities.iter().count()); + } + + schedule.add_system_to_stage("PreArchetypeChange", insert.system()); + schedule.add_system_to_stage("PostArchetypeChange", read.system()); + + let mut executor = ParallelExecutor::default(); + schedule.initialize(&mut world, &mut resources); + executor.run(&mut schedule, &mut world, &mut resources); + } + + #[test] + fn intra_stage_archetype_change_prepare() { + let mut world = World::new(); + let mut resources = Resources::default(); + resources.insert(ComputeTaskPool(TaskPool::default())); + + let mut schedule = Schedule::default(); + schedule.add_stage("update"); + + fn insert(world: &mut World, _resources: &mut Resources) { + world.spawn((1u32,)); + } + + fn read(query: Query<&u32>, entities: Query) { + for entity in &mut entities.iter() { + // query.get() does a "system permission check" that will fail if the entity is from a + // new archetype which hasnt been "prepared yet" + query.get_component::(entity).unwrap(); + } + + assert_eq!(1, entities.iter().count()); + } + + schedule.add_system_to_stage("update", insert.thread_local_system()); + schedule.add_system_to_stage("update", read.system()); + + let mut executor = ParallelExecutor::default(); + executor.run(&mut schedule, &mut world, &mut resources); + } + + #[test] + fn schedule() { + let mut world = World::new(); + let mut resources = Resources::default(); + resources.insert(ComputeTaskPool(TaskPool::default())); + resources.insert(CompletedSystems::default()); + resources.insert(1.0f64); + resources.insert(2isize); + + world.spawn((1.0f32,)); + world.spawn((1u32, 1u64)); + world.spawn((2u32,)); + + let mut schedule = Schedule::default(); + schedule.add_stage("A"); // component queries + schedule.add_stage("B"); // thread local + schedule.add_stage("C"); // resources + + // A system names + const READ_U32_SYSTEM_NAME: &str = "read_u32"; + const WRITE_FLOAT_SYSTEM_NAME: &str = "write_float"; + const READ_U32_WRITE_U64_SYSTEM_NAME: &str = "read_u32_write_u64"; + const READ_U64_SYSTEM_NAME: &str = "read_u64"; + + // B system names + const WRITE_U64_SYSTEM_NAME: &str = "write_u64"; + const THREAD_LOCAL_SYSTEM_SYSTEM_NAME: &str = "thread_local_system"; + const WRITE_F32_SYSTEM_NAME: &str = "write_f32"; + + // C system names + const READ_F64_RES_SYSTEM_NAME: &str = "read_f64_res"; + const READ_ISIZE_RES_SYSTEM_NAME: &str = "read_isize_res"; + const READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME: &str = "read_isize_write_f64_res"; + const WRITE_F64_RES_SYSTEM_NAME: &str = "write_f64_res"; + + // A systems + + fn read_u32(completed_systems: Res, _query: Query<&u32>) { + let mut completed_systems = completed_systems.completed_systems.lock(); + completed_systems.insert(READ_U32_SYSTEM_NAME); + } + + fn write_float(completed_systems: Res, _query: Query<&f32>) { + let mut completed_systems = completed_systems.completed_systems.lock(); + completed_systems.insert(WRITE_FLOAT_SYSTEM_NAME); + } + + fn read_u32_write_u64( + completed_systems: Res, + _query: Query<(&u32, &mut u64)>, + ) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(!completed_systems.contains(READ_U64_SYSTEM_NAME)); + completed_systems.insert(READ_U32_WRITE_U64_SYSTEM_NAME); + } + + fn read_u64(completed_systems: Res, _query: Query<&u64>) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(READ_U32_WRITE_U64_SYSTEM_NAME)); + assert!(!completed_systems.contains(WRITE_U64_SYSTEM_NAME)); + completed_systems.insert(READ_U64_SYSTEM_NAME); + } + + schedule.add_system_to_stage("A", read_u32.system()); + schedule.add_system_to_stage("A", write_float.system()); + schedule.add_system_to_stage("A", read_u32_write_u64.system()); + schedule.add_system_to_stage("A", read_u64.system()); + + // B systems + + fn write_u64(completed_systems: Res, _query: Query<&mut u64>) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(READ_U64_SYSTEM_NAME)); + assert!(!completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); + assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); + completed_systems.insert(WRITE_U64_SYSTEM_NAME); + } + + fn thread_local_system(_world: &mut World, resources: &mut Resources) { + let completed_systems = resources.get::().unwrap(); + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); + assert!(!completed_systems.contains(WRITE_F32_SYSTEM_NAME)); + completed_systems.insert(THREAD_LOCAL_SYSTEM_SYSTEM_NAME); + } + + fn write_f32(completed_systems: Res, _query: Query<&mut f32>) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(WRITE_U64_SYSTEM_NAME)); + assert!(completed_systems.contains(THREAD_LOCAL_SYSTEM_SYSTEM_NAME)); + assert!(!completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); + completed_systems.insert(WRITE_F32_SYSTEM_NAME); + } + + schedule.add_system_to_stage("B", write_u64.system()); + schedule.add_system_to_stage("B", thread_local_system.thread_local_system()); + schedule.add_system_to_stage("B", write_f32.system()); + + // C systems + + fn read_f64_res(completed_systems: Res, _f64_res: Res) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(WRITE_F32_SYSTEM_NAME)); + assert!(!completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); + assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); + completed_systems.insert(READ_F64_RES_SYSTEM_NAME); + } + + fn read_isize_res(completed_systems: Res, _isize_res: Res) { + let mut completed_systems = completed_systems.completed_systems.lock(); + completed_systems.insert(READ_ISIZE_RES_SYSTEM_NAME); + } + + fn read_isize_write_f64_res( + completed_systems: Res, + _isize_res: Res, + _f64_res: ResMut, + ) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); + assert!(!completed_systems.contains(WRITE_F64_RES_SYSTEM_NAME)); + completed_systems.insert(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME); + } + + fn write_f64_res(completed_systems: Res, _f64_res: ResMut) { + let mut completed_systems = completed_systems.completed_systems.lock(); + assert!(completed_systems.contains(READ_F64_RES_SYSTEM_NAME)); + assert!(completed_systems.contains(READ_ISIZE_WRITE_F64_RES_SYSTEM_NAME)); + completed_systems.insert(WRITE_F64_RES_SYSTEM_NAME); + } + + schedule.add_system_to_stage("C", read_f64_res.system()); + schedule.add_system_to_stage("C", read_isize_res.system()); + schedule.add_system_to_stage("C", read_isize_write_f64_res.system()); + schedule.add_system_to_stage("C", write_f64_res.system()); + + fn run_executor_and_validate( + executor: &mut ParallelExecutor, + schedule: &mut Schedule, + world: &mut World, + resources: &mut Resources, + ) { + executor.run(schedule, world, resources); + + assert_eq!( + executor.stages[0].system_dependents, + vec![vec![], vec![], vec![3], vec![]] + ); + assert_eq!( + executor.stages[1].system_dependents, + vec![vec![1], vec![2], vec![]] + ); + assert_eq!( + executor.stages[2].system_dependents, + vec![vec![2, 3], vec![], vec![3], vec![]] + ); + + let stage_0_len = executor.stages[0].system_dependencies.len(); + let mut read_u64_deps = FixedBitSet::with_capacity(stage_0_len); + read_u64_deps.insert(2); + + assert_eq!( + executor.stages[0].system_dependencies, + vec![ + FixedBitSet::with_capacity(stage_0_len), + FixedBitSet::with_capacity(stage_0_len), + FixedBitSet::with_capacity(stage_0_len), + read_u64_deps, + ] + ); + + let stage_1_len = executor.stages[1].system_dependencies.len(); + let mut thread_local_deps = FixedBitSet::with_capacity(stage_1_len); + thread_local_deps.insert(0); + let mut write_f64_deps = FixedBitSet::with_capacity(stage_1_len); + write_f64_deps.insert(1); + assert_eq!( + executor.stages[1].system_dependencies, + vec![ + FixedBitSet::with_capacity(stage_1_len), + thread_local_deps, + write_f64_deps + ] + ); + + let stage_2_len = executor.stages[2].system_dependencies.len(); + let mut read_isize_write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); + read_isize_write_f64_res_deps.insert(0); + let mut write_f64_res_deps = FixedBitSet::with_capacity(stage_2_len); + write_f64_res_deps.insert(0); + write_f64_res_deps.insert(2); + assert_eq!( + executor.stages[2].system_dependencies, + vec![ + FixedBitSet::with_capacity(stage_2_len), + FixedBitSet::with_capacity(stage_2_len), + read_isize_write_f64_res_deps, + write_f64_res_deps + ] + ); + + let completed_systems = resources.get::().unwrap(); + assert_eq!( + completed_systems.completed_systems.lock().len(), + 11, + "completed_systems should have been incremented once for each system" + ); + } + + // Stress test the "clean start" case + for _ in 0..1000 { + let mut executor = ParallelExecutor::default(); + run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); + resources + .get::() + .unwrap() + .completed_systems + .lock() + .clear(); + } + + // Stress test the "continue running" case + let mut executor = ParallelExecutor::default(); + run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); + for _ in 0..1000 { + // run again (with completed_systems reset) to ensure executor works correctly across runs + resources + .get::() + .unwrap() + .completed_systems + .lock() + .clear(); + run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources); + } + } +} diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index a16c90a665196..2fadc6a494890 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -268,15 +268,16 @@ impl_into_system!((A, B, C, D, E, F, G)); impl_into_system!((A, B, C, D, E, F, G, H)); impl_into_system!((A, B, C, D, E, F, G, H, I)); impl_into_system!((A, B, C, D, E, F, G, H, I, J)); +impl_into_system!((A, B, C, D, E, F, G, H, I, J, K)); +impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L)); +impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M)); +impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)); +impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)); #[cfg(test)] mod tests { use super::{IntoSystem, Query}; - use crate::{ - resource::{ResMut, Resources}, - schedule::Schedule, - ChangedRes, QuerySet, - }; + use crate::{ChangedRes, QuerySet, System, resource::{ResMut, Resources}, schedule::Schedule}; use bevy_hecs::{Entity, With, World}; #[derive(Debug, Eq, PartialEq)] @@ -339,11 +340,7 @@ mod tests { world.spawn((A, C)); world.spawn((A, D)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", query_system.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, query_system.system()); assert!(*resources.get::().unwrap(), "system ran"); } @@ -376,11 +373,7 @@ mod tests { resources.insert(false); world.spawn((A, B)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", query_system.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, query_system.system()); assert!(*resources.get::().unwrap(), "system ran"); } @@ -401,6 +394,7 @@ mod tests { let mut schedule = Schedule::default(); schedule.add_stage("update"); schedule.add_system_to_stage("update", incr_e_on_flip.system()); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); assert_eq!(*(world.get::(ent).unwrap()), 1); @@ -422,11 +416,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -438,11 +428,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -453,11 +439,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -469,11 +451,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -484,11 +462,15 @@ mod tests { let mut world = World::default(); let mut resources = Resources::default(); world.spawn((A,)); + run_system(&mut world, &mut resources, sys.system()); + } + fn run_system(world: &mut World, resources: &mut Resources, system: Box) { let mut schedule = Schedule::default(); schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); + schedule.add_system_to_stage("update", system); - schedule.run(&mut world, &mut resources); + schedule.initialize(world, resources); + schedule.run(world, resources); } } diff --git a/crates/bevy_ecs/src/system/into_thread_local.rs b/crates/bevy_ecs/src/system/into_thread_local.rs index b83e4df720b9e..f029af89b3ef0 100644 --- a/crates/bevy_ecs/src/system/into_thread_local.rs +++ b/crates/bevy_ecs/src/system/into_thread_local.rs @@ -8,41 +8,26 @@ use bevy_hecs::{ArchetypeComponent, World}; use std::{any::TypeId, borrow::Cow}; #[derive(Debug)] -pub(crate) struct SystemFn +pub(crate) struct ThreadLocalSystemFn where - F: FnMut(&World, &Resources, &mut State) + Send + Sync, - ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, - State: Send + Sync, + Func: FnMut(&mut World, &mut Resources) + Send + Sync, { - pub state: State, - pub func: F, - pub thread_local_func: ThreadLocalF, - pub init_func: Init, - pub thread_local_execution: ThreadLocalExecution, + pub func: Func, pub resource_access: TypeAccess, + pub archetype_component_access: TypeAccess, pub name: Cow<'static, str>, pub id: SystemId, - pub archetype_component_access: TypeAccess, - pub update_func: Update, } -impl System for SystemFn +impl System for ThreadLocalSystemFn where - F: FnMut(&World, &Resources, &mut State) + Send + Sync, - ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, - State: Send + Sync, + Func: FnMut(&mut World, &mut Resources) + Send + Sync, { fn name(&self) -> Cow<'static, str> { self.name.clone() } - fn update(&mut self, world: &World) { - (self.update_func)(world, &mut self.archetype_component_access, &mut self.state); - } + fn update(&mut self, _world: &World) {} fn archetype_component_access(&self) -> &TypeAccess { &self.archetype_component_access @@ -53,29 +38,23 @@ where } fn thread_local_execution(&self) -> ThreadLocalExecution { - self.thread_local_execution + ThreadLocalExecution::Immediate } - #[inline] - fn run(&mut self, world: &World, resources: &Resources) { - (self.func)(world, resources, &mut self.state); - } + fn run(&mut self, _world: &World, _resources: &Resources) {} fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { - (self.thread_local_func)(world, resources, &mut self.state); + (self.func)(world, resources); } - fn initialize(&mut self, world: &mut World, resources: &mut Resources) { - (self.init_func)(world, resources, &mut self.state); - } + fn initialize(&mut self, _world: &mut World, _resources: &mut Resources) {} fn id(&self) -> SystemId { self.id } fn is_initialized(&self) -> bool { - // TODO: either make this correct or remove everything in this file :) - false + true } } @@ -86,18 +65,11 @@ pub trait IntoThreadLocalSystem { impl IntoThreadLocalSystem for F where - F: ThreadLocalSystemFn, + F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, { fn thread_local_system(mut self) -> Box { - Box::new(SystemFn { - state: (), - thread_local_func: move |world, resources, _| { - self.run(world, resources); - }, - func: |_, _, _| {}, - init_func: |_, _, _| {}, - update_func: |_, _, _| {}, - thread_local_execution: ThreadLocalExecution::Immediate, + Box::new(ThreadLocalSystemFn { + func: move |world, resources| (self)(world, resources), name: core::any::type_name::().into(), id: SystemId::new(), resource_access: TypeAccess::default(), @@ -105,17 +77,3 @@ where }) } } - -/// A thread local system function -pub trait ThreadLocalSystemFn: Send + Sync + 'static { - fn run(&mut self, world: &mut World, resource: &mut Resources); -} - -impl ThreadLocalSystemFn for F -where - F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, -{ - fn run(&mut self, world: &mut World, resources: &mut Resources) { - self(world, resources); - } -} \ No newline at end of file From a0bf6ba19a95a852cb0b3054632fd1e1ee685867 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 12:42:45 -0800 Subject: [PATCH 03/12] remove unsafe clone --- .../bevy_ecs/src/resource/resource_query.rs | 57 +------------------ crates/bevy_render/src/draw.rs | 15 +---- 2 files changed, 2 insertions(+), 70 deletions(-) diff --git a/crates/bevy_ecs/src/resource/resource_query.rs b/crates/bevy_ecs/src/resource/resource_query.rs index 84318299bd3d6..1d2f3ddaa8d1e 100644 --- a/crates/bevy_ecs/src/resource/resource_query.rs +++ b/crates/bevy_ecs/src/resource/resource_query.rs @@ -28,12 +28,6 @@ impl<'a, T: Resource> ChangedRes<'a, T> { } } -impl<'a, T: Resource> UnsafeClone for ChangedRes<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { value: self.value } - } -} - unsafe impl Send for ChangedRes<'_, T> {} unsafe impl Sync for ChangedRes<'_, T> {} @@ -63,18 +57,6 @@ impl<'a, T: Resource> Res<'a, T> { } } -/// A clone that is unsafe to perform. You probably shouldn't use this. -pub trait UnsafeClone { - #[allow(clippy::missing_safety_doc)] - unsafe fn unsafe_clone(&self) -> Self; -} - -impl<'a, T: Resource> UnsafeClone for Res<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { value: self.value } - } -} - unsafe impl Send for Res<'_, T> {} unsafe impl Sync for Res<'_, T> {} @@ -128,16 +110,6 @@ impl<'a, T: Resource> DerefMut for ResMut<'a, T> { } } -impl<'a, T: Resource> UnsafeClone for ResMut<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { - value: self.value, - mutated: self.mutated, - _marker: Default::default(), - } - } -} - /// Local resources are unique per-system. Two instances of the same system will each have their own resource. /// Local resources are automatically initialized using the FromResources trait. #[derive(Debug)] @@ -146,15 +118,6 @@ pub struct Local<'a, T: Resource + FromResources> { _marker: PhantomData<&'a T>, } -impl<'a, T: Resource + FromResources> UnsafeClone for Local<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { - value: self.value, - _marker: Default::default(), - } - } -} - impl<'a, T: Resource + FromResources> Deref for Local<'a, T> { type Target = T; @@ -179,7 +142,7 @@ pub trait ResourceQuery { /// Streaming iterators over contiguous homogeneous ranges of resources pub trait FetchResource<'a>: Sized { /// Type of value to be fetched - type Item: UnsafeClone; + type Item; fn access() -> TypeAccess; fn borrow(resources: &Resources); @@ -390,15 +353,6 @@ macro_rules! tuple_impl { $($name::initialize(resources, system_id);)* } } - - #[allow(unused_variables)] - #[allow(non_snake_case)] - impl<$($name: UnsafeClone),*> UnsafeClone for ($($name,)*) { - unsafe fn unsafe_clone(&self) -> Self { - let ($($name,)*) = self; - ($($name.unsafe_clone(),)*) - } - } }; } @@ -452,15 +406,6 @@ macro_rules! tuple_impl_or { } } - #[allow(unused_variables)] - #[allow(non_snake_case)] - impl<$($name: UnsafeClone),*> UnsafeClone for OrRes<($($name,)*)> { - unsafe fn unsafe_clone(&self) -> Self { - let OrRes(($($name,)*)) = self; - OrRes(($($name.unsafe_clone(),)*)) - } - } - impl<$($name,)*> Deref for OrRes<($($name,)*)> { type Target = ($($name,)*); diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 99a9f710bd71b..633ac755468fd 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -12,7 +12,7 @@ use crate::{ use bevy_asset::{Assets, Handle}; use bevy_ecs::{ FetchResource, Query, Res, ResMut, ResourceIndex, ResourceQuery, Resources, SystemId, - TypeAccess, UnsafeClone, + TypeAccess, }; use bevy_property::Properties; use std::{any::TypeId, ops::Range, sync::Arc}; @@ -135,19 +135,6 @@ pub struct DrawContext<'a> { pub current_pipeline: Option>, } -impl<'a> UnsafeClone for DrawContext<'a> { - unsafe fn unsafe_clone(&self) -> Self { - Self { - pipelines: self.pipelines.unsafe_clone(), - shaders: self.shaders.unsafe_clone(), - pipeline_compiler: self.pipeline_compiler.unsafe_clone(), - render_resource_context: self.render_resource_context.unsafe_clone(), - shared_buffers: self.shared_buffers.unsafe_clone(), - current_pipeline: self.current_pipeline.clone(), - } - } -} - impl<'a> ResourceQuery for DrawContext<'a> { type Fetch = FetchDrawContext; } From c09229fc2b4f971dfa59ea2398a33658a0de0a16 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 15:32:46 -0800 Subject: [PATCH 04/12] SystemParam derive, remove resourcequery, reorganize --- crates/bevy_ecs/hecs/macros/src/lib.rs | 93 ++++ crates/bevy_ecs/hecs/src/lib.rs | 2 +- crates/bevy_ecs/src/lib.rs | 2 +- .../bevy_ecs/src/resource/resource_query.rs | 397 +++--------------- crates/bevy_ecs/src/resource/resources.rs | 24 -- crates/bevy_ecs/src/system/into_system.rs | 125 +----- crates/bevy_ecs/src/system/mod.rs | 2 + crates/bevy_ecs/src/system/query/mod.rs | 4 +- crates/bevy_ecs/src/system/system_param.rs | 154 +++++++ crates/bevy_render/src/draw.rs | 78 +--- 10 files changed, 334 insertions(+), 547 deletions(-) create mode 100644 crates/bevy_ecs/src/system/system_param.rs diff --git a/crates/bevy_ecs/hecs/macros/src/lib.rs b/crates/bevy_ecs/hecs/macros/src/lib.rs index 021c5f060bb1c..f5344d7103d78 100644 --- a/crates/bevy_ecs/hecs/macros/src/lib.rs +++ b/crates/bevy_ecs/hecs/macros/src/lib.rs @@ -23,6 +23,10 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro_crate::crate_name; use quote::quote; use syn::{parse_macro_input, DeriveInput, Error, Ident, Index, Lifetime, Path, Result}; +use syn::{ + parse::ParseStream, parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Ident, + Index, Lifetime, Path, +}; /// Implement `Bundle` for a monomorphic struct /// @@ -332,3 +336,92 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { tokens } + +#[derive(Default)] +struct SystemParamFieldAttributes { + pub ignore: bool, +} + +static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param"; + +#[proc_macro_derive(SystemParam, attributes(system_param))] +pub fn derive_system_param(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let fields = match &ast.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => &fields.named, + _ => panic!("expected a struct with named fields"), + }; + + let path_str = if crate_name("bevy").is_ok() { + "bevy::ecs" + } else { + "bevy_ecs" + }; + let path: Path = syn::parse(path_str.parse::().unwrap()).unwrap(); + + let field_attributes = fields + .iter() + .map(|field| { + ( + field, + field + .attrs + .iter() + .find(|a| *a.path.get_ident().as_ref().unwrap() == SYSTEM_PARAM_ATTRIBUTE_NAME) + .map_or_else(SystemParamFieldAttributes::default, |a| { + syn::custom_keyword!(ignore); + let mut attributes = SystemParamFieldAttributes::default(); + a.parse_args_with(|input: ParseStream| { + if input.parse::>()?.is_some() { + attributes.ignore = true; + } + Ok(()) + }) + .expect("invalid 'render_resources' attribute format"); + + attributes + }), + ) + }) + .collect::>(); + let mut fields = Vec::new(); + let mut field_types = Vec::new(); + let mut ignored_fields = Vec::new(); + let mut ignored_field_types = Vec::new(); + for (field, attrs) in field_attributes.iter() { + if attrs.ignore { + ignored_fields.push(field.ident.as_ref().unwrap()); + ignored_field_types.push(&field.ty); + } else { + fields.push(field.ident.as_ref().unwrap()); + field_types.push(&field.ty); + } + } + + let generics = ast.generics; + let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); + + let struct_name = &ast.ident; + + TokenStream::from(quote! { + impl #impl_generics #path::SystemParam for #struct_name#ty_generics { + fn init(system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) { + #(<#field_types>::init(system_state, world, resources);)* + } + + unsafe fn get_param( + system_state: &mut #path::SystemState, + world: &#path::World, + resources: &#path::Resources, + ) -> Self { + #struct_name { + #(#fields: <#field_types>::get_param(system_state, world, resources),)* + #(#ignored_fields: <#ignored_field_types>::default(),)* + } + } + } + }) +} diff --git a/crates/bevy_ecs/hecs/src/lib.rs b/crates/bevy_ecs/hecs/src/lib.rs index a432f98de59b8..d72b2506c849b 100644 --- a/crates/bevy_ecs/hecs/src/lib.rs +++ b/crates/bevy_ecs/hecs/src/lib.rs @@ -98,4 +98,4 @@ pub use lazy_static; pub use query::Fetch; #[cfg(feature = "macros")] -pub use bevy_hecs_macros::{impl_query_set, Bundle}; +pub use bevy_hecs_macros::{impl_query_set, Bundle, SystemParam}; diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 0e73ef4c28cf5..b48f77be5bbaa 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -11,7 +11,7 @@ pub use world::*; pub mod prelude { pub use crate::{ - resource::{ChangedRes, FromResources, Local, OrRes, Res, ResMut, Resource, Resources}, + resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources}, system::{Commands, IntoSystem, IntoThreadLocalSystem, Query, System}, world::WorldBuilderSource, Added, Bundle, Changed, Component, Entity, Mut, Mutated, Or, QuerySet, Ref, RefMut, With, diff --git a/crates/bevy_ecs/src/resource/resource_query.rs b/crates/bevy_ecs/src/resource/resource_query.rs index 1d2f3ddaa8d1e..c6e3c4bf61372 100644 --- a/crates/bevy_ecs/src/resource/resource_query.rs +++ b/crates/bevy_ecs/src/resource/resource_query.rs @@ -1,11 +1,10 @@ -use super::{FromResources, Resources}; -use crate::{system::SystemId, Resource, ResourceIndex}; -use bevy_hecs::{smaller_tuples_too, TypeAccess}; +use super::FromResources; +use crate::{Resource, ResourceIndex, Resources, SystemId}; use core::{ ops::{Deref, DerefMut}, ptr::NonNull, }; -use std::{any::TypeId, marker::PhantomData}; +use std::marker::PhantomData; // TODO: align TypeAccess api with Query::Fetch @@ -28,9 +27,6 @@ impl<'a, T: Resource> ChangedRes<'a, T> { } } -unsafe impl Send for ChangedRes<'_, T> {} -unsafe impl Sync for ChangedRes<'_, T> {} - impl<'a, T: Resource> Deref for ChangedRes<'a, T> { type Target = T; @@ -57,9 +53,6 @@ impl<'a, T: Resource> Res<'a, T> { } } -unsafe impl Send for Res<'_, T> {} -unsafe impl Sync for Res<'_, T> {} - impl<'a, T: Resource> Deref for Res<'a, T> { type Target = T; @@ -90,9 +83,6 @@ impl<'a, T: Resource> ResMut<'a, T> { } } -unsafe impl Send for ResMut<'_, T> {} -unsafe impl Sync for ResMut<'_, T> {} - impl<'a, T: Resource> Deref for ResMut<'a, T> { type Target = T; @@ -118,177 +108,8 @@ pub struct Local<'a, T: Resource + FromResources> { _marker: PhantomData<&'a T>, } -impl<'a, T: Resource + FromResources> Deref for Local<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.value } - } -} - -impl<'a, T: Resource + FromResources> DerefMut for Local<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.value } - } -} - -/// A collection of resource types fetch from a `Resources` collection -pub trait ResourceQuery { - type Fetch: for<'a> FetchResource<'a>; - - fn initialize(_resources: &mut Resources, _system_id: Option) {} -} - -/// Streaming iterators over contiguous homogeneous ranges of resources -pub trait FetchResource<'a>: Sized { - /// Type of value to be fetched - type Item; - - fn access() -> TypeAccess; - fn borrow(resources: &Resources); - fn release(resources: &Resources); - - #[allow(clippy::missing_safety_doc)] - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item; - - #[allow(clippy::missing_safety_doc)] - unsafe fn is_some(_resources: &'a Resources, _system_id: Option) -> bool { - true - } -} - -pub trait ResourceFetchSelf { - #[allow(clippy::missing_safety_doc)] - unsafe fn get(resources: &Resources, system_id: Option) -> Self; -} - -impl<'a, Q: ResourceQuery, F: FetchResource<'a, Item=Q>> ResourceFetchSelf for Q { - unsafe fn get(resources: &Resources, system_id: Option) -> Q { - let resources: &'a Resources = std::mem::transmute(resources); - >::get(resources, system_id) - } -} - -impl<'a, T: Resource> ResourceQuery for Res<'a, T> { - type Fetch = FetchResourceRead; -} - -/// Fetches a shared resource reference -#[derive(Debug)] -pub struct FetchResourceRead(NonNull); - -impl<'a, T: Resource> FetchResource<'a> for FetchResourceRead { - type Item = Res<'a, T>; - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - Res::new(resources.get_unsafe_ref::(ResourceIndex::Global)) - } - - fn borrow(resources: &Resources) { - resources.borrow::(); - } - - fn release(resources: &Resources) { - resources.release::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_read(TypeId::of::()); - access - } -} - -impl<'a, T: Resource> ResourceQuery for ChangedRes<'a, T> { - type Fetch = FetchResourceChanged; -} - -/// Fetches a shared resource reference -#[derive(Debug)] -pub struct FetchResourceChanged(NonNull); - -impl<'a, T: Resource> FetchResource<'a> for FetchResourceChanged { - type Item = ChangedRes<'a, T>; - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - ChangedRes::new(resources.get_unsafe_ref::(ResourceIndex::Global)) - } - - unsafe fn is_some(resources: &'a Resources, _system_id: Option) -> bool { - let (added, mutated) = resources.get_unsafe_added_and_mutated::(ResourceIndex::Global); - *added.as_ptr() || *mutated.as_ptr() - } - - fn borrow(resources: &Resources) { - resources.borrow::(); - } - - fn release(resources: &Resources) { - resources.release::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_read(TypeId::of::()); - access - } -} - -impl<'a, T: Resource> ResourceQuery for ResMut<'a, T> { - type Fetch = FetchResourceWrite; -} - -/// Fetches a unique resource reference -#[derive(Debug)] -pub struct FetchResourceWrite(NonNull); - -impl<'a, T: Resource> FetchResource<'a> for FetchResourceWrite { - type Item = ResMut<'a, T>; - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - let (value, type_state) = - resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) - } - - fn borrow(resources: &Resources) { - resources.borrow_mut::(); - } - - fn release(resources: &Resources) { - resources.release_mut::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_write(TypeId::of::()); - access - } -} - -impl<'a, T: Resource + FromResources> ResourceQuery for Local<'a, T> { - type Fetch = FetchResourceLocalMut; - - fn initialize(resources: &mut Resources, id: Option) { - let id = id.expect("Local resources can only be used by systems"); - - // Only add the local resource if it doesn't already exist for this system - if resources.get_local::(id).is_none() { - let value = T::from_resources(resources); - resources.insert_local(id, value); - } - } -} - -/// Fetches a `Local` resource reference -#[derive(Debug)] -pub struct FetchResourceLocalMut(NonNull); - -impl<'a, T: Resource + FromResources> FetchResource<'a> for FetchResourceLocalMut { - type Item = Local<'a, T>; - - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item { - let id = system_id.expect("Local resources can only be used by systems"); +impl<'a, T: Resource + FromResources> Local<'a, T> { + pub(crate) unsafe fn new(resources: &Resources, id: SystemId) -> Self { Local { value: resources .get_unsafe_ref::(ResourceIndex::System(id)) @@ -296,167 +117,61 @@ impl<'a, T: Resource + FromResources> FetchResource<'a> for FetchResourceLocalMu _marker: Default::default(), } } - - fn borrow(resources: &Resources) { - resources.borrow_mut::(); - } - - fn release(resources: &Resources) { - resources.release_mut::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_write(TypeId::of::()); - access - } } -macro_rules! tuple_impl { - ($($name: ident),*) => { - impl<'a, $($name: FetchResource<'a>),*> FetchResource<'a> for ($($name,)*) { - type Item = ($($name::Item,)*); - - #[allow(unused_variables)] - fn borrow(resources: &Resources) { - $($name::borrow(resources);)* - } - - #[allow(unused_variables)] - fn release(resources: &Resources) { - $($name::release(resources);)* - } - - #[allow(unused_variables)] - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item { - ($($name::get(resources, system_id),)*) - } - - #[allow(unused_variables)] - unsafe fn is_some(resources: &'a Resources, system_id: Option) -> bool { - true $(&& $name::is_some(resources, system_id))* - } - - #[allow(unused_mut)] - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - $(access.union(&$name::access());)* - access - } - } - - impl<$($name: ResourceQuery),*> ResourceQuery for ($($name,)*) { - type Fetch = ($($name::Fetch,)*); - - #[allow(unused_variables)] - fn initialize(resources: &mut Resources, system_id: Option) { - $($name::initialize(resources, system_id);)* - } - } - }; -} - -smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A); - -#[derive(Debug)] -pub struct OrRes(T); - -#[derive(Debug)] -pub struct FetchResourceOr(NonNull); - -macro_rules! tuple_impl_or { - ($($name: ident),*) => { - impl<'a, $($name: FetchResource<'a>),*> FetchResource<'a> for FetchResourceOr<($($name,)*)> { - type Item = OrRes<($($name::Item,)*)>; - - #[allow(unused_variables)] - fn borrow(resources: &Resources) { - $($name::borrow(resources);)* - } - - #[allow(unused_variables)] - fn release(resources: &Resources) { - $($name::release(resources);)* - } - - #[allow(unused_variables)] - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item { - OrRes(($($name::get(resources, system_id),)*)) - } - - #[allow(unused_variables)] - unsafe fn is_some(resources: &'a Resources, system_id: Option) -> bool { - false $(|| $name::is_some(resources, system_id))* - } - - #[allow(unused_mut)] - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - $(access.union(&$name::access());)* - access - } - } - - impl<$($name: ResourceQuery),*> ResourceQuery for OrRes<($($name,)*)> { - type Fetch = FetchResourceOr<($($name::Fetch,)*)>; - - #[allow(unused_variables)] - fn initialize(resources: &mut Resources, system_id: Option) { - $($name::initialize(resources, system_id);)* - } - } - - impl<$($name,)*> Deref for OrRes<($($name,)*)> { - type Target = ($($name,)*); - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - }; -} - -smaller_tuples_too!(tuple_impl_or, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A); - -#[cfg(test)] -mod tests { - use super::*; +impl<'a, T: Resource + FromResources> Deref for Local<'a, T> { + type Target = T; - #[test] - fn changed_resource() { - let mut resources = Resources::default(); - resources.insert(123); - assert_eq!( - resources.query::>().as_deref(), - Some(&(123 as i32)) - ); - resources.clear_trackers(); - assert_eq!(resources.query::>().as_deref(), None); - *resources.query::>().unwrap() += 1; - assert_eq!( - resources.query::>().as_deref(), - Some(&(124 as i32)) - ); + fn deref(&self) -> &T { + unsafe { &*self.value } } +} - #[test] - fn or_changed_resource() { - let mut resources = Resources::default(); - resources.insert(123); - resources.insert(0.2); - assert!(resources - .query::, ChangedRes)>>() - .is_some(),); - resources.clear_trackers(); - assert!(resources - .query::, ChangedRes)>>() - .is_none(),); - *resources.query::>().unwrap() += 1; - assert!(resources - .query::, ChangedRes)>>() - .is_some(),); - assert!(resources - .query::<(ChangedRes, ChangedRes)>() - .is_none(),); +impl<'a, T: Resource + FromResources> DerefMut for Local<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.value } } } + +// #[cfg(test)] +// mod tests { +// use super::*; + +// #[test] +// fn changed_resource() { +// let mut resources = Resources::default(); +// resources.insert(123); +// assert_eq!( +// resources.query::>().as_deref(), +// Some(&(123 as i32)) +// ); +// resources.clear_trackers(); +// assert_eq!(resources.query::>().as_deref(), None); +// *resources.query::>().unwrap() += 1; +// assert_eq!( +// resources.query::>().as_deref(), +// Some(&(124 as i32)) +// ); +// } + +// #[test] +// fn or_changed_resource() { +// let mut resources = Resources::default(); +// resources.insert(123); +// resources.insert(0.2); +// assert!(resources +// .query::, ChangedRes)>>() +// .is_some(),); +// resources.clear_trackers(); +// assert!(resources +// .query::, ChangedRes)>>() +// .is_none(),); +// *resources.query::>().unwrap() += 1; +// assert!(resources +// .query::, ChangedRes)>>() +// .is_some(),); +// assert!(resources +// .query::<(ChangedRes, ChangedRes)>() +// .is_none(),); +// } +// } diff --git a/crates/bevy_ecs/src/resource/resources.rs b/crates/bevy_ecs/src/resource/resources.rs index a912599e16503..fc0c9c5a6bcf3 100644 --- a/crates/bevy_ecs/src/resource/resources.rs +++ b/crates/bevy_ecs/src/resource/resources.rs @@ -1,4 +1,3 @@ -use super::{FetchResource, ResourceQuery}; use crate::system::SystemId; use bevy_hecs::{Archetype, AtomicBorrow, Entity, Ref, RefMut, TypeInfo, TypeState}; use bevy_utils::HashMap; @@ -264,29 +263,6 @@ impl Resources { }) } - pub fn query(&self) -> Option<::Item> { - unsafe { - if Q::Fetch::is_some(&self, None) { - Some(Q::Fetch::get(&self, None)) - } else { - None - } - } - } - - pub fn query_system( - &self, - id: SystemId, - ) -> Option<::Item> { - unsafe { - if Q::Fetch::is_some(&self, Some(id)) { - Some(Q::Fetch::get(&self, Some(id))) - } else { - None - } - } - } - #[inline] #[allow(clippy::missing_safety_doc)] pub unsafe fn get_unsafe_ref(&self, resource_index: ResourceIndex) -> NonNull { diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 2fadc6a494890..bc597c544ae2c 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,21 +1,18 @@ -use crate::{ - Commands, FetchResource, Query, QuerySet, QueryTuple, ResourceFetchSelf, ResourceQuery, - Resources, System, SystemId, ThreadLocalExecution, -}; -use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, TypeAccess, World}; +use crate::{Commands, Resources, System, SystemId, SystemParam, ThreadLocalExecution}; +use bevy_hecs::{ArchetypeComponent, QueryAccess, TypeAccess, World}; use std::{any::TypeId, borrow::Cow}; pub struct SystemState { - id: SystemId, - name: Cow<'static, str>, - is_initialized: bool, - archetype_component_access: TypeAccess, - resource_access: TypeAccess, - query_archetype_component_accesses: Vec>, - query_accesses: Vec>, - query_type_names: Vec<&'static str>, - commands: Commands, - current_query_index: usize, + pub(crate) id: SystemId, + pub(crate) name: Cow<'static, str>, + pub(crate) is_initialized: bool, + pub(crate) archetype_component_access: TypeAccess, + pub(crate) resource_access: TypeAccess, + pub(crate) query_archetype_component_accesses: Vec>, + pub(crate) query_accesses: Vec>, + pub(crate) query_type_names: Vec<&'static str>, + pub(crate) commands: Commands, + pub(crate) current_query_index: usize, } impl SystemState { @@ -131,91 +128,6 @@ where } } -pub trait SystemParam { - fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources); - fn get_param(system_state: &mut SystemState, world: &World, resources: &Resources) -> Self; -} - -impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { - #[inline] - fn get_param(system_state: &mut SystemState, world: &World, _resources: &Resources) -> Self { - let query_index = system_state.current_query_index; - unsafe { - let world: &'a World = std::mem::transmute(world); - let archetype_component_access: &'a TypeAccess = - std::mem::transmute(&system_state.query_archetype_component_accesses[query_index]); - system_state.current_query_index += 1; - Query::new(world, archetype_component_access) - } - } - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - system_state - .query_archetype_component_accesses - .push(TypeAccess::default()); - system_state - .query_accesses - .push(vec![::access()]); - system_state - .query_type_names - .push(std::any::type_name::()); - } -} - -impl SystemParam for QuerySet { - #[inline] - fn get_param(system_state: &mut SystemState, world: &World, _resources: &Resources) -> Self { - let query_index = system_state.current_query_index; - system_state.current_query_index += 1; - unsafe { - QuerySet::new( - world, - &system_state.query_archetype_component_accesses[query_index], - ) - } - } - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - system_state - .query_archetype_component_accesses - .push(TypeAccess::default()); - system_state.query_accesses.push(T::get_accesses()); - system_state - .query_type_names - .push(std::any::type_name::()); - } -} - -impl SystemParam for R -where - R: ResourceQuery + ResourceFetchSelf, -{ - fn init(system_state: &mut SystemState, _world: &World, resources: &mut Resources) { - R::initialize(resources, Some(system_state.id)); - system_state - .resource_access - .union(&::access()); - } - - #[inline] - fn get_param(system_state: &mut SystemState, _world: &World, resources: &Resources) -> Self { - unsafe { ::get(resources, Some(system_state.id)) } - } -} - -impl SystemParam for Commands { - fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { - system_state - .commands - .set_entity_reserver(world.get_entity_reserver()) - } - - #[inline] - fn get_param(system_state: &mut SystemState, _world: &World, _resources: &Resources) -> Self { - system_state.commands.clone() - } -} - pub trait IntoSystem { fn system(self) -> Box; } @@ -226,6 +138,7 @@ macro_rules! impl_into_system { where Func: FnMut($($param),*) + Send + Sync + 'static, { #[allow(unused_variables)] + #[allow(unused_unsafe)] fn system(mut self) -> Box { Box::new(FuncSystem { state: SystemState { @@ -242,7 +155,9 @@ macro_rules! impl_into_system { }, func: move |state, world, resources| { state.reset_indices(); - self($($param::get_param(state, world, resources)),*); + unsafe { + self($($param::get_param(state, world, resources)),*); + } }, thread_local_func: |state, world, resources| { state.commands.apply(world, resources); @@ -276,8 +191,12 @@ impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)); #[cfg(test)] mod tests { - use super::{IntoSystem, Query}; - use crate::{ChangedRes, QuerySet, System, resource::{ResMut, Resources}, schedule::Schedule}; + use super::IntoSystem; + use crate::{ + resource::{ResMut, Resources}, + schedule::Schedule, + ChangedRes, Query, QuerySet, System, + }; use bevy_hecs::{Entity, With, World}; #[derive(Debug, Eq, PartialEq)] diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 975580b390bb6..a1c7b001c49f6 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -6,6 +6,7 @@ mod profiler; mod query; #[allow(clippy::module_inception)] mod system; +mod system_param; pub use commands::*; pub use into_system::*; @@ -14,3 +15,4 @@ pub use into_thread_local::*; pub use profiler::*; pub use query::*; pub use system::*; +pub use system_param::*; diff --git a/crates/bevy_ecs/src/system/query/mod.rs b/crates/bevy_ecs/src/system/query/mod.rs index f7494ad3d8e64..396b3ac5dbfe2 100644 --- a/crates/bevy_ecs/src/system/query/mod.rs +++ b/crates/bevy_ecs/src/system/query/mod.rs @@ -28,7 +28,7 @@ pub enum QueryError { impl<'a, Q: HecsQuery> Query<'a, Q> { #[inline] - pub fn new(world: &'a World, component_access: &'a TypeAccess) -> Self { + pub unsafe fn new(world: &'a World, component_access: &'a TypeAccess) -> Self { Self { world, component_access, @@ -59,7 +59,6 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { self.world.query_unchecked() } - #[inline] pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -68,7 +67,6 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) } } - #[inline] pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs new file mode 100644 index 0000000000000..a67c53c5e65c4 --- /dev/null +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -0,0 +1,154 @@ +pub use bevy_hecs::SystemParam; + +use crate::{ + ChangedRes, Commands, FromResources, Local, Query, QuerySet, QueryTuple, Res, ResMut, Resource, + ResourceIndex, Resources, SystemState, +}; +use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, TypeAccess, World}; +use std::any::TypeId; +pub trait SystemParam { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources); + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + resources: &Resources, + ) -> Self; +} + +impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + _resources: &Resources, + ) -> Self { + let query_index = system_state.current_query_index; + let world: &'a World = std::mem::transmute(world); + let archetype_component_access: &'a TypeAccess = + std::mem::transmute(&system_state.query_archetype_component_accesses[query_index]); + system_state.current_query_index += 1; + Query::new(world, archetype_component_access) + } + + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state + .query_accesses + .push(vec![::access()]); + system_state + .query_type_names + .push(std::any::type_name::()); + } +} + +impl SystemParam for QuerySet { + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + _resources: &Resources, + ) -> Self { + let query_index = system_state.current_query_index; + system_state.current_query_index += 1; + QuerySet::new( + world, + &system_state.query_archetype_component_accesses[query_index], + ) + } + + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state.query_accesses.push(T::get_accesses()); + system_state + .query_type_names + .push(std::any::type_name::()); + } +} + +impl SystemParam for Commands { + fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { + system_state + .commands + .set_entity_reserver(world.get_entity_reserver()) + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + _world: &World, + _resources: &Resources, + ) -> Self { + system_state.commands.clone() + } +} + +impl<'a, T: Resource> SystemParam for Res<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state.resource_access.add_read(TypeId::of::()); + } + + unsafe fn get_param( + _system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Self { + Res::new(resources.get_unsafe_ref::(ResourceIndex::Global)) + } +} + +impl<'a, T: Resource> SystemParam for ResMut<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state.resource_access.add_write(TypeId::of::()); + } + + unsafe fn get_param( + _system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Self { + let (value, type_state) = + resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); + ResMut::new(value, type_state.mutated()) + } +} + +impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state.resource_access.add_read(TypeId::of::()); + } + + unsafe fn get_param( + _system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Self { + let (added, mutated) = resources.get_unsafe_added_and_mutated::(ResourceIndex::Global); + if *added.as_ptr() || *mutated.as_ptr() { + ChangedRes::new(resources.get_unsafe_ref::(ResourceIndex::Global)) + } else { + todo!("return option"); + } + } +} + +impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, resources: &mut Resources) { + system_state.resource_access.add_write(TypeId::of::()); + if resources.get_local::(system_state.id).is_none() { + let value = T::from_resources(resources); + resources.insert_local(system_state.id, value); + } + } + + unsafe fn get_param( + system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Self { + Local::new(resources, system_state.id) + } +} diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 633ac755468fd..b61e8d7b49ec1 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -10,12 +10,9 @@ use crate::{ shader::Shader, }; use bevy_asset::{Assets, Handle}; -use bevy_ecs::{ - FetchResource, Query, Res, ResMut, ResourceIndex, ResourceQuery, Resources, SystemId, - TypeAccess, -}; +use bevy_ecs::{Query, Res, ResMut, SystemParam}; use bevy_property::Properties; -use std::{any::TypeId, ops::Range, sync::Arc}; +use std::{ops::Range, sync::Arc}; use thiserror::Error; /// A queued command for the renderer @@ -125,87 +122,20 @@ pub enum DrawError { BufferAllocationFailure, } -//#[derive(Debug)] +#[derive(SystemParam)] pub struct DrawContext<'a> { pub pipelines: ResMut<'a, Assets>, pub shaders: ResMut<'a, Assets>, pub pipeline_compiler: ResMut<'a, PipelineCompiler>, pub render_resource_context: Res<'a, Box>, pub shared_buffers: Res<'a, SharedBuffers>, + #[system_param(ignore)] pub current_pipeline: Option>, } -impl<'a> ResourceQuery for DrawContext<'a> { - type Fetch = FetchDrawContext; -} - #[derive(Debug)] pub struct FetchDrawContext; -// TODO: derive this impl -impl<'a> FetchResource<'a> for FetchDrawContext { - type Item = DrawContext<'a>; - - fn borrow(resources: &Resources) { - resources.borrow_mut::>(); - resources.borrow_mut::>(); - resources.borrow_mut::(); - resources.borrow::>(); - resources.borrow::(); - } - - fn release(resources: &Resources) { - resources.release_mut::>(); - resources.release_mut::>(); - resources.release_mut::(); - resources.release::>(); - resources.release::(); - } - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - let pipelines = { - let (value, type_state) = resources - .get_unsafe_ref_with_type_state::>( - ResourceIndex::Global, - ); - ResMut::new(value, type_state.mutated()) - }; - let shaders = { - let (value, type_state) = - resources.get_unsafe_ref_with_type_state::>(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) - }; - let pipeline_compiler = { - let (value, type_state) = - resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) - }; - - DrawContext { - pipelines, - shaders, - pipeline_compiler, - render_resource_context: Res::new( - resources.get_unsafe_ref::>(ResourceIndex::Global), - ), - shared_buffers: Res::new( - resources.get_unsafe_ref::(ResourceIndex::Global), - ), - current_pipeline: None, - } - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_write(TypeId::of::>()); - access.add_write(TypeId::of::>()); - access.add_write(TypeId::of::()); - access.add_read(TypeId::of::>()); - access.add_read(TypeId::of::()); - access - } -} - impl<'a> DrawContext<'a> { pub fn get_uniform_buffer( &self, From cee1f047a8f302398541e12df8795152349406ce Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 16:12:25 -0800 Subject: [PATCH 05/12] get_params returns options --- crates/bevy_ecs/hecs/macros/src/lib.rs | 8 +-- crates/bevy_ecs/src/system/into_system.rs | 42 +++++++------ crates/bevy_ecs/src/system/system_param.rs | 71 ++++++++++++++++------ 3 files changed, 80 insertions(+), 41 deletions(-) diff --git a/crates/bevy_ecs/hecs/macros/src/lib.rs b/crates/bevy_ecs/hecs/macros/src/lib.rs index f5344d7103d78..c4784766667ab 100644 --- a/crates/bevy_ecs/hecs/macros/src/lib.rs +++ b/crates/bevy_ecs/hecs/macros/src/lib.rs @@ -416,11 +416,11 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { system_state: &mut #path::SystemState, world: &#path::World, resources: &#path::Resources, - ) -> Self { - #struct_name { - #(#fields: <#field_types>::get_param(system_state, world, resources),)* + ) -> Option { + Some(#struct_name { + #(#fields: <#field_types>::get_param(system_state, world, resources)?,)* #(#ignored_fields: <#ignored_field_types>::default(),)* - } + }) } } }) diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index bc597c544ae2c..dda3bd404cce9 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,4 +1,4 @@ -use crate::{Commands, Resources, System, SystemId, SystemParam, ThreadLocalExecution}; +use crate::{Commands, FlattenOptions, Resources, System, SystemId, SystemParam, ThreadLocalExecution}; use bevy_hecs::{ArchetypeComponent, QueryAccess, TypeAccess, World}; use std::{any::TypeId, borrow::Cow}; @@ -133,12 +133,13 @@ pub trait IntoSystem { } macro_rules! impl_into_system { - (($($param: ident),*)) => { + ($($param: ident),*) => { impl IntoSystem<($($param,)*)> for Func where Func: FnMut($($param),*) + Send + Sync + 'static, { #[allow(unused_variables)] #[allow(unused_unsafe)] + #[allow(non_snake_case)] fn system(mut self) -> Box { Box::new(FuncSystem { state: SystemState { @@ -156,7 +157,9 @@ macro_rules! impl_into_system { func: move |state, world, resources| { state.reset_indices(); unsafe { - self($($param::get_param(state, world, resources)),*); + if let Some(($($param,)*)) = ($($param::get_param(state, world, resources),)*).flatten_options() { + self($($param),*); + } } }, thread_local_func: |state, world, resources| { @@ -172,22 +175,23 @@ macro_rules! impl_into_system { }; } -impl_into_system!(()); -impl_into_system!((A)); -impl_into_system!((A, B)); -impl_into_system!((A, B, C)); -impl_into_system!((A, B, C, D)); -impl_into_system!((A, B, C, D, E)); -impl_into_system!((A, B, C, D, E, F)); -impl_into_system!((A, B, C, D, E, F, G)); -impl_into_system!((A, B, C, D, E, F, G, H)); -impl_into_system!((A, B, C, D, E, F, G, H, I)); -impl_into_system!((A, B, C, D, E, F, G, H, I, J)); -impl_into_system!((A, B, C, D, E, F, G, H, I, J, K)); -impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L)); -impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M)); -impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)); -impl_into_system!((A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)); +impl_into_system!(); +impl_into_system!(A); +impl_into_system!(A, B); +impl_into_system!(A, B, C); +impl_into_system!(A, B, C, D); +impl_into_system!(A, B, C, D, E); +impl_into_system!(A, B, C, D, E, F); +impl_into_system!(A, B, C, D, E, F, G); +impl_into_system!(A, B, C, D, E, F, G, H); +impl_into_system!(A, B, C, D, E, F, G, H, I); +impl_into_system!(A, B, C, D, E, F, G, H, I, J); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); #[cfg(test)] mod tests { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index a67c53c5e65c4..26dfcc63f3e51 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -6,13 +6,14 @@ use crate::{ }; use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, TypeAccess, World}; use std::any::TypeId; -pub trait SystemParam { + +pub trait SystemParam: Sized { fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources); unsafe fn get_param( system_state: &mut SystemState, world: &World, resources: &Resources, - ) -> Self; + ) -> Option; } impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { @@ -21,13 +22,13 @@ impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { system_state: &mut SystemState, world: &World, _resources: &Resources, - ) -> Self { + ) -> Option { let query_index = system_state.current_query_index; let world: &'a World = std::mem::transmute(world); let archetype_component_access: &'a TypeAccess = std::mem::transmute(&system_state.query_archetype_component_accesses[query_index]); system_state.current_query_index += 1; - Query::new(world, archetype_component_access) + Some(Query::new(world, archetype_component_access)) } fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { @@ -49,13 +50,13 @@ impl SystemParam for QuerySet { system_state: &mut SystemState, world: &World, _resources: &Resources, - ) -> Self { + ) -> Option { let query_index = system_state.current_query_index; system_state.current_query_index += 1; - QuerySet::new( + Some(QuerySet::new( world, &system_state.query_archetype_component_accesses[query_index], - ) + )) } fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { @@ -81,8 +82,8 @@ impl SystemParam for Commands { system_state: &mut SystemState, _world: &World, _resources: &Resources, - ) -> Self { - system_state.commands.clone() + ) -> Option { + Some(system_state.commands.clone()) } } @@ -95,8 +96,8 @@ impl<'a, T: Resource> SystemParam for Res<'a, T> { _system_state: &mut SystemState, _world: &World, resources: &Resources, - ) -> Self { - Res::new(resources.get_unsafe_ref::(ResourceIndex::Global)) + ) -> Option { + Some(Res::new(resources.get_unsafe_ref::(ResourceIndex::Global))) } } @@ -109,10 +110,10 @@ impl<'a, T: Resource> SystemParam for ResMut<'a, T> { _system_state: &mut SystemState, _world: &World, resources: &Resources, - ) -> Self { + ) -> Option { let (value, type_state) = resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) + Some(ResMut::new(value, type_state.mutated())) } } @@ -125,12 +126,12 @@ impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { _system_state: &mut SystemState, _world: &World, resources: &Resources, - ) -> Self { + ) -> Option { let (added, mutated) = resources.get_unsafe_added_and_mutated::(ResourceIndex::Global); if *added.as_ptr() || *mutated.as_ptr() { - ChangedRes::new(resources.get_unsafe_ref::(ResourceIndex::Global)) + Some(ChangedRes::new(resources.get_unsafe_ref::(ResourceIndex::Global))) } else { - todo!("return option"); + None } } } @@ -148,7 +149,41 @@ impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> { system_state: &mut SystemState, _world: &World, resources: &Resources, - ) -> Self { - Local::new(resources, system_state.id) + ) -> Option { + Some(Local::new(resources, system_state.id)) } } + +pub trait FlattenOptions { + fn flatten_options(self) -> Option; +} + +macro_rules! impl_system_param_tuple { + ($($param: ident),*) => { + #[allow(non_snake_case)] + impl<$($param: SystemParam),*> FlattenOptions<($($param,)*)> for ($(Option<$param>,)*) { + fn flatten_options(self) -> Option<($($param,)*)> { + let ($($param,)*) = self; + Some(($($param?,)*)) + } + } + }; +} + +impl_system_param_tuple!(); +impl_system_param_tuple!(A); +impl_system_param_tuple!(A, B); +impl_system_param_tuple!(A, B, C); +impl_system_param_tuple!(A, B, C, D); +impl_system_param_tuple!(A, B, C, D, E); +impl_system_param_tuple!(A, B, C, D, E, F); +impl_system_param_tuple!(A, B, C, D, E, F, G); +impl_system_param_tuple!(A, B, C, D, E, F, G, H); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); \ No newline at end of file From 6237ca299725c325da39d780acb1280b36de4ca1 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 16:46:52 -0800 Subject: [PATCH 06/12] SystemParam tuples + inlining --- crates/bevy_ecs/hecs/src/world.rs | 11 +++++++++ crates/bevy_ecs/src/system/into_system.rs | 6 +++-- crates/bevy_ecs/src/system/query/mod.rs | 8 +++++++ crates/bevy_ecs/src/system/system_param.rs | 26 ++++++++++++++-------- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/world.rs b/crates/bevy_ecs/hecs/src/world.rs index f2bed53072c6d..8f3768f7ea6da 100644 --- a/crates/bevy_ecs/hecs/src/world.rs +++ b/crates/bevy_ecs/hecs/src/world.rs @@ -249,6 +249,7 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` + #[inline] pub fn query(&self) -> QueryIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -281,6 +282,7 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` + #[inline] pub fn query_mut(&mut self) -> QueryIter<'_, Q> { // SAFE: unique mutable access unsafe { self.query_unchecked() } @@ -288,6 +290,7 @@ impl World { /// Like `query`, but instead of returning a single iterator it returns a "batched iterator", /// where each batch is `batch_size`. This is generally used for parallel iteration. + #[inline] pub fn query_batched(&self, batch_size: usize) -> BatchedIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -298,6 +301,7 @@ impl World { /// Like `query`, but instead of returning a single iterator it returns a "batched iterator", /// where each batch is `batch_size`. This is generally used for parallel iteration. + #[inline] pub fn query_batched_mut(&mut self, batch_size: usize) -> BatchedIter<'_, Q> { // SAFE: unique mutable access unsafe { self.query_batched_unchecked(batch_size) } @@ -316,6 +320,7 @@ impl World { /// # Safety /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. + #[inline] pub unsafe fn query_unchecked(&self) -> QueryIter<'_, Q> { QueryIter::new(&self.archetypes) } @@ -347,6 +352,7 @@ impl World { /// let (number, flag) = world.query_one::<(&i32, &bool)>(a).unwrap(); /// assert_eq!(*number, 123); /// ``` + #[inline] pub fn query_one( &self, entity: Entity, @@ -372,6 +378,7 @@ impl World { /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` + #[inline] pub fn query_one_mut( &mut self, entity: Entity, @@ -387,6 +394,7 @@ impl World { /// # Safety /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. + #[inline] pub unsafe fn query_one_unchecked( &self, entity: Entity, @@ -399,6 +407,7 @@ impl World { } /// Borrow the `T` component of `entity` + #[inline] pub fn get(&self, entity: Entity) -> Result<&'_ T, ComponentError> { unsafe { let loc = self.entities.get(entity)?; @@ -414,6 +423,7 @@ impl World { } /// Mutably borrow the `T` component of `entity` + #[inline] pub fn get_mut(&mut self, entity: Entity) -> Result, ComponentError> { // SAFE: uniquely borrows world unsafe { self.get_mut_unchecked(entity) } @@ -434,6 +444,7 @@ impl World { /// # Safety /// This does not check for mutable access correctness. To be safe, make sure this is the only /// thing accessing this entity's T component. + #[inline] pub unsafe fn get_mut_unchecked( &self, entity: Entity, diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index dda3bd404cce9..42219ea8bfcfb 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,4 +1,6 @@ -use crate::{Commands, FlattenOptions, Resources, System, SystemId, SystemParam, ThreadLocalExecution}; +use crate::{ + Commands, Resources, System, SystemId, SystemParam, ThreadLocalExecution, +}; use bevy_hecs::{ArchetypeComponent, QueryAccess, TypeAccess, World}; use std::{any::TypeId, borrow::Cow}; @@ -157,7 +159,7 @@ macro_rules! impl_into_system { func: move |state, world, resources| { state.reset_indices(); unsafe { - if let Some(($($param,)*)) = ($($param::get_param(state, world, resources),)*).flatten_options() { + if let Some(($($param,)*)) = <($($param,)*)>::get_param(state, world, resources) { self($($param),*); } } diff --git a/crates/bevy_ecs/src/system/query/mod.rs b/crates/bevy_ecs/src/system/query/mod.rs index 396b3ac5dbfe2..e029ca7418a2a 100644 --- a/crates/bevy_ecs/src/system/query/mod.rs +++ b/crates/bevy_ecs/src/system/query/mod.rs @@ -37,6 +37,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Iterates over the query results. This can only be called for read-only queries + #[inline] pub fn iter(&self) -> QueryIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -46,6 +47,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Iterates over the query results + #[inline] pub fn iter_mut(&mut self) -> QueryIter<'_, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { self.world.query_unchecked() } @@ -54,11 +56,13 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// Iterates over the query results /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict self.world.query_unchecked() } + #[inline] pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -67,12 +71,14 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) } } + #[inline] pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) } } /// Gets the query result for the given `entity` + #[inline] pub fn get(&self, entity: Entity) -> Result<::Item, QueryError> where Q::Fetch: ReadOnlyFetch, @@ -86,6 +92,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Gets the query result for the given `entity` + #[inline] pub fn get_mut(&mut self, entity: Entity) -> Result<::Item, QueryError> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { @@ -98,6 +105,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// Gets the query result for the given `entity` /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] pub unsafe fn get_unsafe( &self, entity: Entity, diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 26dfcc63f3e51..641826e4542e1 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -92,6 +92,7 @@ impl<'a, T: Resource> SystemParam for Res<'a, T> { system_state.resource_access.add_read(TypeId::of::()); } + #[inline] unsafe fn get_param( _system_state: &mut SystemState, _world: &World, @@ -106,6 +107,7 @@ impl<'a, T: Resource> SystemParam for ResMut<'a, T> { system_state.resource_access.add_write(TypeId::of::()); } + #[inline] unsafe fn get_param( _system_state: &mut SystemState, _world: &World, @@ -122,6 +124,7 @@ impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { system_state.resource_access.add_read(TypeId::of::()); } + #[inline] unsafe fn get_param( _system_state: &mut SystemState, _world: &World, @@ -145,6 +148,7 @@ impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> { } } + #[inline] unsafe fn get_param( system_state: &mut SystemState, _world: &World, @@ -154,17 +158,21 @@ impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> { } } -pub trait FlattenOptions { - fn flatten_options(self) -> Option; -} - macro_rules! impl_system_param_tuple { ($($param: ident),*) => { - #[allow(non_snake_case)] - impl<$($param: SystemParam),*> FlattenOptions<($($param,)*)> for ($(Option<$param>,)*) { - fn flatten_options(self) -> Option<($($param,)*)> { - let ($($param,)*) = self; - Some(($($param?,)*)) + #[allow(unused_variables)] + impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) { + $($param::init(system_state, world, resources);)* + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + resources: &Resources, + ) -> Option { + Some(($($param::get_param(system_state, world, resources)?,)*)) } } }; From 6b65e783e69a5dc826dc0699d7268f4efe8fe286 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 17:25:22 -0800 Subject: [PATCH 07/12] fix or --- crates/bevy_ecs/hecs/src/query.rs | 2 +- crates/bevy_ecs/src/system/into_system.rs | 39 +++++++++++++++++++++- crates/bevy_ecs/src/system/system_param.rs | 32 +++++++++++++++++- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 81d6d35943c01..93445ee86fe54 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -296,7 +296,7 @@ impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10); /// .collect::>(); /// assert_eq!(components, &[(false, 457)]); /// ``` -pub struct Or(PhantomData); +pub struct Or(pub T); //pub struct Or(PhantomData<(Q1, Q2, Q3)>); #[doc(hidden)] diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 42219ea8bfcfb..c199d71132af1 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -203,7 +203,7 @@ mod tests { schedule::Schedule, ChangedRes, Query, QuerySet, System, }; - use bevy_hecs::{Entity, With, World}; + use bevy_hecs::{Entity, Or, With, World}; #[derive(Debug, Eq, PartialEq)] struct A; @@ -332,6 +332,43 @@ mod tests { assert_eq!(*(world.get::(ent).unwrap()), 2); } + #[test] + fn changed_resource_or_system() { + fn incr_e_on_flip(_or: Or<(Option>, Option>)>, mut query: Query<&mut i32>) { + for mut i in query.iter_mut() { + *i += 1; + } + } + + let mut world = World::default(); + let mut resources = Resources::default(); + resources.insert(false); + resources.insert::(10); + let ent = world.spawn((0,)); + + let mut schedule = Schedule::default(); + schedule.add_stage("update"); + schedule.add_system_to_stage("update", incr_e_on_flip.system()); + schedule.initialize(&mut world, &mut resources); + + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 1); + + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 1); + + *resources.get_mut::().unwrap() = true; + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 2); + + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 2); + + *resources.get_mut::().unwrap() = 20; + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 3); + } + #[test] #[should_panic] fn conflicting_query_mut_system() { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 641826e4542e1..fd03fe9119d38 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -4,7 +4,7 @@ use crate::{ ChangedRes, Commands, FromResources, Local, Query, QuerySet, QueryTuple, Res, ResMut, Resource, ResourceIndex, Resources, SystemState, }; -use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, TypeAccess, World}; +use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, TypeAccess, World, Or}; use std::any::TypeId; pub trait SystemParam: Sized { @@ -175,6 +175,36 @@ macro_rules! impl_system_param_tuple { Some(($($param::get_param(system_state, world, resources)?,)*)) } } + + #[allow(unused_variables)] + #[allow(unused_mut)] + #[allow(non_snake_case)] + impl<$($param: SystemParam),*> SystemParam for Or<($(Option<$param>,)*)> { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) { + $($param::init(system_state, world, resources);)* + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + resources: &Resources, + ) -> Option { + let mut has_some = false; + $( + let $param = $param::get_param(system_state, world, resources); + if $param.is_some() { + has_some = true; + } + )* + + if has_some { + Some(Or(($($param,)*))) + } else { + None + } + } + } }; } From 202fc1c44b912a776d3f5f83935dd068635861e4 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 7 Nov 2020 18:04:08 -0800 Subject: [PATCH 08/12] remove lock from commands --- .../src/schedule/parallel_executor.rs | 4 +- crates/bevy_ecs/src/system/commands.rs | 168 +++++++----------- crates/bevy_ecs/src/system/into_system.rs | 6 +- crates/bevy_ecs/src/system/system_param.rs | 21 ++- crates/bevy_render/src/render_graph/system.rs | 2 +- .../src/hierarchy/child_builder.rs | 73 ++++---- .../hierarchy/hierarchy_maintenance_system.rs | 3 +- .../src/transform_propagate_system.rs | 5 +- examples/2d/sprite.rs | 2 +- examples/2d/sprite_sheet.rs | 2 +- examples/2d/texture_atlas.rs | 2 +- examples/3d/3d_scene.rs | 2 +- examples/3d/load_gltf.rs | 2 +- examples/3d/msaa.rs | 2 +- examples/3d/parenting.rs | 2 +- examples/3d/spawner.rs | 2 +- examples/3d/texture.rs | 2 +- examples/3d/z_sort_debug.rs | 2 +- examples/asset/asset_loading.rs | 2 +- examples/asset/hot_asset_reloading.rs | 2 +- examples/ecs/ecs_guide.rs | 4 +- examples/ecs/hierarchy.rs | 4 +- examples/ecs/parallel_query.rs | 2 +- examples/game/breakout.rs | 4 +- examples/ios/src/lib.rs | 2 +- examples/scene/scene.rs | 2 +- examples/shader/mesh_custom_attribute.rs | 2 +- examples/shader/shader_custom_material.rs | 2 +- examples/shader/shader_defs.rs | 2 +- examples/ui/button.rs | 2 +- examples/ui/font_atlas_debug.rs | 4 +- examples/ui/text.rs | 2 +- examples/ui/ui.rs | 2 +- examples/wasm/assets_wasm.rs | 2 +- examples/window/multiple_windows.rs | 2 +- 35 files changed, 150 insertions(+), 194 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/parallel_executor.rs b/crates/bevy_ecs/src/schedule/parallel_executor.rs index d647f192ef239..5f6820dbb9bde 100644 --- a/crates/bevy_ecs/src/schedule/parallel_executor.rs +++ b/crates/bevy_ecs/src/schedule/parallel_executor.rs @@ -556,7 +556,7 @@ mod tests { schedule.add_stage("PreArchetypeChange"); schedule.add_stage("PostArchetypeChange"); - fn insert(mut commands: Commands) { + fn insert(commands: &mut Commands) { commands.spawn((1u32,)); } @@ -603,6 +603,7 @@ mod tests { schedule.add_system_to_stage("update", insert.thread_local_system()); schedule.add_system_to_stage("update", read.system()); + schedule.initialize(&mut world, &mut resources); let mut executor = ParallelExecutor::default(); executor.run(&mut schedule, &mut world, &mut resources); @@ -743,6 +744,7 @@ mod tests { schedule.add_system_to_stage("C", read_isize_res.system()); schedule.add_system_to_stage("C", read_isize_write_f64_res.system()); schedule.add_system_to_stage("C", write_f64_res.system()); + schedule.initialize(&mut world, &mut resources); fn run_executor_and_validate( executor: &mut ParallelExecutor, diff --git a/crates/bevy_ecs/src/system/commands.rs b/crates/bevy_ecs/src/system/commands.rs index 6f6981c771c05..f4fb77b342fb7 100644 --- a/crates/bevy_ecs/src/system/commands.rs +++ b/crates/bevy_ecs/src/system/commands.rs @@ -157,13 +157,13 @@ impl Command for InsertLocalResource { } #[derive(Default)] -pub struct CommandsInternal { - pub commands: Vec>, - pub current_entity: Option, - pub entity_reserver: Option, +pub struct Commands { + commands: Vec>, + current_entity: Option, + entity_reserver: Option, } -impl CommandsInternal { +impl Commands { pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { let entity = self .entity_reserver @@ -175,53 +175,6 @@ impl CommandsInternal { self } - pub fn with_bundle( - &mut self, - components: impl DynamicBundle + Send + Sync + 'static, - ) -> &mut Self { - let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first."); - self.commands.push(Box::new(Insert { - entity: current_entity, - components, - })); - self - } - - pub fn with(&mut self, component: impl Component) -> &mut Self { - let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first."); - self.commands.push(Box::new(InsertOne { - entity: current_entity, - component, - })); - self - } - - pub fn add_command(&mut self, command: C) -> &mut Self { - self.commands.push(Box::new(command)); - self - } - - pub fn add_command_boxed(&mut self, command: Box) -> &mut Self { - self.commands.push(command); - self - } -} - -/// A queue of [Command]s to run on the current [World] and [Resources]. Todo: remove arc here -#[derive(Default, Clone)] -pub struct Commands { - pub commands: Arc>, -} - -impl Commands { - pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.spawn(components); - } - self - } - pub fn spawn_batch(&mut self, components_iter: I) -> &mut Self where I: IntoIterator + Send + Sync + 'static, @@ -235,25 +188,6 @@ impl Commands { self.add_command(Despawn { entity }) } - pub fn with(&mut self, component: impl Component) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.with(component); - } - self - } - - pub fn with_bundle( - &mut self, - components: impl DynamicBundle + Send + Sync + 'static, - ) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.with_bundle(components); - } - self - } - pub fn insert( &mut self, entity: Entity, @@ -281,61 +215,85 @@ impl Commands { }) } + pub fn remove_one(&mut self, entity: Entity) -> &mut Self + where + T: Component, + { + self.add_command(RemoveOne:: { + entity, + phantom: PhantomData, + }) + } + + pub fn remove(&mut self, entity: Entity) -> &mut Self + where + T: Bundle + Send + Sync + 'static, + { + self.add_command(Remove:: { + entity, + phantom: PhantomData, + }) + } + + pub fn with_bundle( + &mut self, + components: impl DynamicBundle + Send + Sync + 'static, + ) -> &mut Self { + let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first."); + self.commands.push(Box::new(Insert { + entity: current_entity, + components, + })); + self + } + + pub fn with(&mut self, component: impl Component) -> &mut Self { + let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first."); + self.commands.push(Box::new(InsertOne { + entity: current_entity, + component, + })); + self + } + pub fn add_command(&mut self, command: C) -> &mut Self { - self.commands.lock().add_command(command); + self.commands.push(Box::new(command)); self } pub fn add_command_boxed(&mut self, command: Box) -> &mut Self { - self.commands.lock().add_command_boxed(command); + self.commands.push(command); self } - pub fn apply(&self, world: &mut World, resources: &mut Resources) { - let mut commands = self.commands.lock(); - for command in commands.commands.drain(..) { + pub fn apply(&mut self, world: &mut World, resources: &mut Resources) { + for command in self.commands.drain(..) { command.write(world, resources); } } pub fn current_entity(&self) -> Option { - let commands = self.commands.lock(); - commands.current_entity + self.current_entity } - pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self { - { - let commands = self.commands.lock(); - let current_entity = commands - .current_entity - .expect("The 'current entity' is not set. You should spawn an entity first."); - f(current_entity); - } - self + pub fn set_current_entity(&mut self, entity: Entity) { + self.current_entity = Some(entity); } - pub fn remove_one(&mut self, entity: Entity) -> &mut Self - where - T: Component, - { - self.add_command(RemoveOne:: { - entity, - phantom: PhantomData, - }) + pub fn clear_current_entity(&mut self) { + self.current_entity = None; } - pub fn remove(&mut self, entity: Entity) -> &mut Self - where - T: Bundle + Send + Sync + 'static, - { - self.add_command(Remove:: { - entity, - phantom: PhantomData, - }) + pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self { + let current_entity = self + .current_entity + .expect("The 'current entity' is not set. You should spawn an entity first."); + f(current_entity); + self } - pub fn set_entity_reserver(&self, entity_reserver: EntityReserver) { - self.commands.lock().entity_reserver = Some(entity_reserver); + pub fn set_entity_reserver(&mut self, entity_reserver: EntityReserver) { + self.entity_reserver = Some(entity_reserver); } } diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index c199d71132af1..c3d9c42739a47 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -274,14 +274,14 @@ mod tests { fn or_query_set_system() { // Regression test for issue #762 use crate::{Added, Changed, Mutated, Or}; - fn query_system( + let query_system = move | mut ran: ResMut, set: QuerySet<( Query, Changed)>>, Query, Added)>>, Query, Mutated)>>, )>, - ) { + | { let changed = set.q0().iter().count(); let added = set.q1().iter().count(); let mutated = set.q2().iter().count(); @@ -291,7 +291,7 @@ mod tests { assert_eq!(mutated, 0); *ran = true; - } + }; let mut world = World::default(); let mut resources = Resources::default(); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index fd03fe9119d38..c9561f2b038d2 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -4,7 +4,7 @@ use crate::{ ChangedRes, Commands, FromResources, Local, Query, QuerySet, QueryTuple, Res, ResMut, Resource, ResourceIndex, Resources, SystemState, }; -use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, TypeAccess, World, Or}; +use bevy_hecs::{ArchetypeComponent, Fetch, Or, Query as HecsQuery, TypeAccess, World}; use std::any::TypeId; pub trait SystemParam: Sized { @@ -70,7 +70,7 @@ impl SystemParam for QuerySet { } } -impl SystemParam for Commands { +impl<'a> SystemParam for &'a mut Commands { fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { system_state .commands @@ -83,7 +83,8 @@ impl SystemParam for Commands { _world: &World, _resources: &Resources, ) -> Option { - Some(system_state.commands.clone()) + let commands: &'a mut Commands = std::mem::transmute(&mut system_state.commands); + Some(commands) } } @@ -98,7 +99,9 @@ impl<'a, T: Resource> SystemParam for Res<'a, T> { _world: &World, resources: &Resources, ) -> Option { - Some(Res::new(resources.get_unsafe_ref::(ResourceIndex::Global))) + Some(Res::new( + resources.get_unsafe_ref::(ResourceIndex::Global), + )) } } @@ -132,7 +135,9 @@ impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { ) -> Option { let (added, mutated) = resources.get_unsafe_added_and_mutated::(ResourceIndex::Global); if *added.as_ptr() || *mutated.as_ptr() { - Some(ChangedRes::new(resources.get_unsafe_ref::(ResourceIndex::Global))) + Some(ChangedRes::new( + resources.get_unsafe_ref::(ResourceIndex::Global), + )) } else { None } @@ -174,7 +179,7 @@ macro_rules! impl_system_param_tuple { ) -> Option { Some(($($param::get_param(system_state, world, resources)?,)*)) } - } + } #[allow(unused_variables)] #[allow(unused_mut)] @@ -204,7 +209,7 @@ macro_rules! impl_system_param_tuple { None } } - } + } }; } @@ -224,4 +229,4 @@ impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M); impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); \ No newline at end of file +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); diff --git a/crates/bevy_render/src/render_graph/system.rs b/crates/bevy_render/src/render_graph/system.rs index 99b31bf4152fd..875c33298c653 100644 --- a/crates/bevy_render/src/render_graph/system.rs +++ b/crates/bevy_render/src/render_graph/system.rs @@ -3,7 +3,7 @@ use bevy_ecs::{Resources, World}; pub fn render_graph_schedule_executor_system(world: &mut World, resources: &mut Resources) { // run render graph systems - let (mut system_schedule, commands) = { + let (mut system_schedule, mut commands) = { let mut render_graph = resources.get_mut::().unwrap(); (render_graph.take_schedule(), render_graph.take_commands()) }; diff --git a/crates/bevy_transform/src/hierarchy/child_builder.rs b/crates/bevy_transform/src/hierarchy/child_builder.rs index 74bff7c2e1f3e..b42772cfb8bc7 100644 --- a/crates/bevy_transform/src/hierarchy/child_builder.rs +++ b/crates/bevy_transform/src/hierarchy/child_builder.rs @@ -1,7 +1,5 @@ use crate::prelude::{Children, Parent, PreviousParent}; -use bevy_ecs::{ - Command, Commands, CommandsInternal, Component, DynamicBundle, Entity, Resources, World, -}; +use bevy_ecs::{Command, Commands, Component, DynamicBundle, Entity, Resources, World}; use smallvec::SmallVec; #[derive(Debug)] @@ -42,7 +40,7 @@ pub struct PushChildren { } pub struct ChildBuilder<'a> { - commands: &'a mut CommandsInternal, + commands: &'a mut Commands, push_children: PushChildren, } @@ -75,12 +73,12 @@ impl<'a> ChildBuilder<'a> { self.commands.spawn(components); self.push_children .children - .push(self.commands.current_entity.unwrap()); + .push(self.commands.current_entity().unwrap()); self } pub fn current_entity(&self) -> Option { - self.commands.current_entity + self.commands.current_entity() } pub fn with_bundle( @@ -99,7 +97,7 @@ impl<'a> ChildBuilder<'a> { pub fn for_current_entity(&mut self, func: impl FnOnce(Entity)) -> &mut Self { let current_entity = self .commands - .current_entity + .current_entity() .expect("The 'current entity' is not set. You should spawn an entity first."); func(current_entity); self @@ -114,56 +112,47 @@ pub trait BuildChildren { impl BuildChildren for Commands { fn with_children(&mut self, parent: impl FnOnce(&mut ChildBuilder)) -> &mut Self { - { - let mut commands = self.commands.lock(); - let current_entity = commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); - commands.current_entity = None; - let push_children = { - let mut builder = ChildBuilder { - commands: &mut commands, - push_children: PushChildren { - children: SmallVec::default(), - parent: current_entity, - }, - }; - parent(&mut builder); - builder.push_children + let current_entity = self.current_entity().expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); + self.clear_current_entity(); + let push_children = { + let mut builder = ChildBuilder { + commands: self, + push_children: PushChildren { + children: SmallVec::default(), + parent: current_entity, + }, }; + parent(&mut builder); + builder.push_children + }; - commands.current_entity = Some(current_entity); - commands.add_command(push_children); - } + self.set_current_entity(current_entity); + self.add_command(push_children); self } fn push_children(&mut self, parent: Entity, children: &[Entity]) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.add_command(PushChildren { - children: SmallVec::from(children), - parent, - }); - } + self.add_command(PushChildren { + children: SmallVec::from(children), + parent, + }); self } fn insert_children(&mut self, parent: Entity, index: usize, children: &[Entity]) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.add_command(InsertChildren { - children: SmallVec::from(children), - index, - parent, - }); - } + self.add_command(InsertChildren { + children: SmallVec::from(children), + index, + parent, + }); self } } impl<'a> BuildChildren for ChildBuilder<'a> { fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self { - let current_entity = self.commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); - self.commands.current_entity = None; + let current_entity = self.commands.current_entity().expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); + self.commands.clear_current_entity(); let push_children = { let mut builder = ChildBuilder { commands: self.commands, @@ -177,7 +166,7 @@ impl<'a> BuildChildren for ChildBuilder<'a> { builder.push_children }; - self.commands.current_entity = Some(current_entity); + self.commands.set_current_entity(current_entity); self.commands.add_command(push_children); self } diff --git a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs index ea249e61a34f5..b7e4c069d7917 100644 --- a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs +++ b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs @@ -4,7 +4,7 @@ use bevy_utils::HashMap; use smallvec::SmallVec; pub fn parent_update_system( - mut commands: Commands, + commands: &mut Commands, removed_parent_query: Query>, // TODO: ideally this only runs when the Parent component has changed mut changed_parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>, @@ -122,6 +122,7 @@ mod test { }); let parent = parent.unwrap(); commands.apply(&mut world, &mut resources); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); assert_eq!( diff --git a/crates/bevy_transform/src/transform_propagate_system.rs b/crates/bevy_transform/src/transform_propagate_system.rs index 287aa9a7a870f..4727b24528f0d 100644 --- a/crates/bevy_transform/src/transform_propagate_system.rs +++ b/crates/bevy_transform/src/transform_propagate_system.rs @@ -88,10 +88,10 @@ mod test { ), ]) .collect::>(); - // we need to run the schedule three times because components need to be filled in + // we need to run the schedule two times because components need to be filled in // to resolve this problem in code, just add the correct components, or use Commands // which adds all of the components needed with the correct state (see next test) - schedule.run(&mut world, &mut resources); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); schedule.run(&mut world, &mut resources); @@ -142,6 +142,7 @@ mod test { .for_current_entity(|entity| children.push(entity)); }); commands.apply(&mut world, &mut resources); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); assert_eq!( diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs index b4cff1721d19b..3e12a7068fb13 100644 --- a/examples/2d/sprite.rs +++ b/examples/2d/sprite.rs @@ -8,7 +8,7 @@ fn main() { } fn setup( - mut commands: Commands, + commands: &mut Commands, asset_server: Res, mut materials: ResMut>, ) { diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index ae774b6120a1f..4af3f2aab1893 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -21,7 +21,7 @@ fn animate_sprite_system( } fn setup( - mut commands: Commands, + commands: &mut Commands, asset_server: Res, mut texture_atlases: ResMut>, ) { diff --git a/examples/2d/texture_atlas.rs b/examples/2d/texture_atlas.rs index e6036d1d34414..564ce5d8a74ac 100644 --- a/examples/2d/texture_atlas.rs +++ b/examples/2d/texture_atlas.rs @@ -21,7 +21,7 @@ fn setup(mut rpg_sprite_handles: ResMut, asset_server: Res, asset_server: Res, mut texture_atlases: ResMut>, diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index e053e10fa818f..5d42e4a903b2c 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -10,7 +10,7 @@ fn main() { /// set up a simple 3D scene fn setup( - mut commands: Commands, + commands: &mut Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index 248c525a5c4d4..f8786c0c210c7 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -8,7 +8,7 @@ fn main() { .run(); } -fn setup(mut commands: Commands, asset_server: Res) { +fn setup(commands: &mut Commands, asset_server: Res) { commands .spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf")) .spawn(LightComponents { diff --git a/examples/3d/msaa.rs b/examples/3d/msaa.rs index 264facd4dbf94..43b6d1b1c4fa8 100644 --- a/examples/3d/msaa.rs +++ b/examples/3d/msaa.rs @@ -13,7 +13,7 @@ fn main() { /// set up a simple 3D scene fn setup( - mut commands: Commands, + commands: &mut Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { diff --git a/examples/3d/parenting.rs b/examples/3d/parenting.rs index f97b35c71a405..38d3d7983eb5c 100644 --- a/examples/3d/parenting.rs +++ b/examples/3d/parenting.rs @@ -23,7 +23,7 @@ fn rotator_system(time: Res