1
2use std::time::{Duration, Instant};
3
4const NANOS_PER_SECOND: f64 = 1_000_000_000.0;
5
6pub struct FixedStep {
7 last_time: Instant,
8 update_interval: Duration,
9 accumulator: Duration,
10 update_counter: u32,
11 update_limit: u32,
12}
13
14impl FixedStep {
15 pub fn start(hz: f64) -> Self {
17 let seconds = 1.0 / hz;
18 let full_seconds = seconds as u64;
19 let remaining_nanos = (seconds.fract() * NANOS_PER_SECOND) as u32;
20 FixedStep {
21 update_interval: Duration::new(full_seconds, remaining_nanos),
22 last_time: Instant::now(),
23 accumulator: Duration::new(0, 0),
24 update_counter: 0,
25 update_limit: 3,
26 }
27 }
28
29 pub fn limit(mut self, limit: u32) -> Self {
35 self.update_limit = limit;
36 self
37 }
38
39 pub fn unlimit(mut self) -> Self {
41 self.update_limit = ::std::u32::MAX;
42 self
43 }
44
45 pub fn reset(&mut self) {
47 self.last_time = Instant::now();
48 self.update_counter = 0;
49 self.accumulator = Duration::new(0, 0);
50 }
51
52 pub fn update(&mut self) -> bool {
54 let now = Instant::now();
55 self.accumulator += now - self.last_time;
56 self.last_time = now;
57 if self.accumulator >= self.update_interval {
58 self.update_counter += 1;
60 if self.update_counter > self.update_limit {
61 self.accumulator = Duration::new(0, 0);
64 self.update_counter = 0;
65 false
66 } else {
67 self.accumulator -= self.update_interval;
68 true
69 }
70 } else {
71 false
73 }
74 }
75
76 pub fn render_delta(&mut self) -> f64 {
80 self.update_counter = 0;
81 duration_to_float(self.accumulator) / duration_to_float(self.update_interval)
82 }
83}
84
85fn duration_to_float(dur: Duration) -> f64 {
86 (dur.as_secs() as f64 + dur.subsec_nanos() as f64 / NANOS_PER_SECOND)
87}
88
89#[deprecated]
91#[macro_export]
92macro_rules! fixedstep_loop {
93 {
94 Step($ticks:expr, $skip:expr),
95 Update => $Update:block,
96 Render($delta:pat) => $Render:block,
97 } => {
98 {
99 use std::time::{Duration, Instant};
100 let ticks = 1.0 / $ticks as f64;
101 let ticks_s = ticks as u64;
102 let ticks_ns = (ticks.fract() * 1000_000_000.0) as u32;
103 let update_interval = Duration::new(ticks_s, ticks_ns);
104 let skip_threshold: i32 = 3;
105
106 let mut last = Instant::now();
107 let mut accumulator = Duration::new(0, 0);
108
109 let mut should_close = false;
110
111 while !should_close
112 {
113 let now = Instant::now();
114 accumulator += now - last;
115 last = now;
116
117 let mut update_count = 0;
118 while accumulator > update_interval && update_count < skip_threshold
119 {
120 should_close = $Update;
121 update_count += 1;
122 accumulator -= update_interval;
123 }
124
125 if $skip && accumulator > update_interval
128 {
129 accumulator = Duration::new(0, 0);
130 }
131
132 let elapsed = last.elapsed();
133 let $delta = ((elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1000_000_000.0) / ticks).min(1.0);
134 $Render
135 }
136 }
137 };
138 {
139 Step($ticks:expr),
140 Update => $Update:block,
141 Render($delta:pat) => $Render:block,
142 } => {
143 fixedstep_loop!(
144 Step($ticks, true),
145 Update => $Update,
146 Render($delta) => $Render,
147 )
148 };
149 {
150 Update => $Update:block,
151 Render($delta:pat) => $Render:block,
152 } => {
153 fixedstep_loop!(
154 Step(60),
155 Update => $Update,
156 Render($delta) => $Render,
157 )
158 };
159}