Skip to content

Use bundle wrappers to select the insert mode #19768

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 3 additions & 2 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
#[inline]
fn get_components(
self,
func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
insert_mode: #ecs_path::bundle::InsertMode,
func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::bundle::InsertMode, #ecs_path::ptr::OwningPtr<'_>)
) {
#(<#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(self.#active_field_tokens, &mut *func);)*
#(<#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(self.#active_field_tokens, insert_mode, &mut *func);)*
}
}
};
Expand Down
226 changes: 134 additions & 92 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,11 @@ pub trait DynamicBundle {
/// Calls `func` on each value, in the order of this bundle's [`Component`]s. This passes
/// ownership of the component values to `func`.
#[doc(hidden)]
fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) -> Self::Effect;
fn get_components(
self,
insert_mode: InsertMode,
func: &mut impl FnMut(StorageType, InsertMode, OwningPtr<'_>),
) -> Self::Effect;
}

/// An operation on an [`Entity`] that occurs _after_ inserting the [`Bundle`] that defined this bundle effect.
Expand Down Expand Up @@ -316,8 +320,12 @@ unsafe impl<C: Component> BundleFromComponents for C {
impl<C: Component> DynamicBundle for C {
type Effect = ();
#[inline]
fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) -> Self::Effect {
OwningPtr::make(self, |ptr| func(C::STORAGE_TYPE, ptr));
fn get_components(
self,
insert_mode: InsertMode,
func: &mut impl FnMut(StorageType, InsertMode, OwningPtr<'_>),
) -> Self::Effect {
OwningPtr::make(self, |ptr| func(C::STORAGE_TYPE, insert_mode, ptr));
}
}

Expand Down Expand Up @@ -408,14 +416,14 @@ macro_rules! tuple_impl {
reason = "Zero-length tuples will generate a function body equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
)]
#[inline(always)]
fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) -> Self::Effect {
fn get_components(self, insert_mode: InsertMode, func: &mut impl FnMut(StorageType, InsertMode, OwningPtr<'_>)) -> Self::Effect {
#[allow(
non_snake_case,
reason = "The names of these variables are provided by the caller, not by us."
)]
let ($(mut $name,)*) = self;
($(
$name.get_components(&mut *func),
$name.get_components(insert_mode, &mut *func),
)*)
}
}
Expand Down Expand Up @@ -498,6 +506,66 @@ pub enum InsertMode {
Keep,
}

pub struct ReplaceIfCollides<B: Bundle>(pub B);
// SAFETY: This internally relies on the wrapped Bundle implementation, which is sound.
unsafe impl<B: Bundle> Bundle for ReplaceIfCollides<B> {
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
B::component_ids(components, ids);
}

fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
B::get_component_ids(components, ids);
}

fn register_required_components(
components: &mut ComponentsRegistrator,
required_components: &mut RequiredComponents,
) {
B::register_required_components(components, required_components);
}
}
impl<B: Bundle> DynamicBundle for ReplaceIfCollides<B> {
type Effect = ();

fn get_components(
self,
_insert_mode: InsertMode,
func: &mut impl FnMut(StorageType, InsertMode, OwningPtr<'_>),
) {
self.0.get_components(InsertMode::Replace, func);
}
}

pub struct IgnoreIfCollides<B: Bundle>(pub B);
// SAFETY: This internally relies on the wrapped Bundle implementation, which is sound.
unsafe impl<B: Bundle> Bundle for IgnoreIfCollides<B> {
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
B::component_ids(components, ids);
}

fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
B::get_component_ids(components, ids);
}

fn register_required_components(
components: &mut ComponentsRegistrator,
required_components: &mut RequiredComponents,
) {
B::register_required_components(components, required_components);
}
}
impl<B: Bundle> DynamicBundle for IgnoreIfCollides<B> {
type Effect = ();

fn get_components(
self,
_insert_mode: InsertMode,
func: &mut impl FnMut(StorageType, InsertMode, OwningPtr<'_>),
) {
self.0.get_components(InsertMode::Keep, func);
}
}

/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`].
///
/// [`World`]: crate::world::World
Expand Down Expand Up @@ -673,55 +741,57 @@ impl BundleInfo {
table_row: TableRow,
change_tick: Tick,
bundle: T,
insert_mode: InsertMode,
caller: MaybeLocation,
) -> T::Effect {
// NOTE: get_components calls this closure on each component in "bundle order".
// bundle_info.component_ids are also in "bundle order"
let mut bundle_component = 0;
let after_effect = bundle.get_components(&mut |storage_type, component_ptr| {
let component_id = *self.component_ids.get_unchecked(bundle_component);
// SAFETY: bundle_component is a valid index for this bundle
let status = unsafe { bundle_component_status.get_status(bundle_component) };
match storage_type {
StorageType::Table => {
let column =
let after_effect = bundle.get_components(
InsertMode::Replace,
&mut |storage_type, insert_mode, component_ptr| {
let component_id = *self.component_ids.get_unchecked(bundle_component);
// SAFETY: bundle_component is a valid index for this bundle
let status = unsafe { bundle_component_status.get_status(bundle_component) };
match storage_type {
StorageType::Table => {
let column =
// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
// the target table contains the component.
unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
match (status, insert_mode) {
(ComponentStatus::Added, _) => {
column.initialize(table_row, component_ptr, change_tick, caller);
}
(ComponentStatus::Existing, InsertMode::Replace) => {
column.replace(table_row, component_ptr, change_tick, caller);
}
(ComponentStatus::Existing, InsertMode::Keep) => {
if let Some(drop_fn) = table.get_drop_for(component_id) {
drop_fn(component_ptr);
match (status, insert_mode) {
(ComponentStatus::Added, _) => {
column.initialize(table_row, component_ptr, change_tick, caller);
}
(ComponentStatus::Existing, InsertMode::Replace) => {
column.replace(table_row, component_ptr, change_tick, caller);
}
(ComponentStatus::Existing, InsertMode::Keep) => {
if let Some(drop_fn) = table.get_drop_for(component_id) {
drop_fn(component_ptr);
}
}
}
}
}
StorageType::SparseSet => {
let sparse_set =
StorageType::SparseSet => {
let sparse_set =
// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
// a sparse set exists for the component.
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
match (status, insert_mode) {
(ComponentStatus::Added, _) | (_, InsertMode::Replace) => {
sparse_set.insert(entity, component_ptr, change_tick, caller);
}
(ComponentStatus::Existing, InsertMode::Keep) => {
if let Some(drop_fn) = sparse_set.get_drop() {
drop_fn(component_ptr);
match (status, insert_mode) {
(ComponentStatus::Added, _) | (_, InsertMode::Replace) => {
sparse_set.insert(entity, component_ptr, change_tick, caller);
}
(ComponentStatus::Existing, InsertMode::Keep) => {
if let Some(drop_fn) = sparse_set.get_drop() {
drop_fn(component_ptr);
}
}
}
}
}
}
bundle_component += 1;
});
bundle_component += 1;
},
);

for required_component in required_components {
required_component.initialize(
Expand Down Expand Up @@ -1173,7 +1243,6 @@ impl<'w> BundleInserter<'w> {
entity: Entity,
location: EntityLocation,
bundle: T,
insert_mode: InsertMode,
caller: MaybeLocation,
relationship_hook_mode: RelationshipHookMode,
) -> (EntityLocation, T::Effect) {
Expand All @@ -1187,23 +1256,22 @@ impl<'w> BundleInserter<'w> {
// SAFETY: Mutable references do not alias and will be dropped after this block
let mut deferred_world = self.world.into_deferred();

if insert_mode == InsertMode::Replace {
if archetype.has_replace_observer() {
deferred_world.trigger_observers(
REPLACE,
Some(entity),
archetype_after_insert.iter_existing(),
caller,
);
}
deferred_world.trigger_on_replace(
archetype,
entity,
// TEMP: not sure what to do with this
if archetype.has_replace_observer() {
deferred_world.trigger_observers(
REPLACE,
Some(entity),
archetype_after_insert.iter_existing(),
caller,
relationship_hook_mode,
);
}
deferred_world.trigger_on_replace(
archetype,
entity,
archetype_after_insert.iter_existing(),
caller,
relationship_hook_mode,
);
}

let table = self.table.as_mut();
Expand All @@ -1229,7 +1297,6 @@ impl<'w> BundleInserter<'w> {
location.table_row,
self.change_tick,
bundle,
insert_mode,
caller,
);

Expand Down Expand Up @@ -1270,7 +1337,6 @@ impl<'w> BundleInserter<'w> {
result.table_row,
self.change_tick,
bundle,
insert_mode,
caller,
);

Expand Down Expand Up @@ -1352,7 +1418,6 @@ impl<'w> BundleInserter<'w> {
move_result.new_row,
self.change_tick,
bundle,
insert_mode,
caller,
);

Expand Down Expand Up @@ -1381,44 +1446,22 @@ impl<'w> BundleInserter<'w> {
caller,
);
}
match insert_mode {
InsertMode::Replace => {
// Insert triggers for both new and existing components if we're replacing them.
deferred_world.trigger_on_insert(
new_archetype,
entity,
archetype_after_insert.iter_inserted(),
caller,
relationship_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
INSERT,
Some(entity),
archetype_after_insert.iter_inserted(),
caller,
);
}
}
InsertMode::Keep => {
// Insert triggers only for new components if we're not replacing them (since
// nothing is actually inserted).
deferred_world.trigger_on_insert(
new_archetype,
entity,
archetype_after_insert.iter_added(),
caller,
relationship_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
INSERT,
Some(entity),
archetype_after_insert.iter_added(),
caller,
);
}
}
// TEMP: not sure what to do with this
// Insert triggers for both new and existing components if we're replacing them.
deferred_world.trigger_on_insert(
new_archetype,
entity,
archetype_after_insert.iter_inserted(),
caller,
relationship_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
INSERT,
Some(entity),
archetype_after_insert.iter_inserted(),
caller,
);
}
}

Expand Down Expand Up @@ -1809,7 +1852,6 @@ impl<'w> BundleSpawner<'w> {
table_row,
self.change_tick,
bundle,
InsertMode::Replace,
caller,
);
entities.set(entity.index(), Some(location));
Expand Down
19 changes: 15 additions & 4 deletions crates/bevy_ecs/src/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,15 @@ impl<R: Relationship, L: SpawnableList<R>> DynamicBundle for SpawnRelatedBundle<

fn get_components(
self,
func: &mut impl FnMut(crate::component::StorageType, bevy_ptr::OwningPtr<'_>),
insert_mode: crate::bundle::InsertMode,
func: &mut impl FnMut(
crate::component::StorageType,
crate::bundle::InsertMode,
bevy_ptr::OwningPtr<'_>,
),
) -> Self::Effect {
<R::RelationshipTarget as RelationshipTarget>::with_capacity(self.list.size_hint())
.get_components(func);
.get_components(insert_mode, func);
self
}
}
Expand All @@ -244,9 +249,15 @@ impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {

fn get_components(
self,
func: &mut impl FnMut(crate::component::StorageType, bevy_ptr::OwningPtr<'_>),
insert_mode: crate::bundle::InsertMode,
func: &mut impl FnMut(
crate::component::StorageType,
crate::bundle::InsertMode,
bevy_ptr::OwningPtr<'_>,
),
) -> Self::Effect {
<R::RelationshipTarget as RelationshipTarget>::with_capacity(1).get_components(func);
<R::RelationshipTarget as RelationshipTarget>::with_capacity(1)
.get_components(insert_mode, func);
self
}
}
Expand Down
Loading
Loading