1 #include "core/command.hpp"
2 #include "core/framerate.hpp"
3 #include "cmdhelp/turbo.hpp"
4 #include "core/instance.hpp"
5 #include "core/keymapper.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/messages.hpp"
8 #include "library/minmax.hpp"
20 bool graphics_driver_is_dummy();
22 framerate_regulator::framerate_regulator(command::group
& _cmd
)
24 turbo_p(cmd
, CTURBO::p
, [this]() { this->turboed
= true; }),
25 turbo_r(cmd
, CTURBO::r
, [this]() { this->turboed
= false; }),
26 turbo_t(cmd
, CTURBO::t
, [this]() { this->turboed
= !this->turboed
; }),
27 setspeed_t(cmd
, CTURBO::ss
, [this](const std::string
& args
) { this->set_speed_cmd(args
); }),
28 spd_inc(cmd
, CTURBO::inc
, [this]() { this->increase_speed(); }),
29 spd_dec(cmd
, CTURBO::dec
, [this]() { this->decrease_speed(); })
31 framerate_realtime_locked
= false;
33 time_at_last_update
= 0;
36 for(unsigned i
= 0; i
< FRAMERATE_HISTORY_FRAMES
; i
++)
37 frame_start_times
[i
] = 0;
38 nominal_framerate
= 60;
39 multiplier_framerate
= 1;
43 //Set the speed multiplier. Note that INFINITE is a valid multiplier.
44 void framerate_regulator::set_speed_multiplier(double multiplier
) throw()
46 threads::alock
h(framerate_lock
);
47 multiplier_framerate
= multiplier
;
48 framerate_realtime_locked
= false;
51 //Get the speed multiplier. Note that this may be INFINITE.
52 double framerate_regulator::get_speed_multiplier() throw()
54 threads::alock
h(framerate_lock
);
55 return multiplier_framerate
;
58 void framerate_regulator::freeze_time(uint64_t curtime
)
60 get_time(curtime
, true);
64 void framerate_regulator::unfreeze_time(uint64_t curtime
)
67 last_time_update
= curtime
;
71 void framerate_regulator::set_nominal_framerate(double fps
) throw()
73 threads::alock
h(framerate_lock
);
74 double old_nominal_framerate
= nominal_framerate
;
75 nominal_framerate
= fps
;
76 //If framerate is realtime-locked, adjust the framerate multiplier as nominal framerate changes.
77 //E.g. if multiplier is 1/30 and nominal framerate changes from 60 to 30, the multiplier needs to be
79 if(framerate_realtime_locked
) {
80 multiplier_framerate
*= old_nominal_framerate
/ nominal_framerate
;
84 double framerate_regulator::get_realized_multiplier() throw()
86 threads::alock
h(framerate_lock
);
87 return get_realized_fps() / nominal_framerate
;
90 void framerate_regulator::ack_frame_tick(uint64_t usec
) throw()
93 add_frame(get_time(usec
, true));
96 uint64_t framerate_regulator::to_wait_frame(uint64_t usec
) throw()
98 auto target
= read_fps();
99 if(!frame_number
|| target
.first
|| turboed
|| graphics_driver_is_dummy())
101 uint64_t lintime
= get_time(usec
, true);
102 uint64_t frame_lasted
= lintime
- frame_start_times
[0];
103 uint64_t frame_should_last
= 1000000 / target
.second
;
104 if(frame_lasted
>= frame_should_last
)
105 return 0; //We are late.
106 uint64_t history_frames
= min(frame_number
, static_cast<uint64_t>(FRAMERATE_HISTORY_FRAMES
));
107 uint64_t history_lasted
= lintime
- frame_start_times
[history_frames
- 1];
108 uint64_t history_should_last
= history_frames
* 1000000 / target
.second
;
109 if(history_lasted
>= history_should_last
)
111 return min(history_should_last
- history_lasted
, frame_should_last
- frame_lasted
);
114 uint64_t framerate_regulator::get_utime()
117 gettimeofday(&tv
, NULL
);
118 return static_cast<uint64_t>(tv
.tv_sec
) * 1000000 + tv
.tv_usec
;
121 #define MAXSLEEP 500000
123 void framerate_regulator::wait_usec(uint64_t usec
)
125 uint64_t sleep_end
= get_utime() + usec
;
127 uint64_t time_now
= get_utime();
128 if(time_now
>= sleep_end
)
130 if(sleep_end
< time_now
+ MAXSLEEP
)
131 usleep(sleep_end
- time_now
);
137 uint64_t framerate_regulator::get_time(uint64_t curtime
, bool update
)
139 if(curtime
< last_time_update
|| time_frozen
)
140 return time_at_last_update
;
142 time_at_last_update
+= (curtime
- last_time_update
);
143 last_time_update
= curtime
;
144 return time_at_last_update
;
146 return time_at_last_update
+ (curtime
- last_time_update
);
149 double framerate_regulator::get_realized_fps()
153 unsigned loadidx
= min(frame_number
- 1, static_cast<uint64_t>(FRAMERATE_HISTORY_FRAMES
) - 1);
154 return (1000000.0 * loadidx
) / (frame_start_times
[0] - frame_start_times
[loadidx
] + 1);
157 void framerate_regulator::add_frame(uint64_t linear_time
)
159 for(size_t i
= FRAMERATE_HISTORY_FRAMES
- 2; i
< FRAMERATE_HISTORY_FRAMES
; i
--)
160 frame_start_times
[i
+ 1] = frame_start_times
[i
];
161 frame_start_times
[0] = linear_time
;
165 std::pair
<bool, double> framerate_regulator::read_fps()
169 threads::alock
h(framerate_lock
);
170 n
= nominal_framerate
;
171 m
= multiplier_framerate
;
173 if(m
== std::numeric_limits
<double>::infinity())
174 return std::make_pair(true, 0);
176 return std::make_pair(false, n
* m
);
179 void framerate_regulator::set_speed_cmd(const std::string
& args
)
181 if(args
== "turbo") {
182 set_speed_multiplier(std::numeric_limits
<double>::infinity());
186 double mul
= parse_value
<double>(args
);
188 throw 42; //Zero and negative is not allowed.
189 set_speed_multiplier(mul
);
191 messages
<< "Expected positive speed multiplier or \"turbo\"" << std::endl
;
197 double seconds_per_frame
[] = {4, 3, 2, 1, 0.5, 0.2};
198 double relative_speed
[] = {0.01, 0.04, 0.1, 0.2, 0.25, 0.333, 0.5, 1, 2, 3, 5, 10};
200 std::vector
<std::pair
<double, bool>> construct_speedscale(double basefps
)
202 std::vector
<std::pair
<double, bool>> ret
;
205 unsigned size1
= sizeof(seconds_per_frame
)/sizeof(seconds_per_frame
[0]);
206 unsigned size2
= sizeof(relative_speed
)/sizeof(relative_speed
[0]);
207 while(idx1
< size1
|| idx2
< size2
) {
208 double x1
= std::numeric_limits
<double>::infinity();
209 double x2
= std::numeric_limits
<double>::infinity();
210 if(idx1
< size1
) x1
= 1 / (seconds_per_frame
[idx1
] * basefps
);
211 if(idx2
< size2
) x2
= relative_speed
[idx2
];
213 ret
.push_back(std::make_pair(x1
, true));
216 ret
.push_back(std::make_pair(x2
, false));
219 ret
.push_back(std::make_pair(x2
, true));
228 //Step should be ODD.
229 void framerate_regulator::set_speedstep(unsigned step
)
231 auto scale
= construct_speedscale(nominal_framerate
);
232 step
= (step
- 1) / 2;
233 if(step
>= scale
.size()) {
235 multiplier_framerate
= std::numeric_limits
<double>::infinity();
236 framerate_realtime_locked
= false;
237 messages
<< "Speed set to turbo." << std::endl
;
240 auto _step
= scale
[step
];
241 multiplier_framerate
= _step
.first
;
242 framerate_realtime_locked
= _step
.second
;
243 if(framerate_realtime_locked
)
244 messages
<< "Speed set to " << multiplier_framerate
* nominal_framerate
<< "fps." << std::endl
;
246 messages
<< "Speed set to " << (double)(unsigned)(multiplier_framerate
* 1000) / 10 << "%."
250 #define SPD_TOLERANCE 1e-10
252 //{1/100, 1/fps, 2/fps, 1/10, 1/5, 1/3, 1/2, 1, 2, 3, 5, 10}
253 //Step can be EVEN if between steps.
254 unsigned framerate_regulator::get_speedstep()
256 auto scale
= construct_speedscale(nominal_framerate
);
257 if(multiplier_framerate
== std::numeric_limits
<double>::infinity())
258 return 2 * scale
.size() + 1; //Infinity.
261 if(multiplier_framerate
< i
.first
)
262 return idx
; //Between steps.
263 if(fabs(multiplier_framerate
) - i
.first
< SPD_TOLERANCE
)
264 return idx
+ 1; //On step.
267 return 2 * scale
.size(); //Above maximum step but below infinity.
270 void framerate_regulator::increase_speed() throw()
272 threads::alock
h(framerate_lock
);
273 unsigned step
= get_speedstep();
274 if(step
< 2) return; //At or below minimum speed in scale.
276 step
-=2; //If step is odd, decrement by 2 (full step).
278 step
--; //If step is even, decrement by 1 to reach previous step.
282 void framerate_regulator::decrease_speed() throw()
284 threads::alock
h(framerate_lock
);
285 unsigned step
= get_speedstep();
286 if(multiplier_framerate
== std::numeric_limits
<double>::infinity()) return; //Already turbo.
288 step
+=2; //If step is odd, increment by 2 (full step).
290 step
++; //If step is even, increment by 1 to reach next step.