3
3
//! This module contains the [`Bundle`] trait and some other helper types.
4
4
5
5
pub use bevy_ecs_macros:: Bundle ;
6
- use bevy_utils:: HashSet ;
6
+ use bevy_utils:: { HashMap , HashSet } ;
7
7
8
8
use crate :: {
9
9
archetype:: {
@@ -135,10 +135,10 @@ use std::any::TypeId;
135
135
/// [`Query`]: crate::system::Query
136
136
// Some safety points:
137
137
// - [`Bundle::component_ids`] must return the [`ComponentId`] for each component type in the
138
- // bundle, in the _exact_ order that [`Bundle ::get_components`] is called.
138
+ // bundle, in the _exact_ order that [`DynamicBundle ::get_components`] is called.
139
139
// - [`Bundle::from_components`] must call `func` exactly once for each [`ComponentId`] returned by
140
140
// [`Bundle::component_ids`].
141
- pub unsafe trait Bundle : Send + Sync + ' static {
141
+ pub unsafe trait Bundle : DynamicBundle + Send + Sync + ' static {
142
142
/// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s
143
143
#[ doc( hidden) ]
144
144
fn component_ids (
@@ -159,7 +159,10 @@ pub unsafe trait Bundle: Send + Sync + 'static {
159
159
// Ensure that the `OwningPtr` is used correctly
160
160
F : for < ' a > FnMut ( & ' a mut T ) -> OwningPtr < ' a > ,
161
161
Self : Sized ;
162
+ }
162
163
164
+ /// The parts from [`Bundle`] that don't require statically knowing the components of the bundle.
165
+ pub trait DynamicBundle {
163
166
// SAFETY:
164
167
// The `StorageType` argument passed into [`Bundle::get_components`] must be correct for the
165
168
// component being fetched.
@@ -192,7 +195,9 @@ unsafe impl<C: Component> Bundle for C {
192
195
// Safety: The id given in `component_ids` is for `Self`
193
196
func ( ctx) . read ( )
194
197
}
198
+ }
195
199
200
+ impl < C : Component > DynamicBundle for C {
196
201
#[ inline]
197
202
fn get_components ( self , func : & mut impl FnMut ( StorageType , OwningPtr < ' _ > ) ) {
198
203
OwningPtr :: make ( self , |ptr| func ( C :: Storage :: STORAGE_TYPE , ptr) ) ;
@@ -203,7 +208,7 @@ macro_rules! tuple_impl {
203
208
( $( $name: ident) ,* ) => {
204
209
// SAFETY:
205
210
// - `Bundle::component_ids` calls `ids` for each component type in the
206
- // bundle, in the exact order that `Bundle ::get_components` is called.
211
+ // bundle, in the exact order that `DynamicBundle ::get_components` is called.
207
212
// - `Bundle::from_components` calls `func` exactly once for each `ComponentId` returned by `Bundle::component_ids`.
208
213
// - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct
209
214
// `StorageType` into the callback.
@@ -223,7 +228,9 @@ macro_rules! tuple_impl {
223
228
// https://doc.rust-lang.org/reference/expressions.html#evaluation-order-of-operands
224
229
( $( <$name as Bundle >:: from_components( ctx, func) , ) * )
225
230
}
231
+ }
226
232
233
+ impl <$( $name: Bundle ) ,* > DynamicBundle for ( $( $name, ) * ) {
227
234
#[ allow( unused_variables, unused_mut) ]
228
235
#[ inline( always) ]
229
236
fn get_components( self , func: & mut impl FnMut ( StorageType , OwningPtr <' _>) ) {
@@ -376,7 +383,7 @@ impl BundleInfo {
376
383
/// `entity`, `bundle` must match this [`BundleInfo`]'s type
377
384
#[ inline]
378
385
#[ allow( clippy:: too_many_arguments) ]
379
- unsafe fn write_components < T : Bundle , S : BundleComponentStatus > (
386
+ unsafe fn write_components < T : DynamicBundle , S : BundleComponentStatus > (
380
387
& self ,
381
388
table : & mut Table ,
382
389
sparse_sets : & mut SparseSets ,
@@ -527,7 +534,7 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
527
534
/// `entity` must currently exist in the source archetype for this inserter. `archetype_row`
528
535
/// must be `entity`'s location in the archetype. `T` must match this [`BundleInfo`]'s type
529
536
#[ inline]
530
- pub unsafe fn insert < T : Bundle > (
537
+ pub unsafe fn insert < T : DynamicBundle > (
531
538
& mut self ,
532
539
entity : Entity ,
533
540
location : EntityLocation ,
@@ -677,7 +684,7 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
677
684
/// # Safety
678
685
/// `entity` must be allocated (but non-existent), `T` must match this [`BundleInfo`]'s type
679
686
#[ inline]
680
- pub unsafe fn spawn_non_existent < T : Bundle > (
687
+ pub unsafe fn spawn_non_existent < T : DynamicBundle > (
681
688
& mut self ,
682
689
entity : Entity ,
683
690
bundle : T ,
@@ -712,7 +719,12 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
712
719
#[ derive( Default ) ]
713
720
pub struct Bundles {
714
721
bundle_infos : Vec < BundleInfo > ,
722
+ /// Cache static [`BundleId`]
715
723
bundle_ids : TypeIdMap < BundleId > ,
724
+ /// Cache dynamic [`BundleId`] with multiple components
725
+ dynamic_bundle_ids : HashMap < Vec < ComponentId > , ( BundleId , Vec < StorageType > ) > ,
726
+ /// Cache optimized dynamic [`BundleId`] with single component
727
+ dynamic_component_bundle_ids : HashMap < ComponentId , ( BundleId , StorageType ) > ,
716
728
}
717
729
718
730
impl Bundles {
@@ -726,6 +738,7 @@ impl Bundles {
726
738
self . bundle_ids . get ( & type_id) . cloned ( )
727
739
}
728
740
741
+ /// Initializes a new [`BundleInfo`] for a statically known type.
729
742
pub ( crate ) fn init_info < ' a , T : Bundle > (
730
743
& ' a mut self ,
731
744
components : & mut Components ,
@@ -745,6 +758,64 @@ impl Bundles {
745
758
// SAFETY: index either exists, or was initialized
746
759
unsafe { self . bundle_infos . get_unchecked ( id. 0 ) }
747
760
}
761
+
762
+ /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`].
763
+ ///
764
+ /// # Panics
765
+ ///
766
+ /// Panics if any of the provided [`ComponentId`]s do not exist in the
767
+ /// provided [`Components`].
768
+ pub ( crate ) fn init_dynamic_info (
769
+ & mut self ,
770
+ components : & mut Components ,
771
+ component_ids : & [ ComponentId ] ,
772
+ ) -> ( & BundleInfo , & Vec < StorageType > ) {
773
+ let bundle_infos = & mut self . bundle_infos ;
774
+
775
+ // Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry`
776
+ let ( _, ( bundle_id, storage_types) ) = self
777
+ . dynamic_bundle_ids
778
+ . raw_entry_mut ( )
779
+ . from_key ( component_ids)
780
+ . or_insert_with ( || {
781
+ (
782
+ Vec :: from ( component_ids) ,
783
+ initialize_dynamic_bundle ( bundle_infos, components, Vec :: from ( component_ids) ) ,
784
+ )
785
+ } ) ;
786
+
787
+ // SAFETY: index either exists, or was initialized
788
+ let bundle_info = unsafe { bundle_infos. get_unchecked ( bundle_id. 0 ) } ;
789
+
790
+ ( bundle_info, storage_types)
791
+ }
792
+
793
+ /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component.
794
+ ///
795
+ /// # Panics
796
+ ///
797
+ /// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].
798
+ pub ( crate ) fn init_component_info (
799
+ & mut self ,
800
+ components : & mut Components ,
801
+ component_id : ComponentId ,
802
+ ) -> ( & BundleInfo , StorageType ) {
803
+ let bundle_infos = & mut self . bundle_infos ;
804
+ let ( bundle_id, storage_types) = self
805
+ . dynamic_component_bundle_ids
806
+ . entry ( component_id)
807
+ . or_insert_with ( || {
808
+ let ( id, storage_type) =
809
+ initialize_dynamic_bundle ( bundle_infos, components, vec ! [ component_id] ) ;
810
+ // SAFETY: `storage_type` guaranteed to have length 1
811
+ ( id, storage_type[ 0 ] )
812
+ } ) ;
813
+
814
+ // SAFETY: index either exists, or was initialized
815
+ let bundle_info = unsafe { bundle_infos. get_unchecked ( bundle_id. 0 ) } ;
816
+
817
+ ( bundle_info, * storage_types)
818
+ }
748
819
}
749
820
750
821
/// # Safety
@@ -784,3 +855,28 @@ unsafe fn initialize_bundle(
784
855
785
856
BundleInfo { id, component_ids }
786
857
}
858
+
859
+ /// Asserts that all components are part of [`Components`]
860
+ /// and initializes a [`BundleInfo`].
861
+ fn initialize_dynamic_bundle (
862
+ bundle_infos : & mut Vec < BundleInfo > ,
863
+ components : & Components ,
864
+ component_ids : Vec < ComponentId > ,
865
+ ) -> ( BundleId , Vec < StorageType > ) {
866
+ // Assert component existence
867
+ let storage_types = component_ids. iter ( ) . map ( |& id| {
868
+ components. get_info ( id) . unwrap_or_else ( || {
869
+ panic ! (
870
+ "init_dynamic_info called with component id {id:?} which doesn't exist in this world"
871
+ )
872
+ } ) . storage_type ( )
873
+ } ) . collect ( ) ;
874
+
875
+ let id = BundleId ( bundle_infos. len ( ) ) ;
876
+ let bundle_info =
877
+ // SAFETY: `component_ids` are valid as they were just checked
878
+ unsafe { initialize_bundle ( "<dynamic bundle>" , components, component_ids, id) } ;
879
+ bundle_infos. push ( bundle_info) ;
880
+
881
+ ( id, storage_types)
882
+ }
0 commit comments