-
-
Couldn't load subscription status.
- Fork 4.2k
Construct and deconstruct entities to improve entity allocation #19451
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
base: main
Are you sure you want to change the base?
Changes from 59 commits
6a7c2e9
65a7ea9
4c6f613
4eab25c
8556b19
f4e06ad
3df6ab0
7b044c0
5c58266
01d6785
c100f9e
ea9a3be
0c1c9c3
5a69ebf
019c154
58ee663
85b0d03
4844dda
7e39f9d
9cce28f
b415dc8
38a9710
8956adc
0c194b7
180b349
20a6ee3
068a2a9
763877f
9baf15a
63effde
7967807
866476e
531658c
d899b38
eabc340
9df7a76
6a596cb
5ca8e38
91f439c
9d86e69
d86d241
d0ed587
80479d0
185a6de
4f956bd
6bbfee8
b543a4a
10f11ba
e78deec
5422813
e1b5386
271c189
9de86df
0b3da34
fb897d2
1b6f327
5085d4f
20386ff
4c0d752
ab0856b
71675a3
fdf1b55
f9b26d6
e129d77
d42d9ea
bac7bd2
e9df300
3fcbb24
d0ca8b1
4bb74b2
deb6b4d
5a058c0
e73f7bf
3062966
01aa792
caf0581
9c56341
9981716
762c65b
b743a44
2add4ca
97047a9
fedc90a
ecf617f
6d12925
45780c2
57816f7
5c69793
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,21 @@ | ||
| use alloc::{boxed::Box, collections::VecDeque, vec::Vec}; | ||
| use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet}; | ||
| use bevy_ptr::{Ptr, PtrMut}; | ||
| use bevy_utils::prelude::DebugName; | ||
| use bumpalo::Bump; | ||
| use core::{any::TypeId, cell::LazyCell, ops::Range}; | ||
| use derive_more::derive::From; | ||
|
|
||
| use crate::{ | ||
| archetype::Archetype, | ||
| bundle::{Bundle, BundleId, InsertMode}, | ||
| component::{Component, ComponentCloneBehavior, ComponentCloneFn, ComponentId, ComponentInfo}, | ||
| entity::{hash_map::EntityHashMap, Entities, Entity, EntityMapper}, | ||
| entity::{hash_map::EntityHashMap, Entity, EntityMapper}, | ||
| query::DebugCheckedUnwrap, | ||
| relationship::RelationshipHookMode, | ||
| world::World, | ||
| }; | ||
| use alloc::{boxed::Box, collections::VecDeque, vec::Vec}; | ||
| use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet}; | ||
| use bevy_ptr::{Ptr, PtrMut}; | ||
| use bevy_utils::prelude::DebugName; | ||
| use bumpalo::Bump; | ||
| use core::{any::TypeId, cell::LazyCell, ops::Range}; | ||
| use derive_more::From; | ||
|
|
||
| use super::EntitiesAllocator; | ||
|
|
||
| /// Provides read access to the source component (the component being cloned) in a [`ComponentCloneFn`]. | ||
| pub struct SourceComponent<'a> { | ||
|
|
@@ -78,7 +79,7 @@ pub struct ComponentCloneCtx<'a, 'b> { | |
| target_component_written: bool, | ||
| bundle_scratch: &'a mut BundleScratch<'b>, | ||
| bundle_scratch_allocator: &'b Bump, | ||
| entities: &'a Entities, | ||
| allocator: &'a EntitiesAllocator, | ||
| source: Entity, | ||
| target: Entity, | ||
| component_info: &'a ComponentInfo, | ||
|
|
@@ -104,7 +105,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { | |
| target: Entity, | ||
| bundle_scratch_allocator: &'b Bump, | ||
| bundle_scratch: &'a mut BundleScratch<'b>, | ||
| entities: &'a Entities, | ||
| allocator: &'a EntitiesAllocator, | ||
| component_info: &'a ComponentInfo, | ||
| entity_cloner: &'a mut EntityClonerState, | ||
| mapper: &'a mut dyn EntityMapper, | ||
|
|
@@ -118,7 +119,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { | |
| bundle_scratch, | ||
| target_component_written: false, | ||
| bundle_scratch_allocator, | ||
| entities, | ||
| allocator, | ||
| mapper, | ||
| component_info, | ||
| state: entity_cloner, | ||
|
|
@@ -271,7 +272,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { | |
|
|
||
| /// Queues the `entity` to be cloned by the current [`EntityCloner`] | ||
| pub fn queue_entity_clone(&mut self, entity: Entity) { | ||
| let target = self.entities.reserve_entity(); | ||
| let target = self.allocator.alloc(); | ||
| self.mapper.set_mapped(entity, target); | ||
| self.state.clone_queue.push_back(entity); | ||
| } | ||
|
|
@@ -536,6 +537,10 @@ impl EntityCloner { | |
| relationship_hook_insert_mode: RelationshipHookMode, | ||
| ) -> Entity { | ||
| let target = mapper.get_mapped(source); | ||
| // The target may need to be constructed if it hasn't been already. | ||
| // If this fails, it either didn't need to be constructed (ok) or doesn't exist (caught better later). | ||
| let _ = world.construct_empty(target); | ||
|
|
||
| // PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`. | ||
| let bundle_scratch_allocator = Bump::new(); | ||
| let mut bundle_scratch: BundleScratch; | ||
|
|
@@ -554,14 +559,17 @@ impl EntityCloner { | |
| #[cfg(not(feature = "bevy_reflect"))] | ||
| let app_registry = Option::<()>::None; | ||
|
|
||
| let source_archetype = source_entity.archetype(); | ||
| let source_archetype = source_entity | ||
| .archetype() | ||
| .expect("Source entity must exist constructed"); | ||
| bundle_scratch = BundleScratch::with_capacity(source_archetype.component_count()); | ||
|
|
||
| let target_archetype = LazyCell::new(|| { | ||
| world | ||
| .get_entity(target) | ||
| .expect("Target entity must exist") | ||
| .archetype() | ||
| .expect("Target entity must exist constructed") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the "(caught better later)" mentioned in a comment further up? Is this considered to be better because the alternative would be panicking above and unsafely unwrap here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the two expects are. Before, this didn't worry about the unconstructed/null case. So the additional construct and expect lines just let us continue in that assumption for the rest of this. |
||
| }); | ||
|
|
||
| filter.clone_components(source_archetype, target_archetype, |component| { | ||
|
|
@@ -598,7 +606,7 @@ impl EntityCloner { | |
| target, | ||
| &bundle_scratch_allocator, | ||
| &mut bundle_scratch, | ||
| world.entities(), | ||
| world.entities_allocator(), | ||
| info, | ||
| state, | ||
| mapper, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these expects actually more useful than an unwrap with a comment above?
,expect() wastes binary space with strings, often gives worse error messages, and don't get automatically updated as the error type changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up collapsing them together. Now, instead of "expect the entity exists" and "expect it is constructed" it is just one "expect the entity exists and is constructed". I think that strikes a good balance.