@@ -4,23 +4,47 @@ use std::marker::PhantomData;
44use bevy_ecs:: {
55 archetype:: { Archetype , ArchetypeComponentId } ,
66 component:: { ComponentId , Tick } ,
7- prelude:: { Changed , Entity , Or , World } ,
7+ prelude:: { Changed , Entity , Or , Resource , World } ,
88 query:: { Access , FilteredAccess , QueryItem , ReadFetch , ReadOnlyWorldQuery , WorldQuery } ,
99 storage:: { Table , TableRow } ,
1010 world:: unsafe_world_cell:: UnsafeWorldCell ,
1111} ;
1212use bevy_utils:: HashMap ;
1313
14- use crate :: { asset_changes :: AssetChanges , Asset , Handle , HandleId } ;
14+ use crate :: { Asset , AssetId , Handle } ;
1515
16- #[ derive( Clone , Copy ) ]
17- struct AssetChangeCheck < ' w > {
18- handle_change_map : & ' w HashMap < HandleId , Tick > ,
16+ #[ doc( hidden) ]
17+ #[ derive( Resource ) ]
18+ pub struct AssetChanges < A : Asset > {
19+ pub ( crate ) change_ticks : HashMap < AssetId < A > , Tick > ,
20+ pub ( crate ) last_change_tick : Tick ,
21+ }
22+ impl < A : Asset > Default for AssetChanges < A > {
23+ fn default ( ) -> Self {
24+ Self {
25+ change_ticks : Default :: default ( ) ,
26+ last_change_tick : Tick :: new ( 0 ) ,
27+ }
28+ }
29+ }
30+
31+ struct AssetChangeCheck < ' w , A : Asset > {
32+ handle_change_map : & ' w HashMap < AssetId < A > , Tick > ,
1933 last_run : Tick ,
2034 this_run : Tick ,
2135}
22- impl < ' w > AssetChangeCheck < ' w > {
23- fn new < T : Asset > ( changes : & ' w AssetChanges < T > , last_run : Tick , this_run : Tick ) -> Self {
36+ impl < A : Asset > Clone for AssetChangeCheck < ' _ , A > {
37+ fn clone ( & self ) -> Self {
38+ AssetChangeCheck {
39+ handle_change_map : self . handle_change_map ,
40+ last_run : self . last_run ,
41+ this_run : self . this_run ,
42+ }
43+ }
44+ }
45+ impl < A : Asset > Copy for AssetChangeCheck < ' _ , A > { }
46+ impl < ' w , A : Asset > AssetChangeCheck < ' w , A > {
47+ fn new ( changes : & ' w AssetChanges < A > , last_run : Tick , this_run : Tick ) -> Self {
2448 Self {
2549 handle_change_map : & changes. change_ticks ,
2650 last_run,
@@ -29,7 +53,7 @@ impl<'w> AssetChangeCheck<'w> {
2953 }
3054 // TODO(perf): some sort of caching? Each check has two levels of indirection,
3155 // which is not optimal.
32- fn has_changed < T : Asset > ( & self , handle : & Handle < T > ) -> bool {
56+ fn has_changed ( & self , handle : & Handle < A > ) -> bool {
3357 if let Some ( & handle_change_tick) = self . handle_change_map . get ( & handle. id ( ) ) {
3458 handle_change_tick. is_newer_than ( self . last_run , self . this_run )
3559 } else {
@@ -38,29 +62,29 @@ impl<'w> AssetChangeCheck<'w> {
3862 }
3963}
4064
41- /// A shortcut for the commonly used `Or<(Changed<Handle<T >>, AssetChanged<T >)>`
65+ /// A shortcut for the commonly used `Or<(Changed<Handle<A >>, AssetChanged<A >)>`
4266/// query.
4367///
44- /// If you want to react to changes to `Handle<T >` component of an entity, you have
68+ /// If you want to react to changes to `Handle<A >` component of an entity, you have
4569/// two cases to worry about:
46- /// - The `Handle<T >` was changed through a `Query<&mut Handle<T >>`.
47- /// - The `T ` in `Assets<T >` pointed by `Handle<T >` was changed through a
48- /// `ResMut<Assets<T >>::get_mut`.
70+ /// - The `Handle<A >` was changed through a `Query<&mut Handle<A >>`.
71+ /// - The `A ` in `Assets<A >` pointed by `Handle<A >` was changed through a
72+ /// `ResMut<Assets<A >>::get_mut`.
4973///
5074/// To properly handle both those cases, you need to combine the `Changed` and `AssetChanged`
5175/// filters. This query type does it for you.
52- pub type AssetOrHandleChanged < T > = Or < ( Changed < Handle < T > > , AssetChanged < T > ) > ;
76+ pub type AssetOrHandleChanged < A > = Or < ( Changed < Handle < A > > , AssetChanged < A > ) > ;
5377
54- /// Query filter to get all entities which `Handle<T >` component underlying asset changed.
78+ /// Query filter to get all entities which `Handle<A >` component underlying asset changed.
5579///
56- /// Unlike `Changed<Handle<T >>`, this filter returns entities for which **the underlying `T `
80+ /// Unlike `Changed<Handle<A >>`, this filter returns entities for which **the underlying `A `
5781/// asset** was changed. For example, when modifying a `Mesh`, you would do so through the
58- /// [`Assets<Mesh>::get_mut`] method. This does not change the `Handle<T >` that entities have
59- /// as component, so `Changed<Handle<T >>` **will not do anything**. While with `AssetChanged<T >`,
82+ /// [`Assets<Mesh>::get_mut`] method. This does not change the `Handle<A >` that entities have
83+ /// as component, so `Changed<Handle<A >>` **will not do anything**. While with `AssetChanged<A >`,
6084/// all entities with the modified mesh will be queried.
6185///
6286/// # Notes
63- /// **performance**: This reads a hashmap once per entity with a `Handle<T >` component, this might
87+ /// **performance**: This reads a hashmap once per entity with a `Handle<A >` component, this might
6488/// result in slowdown, particularly if there are a lot of such entities. Note that if there was no
6589/// updates, it will simply skip everything.
6690///
@@ -73,15 +97,15 @@ pub type AssetOrHandleChanged<T> = Or<(Changed<Handle<T>>, AssetChanged<T>)>;
7397///
7498/// [`AssetStage::AssetEvents`]: crate::AssetStage::AssetEvents
7599/// [`Assets<Mesh>::get_mut`]: crate::Assets::get_mut
76- pub struct AssetChanged < T > ( PhantomData < T > ) ;
100+ pub struct AssetChanged < A > ( PhantomData < A > ) ;
77101
78102/// Fetch for [`AssetChanged`].
79103#[ doc( hidden) ]
80- pub struct AssetChangedFetch < ' w , T : Asset > {
81- inner : Option < ReadFetch < ' w , Handle < T > > > ,
82- check : AssetChangeCheck < ' w > ,
104+ pub struct AssetChangedFetch < ' w , A : Asset > {
105+ inner : Option < ReadFetch < ' w , Handle < A > > > ,
106+ check : AssetChangeCheck < ' w , A > ,
83107}
84- impl < ' w , T : Asset > Clone for AssetChangedFetch < ' w , T > {
108+ impl < ' w , A : Asset > Clone for AssetChangedFetch < ' w , A > {
85109 fn clone ( & self ) -> Self {
86110 Self {
87111 inner : self . inner ,
@@ -92,31 +116,31 @@ impl<'w, T: Asset> Clone for AssetChangedFetch<'w, T> {
92116
93117/// State for [`AssetChanged`].
94118#[ doc( hidden) ]
95- pub struct AssetChangedState < T : Asset > {
119+ pub struct AssetChangedState < A : Asset > {
96120 handle_id : ComponentId ,
97121 asset_changes_id : ComponentId ,
98122 asset_changes_archetype_id : ArchetypeComponentId ,
99- _asset : PhantomData < fn ( T ) > ,
123+ _asset : PhantomData < fn ( A ) > ,
100124}
101125
102126// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
103- unsafe impl < T : Asset > WorldQuery for AssetChanged < T > {
127+ unsafe impl < A : Asset > WorldQuery for AssetChanged < A > {
104128 type ReadOnly = Self ;
105- type Fetch < ' w > = AssetChangedFetch < ' w , T > ;
106- type State = AssetChangedState < T > ;
129+ type Fetch < ' w > = AssetChangedFetch < ' w , A > ;
130+ type State = AssetChangedState < A > ;
107131
108132 type Item < ' w > = bool ;
109133
110134 fn shrink < ' wlong : ' wshort , ' wshort > ( item : QueryItem < ' wlong , Self > ) -> QueryItem < ' wshort , Self > {
111135 item
112136 }
113137
114- fn init_state ( world : & mut World ) -> AssetChangedState < T > {
138+ fn init_state ( world : & mut World ) -> AssetChangedState < A > {
115139 let asset_changes_resource_id =
116- if let Some ( id) = world. components ( ) . resource_id :: < AssetChanges < T > > ( ) {
140+ if let Some ( id) = world. components ( ) . resource_id :: < AssetChanges < A > > ( ) {
117141 id
118142 } else {
119- world. init_resource :: < AssetChanges < T > > ( )
143+ world. init_resource :: < AssetChanges < A > > ( )
120144 } ;
121145 let asset_changes_archetype_id = world
122146 . storages ( )
@@ -125,7 +149,7 @@ unsafe impl<T: Asset> WorldQuery for AssetChanged<T> {
125149 . unwrap ( )
126150 . id ( ) ;
127151 AssetChangedState {
128- handle_id : world. init_component :: < Handle < T > > ( ) ,
152+ handle_id : world. init_component :: < Handle < A > > ( ) ,
129153 asset_changes_id : asset_changes_resource_id,
130154 asset_changes_archetype_id,
131155 _asset : PhantomData ,
@@ -146,27 +170,27 @@ unsafe impl<T: Asset> WorldQuery for AssetChanged<T> {
146170 ) -> Self :: Fetch < ' w > {
147171 let err_msg = || {
148172 panic ! (
149- "AssetChanges<{ty}> was removed, please do not remove AssetChanges<{ty}> when using AssetChanged<{ty}>" ,
150- ty = std:: any:: type_name:: <T >( )
151- )
173+ "AssetChanges<{ty}> was removed, please do not remove AssetChanges<{ty}> when using AssetChanged<{ty}>" ,
174+ ty = std:: any:: type_name:: <A >( )
175+ )
152176 } ;
153177 let changes: & AssetChanges < _ > = world. get_resource ( ) . unwrap_or_else ( err_msg) ;
154178 let has_updates = changes. last_change_tick . is_newer_than ( last_run, this_run) ;
155179 AssetChangedFetch {
156180 inner : has_updates
157181 . then ( || <& _ >:: init_fetch ( world, & state. handle_id , last_run, this_run) ) ,
158- check : AssetChangeCheck :: new :: < T > ( changes, last_run, this_run) ,
182+ check : AssetChangeCheck :: new ( changes, last_run, this_run) ,
159183 }
160184 }
161185
162- const IS_DENSE : bool = <& Handle < T > >:: IS_DENSE ;
186+ const IS_DENSE : bool = <& Handle < A > >:: IS_DENSE ;
163187
164188 // This Fetch filters more than just the archetype.
165189 const IS_ARCHETYPAL : bool = false ;
166190
167191 unsafe fn set_table < ' w > ( fetch : & mut Self :: Fetch < ' w > , state : & Self :: State , table : & ' w Table ) {
168192 if let Some ( inner) = & mut fetch. inner {
169- <& Handle < T > >:: set_table ( inner, & state. handle_id , table) ;
193+ <& Handle < A > >:: set_table ( inner, & state. handle_id , table) ;
170194 }
171195 }
172196
@@ -177,7 +201,7 @@ unsafe impl<T: Asset> WorldQuery for AssetChanged<T> {
177201 table : & ' w Table ,
178202 ) {
179203 if let Some ( inner) = & mut fetch. inner {
180- <& Handle < T > >:: set_archetype ( inner, & state. handle_id , archetype, table) ;
204+ <& Handle < A > >:: set_archetype ( inner, & state. handle_id , archetype, table) ;
181205 }
182206 }
183207
@@ -187,7 +211,7 @@ unsafe impl<T: Asset> WorldQuery for AssetChanged<T> {
187211 table_row : TableRow ,
188212 ) -> Self :: Item < ' w > {
189213 fetch. inner . as_mut ( ) . map_or ( false , |inner| {
190- let handle = <& Handle < T > >:: fetch ( inner, entity, table_row) ;
214+ let handle = <& Handle < A > >:: fetch ( inner, entity, table_row) ;
191215 fetch. check . has_changed ( handle)
192216 } )
193217 }
@@ -203,7 +227,7 @@ unsafe impl<T: Asset> WorldQuery for AssetChanged<T> {
203227
204228 #[ inline]
205229 fn update_component_access ( state : & Self :: State , access : & mut FilteredAccess < ComponentId > ) {
206- <& Handle < T > >:: update_component_access ( & state. handle_id , access) ;
230+ <& Handle < A > >:: update_component_access ( & state. handle_id , access) ;
207231 access. add_read ( state. asset_changes_id ) ;
208232 }
209233
@@ -214,17 +238,20 @@ unsafe impl<T: Asset> WorldQuery for AssetChanged<T> {
214238 access : & mut Access < ArchetypeComponentId > ,
215239 ) {
216240 access. add_read ( state. asset_changes_archetype_id ) ;
217- <& Handle < T > >:: update_archetype_component_access ( & state. handle_id , archetype, access) ;
241+ <& Handle < A > >:: update_archetype_component_access ( & state. handle_id , archetype, access) ;
218242 }
219243}
220244
221245/// SAFETY: read-only access
222- unsafe impl < T : Asset > ReadOnlyWorldQuery for AssetChanged < T > { }
246+ unsafe impl < A : Asset > ReadOnlyWorldQuery for AssetChanged < A > { }
223247
224248#[ cfg( test) ]
225249mod tests {
226- use crate :: { AddAsset , Assets } ;
250+ use crate :: { self as bevy_asset, AssetPlugin } ;
251+
252+ use crate :: { AssetApp , Assets } ;
227253 use bevy_app:: { App , AppExit , Startup , Update } ;
254+ use bevy_core:: TaskPoolPlugin ;
228255 use bevy_ecs:: {
229256 entity:: Entity ,
230257 event:: EventWriter ,
@@ -235,16 +262,15 @@ mod tests {
235262
236263 use super :: * ;
237264
238- #[ derive( bevy_reflect:: TypeUuid , TypePath ) ]
239- #[ uuid = "44115972-f31b-46e5-be5c-2b9aece6a52f" ]
265+ #[ derive( Asset , TypePath , Debug ) ]
240266 struct MyAsset ( u32 , & ' static str ) ;
241267
242268 fn run_app < Marker > ( system : impl IntoSystem < ( ) , ( ) , Marker > ) {
243269 let mut app = App :: new ( ) ;
244- app. add_plugins ( ( bevy_core :: FrameCountPlugin , crate :: AssetPlugin :: default ( ) ) )
245- . add_asset :: < MyAsset > ( )
270+ app. add_plugins ( ( TaskPoolPlugin :: default ( ) , AssetPlugin :: default ( ) ) )
271+ . init_asset :: < MyAsset > ( )
246272 . add_systems ( Update , system) ;
247- app. run ( ) ;
273+ app. update ( ) ;
248274 }
249275
250276 #[ test]
@@ -301,7 +327,7 @@ mod tests {
301327
302328 #[ test]
303329 fn asset_change ( ) {
304- #[ derive( Default , Resource ) ]
330+ #[ derive( Default , PartialEq , Debug , Resource ) ]
305331 struct Counter ( u32 , u32 ) ;
306332
307333 let mut app = App :: new ( ) ;
@@ -324,12 +350,11 @@ mod tests {
324350
325351 fn update_once ( mut assets : ResMut < Assets < MyAsset > > , mut run_count : Local < u32 > ) {
326352 let mut update_index = |i| {
327- let handle = assets
353+ let id = assets
328354 . iter ( )
329355 . find_map ( |( h, a) | ( a. 0 == i) . then_some ( h) )
330356 . unwrap ( ) ;
331- let handle = assets. get_handle ( handle) ;
332- let asset = assets. get_mut ( & handle) . unwrap ( ) ;
357+ let asset = assets. get_mut ( id) . unwrap ( ) ;
333358 asset. 1 = "new_value" ;
334359 } ;
335360 match * run_count {
@@ -339,8 +364,8 @@ mod tests {
339364 } ;
340365 * run_count += 1 ;
341366 }
342- app. add_plugins ( ( bevy_core :: FrameCountPlugin , crate :: AssetPlugin :: default ( ) ) )
343- . add_asset :: < MyAsset > ( )
367+ app. add_plugins ( ( TaskPoolPlugin :: default ( ) , AssetPlugin :: default ( ) ) )
368+ . init_asset :: < MyAsset > ( )
344369 . init_resource :: < Counter > ( )
345370 . add_systems (
346371 Startup ,
@@ -355,10 +380,10 @@ mod tests {
355380 } ,
356381 )
357382 . add_systems ( Update , ( count_update, update_once) . chain ( ) ) ;
358- let assert_counter = |app : & App , assert : Counter | {
383+ #[ track_caller]
384+ fn assert_counter ( app : & App , assert : Counter ) {
359385 let counter = app. world . get_resource :: < Counter > ( ) . unwrap ( ) ;
360- assert_eq ! ( counter. 0 , assert. 0 ) ;
361- assert_eq ! ( counter. 1 , assert. 1 ) ;
386+ assert_eq ! ( counter, & assert) ;
362387 } ;
363388 // First run of the app, `add_systems(Startup…)` runs.
364389 app. update ( ) ; // run_count == 0
0 commit comments