Skip to content

Commit 4790c0d

Browse files
committed
Added Ref to allow immutable access with change detection (#7097)
# Objective - Fixes #7066 ## Solution - Split the ChangeDetection trait into ChangeDetection and ChangeDetectionMut - Added Ref as equivalent to &T with change detection --- ## Changelog - Support for Ref which allow inspecting change detection flags in an immutable way ## Migration Guide - While bevy prelude includes both ChangeDetection and ChangeDetectionMut any code explicitly referencing ChangeDetection might need to be updated to ChangeDetectionMut or both. Specifically any reading logic requires ChangeDetection while writes requires ChangeDetectionMut. use bevy_ecs::change_detection::DetectChanges -> use bevy_ecs::change_detection::{DetectChanges, DetectChangesMut} - Previously Res had methods to access change detection `is_changed` and `is_added` those methods have been moved to the `DetectChanges` trait. If you are including bevy prelude you will have access to these types otherwise you will need to `use bevy_ecs::change_detection::DetectChanges` to continue using them.
1 parent a13b6f8 commit 4790c0d

File tree

14 files changed

+454
-213
lines changed

14 files changed

+454
-213
lines changed

crates/bevy_ecs/src/change_detection.rs

Lines changed: 230 additions & 68 deletions
Large diffs are not rendered by default.

crates/bevy_ecs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub mod prelude {
2828
#[doc(hidden)]
2929
pub use crate::{
3030
bundle::Bundle,
31-
change_detection::DetectChanges,
31+
change_detection::{DetectChanges, DetectChangesMut},
3232
component::Component,
3333
entity::Entity,
3434
event::{EventReader, EventWriter, Events},

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 169 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::{
22
archetype::{Archetype, ArchetypeComponentId},
3-
change_detection::Ticks,
3+
change_detection::{Ticks, TicksMut},
44
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType, Tick},
55
entity::Entity,
66
query::{Access, DebugCheckedUnwrap, FilteredAccess},
77
storage::{ComponentSparseSet, Table, TableRow},
8-
world::{Mut, World},
8+
world::{Mut, Ref, World},
99
};
1010
use bevy_ecs_macros::all_tuples;
1111
pub use bevy_ecs_macros::WorldQuery;
@@ -653,6 +653,167 @@ unsafe impl<T: Component> WorldQuery for &T {
653653
/// SAFETY: access is read only
654654
unsafe impl<T: Component> ReadOnlyWorldQuery for &T {}
655655

656+
#[doc(hidden)]
657+
pub struct RefFetch<'w, T> {
658+
// T::Storage = TableStorage
659+
table_data: Option<(
660+
ThinSlicePtr<'w, UnsafeCell<T>>,
661+
ThinSlicePtr<'w, UnsafeCell<Tick>>,
662+
ThinSlicePtr<'w, UnsafeCell<Tick>>,
663+
)>,
664+
// T::Storage = SparseStorage
665+
sparse_set: Option<&'w ComponentSparseSet>,
666+
667+
last_change_tick: u32,
668+
change_tick: u32,
669+
}
670+
671+
/// SAFETY: `Self` is the same as `Self::ReadOnly`
672+
unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
673+
type Fetch<'w> = RefFetch<'w, T>;
674+
type Item<'w> = Ref<'w, T>;
675+
type ReadOnly = Self;
676+
type State = ComponentId;
677+
678+
fn shrink<'wlong: 'wshort, 'wshort>(item: Ref<'wlong, T>) -> Ref<'wshort, T> {
679+
item
680+
}
681+
682+
const IS_DENSE: bool = {
683+
match T::Storage::STORAGE_TYPE {
684+
StorageType::Table => true,
685+
StorageType::SparseSet => false,
686+
}
687+
};
688+
689+
const IS_ARCHETYPAL: bool = true;
690+
691+
unsafe fn init_fetch<'w>(
692+
world: &'w World,
693+
&component_id: &ComponentId,
694+
last_change_tick: u32,
695+
change_tick: u32,
696+
) -> RefFetch<'w, T> {
697+
RefFetch {
698+
table_data: None,
699+
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
700+
world
701+
.storages()
702+
.sparse_sets
703+
.get(component_id)
704+
.debug_checked_unwrap()
705+
}),
706+
last_change_tick,
707+
change_tick,
708+
}
709+
}
710+
711+
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
712+
RefFetch {
713+
table_data: fetch.table_data,
714+
sparse_set: fetch.sparse_set,
715+
last_change_tick: fetch.last_change_tick,
716+
change_tick: fetch.change_tick,
717+
}
718+
}
719+
720+
#[inline]
721+
unsafe fn set_archetype<'w>(
722+
fetch: &mut RefFetch<'w, T>,
723+
component_id: &ComponentId,
724+
_archetype: &'w Archetype,
725+
table: &'w Table,
726+
) {
727+
if Self::IS_DENSE {
728+
Self::set_table(fetch, component_id, table);
729+
}
730+
}
731+
732+
#[inline]
733+
unsafe fn set_table<'w>(
734+
fetch: &mut RefFetch<'w, T>,
735+
&component_id: &ComponentId,
736+
table: &'w Table,
737+
) {
738+
let column = table.get_column(component_id).debug_checked_unwrap();
739+
fetch.table_data = Some((
740+
column.get_data_slice().into(),
741+
column.get_added_ticks_slice().into(),
742+
column.get_changed_ticks_slice().into(),
743+
));
744+
}
745+
746+
#[inline(always)]
747+
unsafe fn fetch<'w>(
748+
fetch: &mut Self::Fetch<'w>,
749+
entity: Entity,
750+
table_row: TableRow,
751+
) -> Self::Item<'w> {
752+
match T::Storage::STORAGE_TYPE {
753+
StorageType::Table => {
754+
let (table_components, added_ticks, changed_ticks) =
755+
fetch.table_data.debug_checked_unwrap();
756+
Ref {
757+
value: table_components.get(table_row.index()).deref(),
758+
ticks: Ticks {
759+
added: added_ticks.get(table_row.index()).deref(),
760+
changed: changed_ticks.get(table_row.index()).deref(),
761+
change_tick: fetch.change_tick,
762+
last_change_tick: fetch.last_change_tick,
763+
},
764+
}
765+
}
766+
StorageType::SparseSet => {
767+
let (component, ticks) = fetch
768+
.sparse_set
769+
.debug_checked_unwrap()
770+
.get_with_ticks(entity)
771+
.debug_checked_unwrap();
772+
Ref {
773+
value: component.deref(),
774+
ticks: Ticks::from_tick_cells(ticks, fetch.last_change_tick, fetch.change_tick),
775+
}
776+
}
777+
}
778+
}
779+
780+
fn update_component_access(
781+
&component_id: &ComponentId,
782+
access: &mut FilteredAccess<ComponentId>,
783+
) {
784+
assert!(
785+
!access.access().has_write(component_id),
786+
"&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
787+
std::any::type_name::<T>(),
788+
);
789+
access.add_read(component_id);
790+
}
791+
792+
fn update_archetype_component_access(
793+
&component_id: &ComponentId,
794+
archetype: &Archetype,
795+
access: &mut Access<ArchetypeComponentId>,
796+
) {
797+
if let Some(archetype_component_id) = archetype.get_archetype_component_id(component_id) {
798+
access.add_read(archetype_component_id);
799+
}
800+
}
801+
802+
fn init_state(world: &mut World) -> ComponentId {
803+
world.init_component::<T>()
804+
}
805+
806+
fn matches_component_set(
807+
&state: &ComponentId,
808+
set_contains_id: &impl Fn(ComponentId) -> bool,
809+
) -> bool {
810+
set_contains_id(state)
811+
}
812+
}
813+
814+
/// SAFETY: access is read only
815+
unsafe impl<'__w, T: Component> ReadOnlyWorldQuery for Ref<'__w, T> {}
816+
656817
#[doc(hidden)]
657818
pub struct WriteFetch<'w, T> {
658819
// T::Storage = TableStorage
@@ -755,7 +916,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
755916
fetch.table_data.debug_checked_unwrap();
756917
Mut {
757918
value: table_components.get(table_row.index()).deref_mut(),
758-
ticks: Ticks {
919+
ticks: TicksMut {
759920
added: added_ticks.get(table_row.index()).deref_mut(),
760921
changed: changed_ticks.get(table_row.index()).deref_mut(),
761922
change_tick: fetch.change_tick,
@@ -771,7 +932,11 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
771932
.debug_checked_unwrap();
772933
Mut {
773934
value: component.assert_unique().deref_mut(),
774-
ticks: Ticks::from_tick_cells(ticks, fetch.last_change_tick, fetch.change_tick),
935+
ticks: TicksMut::from_tick_cells(
936+
ticks,
937+
fetch.last_change_tick,
938+
fetch.change_tick,
939+
),
775940
}
776941
}
777942
}

crates/bevy_ecs/src/system/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,14 @@ pub fn assert_is_system<In, Out, Params, S: IntoSystem<In, Out, Params>>(sys: S)
130130
mod tests {
131131
use std::any::TypeId;
132132

133-
use crate::prelude::StageLabel;
134-
135133
use crate::{
136134
self as bevy_ecs,
137135
archetype::{ArchetypeComponentId, Archetypes},
138136
bundle::Bundles,
137+
change_detection::DetectChanges,
139138
component::{Component, Components},
140139
entity::{Entities, Entity},
141-
prelude::AnyOf,
140+
prelude::{AnyOf, StageLabel},
142141
query::{Added, Changed, Or, With, Without},
143142
schedule::{Schedule, Stage, SystemStage},
144143
system::{

0 commit comments

Comments
 (0)