Skip to content

Queries as entities #18860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/bevy_audio/src/audio_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ pub struct PlaybackDespawnMarker;
pub struct PlaybackRemoveMarker;

#[derive(SystemParam)]
pub(crate) struct EarPositions<'w, 's> {
pub(crate) query: Query<'w, 's, (Entity, &'static GlobalTransform, &'static SpatialListener)>,
pub(crate) struct EarPositions<'w> {
pub(crate) query: Query<'w, 'w, (Entity, &'static GlobalTransform, &'static SpatialListener)>,
}
impl<'w, 's> EarPositions<'w, 's> {
impl<'w> EarPositions<'w> {
/// Gets a set of transformed ear positions.
///
/// If there are no listeners, use the default values. If a user has added multiple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ use bevy_ecs::system::{ReadOnlySystemParam, SystemParam, SystemState};
struct Foo;

#[derive(SystemParam)]
struct Mutable<'w, 's> {
a: Query<'w, 's, &'static mut Foo>,
struct Mutable<'w> {
a: Query<'w, 'w, &'static mut Foo>,
}

fn main() {

let mut world = World::default();
let state = SystemState::<Mutable>::new(&mut world);
state.get(&world);
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,9 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
}
}

unsafe fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
unsafe fn new_archetype(state: &Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta) }
unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&state.state, archetype, system_meta) }
}

fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
Expand Down
65 changes: 59 additions & 6 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ use crate::{
entity::{Entity, EntityLocation},
observer::Observers,
storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow},
world::DeferredWorld,
};
use alloc::{boxed::Box, vec::Vec};
use bevy_ecs_macros::Event;
use bevy_platform::collections::HashMap;
use core::{
hash::Hash,
Expand Down Expand Up @@ -110,6 +112,9 @@ impl ArchetypeId {
}
}

#[derive(Event)]
pub(crate) struct ArchetypeCreated(pub ArchetypeId);

/// Used in [`ArchetypeAfterBundleInsert`] to track whether components in the bundle are newly
/// added or already existed in the entity's archetype.
#[derive(Copy, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -812,6 +817,41 @@ pub struct ArchetypeRecord {
pub(crate) column: Option<usize>,
}

/// Metadata about the [`ArchetypeId`] and whether it is newly create or not.
pub(crate) struct ArchetypeIdState {
id: ArchetypeId,
is_new: bool,
}

impl ArchetypeIdState {
/// Creates a new [`ArchetypeIdState`] with the given ID and marks it as old.
pub(crate) fn old(id: ArchetypeId) -> Self {
Self { id, is_new: false }
}

/// Get the [`ArchetypeId`].
pub(crate) fn id(&self) -> ArchetypeId {
self.id
}

/// Trigger the `ArchetypeCreated` event if the archetype is new.
pub(crate) fn trigger_if_new(&mut self, world: &mut DeferredWorld) {
if self.is_new {
world.trigger(ArchetypeCreated(self.id));
self.is_new = false;
}
}
}

#[cfg(debug_assertions)]
impl Drop for ArchetypeIdState {
fn drop(&mut self) {
if self.is_new {
panic!("New {:?} was not triggered before being dropped", self.id);
}
}
}

impl Archetypes {
pub(crate) fn new() -> Self {
let mut archetypes = Archetypes {
Expand All @@ -822,13 +862,16 @@ impl Archetypes {
};
// SAFETY: Empty archetype has no components
unsafe {
archetypes.get_id_or_insert(
let mut archetype_id_state = archetypes.get_id_or_insert(
&Components::default(),
&Observers::default(),
TableId::empty(),
Vec::new(),
Vec::new(),
);
// No observers that are listening to the `ArchetypeCreated` event at this
// point so we can safely ignore it
archetype_id_state.is_new = false;
}
archetypes
}
Expand Down Expand Up @@ -924,16 +967,18 @@ impl Archetypes {
/// `table_components` and `sparse_set_components` must be sorted
///
/// # Safety
/// [`TableId`] must exist in tables
/// `table_components` and `sparse_set_components` must exist in `components`
/// - [`TableId`] must exist in tables
/// `table_components` and `sparse_set_components` must exist in `components`
/// - Caller must call `ArchetypeIdState::trigger_if_new` on the returned `ArchetypeIdState`
#[must_use]
pub(crate) unsafe fn get_id_or_insert(
&mut self,
components: &Components,
observers: &Observers,
table_id: TableId,
table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>,
) -> ArchetypeId {
) -> ArchetypeIdState {
let archetype_identity = ArchetypeComponents {
sparse_set_components: sparse_set_components.into_boxed_slice(),
table_components: table_components.into_boxed_slice(),
Expand All @@ -942,10 +987,13 @@ impl Archetypes {
let archetypes = &mut self.archetypes;
let archetype_component_count = &mut self.archetype_component_count;
let component_index = &mut self.by_component;
*self
let mut is_new = false;
let is_new_ref = &mut is_new;
let archetype_id = *self
.by_components
.entry(archetype_identity)
.or_insert_with_key(move |identity| {
*is_new_ref = true;
let ArchetypeComponents {
table_components,
sparse_set_components,
Expand Down Expand Up @@ -975,7 +1023,12 @@ impl Archetypes {
.zip(sparse_set_archetype_components),
));
id
})
});

ArchetypeIdState {
id: archetype_id,
is_new,
}
}

/// Returns the number of components that are stored in archetypes.
Expand Down
66 changes: 41 additions & 25 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ pub use bevy_ecs_macros::Bundle;

use crate::{
archetype::{
Archetype, ArchetypeAfterBundleInsert, ArchetypeId, Archetypes, BundleComponentStatus,
ComponentStatus, SpawnBundleStatus,
Archetype, ArchetypeAfterBundleInsert, ArchetypeId, ArchetypeIdState, Archetypes,
BundleComponentStatus, ComponentStatus, SpawnBundleStatus,
},
change_detection::MaybeLocation,
component::{
Expand Down Expand Up @@ -747,12 +747,12 @@ impl BundleInfo {
components: &Components,
observers: &Observers,
archetype_id: ArchetypeId,
) -> ArchetypeId {
) -> ArchetypeIdState {
if let Some(archetype_after_insert_id) = archetypes[archetype_id]
.edges()
.get_archetype_after_bundle_insert(self.id)
{
return archetype_after_insert_id;
return ArchetypeIdState::old(archetype_after_insert_id);
}
let mut new_table_components = Vec::new();
let mut new_sparse_set_components = Vec::new();
Expand Down Expand Up @@ -806,7 +806,7 @@ impl BundleInfo {
added,
existing,
);
archetype_id
ArchetypeIdState::old(archetype_id)
} else {
let table_id;
let table_components;
Expand Down Expand Up @@ -854,7 +854,7 @@ impl BundleInfo {
.edges_mut()
.cache_archetype_after_bundle_insert(
self.id,
new_archetype_id,
new_archetype_id.id(),
bundle_status,
added_required_components,
added,
Expand All @@ -879,6 +879,7 @@ impl BundleInfo {
///
/// # Safety
/// `archetype_id` must exist and components in `bundle_info` must exist
/// Caller must call `ArchetypeIdState::trigger_if_new` on the returned value
pub(crate) unsafe fn remove_bundle_from_archetype(
&self,
archetypes: &mut Archetypes,
Expand All @@ -887,7 +888,7 @@ impl BundleInfo {
observers: &Observers,
archetype_id: ArchetypeId,
intersection: bool,
) -> Option<ArchetypeId> {
) -> Option<ArchetypeIdState> {
// Check the archetype graph to see if the bundle has been
// removed from this archetype in the past.
let archetype_after_remove_result = {
Expand All @@ -900,7 +901,7 @@ impl BundleInfo {
};
let result = if let Some(result) = archetype_after_remove_result {
// This bundle removal result is cached. Just return that!
result
result.map(ArchetypeIdState::old)
} else {
let mut next_table_components;
let mut next_sparse_set_components;
Expand Down Expand Up @@ -963,15 +964,16 @@ impl BundleInfo {
Some(new_archetype_id)
};
let current_archetype = &mut archetypes[archetype_id];
let archetype_id_result = result.as_ref().map(ArchetypeIdState::id);
// Cache the result in an edge.
if intersection {
current_archetype
.edges_mut()
.cache_archetype_after_bundle_remove(self.id(), result);
.cache_archetype_after_bundle_remove(self.id(), archetype_id_result);
} else {
current_archetype
.edges_mut()
.cache_archetype_after_bundle_take(self.id(), result);
.cache_archetype_after_bundle_take(self.id(), archetype_id_result);
}
result
}
Expand Down Expand Up @@ -1036,14 +1038,15 @@ impl<'w> BundleInserter<'w> {
// SAFETY: We will not make any accesses to the command queue, component or resource data of this world
let bundle_info = world.bundles.get_unchecked(bundle_id);
let bundle_id = bundle_info.id();
let new_archetype_id = bundle_info.insert_bundle_into_archetype(
let mut archetype_id_state = bundle_info.insert_bundle_into_archetype(
&mut world.archetypes,
&mut world.storages,
&world.components,
&world.observers,
archetype_id,
);
if new_archetype_id == archetype_id {

let inserter = if archetype_id_state.id() == archetype_id {
let archetype = &mut world.archetypes[archetype_id];
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
let archetype_after_insert = unsafe {
Expand All @@ -1064,8 +1067,9 @@ impl<'w> BundleInserter<'w> {
world: world.as_unsafe_world_cell(),
}
} else {
let (archetype, new_archetype) =
world.archetypes.get_2_mut(archetype_id, new_archetype_id);
let (archetype, new_archetype) = world
.archetypes
.get_2_mut(archetype_id, archetype_id_state.id());
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
let archetype_after_insert = unsafe {
archetype
Expand Down Expand Up @@ -1103,7 +1107,11 @@ impl<'w> BundleInserter<'w> {
world: world.as_unsafe_world_cell(),
}
}
}
};

archetype_id_state.trigger_if_new(&mut inserter.world.into_deferred());

inserter
}

/// # Safety
Expand Down Expand Up @@ -1421,7 +1429,7 @@ impl<'w> BundleRemover<'w> {
) -> Option<Self> {
let bundle_info = world.bundles.get_unchecked(bundle_id);
// SAFETY: Caller ensures archetype and bundle ids are correct.
let new_archetype_id = unsafe {
let mut new_archetype_id = unsafe {
bundle_info.remove_bundle_from_archetype(
&mut world.archetypes,
&mut world.storages,
Expand All @@ -1431,11 +1439,13 @@ impl<'w> BundleRemover<'w> {
!require_all,
)?
};
if new_archetype_id == archetype_id {
if new_archetype_id.id() == archetype_id {
return None;
}
let (old_archetype, new_archetype) =
world.archetypes.get_2_mut(archetype_id, new_archetype_id);

let (old_archetype, new_archetype) = world
.archetypes
.get_2_mut(archetype_id, new_archetype_id.id());

let tables = if old_archetype.table_id() == new_archetype.table_id() {
None
Expand All @@ -1447,13 +1457,17 @@ impl<'w> BundleRemover<'w> {
Some((old.into(), new.into()))
};

Some(Self {
let remover = Self {
bundle_info: bundle_info.into(),
new_archetype: new_archetype.into(),
old_archetype: old_archetype.into(),
old_and_new_table: tables,
world: world.as_unsafe_world_cell(),
})
};

new_archetype_id.trigger_if_new(&mut remover.world.into_deferred());

Some(remover)
}

/// This can be passed to [`remove`](Self::remove) as the `pre_remove` function if you don't want to do anything before removing.
Expand Down Expand Up @@ -1675,22 +1689,24 @@ impl<'w> BundleSpawner<'w> {
change_tick: Tick,
) -> Self {
let bundle_info = world.bundles.get_unchecked(bundle_id);
let new_archetype_id = bundle_info.insert_bundle_into_archetype(
let mut archetype_id_state = bundle_info.insert_bundle_into_archetype(
&mut world.archetypes,
&mut world.storages,
&world.components,
&world.observers,
ArchetypeId::EMPTY,
);
let archetype = &mut world.archetypes[new_archetype_id];
let archetype = &mut world.archetypes[archetype_id_state.id()];
let table = &mut world.storages.tables[archetype.table_id()];
Self {
let spawner = Self {
bundle_info: bundle_info.into(),
table: table.into(),
archetype: archetype.into(),
change_tick,
world: world.as_unsafe_world_cell(),
}
};
archetype_id_state.trigger_if_new(&mut spawner.world.into_deferred());
spawner
}

#[inline]
Expand Down
Loading
Loading