Fix the speed throttle
[lsnes.git] / src / core / framerate.cpp
blob1d3d66f1929d3af1f6dabacbe3f66b127b84bd49
1 #include "core/framerate.hpp"
2 #include "core/settings.hpp"
4 #include <cstdlib>
5 #include <string>
6 #include <sstream>
7 #include <iostream>
8 #include <stdexcept>
9 #include <sys/time.h>
10 #include <unistd.h>
12 #define DEFAULT_NOMINAL_RATE 60
13 #define HISTORY_FRAMES 10
15 namespace
17 uint64_t last_time_update = 0;
18 uint64_t time_at_last_update = 0;
19 bool time_frozen = true;
20 uint64_t frame_number = 0;
21 uint64_t frame_start_times[HISTORY_FRAMES];
22 double nominal_rate = DEFAULT_NOMINAL_RATE;
23 bool target_nominal = true;
24 double target_fps = DEFAULT_NOMINAL_RATE;
25 bool target_infinite = false;
27 uint64_t get_time(uint64_t curtime, bool update)
29 if(curtime < last_time_update || time_frozen)
30 return time_at_last_update;
31 if(update) {
32 time_at_last_update += (curtime - last_time_update);
33 last_time_update = curtime;
34 return time_at_last_update;
35 } else
36 return time_at_last_update + (curtime - last_time_update);
39 double get_realized_fps()
41 if(frame_number < 2)
42 return 0;
43 if(frame_number >= HISTORY_FRAMES)
44 return (1000000.0 * (HISTORY_FRAMES - 1)) / (frame_start_times[0] - frame_start_times[HISTORY_FRAMES - 1] + 1);
45 return (1000000.0 * (frame_number - 1)) / (frame_start_times[0] - frame_start_times[frame_number - 1] + 1);
48 void add_frame(uint64_t linear_time)
50 for(size_t i = HISTORY_FRAMES - 2; i < HISTORY_FRAMES; i--)
51 frame_start_times[i + 1] = frame_start_times[i];
52 frame_start_times[0] = linear_time;
53 frame_number++;
56 struct setting_targetfps : public setting
58 setting_targetfps() throw(std::bad_alloc)
59 : setting("targetfps")
63 void blank() throw(std::bad_alloc, std::runtime_error)
65 target_nominal = true;
66 target_infinite = false;
67 target_fps = nominal_rate;
70 bool is_set() throw()
72 return !target_nominal;
75 virtual void set(const std::string& value) throw(std::bad_alloc, std::runtime_error)
77 double tmp;
78 const char* s;
79 char* e;
80 if(value == "infinite") {
81 target_infinite = true;
82 target_nominal = false;
83 return;
85 s = value.c_str();
86 tmp = strtod(s, &e);
87 if(*e)
88 throw std::runtime_error("Invalid frame rate");
89 if(tmp < 0.001)
90 throw std::runtime_error("Target frame rate must be at least 0.001fps");
91 target_fps = tmp;
92 target_infinite = false;
93 target_nominal = false;
96 virtual std::string get() throw(std::bad_alloc)
98 if(target_nominal)
99 return "";
100 else if(target_infinite)
101 return "infinite";
102 else {
103 std::ostringstream o;
104 o << target_fps;
105 return o.str();
109 } targetfps;
112 void freeze_time(uint64_t curtime)
114 get_time(curtime, true);
115 time_frozen = true;
118 void unfreeze_time(uint64_t curtime)
120 if(time_frozen)
121 last_time_update = curtime;
122 time_frozen = false;
125 void set_nominal_framerate(double fps) throw()
127 nominal_rate = fps;
128 if(target_nominal) {
129 target_fps = nominal_rate;
130 target_infinite = false;
134 double get_framerate() throw()
136 return 100.0 * get_realized_fps() / nominal_rate;
139 void ack_frame_tick(uint64_t usec) throw()
141 unfreeze_time(usec);
142 add_frame(get_time(usec, true));
145 uint64_t to_wait_frame(uint64_t usec) throw()
147 if(!frame_number || target_infinite)
148 return 0;
149 uint64_t lintime = get_time(usec, true);
150 uint64_t frame_lasted = lintime - frame_start_times[0];
151 uint64_t frame_should_last = 1000000 / target_fps;
152 if(frame_lasted >= frame_should_last)
153 return 0; //We are late.
154 uint64_t maxwait = frame_should_last - frame_lasted;
155 uint64_t history_frames = (frame_number < HISTORY_FRAMES) ? frame_number : HISTORY_FRAMES;
156 uint64_t history_lasted = lintime - frame_start_times[history_frames - 1];
157 uint64_t history_should_last = history_frames * 1000000 / target_fps;
158 if(history_lasted >= history_should_last)
159 return 0;
160 uint64_t history_wait = history_should_last - history_lasted;
161 if(history_wait > maxwait)
162 history_wait = maxwait;
163 return history_wait;
167 uint64_t get_utime()
169 struct timeval tv;
170 gettimeofday(&tv, NULL);
171 return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
174 #define MAXSLEEP 500000
176 void wait_usec(uint64_t usec)
178 uint64_t sleep_end = get_utime() + usec;
179 while(1) {
180 uint64_t time_now = get_utime();
181 if(time_now >= sleep_end)
182 break;
183 if(sleep_end < time_now + MAXSLEEP)
184 usleep(sleep_end - time_now);
185 else
186 usleep(MAXSLEEP);