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,36 @@ 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
86
+ const WIDTH : usize = 200 ;
87
+ const HEIGHT : usize = 200 ;
88
+
40
89
fn setup (
41
90
mut commands : Commands ,
91
+ args : Res < Args > ,
42
92
mut meshes : ResMut < Assets < Mesh > > ,
43
- mut materials : ResMut < Assets < StandardMaterial > > ,
93
+ material_assets : ResMut < Assets < StandardMaterial > > ,
94
+ images : ResMut < Assets < Image > > ,
44
95
) {
45
96
warn ! ( include_str!( "warning_string.txt" ) ) ;
46
97
47
- const WIDTH : usize = 200 ;
48
- const HEIGHT : usize = 200 ;
98
+ let args = args. into_inner ( ) ;
99
+ let images = images. into_inner ( ) ;
100
+ let material_assets = material_assets. into_inner ( ) ;
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
+ let materials = init_materials ( args, & material_textures, material_assets) ;
106
+
107
+ let mut material_rng = StdRng :: seed_from_u64 ( 42 ) ;
108
+ match args. layout {
109
+ Layout :: Sphere => {
57
110
// NOTE: This pattern is good for testing performance of culling as it provides roughly
58
111
// the same number of visible meshes regardless of the viewing angle.
59
112
const N_POINTS : usize = WIDTH * HEIGHT * 4 ;
@@ -65,8 +118,8 @@ fn setup(
65
118
fibonacci_spiral_on_sphere ( golden_ratio, i, N_POINTS ) ;
66
119
let unit_sphere_p = spherical_polar_to_cartesian ( spherical_polar_theta_phi) ;
67
120
commands. spawn ( PbrBundle {
68
- mesh : mesh. clone_weak ( ) ,
69
- material : material . clone_weak ( ) ,
121
+ mesh : mesh. clone ( ) ,
122
+ material : materials . choose ( & mut material_rng ) . unwrap ( ) . clone ( ) ,
70
123
transform : Transform :: from_translation ( ( radius * unit_sphere_p) . as_vec3 ( ) ) ,
71
124
..default ( )
72
125
} ) ;
@@ -86,14 +139,14 @@ fn setup(
86
139
}
87
140
// cube
88
141
commands. spawn ( PbrBundle {
89
- mesh : mesh. clone_weak ( ) ,
90
- material : material . clone_weak ( ) ,
142
+ mesh : mesh. clone ( ) ,
143
+ material : materials . choose ( & mut material_rng ) . unwrap ( ) . clone ( ) ,
91
144
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 , 0.0 ) ,
92
145
..default ( )
93
146
} ) ;
94
147
commands. spawn ( PbrBundle {
95
- mesh : mesh. clone_weak ( ) ,
96
- material : material . clone_weak ( ) ,
148
+ mesh : mesh. clone ( ) ,
149
+ material : materials . choose ( & mut material_rng ) . unwrap ( ) . clone ( ) ,
97
150
transform : Transform :: from_xyz (
98
151
( x as f32 ) * 2.5 ,
99
152
HEIGHT as f32 * 2.5 ,
@@ -102,14 +155,14 @@ fn setup(
102
155
..default ( )
103
156
} ) ;
104
157
commands. spawn ( PbrBundle {
105
- mesh : mesh. clone_weak ( ) ,
106
- material : material . clone_weak ( ) ,
158
+ mesh : mesh. clone ( ) ,
159
+ material : materials . choose ( & mut material_rng ) . unwrap ( ) . clone ( ) ,
107
160
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , 0.0 , ( y as f32 ) * 2.5 ) ,
108
161
..default ( )
109
162
} ) ;
110
163
commands. spawn ( PbrBundle {
111
- mesh : mesh. clone_weak ( ) ,
112
- material : material . clone_weak ( ) ,
164
+ mesh : mesh. clone ( ) ,
165
+ material : materials . choose ( & mut material_rng ) . unwrap ( ) . clone ( ) ,
113
166
transform : Transform :: from_xyz ( 0.0 , ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 ) ,
114
167
..default ( )
115
168
} ) ;
@@ -123,20 +176,67 @@ fn setup(
123
176
}
124
177
}
125
178
126
- // add one cube, the only one with strong handles
127
- // also serves as a reference point during rotation
128
- commands. spawn ( PbrBundle {
129
- mesh,
130
- material,
131
- transform : Transform {
132
- translation : Vec3 :: new ( 0.0 , HEIGHT as f32 * 2.5 , 0.0 ) ,
133
- scale : Vec3 :: splat ( 5.0 ) ,
134
- ..default ( )
135
- } ,
179
+ commands. spawn ( DirectionalLightBundle { ..default ( ) } ) ;
180
+ }
181
+
182
+ fn init_textures ( args : & Args , images : & mut Assets < Image > ) -> Vec < Handle < Image > > {
183
+ let mut color_rng = StdRng :: seed_from_u64 ( 42 ) ;
184
+ let color_bytes: Vec < u8 > = ( 0 ..( args. material_texture_count * 4 ) )
185
+ . map ( |i| if ( i % 4 ) == 3 { 255 } else { color_rng. gen ( ) } )
186
+ . collect ( ) ;
187
+ color_bytes
188
+ . chunks ( 4 )
189
+ . map ( |pixel| {
190
+ images. add ( Image :: new_fill (
191
+ Extent3d {
192
+ width : 1 ,
193
+ height : 1 ,
194
+ depth_or_array_layers : 1 ,
195
+ } ,
196
+ TextureDimension :: D2 ,
197
+ pixel,
198
+ TextureFormat :: Rgba8UnormSrgb ,
199
+ ) )
200
+ } )
201
+ . collect ( )
202
+ }
203
+
204
+ fn init_materials (
205
+ args : & Args ,
206
+ textures : & [ Handle < Image > ] ,
207
+ assets : & mut Assets < StandardMaterial > ,
208
+ ) -> Vec < Handle < StandardMaterial > > {
209
+ let capacity = if args. vary_material_data {
210
+ match args. layout {
211
+ Layout :: Cube => ( WIDTH - WIDTH / 10 ) * ( HEIGHT - HEIGHT / 10 ) ,
212
+ Layout :: Sphere => WIDTH * HEIGHT * 4 ,
213
+ }
214
+ } else {
215
+ args. material_texture_count
216
+ }
217
+ . max ( 1 ) ;
218
+
219
+ let mut materials = Vec :: with_capacity ( capacity) ;
220
+ materials. push ( assets. add ( StandardMaterial {
221
+ base_color : Color :: WHITE ,
222
+ base_color_texture : textures. get ( 0 ) . cloned ( ) ,
136
223
..default ( )
137
- } ) ;
224
+ } ) ) ;
138
225
139
- commands. spawn ( DirectionalLightBundle { ..default ( ) } ) ;
226
+ let mut color_rng = StdRng :: seed_from_u64 ( 42 ) ;
227
+ let mut texture_rng = StdRng :: seed_from_u64 ( 42 ) ;
228
+ materials. extend (
229
+ std:: iter:: repeat_with ( || {
230
+ assets. add ( StandardMaterial {
231
+ base_color : Color :: rgb_u8 ( color_rng. gen ( ) , color_rng. gen ( ) , color_rng. gen ( ) ) ,
232
+ base_color_texture : textures. choose ( & mut texture_rng) . cloned ( ) ,
233
+ ..default ( )
234
+ } )
235
+ } )
236
+ . take ( capacity - materials. len ( ) ) ,
237
+ ) ;
238
+
239
+ materials
140
240
}
141
241
142
242
// NOTE: This epsilon value is apparently optimal for optimizing for the average
@@ -159,9 +259,18 @@ fn spherical_polar_to_cartesian(p: DVec2) -> DVec3 {
159
259
}
160
260
161
261
// System for rotating the camera
162
- fn move_camera ( time : Res < Time > , mut camera_query : Query < & mut Transform , With < Camera > > ) {
262
+ fn move_camera (
263
+ time : Res < Time > ,
264
+ args : Res < Args > ,
265
+ mut camera_query : Query < & mut Transform , With < Camera > > ,
266
+ ) {
163
267
let mut camera_transform = camera_query. single_mut ( ) ;
164
- let delta = time. delta_seconds ( ) * 0.15 ;
268
+ let delta = 0.15
269
+ * if args. benchmark {
270
+ 1.0 / 60.0
271
+ } else {
272
+ time. delta_seconds ( )
273
+ } ;
165
274
camera_transform. rotate_z ( delta) ;
166
275
camera_transform. rotate_x ( delta) ;
167
276
}
0 commit comments