Fix SA1 open bus
[lsnes.git] / src / core / framerate.cpp
blobe00a1030446d884d22c18cefc5a044533119b209
1 #include "core/command.hpp"
2 #include "core/framerate.hpp"
3 #include "core/keymapper.hpp"
4 #include "core/settings.hpp"
5 #include "core/moviedata.hpp"
6 #include "core/moviefile.hpp"
7 #include "library/minmax.hpp"
9 #include <cstdlib>
10 #include <string>
11 #include <sstream>
12 #include <iostream>
13 #include <stdexcept>
14 #include <sys/time.h>
15 #include <unistd.h>
16 #include <limits>
18 #define HISTORY_FRAMES 10
20 bool graphics_driver_is_dummy();
22 namespace
24 uint64_t last_time_update = 0;
25 uint64_t time_at_last_update = 0;
26 bool time_frozen = true;
27 uint64_t frame_number = 0;
28 uint64_t frame_start_times[HISTORY_FRAMES];
29 //Framerate.
30 double nominal_framerate = 60;
31 double multiplier_framerate = 1;
32 threads::lock framerate_lock;
33 bool turboed = false;
35 uint64_t get_time(uint64_t curtime, bool update)
37 if(curtime < last_time_update || time_frozen)
38 return time_at_last_update;
39 if(update) {
40 time_at_last_update += (curtime - last_time_update);
41 last_time_update = curtime;
42 return time_at_last_update;
43 } else
44 return time_at_last_update + (curtime - last_time_update);
47 double get_realized_fps()
49 if(frame_number < 2)
50 return 0;
51 unsigned loadidx = min(frame_number - 1, static_cast<uint64_t>(HISTORY_FRAMES) - 1);
52 return (1000000.0 * loadidx) / (frame_start_times[0] - frame_start_times[loadidx] + 1);
55 void add_frame(uint64_t linear_time)
57 for(size_t i = HISTORY_FRAMES - 2; i < HISTORY_FRAMES; i--)
58 frame_start_times[i + 1] = frame_start_times[i];
59 frame_start_times[0] = linear_time;
60 frame_number++;
63 std::pair<bool, double> read_fps()
65 double n, m;
67 threads::alock h(framerate_lock);
68 n = nominal_framerate;
69 m = multiplier_framerate;
71 if(m == std::numeric_limits<double>::infinity())
72 return std::make_pair(true, 0);
73 else
74 return std::make_pair(false, n * m);
77 command::fnptr<> tturbo(lsnes_cmd, "toggle-turbo", "Toggle turbo",
78 "Syntax: toggle-turbo\nToggle turbo mode.\n",
79 []() throw(std::bad_alloc, std::runtime_error) {
80 turboed = !turboed;
81 });
83 command::fnptr<> pturbo(lsnes_cmd, "+turbo", "Activate turbo",
84 "Syntax: +turbo\nActivate turbo mode.\n",
85 []() throw(std::bad_alloc, std::runtime_error) {
86 turboed = true;
87 });
89 command::fnptr<> nturbo(lsnes_cmd, "-turbo", "Deactivate turbo",
90 "Syntax: -turbo\nDeactivate turbo mode.\n",
91 []() throw(std::bad_alloc, std::runtime_error) {
92 turboed = false;
93 });
95 keyboard::invbind turboh(lsnes_mapper, "+turbo", "Speed‣Turbo hold");
96 keyboard::invbind turbot(lsnes_mapper, "toggle-turbo", "Speed‣Turbo toggle");
100 //Set the speed multiplier. Note that INFINITE is a valid multiplier.
101 void set_speed_multiplier(double multiplier) throw()
103 threads::alock h(framerate_lock);
104 multiplier_framerate = multiplier;
107 //Get the speed multiplier. Note that this may be INFINITE.
108 double get_speed_multiplier() throw()
110 threads::alock h(framerate_lock);
111 return multiplier_framerate;
114 void freeze_time(uint64_t curtime)
116 get_time(curtime, true);
117 time_frozen = true;
120 void unfreeze_time(uint64_t curtime)
122 if(time_frozen)
123 last_time_update = curtime;
124 time_frozen = false;
127 void set_nominal_framerate(double fps) throw()
129 threads::alock h(framerate_lock);
130 nominal_framerate = fps;
133 double get_realized_multiplier() throw()
135 threads::alock h(framerate_lock);
136 return get_realized_fps() / nominal_framerate;
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 auto target = read_fps();
148 if(!frame_number || target.first || turboed || graphics_driver_is_dummy())
149 return 0;
150 uint64_t lintime = get_time(usec, true);
151 uint64_t frame_lasted = lintime - frame_start_times[0];
152 uint64_t frame_should_last = 1000000 / target.second;
153 if(frame_lasted >= frame_should_last)
154 return 0; //We are late.
155 uint64_t history_frames = min(frame_number, static_cast<uint64_t>(HISTORY_FRAMES));
156 uint64_t history_lasted = lintime - frame_start_times[history_frames - 1];
157 uint64_t history_should_last = history_frames * 1000000 / target.second;
158 if(history_lasted >= history_should_last)
159 return 0;
160 return min(history_should_last - history_lasted, frame_should_last - frame_lasted);
163 uint64_t get_utime()
165 struct timeval tv;
166 gettimeofday(&tv, NULL);
167 return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
170 #define MAXSLEEP 500000
172 void wait_usec(uint64_t usec)
174 uint64_t sleep_end = get_utime() + usec;
175 while(1) {
176 uint64_t time_now = get_utime();
177 if(time_now >= sleep_end)
178 break;
179 if(sleep_end < time_now + MAXSLEEP)
180 usleep(sleep_end - time_now);
181 else
182 usleep(MAXSLEEP);