radiant_utils/misc/
loops.rs

1use std::time::{Instant, Duration};
2use std::thread;
3
4/// Passed to `renderloop()` and `mainloop()` callbacks.
5#[derive(Copy, Clone)]
6pub struct LoopState {
7    /// Time between frames in as Duration.
8    pub delta       : Duration,
9    /// Time between frames in seconds.
10    pub delta_f32   : f32,
11    /// Time since loop was started as Duration.
12    pub elapsed     : Duration,
13    /// Time since loop was started in seconds.
14    pub elapsed_f32 : f32,
15    /// Current frame id.
16    pub frame_id    : u64,
17    /// Current framerate.
18    pub fps         : u32,
19    /// Current state id for `mainloop()`'s `state_callback`.
20    pub state_id    : u32,
21}
22
23/// Main loop helper function. Provides callbacks for game state changes and rendering. Calls
24/// state_callback multiple times if the actual render_callback call interval exceeds the given interval.
25/// Both callbacks receive a LoopState object containing frame delta and fps data.
26pub fn mainloop<F, G>(interval: Duration, mut state_callback: F, mut render_callback: G) where F: FnMut(LoopState) -> bool, G: FnMut(LoopState) -> bool {
27
28    let mut accumulator = Duration::new(0, 0);
29    let mut previous_clock = Instant::now();
30    let start_clock = previous_clock;
31
32    let second = Duration::new(1, 0);
33    let mut second_elapsed = Duration::new(0, 0);
34    let mut frames_elapsed = 0;
35    let mut fps = 0;
36    let mut frame_id = 0;
37
38    loop {
39
40        let now = Instant::now();
41        let delta = now - previous_clock;
42        let elapsed = now - start_clock;
43
44        let mut state_info = LoopState {
45            delta       : delta,
46            delta_f32   : delta.as_secs() as f32 + (delta.subsec_nanos() as f64 / 1000000000.0) as f32,
47            elapsed     : elapsed,
48            elapsed_f32 : elapsed.as_secs() as f32 + (elapsed.subsec_nanos() as f64 / 1000000000.0) as f32,
49            frame_id    : frame_id,
50            fps         : fps,
51            state_id    : 0,
52        };
53
54        if render_callback(state_info) == false {
55            break;
56        }
57
58        // determine thread sleep to maintain X FPS
59        accumulator += delta;
60
61        while accumulator >= interval {
62            accumulator -= interval;
63            if state_callback(state_info) == false {
64                break;
65            }
66            state_info.state_id += 1;
67        }
68
69        // framerate print
70        second_elapsed += now - previous_clock;
71        frames_elapsed += 1;
72        frame_id += 1;
73
74        if second_elapsed >= second {
75            fps = frames_elapsed;
76            second_elapsed -= second;
77            frames_elapsed = 0;
78        }
79
80        previous_clock = now;
81        thread::sleep(interval - accumulator);
82    }
83}
84
85/// Render loop helper function. Provides given callback with frame delta and fps data.
86/// This is a more simple alternative to the main loop helper function.
87pub fn renderloop<G>(mut render_callback: G) where G: FnMut(LoopState) -> bool {
88
89    let mut previous_clock = Instant::now();
90    let start_clock = previous_clock;
91
92    let second = Duration::new(1, 0);
93    let mut second_elapsed = Duration::new(0, 0);
94    let mut frames_elapsed = 0;
95    let mut fps = 0;
96    let mut frame_id = 0;
97
98    loop {
99
100        let now = Instant::now();
101        let delta = now - previous_clock;
102        let elapsed = now - start_clock;
103
104        let state_info = LoopState {
105            delta       : delta,
106            delta_f32   : delta.as_secs() as f32 + (delta.subsec_nanos() as f64 / 1000000000.0) as f32,
107            elapsed     : elapsed,
108            elapsed_f32 : elapsed.as_secs() as f32 + (elapsed.subsec_nanos() as f64 / 1000000000.0) as f32,
109            frame_id    : frame_id,
110            fps         : fps,
111            state_id    : 0,
112        };
113
114        if render_callback(state_info) == false {
115            break;
116        }
117
118        // framerate print
119        second_elapsed += now - previous_clock;
120        frames_elapsed += 1;
121        frame_id += 1;
122
123        if second_elapsed >= second {
124            fps = frames_elapsed;
125            second_elapsed -= second;
126            frames_elapsed = 0;
127        }
128
129        previous_clock = now;
130    }
131}