Skip to main content

bunnymark/
bunnymark.rs

1//! Stress test: lots of bouncing crabs. Prints FPS and frame time once a second.
2//!
3//! Run in release mode, or the numbers mean nothing:
4//!   `cargo run --release --example bunnymark`
5//!   `CRABS=100000 cargo run --release --example bunnymark`
6//!
7//! - Hold left mouse: add 1,000 crabs per frame
8//! - Escape: quit
9//!
10//! Vsync is off so the FPS shows what the engine can actually do.
11//! Set `SECONDS=10` to quit automatically after that long (for scripted runs).
12
13use ponko::*;
14
15struct Crab {
16    pos: Vec2,
17    vel: Vec2,
18    color: Color,
19}
20
21/// Tiny deterministic RNG (xorshift) so runs are comparable.
22struct Rng(u64);
23impl Rng {
24    fn next_f32(&mut self) -> f32 {
25        self.0 ^= self.0 << 13;
26        self.0 ^= self.0 >> 7;
27        self.0 ^= self.0 << 17;
28        (self.0 >> 40) as f32 / (1u64 << 24) as f32
29    }
30}
31
32fn env_number(name: &str, default: f64) -> f64 {
33    std::env::var(name).ok().and_then(|v| v.parse().ok()).unwrap_or(default)
34}
35
36fn main() {
37    let start_count = env_number("CRABS", 10_000.0) as usize;
38    let quit_after = env_number("SECONDS", 0.0);
39
40    Game::new("Crabby Engine: bunnymark", 1280, 720).vsync(false).run(move |ctx| {
41        let crab = ctx.load_texture("crab.png");
42        let mut rng = Rng(0x9E37_79B9_7F4A_7C15);
43        let mut crabs: Vec<Crab> = Vec::new();
44
45        let mut spawn = move |crabs: &mut Vec<Crab>, n: usize, at: Vec2| {
46            crabs.reserve(n);
47            for _ in 0..n {
48                crabs.push(Crab {
49                    pos: at,
50                    vel: vec2(rng.next_f32() * 400.0 - 200.0, rng.next_f32() * 400.0 - 300.0),
51                    color: Color::rgb(0.6 + rng.next_f32() * 0.4, 0.6 + rng.next_f32() * 0.4, 0.6 + rng.next_f32() * 0.4),
52                });
53            }
54        };
55        spawn(&mut crabs, start_count, vec2(20.0, 20.0));
56
57        let mut report_at = 1.0;
58        let mut frames_since = 0u32;
59        let mut worst_ms = 0.0f32;
60        let mut frame_start = Instant::now();
61        let mut update_ms_sum = 0.0f64;
62
63        move |ctx: &mut Ctx| {
64            let now = Instant::now();
65            let frame_ms = now.duration_since(frame_start).as_secs_f32() * 1000.0;
66            frame_start = now;
67            worst_ms = worst_ms.max(frame_ms);
68            frames_since += 1;
69
70            if ctx.mouse_down(MouseButton::Left) {
71                spawn(&mut crabs, 1000, ctx.mouse_pos());
72            }
73
74            let update_start = Instant::now();
75            let dt = ctx.dt();
76            let bounds = ctx.window_size();
77            let gravity = 600.0;
78            for c in &mut crabs {
79                c.vel.y += gravity * dt;
80                c.pos += c.vel * dt;
81                if c.pos.x < 0.0 { c.pos.x = 0.0; c.vel.x = c.vel.x.abs(); }
82                if c.pos.x > bounds.x { c.pos.x = bounds.x; c.vel.x = -c.vel.x.abs(); }
83                if c.pos.y > bounds.y { c.pos.y = bounds.y; c.vel.y = -c.vel.y.abs() * 0.85; }
84                if c.pos.y < 0.0 { c.pos.y = 0.0; c.vel.y = 0.0; }
85                ctx.draw_ex(&crab, c.pos, DrawParams { color: c.color, ..Default::default() });
86            }
87            update_ms_sum += update_start.elapsed().as_secs_f64() * 1000.0;
88
89            if ctx.time() >= report_at {
90                println!(
91                    "crabs: {:>8}  fps: {:>6.1}  avg frame: {:>6.2} ms  worst: {:>6.2} ms  update+draw calls: {:>6.2} ms",
92                    crabs.len(),
93                    frames_since as f64 / 1.0,
94                    1000.0 / frames_since as f64,
95                    worst_ms,
96                    update_ms_sum / frames_since as f64,
97                );
98                report_at += 1.0;
99                frames_since = 0;
100                worst_ms = 0.0;
101                update_ms_sum = 0.0;
102            }
103
104            if ctx.key_pressed(Key::Escape) || (quit_after > 0.0 && ctx.time() >= quit_after) {
105                ctx.quit();
106            }
107        }
108    });
109}