diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 53121a6c7f8839..dd5395170f5b7e 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -291,7 +291,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { ParamSet { @@ -460,7 +460,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { unsafe fn get_param( state: &'s mut Self, system_meta: &#path::system::SystemMeta, - world: &'w #path::world::World, + world: #path::world::interior_mutable_world::InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { #struct_name { diff --git a/crates/bevy_ecs/src/schedule/executor_parallel.rs b/crates/bevy_ecs/src/schedule/executor_parallel.rs index 68dd1f1ea798dc..5a4e5283b60b2a 100644 --- a/crates/bevy_ecs/src/schedule/executor_parallel.rs +++ b/crates/bevy_ecs/src/schedule/executor_parallel.rs @@ -2,7 +2,7 @@ use crate::{ archetype::ArchetypeComponentId, query::Access, schedule::{ParallelSystemExecutor, SystemContainer}, - world::World, + world::{interior_mutable_world::InteriorMutableWorld, World}, }; use async_channel::{Receiver, Sender}; use bevy_tasks::{ComputeTaskPool, Scope, TaskPool}; @@ -124,6 +124,8 @@ impl ParallelSystemExecutor for ParallelExecutor { } } + let world = world.as_interior_mutable(); + ComputeTaskPool::init(TaskPool::default).scope(|scope| { self.prepare_systems(scope, systems, world); if self.should_run.count_ones(..) == 0 { @@ -168,7 +170,7 @@ impl ParallelExecutor { &mut self, scope: &Scope<'_, 'scope, ()>, systems: &'scope mut [SystemContainer], - world: &'scope World, + world: InteriorMutableWorld<'scope>, ) { // These are used as a part of a unit test. #[cfg(test)] diff --git a/crates/bevy_ecs/src/system/commands/parallel_scope.rs b/crates/bevy_ecs/src/system/commands/parallel_scope.rs index 573afa70aae513..ba5c0e1fae3fe9 100644 --- a/crates/bevy_ecs/src/system/commands/parallel_scope.rs +++ b/crates/bevy_ecs/src/system/commands/parallel_scope.rs @@ -6,6 +6,7 @@ use crate::{ entity::Entities, prelude::World, system::{SystemParam, SystemParamFetch, SystemParamState}, + world::interior_mutable_world::InteriorMutableWorld, }; use super::{CommandQueue, Commands}; @@ -58,7 +59,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for ParallelCommandsState { unsafe fn get_param( state: &'s mut Self, _: &crate::system::SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _: u32, ) -> Self::Item { ParallelCommands { diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index 662d031daa2e38..471c19de9b7a17 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -9,7 +9,7 @@ use crate::{ ExclusiveSystemParamItem, ExclusiveSystemParamState, IntoSystem, System, SystemMeta, SystemTypeIdLabel, }, - world::{World, WorldId}, + world::{interior_mutable_world::InteriorMutableWorld, World, WorldId}, }; use bevy_ecs_macros::all_tuples; use std::{borrow::Cow, marker::PhantomData}; @@ -87,7 +87,11 @@ where } #[inline] - unsafe fn run_unsafe(&mut self, _input: Self::In, _world: &World) -> Self::Out { + unsafe fn run_unsafe( + &mut self, + _input: Self::In, + _world: InteriorMutableWorld<'_>, + ) -> Self::Out { panic!("Cannot run exclusive systems with a shared World reference"); } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index c3f672e718734e..397881af15cb62 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -9,7 +9,7 @@ use crate::{ check_system_change_tick, ReadOnlySystemParamFetch, System, SystemParam, SystemParamFetch, SystemParamItem, SystemParamState, }, - world::{World, WorldId}, + world::{interior_mutable_world::InteriorMutableWorld, World, WorldId}, }; use bevy_ecs_macros::all_tuples; use std::{borrow::Cow, fmt::Debug, marker::PhantomData}; @@ -169,7 +169,7 @@ impl SystemState { { self.validate_world_and_update_archetypes(world); // SAFETY: Param is read-only and doesn't allow mutable access to World. It also matches the World this SystemState was created with. - unsafe { self.get_unchecked_manual(world) } + unsafe { self.get_unchecked_manual(world.as_interior_mutable_readonly()) } } /// Retrieve the mutable [`SystemParam`] values. @@ -180,7 +180,7 @@ impl SystemState { ) -> >::Item { self.validate_world_and_update_archetypes(world); // SAFETY: World is uniquely borrowed and matches the World this SystemState was created with. - unsafe { self.get_unchecked_manual(world) } + unsafe { self.get_unchecked_manual(world.as_interior_mutable()) } } /// Applies all state queued up for [`SystemParam`] values. For example, this will apply commands queued up @@ -220,7 +220,7 @@ impl SystemState { #[inline] pub unsafe fn get_unchecked_manual<'w, 's>( &'s mut self, - world: &'w World, + world: InteriorMutableWorld<'w>, ) -> >::Item { let change_tick = world.increment_change_tick(); let param = ::get_param( @@ -393,7 +393,7 @@ where } #[inline] - unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out { + unsafe fn run_unsafe(&mut self, input: Self::In, world: InteriorMutableWorld<'_>) -> Self::Out { let change_tick = world.increment_change_tick(); // Safety: diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 43fcdb41dd3cb7..de4d59420ce1c4 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -2,8 +2,12 @@ use bevy_utils::tracing::warn; use core::fmt::Debug; use crate::{ - archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId, - query::Access, schedule::SystemLabelId, world::World, + archetype::ArchetypeComponentId, + change_detection::MAX_CHANGE_AGE, + component::ComponentId, + query::Access, + schedule::SystemLabelId, + world::{interior_mutable_world::InteriorMutableWorld, World}, }; use std::borrow::Cow; @@ -42,17 +46,14 @@ pub trait System: Send + Sync + 'static { /// /// # Safety /// - /// This might access world and resources in an unsafe manner. This should only be called in one - /// of the following contexts: - /// 1. This system is the only system running on the given world across all threads. - /// 2. This system only runs in parallel with other systems that do not conflict with the - /// [`System::archetype_component_access()`]. - unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out; + /// This function may only be called with a [`InteriorMutableWorld`] that can be used for + /// everything listed in [`System::archetype_component_access`]. + unsafe fn run_unsafe(&mut self, input: Self::In, world: InteriorMutableWorld<'_>) -> Self::Out; /// Runs the system with the given input in the world. fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out { self.update_archetype_component_access(world); // SAFETY: world and resources are exclusively borrowed - unsafe { self.run_unsafe(input, world) } + unsafe { self.run_unsafe(input, world.as_interior_mutable()) } } fn apply_buffers(&mut self, world: &mut World); /// Initialize the system. diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index c1ae3b71664f0a..3862818c3b803e 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -9,7 +9,7 @@ use crate::{ Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery, }, system::{CommandQueue, Commands, Query, SystemMeta}, - world::{FromWorld, World}, + world::{interior_mutable_world::InteriorMutableWorld, FromWorld, World}, }; pub use bevy_ecs_macros::Resource; pub use bevy_ecs_macros::SystemParam; @@ -126,7 +126,7 @@ pub trait SystemParamFetch<'world, 'state>: SystemParamState { unsafe fn get_param( state: &'state mut Self, system_meta: &SystemMeta, - world: &'world World, + world: InteriorMutableWorld<'world>, change_tick: u32, ) -> Self::Item; } @@ -184,9 +184,12 @@ impl<'w, 's, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> SystemPar unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { + // SAFETY: QueryState::new populates `component_access` which is used in `impl SystemParamState for QueryState`, + // so the scheduler only calls this in systems with access to every component used in the query + let world = unsafe { world.world() }; Query::new(world, state, system_meta.last_change_tick, change_tick) } } @@ -214,7 +217,7 @@ fn assert_component_access_compatibility( pub struct ParamSet<'w, 's, T: SystemParam> { param_states: &'s mut T::Fetch, - world: &'w World, + world: InteriorMutableWorld<'w>, system_meta: SystemMeta, change_tick: u32, } @@ -403,7 +406,7 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { let (ptr, ticks) = world @@ -452,7 +455,7 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { world @@ -517,11 +520,10 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResMutState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { let value = world - .as_interior_mutable_migration_internal() .get_resource_mut_with_id(state.component_id) .unwrap_or_else(|| { panic!( @@ -566,11 +568,10 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResMutState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { world - .as_interior_mutable_migration_internal() .get_resource_mut_with_id(state.0.component_id) .map(|value| ResMut { value: value.value, @@ -609,10 +610,10 @@ impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue { unsafe fn get_param( state: &'s mut Self, _system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { - Commands::new(state, world) + Commands::new_from_entities(state, world.entities()) } } @@ -661,10 +662,11 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState { unsafe fn get_param( _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { - world + // SAFTY: `WorldState` has `read_all` access + unsafe { world.world() } } } @@ -787,7 +789,7 @@ impl<'w, 's, T: FromWorld + Send + 'static> SystemParamFetch<'w, 's> for LocalSt unsafe fn get_param( state: &'s mut Self, _system_meta: &SystemMeta, - _world: &'w World, + _world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { Local(state.0.get()) @@ -829,7 +831,7 @@ impl<'w, 's, T: FromWorld + Send + 'static> SystemParamFetch<'w, 's> for LocalSt /// # bevy_ecs::system::assert_is_system(react_on_removal); /// ``` pub struct RemovedComponents<'a, T: Component> { - world: &'a World, + world: InteriorMutableWorld<'a>, component_id: ComponentId, marker: PhantomData, } @@ -837,7 +839,9 @@ pub struct RemovedComponents<'a, T: Component> { impl<'a, T: Component> RemovedComponents<'a, T> { /// Returns an iterator over the entities that had their `T` [`Component`] removed. pub fn iter(&self) -> std::iter::Cloned> { - self.world.removed_with_id(self.component_id) + // SAFETY: removed_with_id does not perform any accesses of resources or components + let world = unsafe { self.world.world() }; + world.removed_with_id(self.component_id) } } @@ -882,7 +886,7 @@ impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for RemovedComponentsState, _change_tick: u32, ) -> Self::Item { RemovedComponents { @@ -1007,7 +1011,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -1057,7 +1061,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -1124,7 +1128,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -1168,7 +1172,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { world.validate_non_send_access::(); @@ -1206,7 +1210,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState { unsafe fn get_param( _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { world.archetypes() @@ -1238,7 +1242,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState { unsafe fn get_param( _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { world.components() @@ -1270,7 +1274,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState { unsafe fn get_param( _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { world.entities() @@ -1302,7 +1306,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState { unsafe fn get_param( _state: &'s mut Self, _system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { world.bundles() @@ -1362,7 +1366,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState { unsafe fn get_param( _state: &'s mut Self, system_meta: &SystemMeta, - _world: &'w World, + _world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { SystemChangeTick { @@ -1448,7 +1452,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for SystemNameState { unsafe fn get_param( state: &'s mut Self, _system_meta: &SystemMeta, - _world: &'w World, + _world: InteriorMutableWorld<'w>, _change_tick: u32, ) -> Self::Item { SystemName { @@ -1476,7 +1480,7 @@ macro_rules! impl_system_param_tuple { unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { @@ -1622,7 +1626,7 @@ where unsafe fn get_param( state: &'state mut Self, system_meta: &SystemMeta, - world: &'world World, + world: InteriorMutableWorld<'world>, change_tick: u32, ) -> Self::Item { // SAFETY: We properly delegate SystemParamState diff --git a/crates/bevy_ecs/src/system/system_piping.rs b/crates/bevy_ecs/src/system/system_piping.rs index 09533a02c32b50..0b58c931739292 100644 --- a/crates/bevy_ecs/src/system/system_piping.rs +++ b/crates/bevy_ecs/src/system/system_piping.rs @@ -3,7 +3,7 @@ use crate::{ component::ComponentId, query::Access, system::{IntoSystem, System}, - world::World, + world::{interior_mutable_world::InteriorMutableWorld, World}, }; use std::borrow::Cow; @@ -78,7 +78,7 @@ impl> System for PipeSystem< self.system_a.is_exclusive() || self.system_b.is_exclusive() } - unsafe fn run_unsafe(&mut self, input: Self::In, world: &World) -> Self::Out { + unsafe fn run_unsafe(&mut self, input: Self::In, world: InteriorMutableWorld<'_>) -> Self::Out { let out = self.system_a.run_unsafe(input, world); self.system_b.run_unsafe(out, world) } diff --git a/crates/bevy_ecs/src/world/interior_mutable_world.rs b/crates/bevy_ecs/src/world/interior_mutable_world.rs index 7bc901598faaf0..46bcf7effbe63e 100644 --- a/crates/bevy_ecs/src/world/interior_mutable_world.rs +++ b/crates/bevy_ecs/src/world/interior_mutable_world.rs @@ -5,7 +5,7 @@ use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, change_detection::{MutUntyped, Ticks}, - component::{ComponentId, ComponentStorage, ComponentTicks, Components}, + component::{ComponentId, ComponentStorage, ComponentTicks, Components, TickCells}, entity::{Entities, Entity, EntityLocation}, prelude::Component, storage::Storages, @@ -205,7 +205,7 @@ impl<'w> InteriorMutableWorld<'w> { self.0.validate_non_send_access_untyped(info.name()); } - let (ptr, ticks) = self.0.get_resource_with_ticks(component_id)?; + let (ptr, ticks) = self.get_resource_with_ticks(component_id)?; // SAFETY: // - index is in-bounds because the column is initialized and non-empty @@ -253,7 +253,7 @@ impl<'w> InteriorMutableWorld<'w> { &self, component_id: ComponentId, ) -> Option> { - let (ptr, ticks) = self.0.get_resource_with_ticks(component_id)?; + let (ptr, ticks) = self.get_resource_with_ticks(component_id)?; // SAFETY: // - This caller ensures that nothing aliases `ticks`. @@ -268,6 +268,26 @@ impl<'w> InteriorMutableWorld<'w> { ticks, }) } + + // Shorthand helper function for getting the data and change ticks for a resource. + #[inline] + pub(crate) fn get_resource_with_ticks( + &self, + component_id: ComponentId, + ) -> Option<(Ptr<'w>, TickCells<'w>)> { + self.0 + .storages + .resources + .get(component_id)? + .get_with_ticks() + } + + pub(crate) fn increment_change_tick(&self) -> u32 { + self.0.increment_change_tick() + } + pub(crate) fn validate_non_send_access(&self) { + self.0.validate_non_send_access::(); + } } /// A interior-mutable reference to a particular [`Entity`] and all of its components diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 85c8baaff2f4f0..1e6edbe65dd7a7 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1000,15 +1000,6 @@ impl World { unsafe { self.as_interior_mutable().get_non_send_resource_mut() } } - // Shorthand helper function for getting the data and change ticks for a resource. - #[inline] - pub(crate) fn get_resource_with_ticks( - &self, - component_id: ComponentId, - ) -> Option<(Ptr<'_>, TickCells<'_>)> { - self.storages.resources.get(component_id)?.get_with_ticks() - } - // Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. #[inline] pub(crate) fn get_resource_archetype_component_id( diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index cdfb462c2324bd..81eeac77a85d58 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -5,6 +5,7 @@ use bevy_ecs::{ ReadOnlySystemParamFetch, ResState, SystemMeta, SystemParam, SystemParamFetch, SystemParamItem, SystemParamState, SystemState, }, + world::interior_mutable_world::InteriorMutableWorld, }; use std::ops::{Deref, DerefMut}; @@ -83,7 +84,7 @@ where unsafe fn get_param( state: &'s mut Self, system_meta: &SystemMeta, - world: &'w World, + world: InteriorMutableWorld<'w>, change_tick: u32, ) -> Self::Item { let main_world = ResState::::get_param( diff --git a/crates/bevy_time/src/fixed_timestep.rs b/crates/bevy_time/src/fixed_timestep.rs index 8a63f67f92bb6b..8e2826517808f4 100644 --- a/crates/bevy_time/src/fixed_timestep.rs +++ b/crates/bevy_time/src/fixed_timestep.rs @@ -5,7 +5,7 @@ use bevy_ecs::{ query::Access, schedule::ShouldRun, system::{IntoSystem, Res, ResMut, Resource, System}, - world::World, + world::{interior_mutable_world::InteriorMutableWorld, World}, }; use bevy_utils::HashMap; use std::borrow::Cow; @@ -193,7 +193,7 @@ impl System for FixedTimestep { false } - unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun { + unsafe fn run_unsafe(&mut self, _input: (), world: InteriorMutableWorld<'_>) -> ShouldRun { // SAFETY: this system inherits the internal system's component access and archetype component // access, which means the caller has ensured running the internal system is safe self.internal_system.run_unsafe((), world)