Lua: Fix type confusion between signed and unsigned
[lsnes.git] / src / core / framerate.cpp
bloba711ef78be50d051b0252cb19056bc48400086c6
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"
10 #include <cstdlib>
11 #include <string>
12 #include <sstream>
13 #include <iostream>
14 #include <stdexcept>
15 #include <sys/time.h>
16 #include <unistd.h>
17 #include <limits>
18 #include <cmath>
20 bool graphics_driver_is_dummy();
22 framerate_regulator::framerate_regulator(command::group& _cmd)
23 : cmd(_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;
32 last_time_update = 0;
33 time_at_last_update = 0;
34 time_frozen = true;
35 frame_number = 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;
40 turboed = false;
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);
61 time_frozen = true;
64 void framerate_regulator::unfreeze_time(uint64_t curtime)
66 if(time_frozen)
67 last_time_update = curtime;
68 time_frozen = false;
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
78 //adjusted to 1/15.
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()
92 unfreeze_time(usec);
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())
100 return 0;
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)
110 return 0;
111 return min(history_should_last - history_lasted, frame_should_last - frame_lasted);
114 uint64_t framerate_regulator::get_utime()
116 struct timeval tv;
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;
126 while(1) {
127 uint64_t time_now = get_utime();
128 if(time_now >= sleep_end)
129 break;
130 if(sleep_end < time_now + MAXSLEEP)
131 usleep(sleep_end - time_now);
132 else
133 usleep(MAXSLEEP);
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;
141 if(update) {
142 time_at_last_update += (curtime - last_time_update);
143 last_time_update = curtime;
144 return time_at_last_update;
145 } else
146 return time_at_last_update + (curtime - last_time_update);
149 double framerate_regulator::get_realized_fps()
151 if(frame_number < 2)
152 return 0;
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;
162 frame_number++;
165 std::pair<bool, double> framerate_regulator::read_fps()
167 double n, m;
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);
175 else
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());
183 return;
185 try {
186 double mul = parse_value<double>(args);
187 if(mul <= 0)
188 throw 42; //Zero and negative is not allowed.
189 set_speed_multiplier(mul);
190 } catch(...) {
191 messages << "Expected positive speed multiplier or \"turbo\"" << std::endl;
195 namespace
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;
203 unsigned idx1 = 0;
204 unsigned idx2 = 0;
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];
212 if(x1 < x2) {
213 ret.push_back(std::make_pair(x1, true));
214 idx1++;
215 } else if(x1 > x2) {
216 ret.push_back(std::make_pair(x2, false));
217 idx2++;
218 } else {
219 ret.push_back(std::make_pair(x2, true));
220 idx1++;
221 idx2++;
224 return ret;
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()) {
234 //Infinity.
235 multiplier_framerate = std::numeric_limits<double>::infinity();
236 framerate_realtime_locked = false;
237 messages << "Speed set to turbo." << std::endl;
238 return;
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;
245 else
246 messages << "Speed set to " << (double)(unsigned)(multiplier_framerate * 1000) / 10 << "%."
247 << std::endl;
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.
259 unsigned idx = 0;
260 for(auto i: scale) {
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.
265 idx += 2;
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.
275 if(step & 1)
276 step-=2; //If step is odd, decrement by 2 (full step).
277 else
278 step--; //If step is even, decrement by 1 to reach previous step.
279 set_speedstep(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.
287 if(step & 1)
288 step+=2; //If step is odd, increment by 2 (full step).
289 else
290 step++; //If step is even, increment by 1 to reach next step.
291 set_speedstep(step);