3
3
//! To measure performance realistically, be sure to run this in release mode.
4
4
//! `cargo run --example many_cubes --release`
5
5
//!
6
- //! By default, this arranges the meshes in a cubical pattern, where the number of visible meshes
7
- //! varies with the viewing angle. You can choose to run the demo with a spherical pattern that
6
+ //! By default, this arranges the meshes in a spherical pattern that
8
7
//! distributes the meshes evenly.
9
8
//!
10
- //! To start the demo using the spherical layout run
11
- //! `cargo run --example many_cubes --release sphere`
9
+ //! See `cargo run --example many_cubes --release -- --help` for more options.
12
10
13
- use std:: f64:: consts:: PI ;
11
+ use std:: { f64:: consts:: PI , str :: FromStr } ;
14
12
13
+ use argh:: FromArgs ;
15
14
use bevy:: {
16
15
diagnostic:: { FrameTimeDiagnosticsPlugin , LogDiagnosticsPlugin } ,
17
16
math:: { DVec2 , DVec3 } ,
18
17
prelude:: * ,
18
+ render:: render_resource:: { Extent3d , TextureDimension , TextureFormat } ,
19
19
window:: { PresentMode , WindowPlugin } ,
20
20
} ;
21
+ use rand:: { rngs:: StdRng , seq:: SliceRandom , Rng , SeedableRng } ;
22
+
23
+ #[ derive( FromArgs , Resource ) ]
24
+ /// `many_cubes` stress test
25
+ struct Args {
26
+ /// how the cube instances should be positioned.
27
+ #[ argh( option, default = "Layout::Sphere" ) ]
28
+ layout : Layout ,
29
+
30
+ /// whether to step the camera animation by a fixed amount such that each frame is the same across runs.
31
+ #[ argh( switch) ]
32
+ benchmark : bool ,
33
+
34
+ /// whether to vary the material data in each instance.
35
+ #[ argh( switch) ]
36
+ vary_material_data : bool ,
37
+
38
+ /// the number of different textures from which to randomly select the material base color. 0 means no textures.
39
+ #[ argh( option, default = "0" ) ]
40
+ material_texture_count : usize ,
41
+ }
42
+
43
+ #[ derive( Default , Clone ) ]
44
+ enum Layout {
45
+ Cube ,
46
+ #[ default]
47
+ Sphere ,
48
+ }
49
+
50
+ impl FromStr for Layout {
51
+ type Err = String ;
52
+
53
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
54
+ match s {
55
+ "cube" => Ok ( Self :: Cube ) ,
56
+ "sphere" => Ok ( Self :: Sphere ) ,
57
+ _ => Err ( format ! (
58
+ "Unknown layout value: '{}', valid options: 'cube', 'sphere'" ,
59
+ s
60
+ ) ) ,
61
+ }
62
+ }
63
+ }
21
64
22
65
fn main ( ) {
66
+ let args: Args = argh:: from_env ( ) ;
67
+
23
68
App :: new ( )
24
69
. add_plugins ( (
25
70
DefaultPlugins . set ( WindowPlugin {
@@ -32,28 +77,47 @@ fn main() {
32
77
FrameTimeDiagnosticsPlugin ,
33
78
LogDiagnosticsPlugin :: default ( ) ,
34
79
) )
80
+ . insert_resource ( args)
35
81
. add_systems ( Startup , setup)
36
82
. add_systems ( Update , ( move_camera, print_mesh_count) )
37
83
. run ( ) ;
38
84
}
39
85
40
86
fn setup (
41
87
mut commands : Commands ,
88
+ args : Res < Args > ,
42
89
mut meshes : ResMut < Assets < Mesh > > ,
43
- mut materials : ResMut < Assets < StandardMaterial > > ,
90
+ materials : ResMut < Assets < StandardMaterial > > ,
91
+ images : ResMut < Assets < Image > > ,
44
92
) {
45
93
warn ! ( include_str!( "warning_string.txt" ) ) ;
46
94
95
+ let args = args. into_inner ( ) ;
96
+ let images = images. into_inner ( ) ;
97
+ let materials = materials. into_inner ( ) ;
98
+
47
99
const WIDTH : usize = 200 ;
48
100
const HEIGHT : usize = 200 ;
101
+
49
102
let mesh = meshes. add ( Mesh :: from ( shape:: Cube { size : 1.0 } ) ) ;
50
- let material = materials. add ( StandardMaterial {
51
- base_color : Color :: PINK ,
52
- ..default ( )
53
- } ) ;
54
103
55
- match std:: env:: args ( ) . nth ( 1 ) . as_deref ( ) {
56
- Some ( "sphere" ) => {
104
+ let material_textures = init_textures ( args, images) ;
105
+
106
+ let material = materials. add ( Color :: PINK . into ( ) ) ;
107
+ let mut texture_rng = StdRng :: seed_from_u64 ( 42 ) ;
108
+ let texture_material = if args. material_texture_count == 1 {
109
+ Some ( materials. add ( StandardMaterial {
110
+ base_color : Color :: WHITE ,
111
+ base_color_texture : get_texture ( args, & mut texture_rng, & material_textures) ,
112
+ ..default ( )
113
+ } ) )
114
+ } else {
115
+ None
116
+ } ;
117
+
118
+ let mut color_rng = StdRng :: seed_from_u64 ( 42 ) ;
119
+ match args. layout {
120
+ Layout :: Sphere => {
57
121
// NOTE: This pattern is good for testing performance of culling as it provides roughly
58
122
// the same number of visible meshes regardless of the viewing angle.
59
123
const N_POINTS : usize = WIDTH * HEIGHT * 4 ;
@@ -66,7 +130,15 @@ fn setup(
66
130
let unit_sphere_p = spherical_polar_to_cartesian ( spherical_polar_theta_phi) ;
67
131
commands. spawn ( PbrBundle {
68
132
mesh : mesh. clone_weak ( ) ,
69
- material : material. clone_weak ( ) ,
133
+ material : get_material (
134
+ args,
135
+ & material,
136
+ texture_material. as_ref ( ) ,
137
+ materials,
138
+ & mut color_rng,
139
+ & mut texture_rng,
140
+ & material_textures,
141
+ ) ,
70
142
transform : Transform :: from_translation ( ( radius * unit_sphere_p) . as_vec3 ( ) ) ,
71
143
..default ( )
72
144
} ) ;
@@ -87,13 +159,29 @@ fn setup(
87
159
// cube
88
160
commands. spawn ( PbrBundle {
89
161
mesh : mesh. clone_weak ( ) ,
90
- material : material. clone_weak ( ) ,
162
+ material : get_material (
163
+ args,
164
+ & material,
165
+ texture_material. as_ref ( ) ,
166
+ materials,
167
+ & mut color_rng,
168
+ & mut texture_rng,
169
+ & material_textures,
170
+ ) ,
91
171
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 , 0.0 ) ,
92
172
..default ( )
93
173
} ) ;
94
174
commands. spawn ( PbrBundle {
95
175
mesh : mesh. clone_weak ( ) ,
96
- material : material. clone_weak ( ) ,
176
+ material : get_material (
177
+ args,
178
+ & material,
179
+ texture_material. as_ref ( ) ,
180
+ materials,
181
+ & mut color_rng,
182
+ & mut texture_rng,
183
+ & material_textures,
184
+ ) ,
97
185
transform : Transform :: from_xyz (
98
186
( x as f32 ) * 2.5 ,
99
187
HEIGHT as f32 * 2.5 ,
@@ -103,13 +191,29 @@ fn setup(
103
191
} ) ;
104
192
commands. spawn ( PbrBundle {
105
193
mesh : mesh. clone_weak ( ) ,
106
- material : material. clone_weak ( ) ,
194
+ material : get_material (
195
+ args,
196
+ & material,
197
+ texture_material. as_ref ( ) ,
198
+ materials,
199
+ & mut color_rng,
200
+ & mut texture_rng,
201
+ & material_textures,
202
+ ) ,
107
203
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , 0.0 , ( y as f32 ) * 2.5 ) ,
108
204
..default ( )
109
205
} ) ;
110
206
commands. spawn ( PbrBundle {
111
207
mesh : mesh. clone_weak ( ) ,
112
- material : material. clone_weak ( ) ,
208
+ material : get_material (
209
+ args,
210
+ & material,
211
+ texture_material. as_ref ( ) ,
212
+ materials,
213
+ & mut color_rng,
214
+ & mut texture_rng,
215
+ & material_textures,
216
+ ) ,
113
217
transform : Transform :: from_xyz ( 0.0 , ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 ) ,
114
218
..default ( )
115
219
} ) ;
@@ -139,6 +243,65 @@ fn setup(
139
243
commands. spawn ( DirectionalLightBundle { ..default ( ) } ) ;
140
244
}
141
245
246
+ fn init_textures ( args : & Args , images : & mut Assets < Image > ) -> Vec < Handle < Image > > {
247
+ let mut color_rng = StdRng :: seed_from_u64 ( 42 ) ;
248
+ let color_bytes: Vec < u8 > = ( 0 ..( args. material_texture_count * 4 ) )
249
+ . map ( |i| if ( i % 4 ) == 3 { 255 } else { color_rng. gen ( ) } )
250
+ . collect ( ) ;
251
+ color_bytes
252
+ . chunks ( 4 )
253
+ . map ( |pixel| {
254
+ images. add ( Image :: new_fill (
255
+ Extent3d {
256
+ width : 1 ,
257
+ height : 1 ,
258
+ depth_or_array_layers : 1 ,
259
+ } ,
260
+ TextureDimension :: D2 ,
261
+ pixel,
262
+ TextureFormat :: Rgba8UnormSrgb ,
263
+ ) )
264
+ } )
265
+ . collect ( )
266
+ }
267
+
268
+ fn get_texture < R : Rng > (
269
+ args : & Args ,
270
+ texture_rng : & mut R ,
271
+ material_textures : & [ Handle < Image > ] ,
272
+ ) -> Option < Handle < Image > > {
273
+ match args. material_texture_count {
274
+ c if c == 0 => None ,
275
+ c if c == 1 => Some ( material_textures[ 0 ] . clone_weak ( ) ) ,
276
+ _ => Some ( material_textures. choose ( texture_rng) . unwrap ( ) . clone_weak ( ) ) ,
277
+ }
278
+ }
279
+
280
+ fn get_material < R : Rng > (
281
+ args : & Args ,
282
+ material : & Handle < StandardMaterial > ,
283
+ texture_material : Option < & Handle < StandardMaterial > > ,
284
+ materials : & mut Assets < StandardMaterial > ,
285
+ color_rng : & mut R ,
286
+ texture_rng : & mut R ,
287
+ material_textures : & [ Handle < Image > ] ,
288
+ ) -> Handle < StandardMaterial > {
289
+ match ( args. vary_material_data , args. material_texture_count ) {
290
+ ( false , 0 ) => material. clone_weak ( ) ,
291
+ ( false , 1 ) => texture_material. unwrap ( ) . clone ( ) ,
292
+ ( false , _) => materials. add ( StandardMaterial {
293
+ base_color : Color :: WHITE ,
294
+ base_color_texture : get_texture ( args, texture_rng, material_textures) ,
295
+ ..default ( )
296
+ } ) ,
297
+ ( true , _) => materials. add ( StandardMaterial {
298
+ base_color : Color :: rgb_u8 ( color_rng. gen ( ) , color_rng. gen ( ) , color_rng. gen ( ) ) ,
299
+ base_color_texture : get_texture ( args, texture_rng, material_textures) ,
300
+ ..default ( )
301
+ } ) ,
302
+ }
303
+ }
304
+
142
305
// NOTE: This epsilon value is apparently optimal for optimizing for the average
143
306
// nearest-neighbor distance. See:
144
307
// http://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/
@@ -159,9 +322,18 @@ fn spherical_polar_to_cartesian(p: DVec2) -> DVec3 {
159
322
}
160
323
161
324
// System for rotating the camera
162
- fn move_camera ( time : Res < Time > , mut camera_query : Query < & mut Transform , With < Camera > > ) {
325
+ fn move_camera (
326
+ time : Res < Time > ,
327
+ args : Res < Args > ,
328
+ mut camera_query : Query < & mut Transform , With < Camera > > ,
329
+ ) {
163
330
let mut camera_transform = camera_query. single_mut ( ) ;
164
- let delta = time. delta_seconds ( ) * 0.15 ;
331
+ let delta = 0.15
332
+ * if args. benchmark {
333
+ 1.0 / 60.0
334
+ } else {
335
+ time. delta_seconds ( )
336
+ } ;
165
337
camera_transform. rotate_z ( delta) ;
166
338
camera_transform. rotate_x ( delta) ;
167
339
}
0 commit comments