@@ -5,18 +5,159 @@ use std::{path::Path, time::Duration};
55
66use bevy_app:: App ;
77use bevy_asset:: {
8+ asset_value,
89 io:: {
910 memory:: { Dir , MemoryAssetReader } ,
1011 AssetSourceBuilder , AssetSourceId ,
1112 } ,
12- AssetApp , AssetLoader , AssetServer , Assets ,
13+ Asset , AssetApp , AssetLoader , AssetServer , Assets , Handle ,
1314} ;
1415use bevy_ecs:: prelude:: * ;
1516use bevy_scene:: { prelude:: * , ScenePatch } ;
1617use bevy_ui:: prelude:: * ;
1718
1819criterion_group ! ( benches, spawn) ;
1920
21+ fn spawn ( c : & mut Criterion ) {
22+ let mut group = c. benchmark_group ( "spawn" ) ;
23+ group. warm_up_time ( Duration :: from_millis ( 500 ) ) ;
24+ group. measurement_time ( Duration :: from_secs ( 4 ) ) ;
25+ group. bench_function ( "ui_immediate_function_scene" , |b| {
26+ let mut app = bench_app ( |_| { } , |_| { } ) ;
27+ b. iter ( move || {
28+ app. world_mut ( ) . spawn_scene ( ui ( ) ) . unwrap ( ) ;
29+ } ) ;
30+ } ) ;
31+ group. bench_function ( "ui_immediate_loaded_scene" , |b| {
32+ let dir = Dir :: default ( ) ;
33+ let mut app = bench_app (
34+ |app| {
35+ in_memory_asset_source ( dir. clone ( ) , app) ;
36+ } ,
37+ |app| {
38+ app. register_asset_loader ( FakeSceneLoader :: new ( button) ) ;
39+ } ,
40+ ) ;
41+
42+ // Insert an asset that the fake loader can fake read.
43+ dir. insert_asset_text ( Path :: new ( "button.bsn" ) , "" ) ;
44+
45+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
46+ let handle = asset_server. load ( "button.bsn" ) ;
47+
48+ run_app_until ( & mut app, || asset_server. is_loaded ( & handle) ) ;
49+
50+ let patch = app
51+ . world ( )
52+ . resource :: < Assets < ScenePatch > > ( )
53+ . get ( & handle)
54+ . unwrap ( ) ;
55+ assert ! ( patch. resolved. is_some( ) ) ;
56+
57+ b. iter ( move || {
58+ app. world_mut ( ) . spawn_scene ( ui_loaded_asset ( ) ) . unwrap ( ) ;
59+ } ) ;
60+
61+ drop ( handle) ;
62+ } ) ;
63+ group. bench_function ( "ui_raw_bundle_no_scene" , |b| {
64+ let mut app = bench_app ( |_| { } , |_| { } ) ;
65+
66+ b. iter ( move || {
67+ app. world_mut ( ) . spawn ( raw_ui ( ) ) ;
68+ } ) ;
69+ } ) ;
70+
71+ group. bench_function ( "handle_template_handle" , |b| {
72+ let dir = Dir :: default ( ) ;
73+ let mut app = bench_app (
74+ |app| {
75+ in_memory_asset_source ( dir. clone ( ) , app) ;
76+ } ,
77+ |app| {
78+ app. init_asset :: < EmptyAsset > ( ) ;
79+ let assets = app. world ( ) . resource :: < AssetServer > ( ) ;
80+ let handles = ( 0 ..10 ) . map ( |_| assets. add ( EmptyAsset ) ) . collect :: < Vec < _ > > ( ) ;
81+ app. register_asset_loader ( FakeSceneLoader :: new ( move || {
82+ asset_handle_scene ( handles. clone ( ) )
83+ } ) ) ;
84+ } ,
85+ ) ;
86+
87+ dir. insert_asset_text ( Path :: new ( "a.bsn" ) , "" ) ;
88+
89+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
90+ let handle = asset_server. load :: < ScenePatch > ( "a.bsn" ) ;
91+
92+ run_app_until ( & mut app, || asset_server. is_loaded ( & handle) ) ;
93+
94+ let world = app. world_mut ( ) ;
95+ b. iter ( || {
96+ for _ in 0 ..100 {
97+ world. spawn_scene ( bsn ! { : "a.bsn" } ) . unwrap ( ) ;
98+ }
99+ } ) ;
100+ } ) ;
101+
102+ group. bench_function ( "handle_template_value" , |b| {
103+ let dir = Dir :: default ( ) ;
104+ let mut app = bench_app (
105+ |app| {
106+ in_memory_asset_source ( dir. clone ( ) , app) ;
107+ } ,
108+ |app| {
109+ app. register_asset_loader ( FakeSceneLoader :: new ( asset_value_scene) ) ;
110+ app. init_asset :: < EmptyAsset > ( ) ;
111+ } ,
112+ ) ;
113+
114+ dir. insert_asset_text ( Path :: new ( "a.bsn" ) , "" ) ;
115+
116+ let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
117+ let handle = asset_server. load :: < ScenePatch > ( "a.bsn" ) ;
118+
119+ run_app_until ( & mut app, || asset_server. is_loaded ( & handle) ) ;
120+
121+ let world = app. world_mut ( ) ;
122+ b. iter ( || {
123+ for _ in 0 ..100 {
124+ world. spawn_scene ( bsn ! { : "a.bsn" } ) . unwrap ( ) ;
125+ }
126+ } ) ;
127+ } ) ;
128+ group. finish ( ) ;
129+ }
130+
131+ #[ derive( Asset , TypePath ) ]
132+ struct EmptyAsset ;
133+
134+ #[ derive( Component , FromTemplate ) ]
135+ #[ expect( unused, reason = "this is just used for init" ) ]
136+ struct AssetReference ( Handle < EmptyAsset > ) ;
137+
138+ fn asset_value_scene ( ) -> impl Scene {
139+ let children = ( 0 ..10 )
140+ . map ( |_| {
141+ bsn ! { AssetReference ( asset_value( EmptyAsset ) ) }
142+ } )
143+ . collect :: < Vec < _ > > ( ) ;
144+ bsn ! {
145+ Children [ { children} ]
146+ }
147+ }
148+
149+ fn asset_handle_scene ( mut handles : Vec < Handle < EmptyAsset > > ) -> impl Scene {
150+ let children = handles
151+ . drain ( ..)
152+ . map ( |handle| {
153+ bsn ! { AssetReference ( { handle. clone( ) } ) }
154+ } )
155+ . collect :: < Vec < _ > > ( ) ;
156+ bsn ! {
157+ Children [ { children} ]
158+ }
159+ }
160+
20161fn ui ( ) -> impl Scene {
21162 bsn ! {
22163 Node
@@ -209,88 +350,47 @@ fn run_app_until(app: &mut App, mut predicate: impl FnMut() -> bool) {
209350 panic ! ( "Ran out of loops to return `Some` from `predicate`" ) ;
210351}
211352
212- fn spawn ( c : & mut Criterion ) {
213- let mut group = c. benchmark_group ( "spawn" ) ;
214- group. warm_up_time ( Duration :: from_millis ( 500 ) ) ;
215- group. measurement_time ( Duration :: from_secs ( 4 ) ) ;
216- group. bench_function ( "ui_immediate_function_scene" , |b| {
217- let mut app = App :: new ( ) ;
218- app. add_plugins ( ( bevy_asset:: AssetPlugin :: default ( ) , bevy_scene:: ScenePlugin ) ) ;
219-
220- b. iter ( move || {
221- app. world_mut ( ) . spawn_scene ( ui ( ) ) . unwrap ( ) ;
222- } ) ;
223- } ) ;
224- group. bench_function ( "ui_immediate_loaded_scene" , |b| {
225- let mut app = App :: new ( ) ;
226- let dir = Dir :: default ( ) ;
227- let dir_clone = dir. clone ( ) ;
228- app. register_asset_source (
229- AssetSourceId :: Default ,
230- AssetSourceBuilder :: new ( move || {
231- Box :: new ( MemoryAssetReader {
232- root : dir_clone. clone ( ) ,
233- } )
234- } ) ,
235- ) ;
236- app. add_plugins ( (
237- bevy_app:: TaskPoolPlugin :: default ( ) ,
238- bevy_asset:: AssetPlugin :: default ( ) ,
239- bevy_scene:: ScenePlugin ,
240- ) ) ;
241- app. finish ( ) ;
242- app. cleanup ( ) ;
243-
244- // Create a fake loader to act as a ScenePatch loaded from a file.
245- app. register_asset_loader ( FakeSceneLoader ) ;
246-
247- #[ derive( TypePath ) ]
248- struct FakeSceneLoader ;
249-
250- impl AssetLoader for FakeSceneLoader {
251- type Asset = ScenePatch ;
252- type Error = std:: io:: Error ;
253- type Settings = ( ) ;
254-
255- async fn load (
256- & self ,
257- _reader : & mut dyn bevy_asset:: io:: Reader ,
258- _settings : & Self :: Settings ,
259- load_context : & mut bevy_asset:: LoadContext < ' _ > ,
260- ) -> Result < Self :: Asset , Self :: Error > {
261- Ok ( ScenePatch :: load_with ( load_context, button ( ) ) )
262- }
263- }
264-
265- // Insert an asset that the fake loader can fake read.
266- dir. insert_asset_text ( Path :: new ( "button.bsn" ) , "" ) ;
267-
268- let asset_server = app. world ( ) . resource :: < AssetServer > ( ) . clone ( ) ;
269- let handle = asset_server. load ( "button.bsn" ) ;
270- assert ! ( app. world( ) . get_resource:: <Assets <ScenePatch >>( ) . is_some( ) ) ;
353+ fn bench_app ( before : impl FnOnce ( & mut App ) , after : impl FnOnce ( & mut App ) ) -> App {
354+ let mut app = App :: new ( ) ;
355+ before ( & mut app) ;
356+ app. add_plugins ( (
357+ bevy_app:: TaskPoolPlugin :: default ( ) ,
358+ bevy_asset:: AssetPlugin :: default ( ) ,
359+ bevy_scene:: ScenePlugin ,
360+ ) ) ;
361+ after ( & mut app) ;
362+ app. finish ( ) ;
363+ app. cleanup ( ) ;
364+ app
365+ }
271366
272- run_app_until ( & mut app, || asset_server. is_loaded ( & handle) ) ;
367+ fn in_memory_asset_source ( dir : Dir , app : & mut App ) {
368+ app. register_asset_source (
369+ AssetSourceId :: Default ,
370+ AssetSourceBuilder :: new ( move || Box :: new ( MemoryAssetReader { root : dir. clone ( ) } ) ) ,
371+ ) ;
372+ }
273373
274- let patch = app
275- . world ( )
276- . resource :: < Assets < ScenePatch > > ( )
277- . get ( & handle)
278- . unwrap ( ) ;
279- assert ! ( patch. resolved. is_some( ) ) ;
374+ #[ derive( TypePath ) ]
375+ struct FakeSceneLoader ( Box < dyn Fn ( ) -> Box < dyn Scene > + Send + Sync > ) ;
280376
281- b. iter ( move || {
282- app. world_mut ( ) . spawn_scene ( ui_loaded_asset ( ) ) . unwrap ( ) ;
283- } ) ;
377+ impl FakeSceneLoader {
378+ pub fn new < S : Scene > ( scene_fn : impl ( Fn ( ) -> S ) + Send + Sync + ' static ) -> Self {
379+ Self ( Box :: new ( move || Box :: new ( scene_fn ( ) ) ) )
380+ }
381+ }
284382
285- drop ( handle) ;
286- } ) ;
287- group. bench_function ( "ui_raw_bundle_no_scene" , |b| {
288- let mut app = App :: new ( ) ;
289- app. add_plugins ( ( bevy_asset:: AssetPlugin :: default ( ) , bevy_scene:: ScenePlugin ) ) ;
383+ impl AssetLoader for FakeSceneLoader {
384+ type Asset = ScenePatch ;
385+ type Error = std:: io:: Error ;
386+ type Settings = ( ) ;
290387
291- b. iter ( move || {
292- app. world_mut ( ) . spawn ( raw_ui ( ) ) ;
293- } ) ;
294- } ) ;
295- group. finish ( ) ;
388+ async fn load (
389+ & self ,
390+ _reader : & mut dyn bevy_asset:: io:: Reader ,
391+ _settings : & Self :: Settings ,
392+ load_context : & mut bevy_asset:: LoadContext < ' _ > ,
393+ ) -> Result < Self :: Asset , Self :: Error > {
394+ Ok ( ScenePatch :: load_with ( load_context, ( self . 0 ) ( ) ) )
395+ }
296396}
0 commit comments