@@ -10,17 +10,18 @@ use crate::{
10
10
storage:: TableId ,
11
11
world:: { World , WorldId } ,
12
12
} ;
13
- use bevy_tasks:: TaskPool ;
13
+ use bevy_tasks:: { ComputeTaskPool , TaskPool } ;
14
14
#[ cfg( feature = "trace" ) ]
15
15
use bevy_utils:: tracing:: Instrument ;
16
16
use fixedbitset:: FixedBitSet ;
17
- use std:: fmt;
17
+ use std:: { fmt, ops :: Deref } ;
18
18
19
19
use super :: { QueryFetch , QueryItem , ROQueryFetch , ROQueryItem } ;
20
20
21
21
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
22
22
pub struct QueryState < Q : WorldQuery , F : WorldQuery = ( ) > {
23
23
world_id : WorldId ,
24
+ task_pool : Option < TaskPool > ,
24
25
pub ( crate ) archetype_generation : ArchetypeGeneration ,
25
26
pub ( crate ) matched_tables : FixedBitSet ,
26
27
pub ( crate ) matched_archetypes : FixedBitSet ,
@@ -61,6 +62,9 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
61
62
62
63
let mut state = Self {
63
64
world_id : world. id ( ) ,
65
+ task_pool : world
66
+ . get_resource :: < ComputeTaskPool > ( )
67
+ . map ( |task_pool| task_pool. deref ( ) . clone ( ) ) ,
64
68
archetype_generation : ArchetypeGeneration :: initial ( ) ,
65
69
matched_table_ids : Vec :: new ( ) ,
66
70
matched_archetype_ids : Vec :: new ( ) ,
@@ -689,15 +693,18 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
689
693
) ;
690
694
}
691
695
692
- /// Runs `func` on each query result in parallel using the given `task_pool` .
696
+ /// Runs `func` on each query result in parallel.
693
697
///
694
698
/// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for
695
699
/// write-queries.
700
+ ///
701
+ /// # Panics
702
+ /// The [`ComputeTaskPool`] resource must be added to the `World` before using this method. If using this from a query
703
+ /// that is being initialized and run from the ECS scheduler, this should never panic.
696
704
#[ inline]
697
705
pub fn par_for_each < ' w , FN : Fn ( ROQueryItem < ' w , Q > ) + Send + Sync + Clone > (
698
706
& mut self ,
699
707
world : & ' w World ,
700
- task_pool : & TaskPool ,
701
708
batch_size : usize ,
702
709
func : FN ,
703
710
) {
@@ -706,7 +713,6 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
706
713
self . update_archetypes ( world) ;
707
714
self . par_for_each_unchecked_manual :: < ROQueryFetch < Q > , FN > (
708
715
world,
709
- task_pool,
710
716
batch_size,
711
717
func,
712
718
world. last_change_tick ( ) ,
@@ -715,12 +721,15 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
715
721
}
716
722
}
717
723
718
- /// Runs `func` on each query result in parallel using the given `task_pool`.
724
+ /// Runs `func` on each query result in parallel.
725
+ ///
726
+ /// # Panics
727
+ /// The [`ComputeTaskPool`] resource must be added to the `World` before using this method. If using this from a query
728
+ /// that is being initialized and run from the ECS scheduler, this should never panic.
719
729
#[ inline]
720
730
pub fn par_for_each_mut < ' w , FN : Fn ( QueryItem < ' w , Q > ) + Send + Sync + Clone > (
721
731
& mut self ,
722
732
world : & ' w mut World ,
723
- task_pool : & TaskPool ,
724
733
batch_size : usize ,
725
734
func : FN ,
726
735
) {
@@ -729,7 +738,6 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
729
738
self . update_archetypes ( world) ;
730
739
self . par_for_each_unchecked_manual :: < QueryFetch < Q > , FN > (
731
740
world,
732
- task_pool,
733
741
batch_size,
734
742
func,
735
743
world. last_change_tick ( ) ,
@@ -738,10 +746,14 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
738
746
}
739
747
}
740
748
741
- /// Runs `func` on each query result in parallel using the given `task_pool` .
749
+ /// Runs `func` on each query result in parallel.
742
750
///
743
751
/// This can only be called for read-only queries.
744
752
///
753
+ /// # Panics
754
+ /// [`ComputeTaskPool`] was not stored in the world at initialzation. If using this from a query
755
+ /// that is being initialized and run from the ECS scheduler, this should never panic.
756
+ ///
745
757
/// # Safety
746
758
///
747
759
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
@@ -750,14 +762,12 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
750
762
pub unsafe fn par_for_each_unchecked < ' w , FN : Fn ( QueryItem < ' w , Q > ) + Send + Sync + Clone > (
751
763
& mut self ,
752
764
world : & ' w World ,
753
- task_pool : & TaskPool ,
754
765
batch_size : usize ,
755
766
func : FN ,
756
767
) {
757
768
self . update_archetypes ( world) ;
758
769
self . par_for_each_unchecked_manual :: < QueryFetch < Q > , FN > (
759
770
world,
760
- task_pool,
761
771
batch_size,
762
772
func,
763
773
world. last_change_tick ( ) ,
@@ -833,6 +843,10 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
833
843
/// the current change tick are given. This is faster than the equivalent
834
844
/// iter() method, but cannot be chained like a normal [`Iterator`].
835
845
///
846
+ /// # Panics
847
+ /// [`ComputeTaskPool`] was not stored in the world at initialzation. If using this from a query
848
+ /// that is being initialized and run from the ECS scheduler, this should never panic.
849
+ ///
836
850
/// # Safety
837
851
///
838
852
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
@@ -846,103 +860,113 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
846
860
> (
847
861
& self ,
848
862
world : & ' w World ,
849
- task_pool : & TaskPool ,
850
863
batch_size : usize ,
851
864
func : FN ,
852
865
last_change_tick : u32 ,
853
866
change_tick : u32 ,
854
867
) {
855
868
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
856
869
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
857
- task_pool. scope ( |scope| {
858
- if QF :: IS_DENSE && <QueryFetch < ' static , F > >:: IS_DENSE {
859
- let tables = & world. storages ( ) . tables ;
860
- for table_id in & self . matched_table_ids {
861
- let table = & tables[ * table_id] ;
862
- let mut offset = 0 ;
863
- while offset < table. len ( ) {
864
- let func = func. clone ( ) ;
865
- let len = batch_size. min ( table. len ( ) - offset) ;
866
- let task = async move {
867
- let mut fetch =
868
- QF :: init ( world, & self . fetch_state , last_change_tick, change_tick) ;
869
- let mut filter = <QueryFetch < F > as Fetch >:: init (
870
- world,
871
- & self . filter_state ,
872
- last_change_tick,
873
- change_tick,
874
- ) ;
875
- let tables = & world. storages ( ) . tables ;
876
- let table = & tables[ * table_id] ;
877
- fetch. set_table ( & self . fetch_state , table) ;
878
- filter. set_table ( & self . filter_state , table) ;
879
- for table_index in offset..offset + len {
880
- if !filter. table_filter_fetch ( table_index) {
881
- continue ;
870
+ self . task_pool
871
+ . as_ref ( )
872
+ . expect ( "Cannot iterate query in parallel. No ComputeTaskPool initialized." )
873
+ . scope ( |scope| {
874
+ if QF :: IS_DENSE && <QueryFetch < ' static , F > >:: IS_DENSE {
875
+ let tables = & world. storages ( ) . tables ;
876
+ for table_id in & self . matched_table_ids {
877
+ let table = & tables[ * table_id] ;
878
+ let mut offset = 0 ;
879
+ while offset < table. len ( ) {
880
+ let func = func. clone ( ) ;
881
+ let len = batch_size. min ( table. len ( ) - offset) ;
882
+ let task = async move {
883
+ let mut fetch = QF :: init (
884
+ world,
885
+ & self . fetch_state ,
886
+ last_change_tick,
887
+ change_tick,
888
+ ) ;
889
+ let mut filter = <QueryFetch < F > as Fetch >:: init (
890
+ world,
891
+ & self . filter_state ,
892
+ last_change_tick,
893
+ change_tick,
894
+ ) ;
895
+ let tables = & world. storages ( ) . tables ;
896
+ let table = & tables[ * table_id] ;
897
+ fetch. set_table ( & self . fetch_state , table) ;
898
+ filter. set_table ( & self . filter_state , table) ;
899
+ for table_index in offset..offset + len {
900
+ if !filter. table_filter_fetch ( table_index) {
901
+ continue ;
902
+ }
903
+ let item = fetch. table_fetch ( table_index) ;
904
+ func ( item) ;
882
905
}
883
- let item = fetch. table_fetch ( table_index) ;
884
- func ( item) ;
885
- }
886
- } ;
887
- #[ cfg( feature = "trace" ) ]
888
- let span = bevy_utils:: tracing:: info_span!(
889
- "par_for_each" ,
890
- query = std:: any:: type_name:: <Q >( ) ,
891
- filter = std:: any:: type_name:: <F >( ) ,
892
- count = len,
893
- ) ;
894
- #[ cfg( feature = "trace" ) ]
895
- let task = task. instrument ( span) ;
896
- scope. spawn ( task) ;
897
- offset += batch_size;
898
- }
899
- }
900
- } else {
901
- let archetypes = & world. archetypes ;
902
- for archetype_id in & self . matched_archetype_ids {
903
- let mut offset = 0 ;
904
- let archetype = & archetypes[ * archetype_id] ;
905
- while offset < archetype. len ( ) {
906
- let func = func. clone ( ) ;
907
- let len = batch_size. min ( archetype. len ( ) - offset) ;
908
- let task = async move {
909
- let mut fetch =
910
- QF :: init ( world, & self . fetch_state , last_change_tick, change_tick) ;
911
- let mut filter = <QueryFetch < F > as Fetch >:: init (
912
- world,
913
- & self . filter_state ,
914
- last_change_tick,
915
- change_tick,
906
+ } ;
907
+ #[ cfg( feature = "trace" ) ]
908
+ let span = bevy_utils:: tracing:: info_span!(
909
+ "par_for_each" ,
910
+ query = std:: any:: type_name:: <Q >( ) ,
911
+ filter = std:: any:: type_name:: <F >( ) ,
912
+ count = len,
916
913
) ;
917
- let tables = & world. storages ( ) . tables ;
918
- let archetype = & world. archetypes [ * archetype_id] ;
919
- fetch. set_archetype ( & self . fetch_state , archetype, tables) ;
920
- filter. set_archetype ( & self . filter_state , archetype, tables) ;
921
-
922
- for archetype_index in offset..offset + len {
923
- if !filter. archetype_filter_fetch ( archetype_index) {
924
- continue ;
914
+ #[ cfg( feature = "trace" ) ]
915
+ let task = task. instrument ( span) ;
916
+ scope. spawn ( task) ;
917
+ offset += batch_size;
918
+ }
919
+ }
920
+ } else {
921
+ let archetypes = & world. archetypes ;
922
+ for archetype_id in & self . matched_archetype_ids {
923
+ let mut offset = 0 ;
924
+ let archetype = & archetypes[ * archetype_id] ;
925
+ while offset < archetype. len ( ) {
926
+ let func = func. clone ( ) ;
927
+ let len = batch_size. min ( archetype. len ( ) - offset) ;
928
+ let task = async move {
929
+ let mut fetch = QF :: init (
930
+ world,
931
+ & self . fetch_state ,
932
+ last_change_tick,
933
+ change_tick,
934
+ ) ;
935
+ let mut filter = <QueryFetch < F > as Fetch >:: init (
936
+ world,
937
+ & self . filter_state ,
938
+ last_change_tick,
939
+ change_tick,
940
+ ) ;
941
+ let tables = & world. storages ( ) . tables ;
942
+ let archetype = & world. archetypes [ * archetype_id] ;
943
+ fetch. set_archetype ( & self . fetch_state , archetype, tables) ;
944
+ filter. set_archetype ( & self . filter_state , archetype, tables) ;
945
+
946
+ for archetype_index in offset..offset + len {
947
+ if !filter. archetype_filter_fetch ( archetype_index) {
948
+ continue ;
949
+ }
950
+ func ( fetch. archetype_fetch ( archetype_index) ) ;
925
951
}
926
- func ( fetch. archetype_fetch ( archetype_index) ) ;
927
- }
928
- } ;
929
-
930
- #[ cfg( feature = "trace" ) ]
931
- let span = bevy_utils:: tracing:: info_span!(
932
- "par_for_each" ,
933
- query = std:: any:: type_name:: <Q >( ) ,
934
- filter = std:: any:: type_name:: <F >( ) ,
935
- count = len,
936
- ) ;
937
- #[ cfg( feature = "trace" ) ]
938
- let task = task. instrument ( span) ;
939
-
940
- scope. spawn ( task) ;
941
- offset += batch_size;
952
+ } ;
953
+
954
+ #[ cfg( feature = "trace" ) ]
955
+ let span = bevy_utils:: tracing:: info_span!(
956
+ "par_for_each" ,
957
+ query = std:: any:: type_name:: <Q >( ) ,
958
+ filter = std:: any:: type_name:: <F >( ) ,
959
+ count = len,
960
+ ) ;
961
+ #[ cfg( feature = "trace" ) ]
962
+ let task = task. instrument ( span) ;
963
+
964
+ scope. spawn ( task) ;
965
+ offset += batch_size;
966
+ }
942
967
}
943
968
}
944
- }
945
- } ) ;
969
+ } ) ;
946
970
}
947
971
948
972
/// Returns a single immutable query result when there is exactly one entity matching
0 commit comments