openMSX
RealTime.cc
Go to the documentation of this file.
1 #include "RealTime.hh"
2 #include "Timer.hh"
3 #include "EventDistributor.hh"
4 #include "EventDelay.hh"
5 #include "Event.hh"
6 #include "GlobalSettings.hh"
7 #include "MSXMotherBoard.hh"
8 #include "Reactor.hh"
9 #include "BooleanSetting.hh"
10 #include "ThrottleManager.hh"
11 #include "unreachable.hh"
12 
13 namespace openmsx {
14 
15 const double SYNC_INTERVAL = 0.08; // s
16 const int64_t MAX_LAG = 200000; // us
17 const uint64_t ALLOWED_LAG = 20000; // us
18 
20  MSXMotherBoard& motherBoard_, GlobalSettings& globalSettings,
21  EventDelay& eventDelay_)
22  : Schedulable(motherBoard_.getScheduler())
23  , motherBoard(motherBoard_)
24  , eventDistributor(motherBoard.getReactor().getEventDistributor())
25  , eventDelay(eventDelay_)
26  , speedManager (globalSettings.getSpeedManager())
27  , throttleManager(globalSettings.getThrottleManager())
28  , pauseSetting (globalSettings.getPauseSetting())
29  , powerSetting (globalSettings.getPowerSetting())
30  , emuTime(EmuTime::zero())
31  , enabled(true)
32 {
33  speedManager.attach(*this);
34  throttleManager.attach(*this);
35  pauseSetting.attach(*this);
36  powerSetting.attach(*this);
37 
38  resync();
39 
40  eventDistributor.registerEventListener(EventType::FINISH_FRAME, *this);
41  eventDistributor.registerEventListener(EventType::FRAME_DRAWN, *this);
42 }
43 
45 {
46  eventDistributor.unregisterEventListener(EventType::FRAME_DRAWN, *this);
47  eventDistributor.unregisterEventListener(EventType::FINISH_FRAME, *this);
48 
49  powerSetting.detach(*this);
50  pauseSetting.detach(*this);
51  throttleManager.detach(*this);
52  speedManager.detach(*this);
53 }
54 
55 double RealTime::getRealDuration(EmuTime::param time1, EmuTime::param time2)
56 {
57  return (time2 - time1).toDouble() / speedManager.getSpeed();
58 }
59 
61 {
62  return EmuDuration(realDur * speedManager.getSpeed());
63 }
64 
65 bool RealTime::timeLeft(uint64_t us, EmuTime::param time)
66 {
67  auto realDuration = static_cast<uint64_t>(
68  getRealDuration(emuTime, time) * 1000000ULL);
69  auto currentRealTime = Timer::getTime();
70  return (currentRealTime + us) <
71  (idealRealTime + realDuration + ALLOWED_LAG);
72 }
73 
74 void RealTime::sync(EmuTime::param time, bool allowSleep)
75 {
76  if (allowSleep) {
78  }
79  internalSync(time, allowSleep);
80  if (allowSleep) {
82  }
83 }
84 
85 void RealTime::internalSync(EmuTime::param time, bool allowSleep)
86 {
87  if (throttleManager.isThrottled()) {
88  auto realDuration = static_cast<uint64_t>(
89  getRealDuration(emuTime, time) * 1000000ULL);
90  idealRealTime += realDuration;
91  auto currentRealTime = Timer::getTime();
92  int64_t sleep = idealRealTime - currentRealTime;
93  if (allowSleep) {
94  // want to sleep for 'sleep' us
95  sleep += static_cast<int64_t>(sleepAdjust);
96  int64_t delta = 0;
97  if (sleep > 0) {
98  Timer::sleep(sleep); // request to sleep for 'sleep+sleepAdjust'
99  int64_t slept = Timer::getTime() - currentRealTime;
100  delta = sleep - slept; // actually slept for 'slept' us
101  }
102  const double ALPHA = 0.2;
103  sleepAdjust = sleepAdjust * (1 - ALPHA) + delta * ALPHA;
104  }
105  if (-sleep > MAX_LAG) {
106  idealRealTime = currentRealTime - MAX_LAG / 2;
107  }
108  }
109  if (allowSleep) {
110  eventDelay.sync(time);
111  }
112 
113  emuTime = time;
114 }
115 
116 void RealTime::executeUntil(EmuTime::param time)
117 {
118  internalSync(time, true);
120 }
121 
122 int RealTime::signalEvent(const Event& event) noexcept
123 {
124  if (!motherBoard.isActive() || !enabled) {
125  // these are global events, only the active machine should
126  // synchronize with real time
127  return 0;
128  }
130  [&](const FinishFrameEvent& ffe) {
131  if (!ffe.needRender()) {
132  // sync but don't sleep
133  sync(getCurrentTime(), false);
134  }
135  },
136  [&](const FrameDrawnEvent&) {
137  // sync and possibly sleep
138  sync(getCurrentTime(), true);
139  },
140  [&](const EventBase /*e*/) {
141  UNREACHABLE;
142  }
143  }, event);
144  return 0;
145 }
146 
147 void RealTime::update(const Setting& /*setting*/) noexcept
148 {
149  resync();
150 }
151 
152 void RealTime::update(const SpeedManager& /*speedManager*/) noexcept
153 {
154  resync();
155 }
156 
157 void RealTime::update(const ThrottleManager& /*throttleManager*/) noexcept
158 {
159  resync();
160 }
161 
163 {
164  if (!enabled) return;
165 
166  idealRealTime = Timer::getTime();
167  sleepAdjust = 0.0;
168  removeSyncPoint();
169  emuTime = getCurrentTime();
171 }
172 
174 {
175  enabled = true;
176  resync();
177 }
178 
180 {
181  enabled = false;
182  removeSyncPoint();
183 }
184 
185 } // namespace openmsx
This class is responsible for translating host events into MSX events.
Definition: EventDelay.hh:27
void sync(EmuTime::param curEmu)
Definition: EventDelay.cc:86
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
This class contains settings that are used by several other class (including some singletons).
double getRealDuration(EmuTime::param time1, EmuTime::param time2)
Convert EmuTime to RealTime.
Definition: RealTime.cc:55
bool timeLeft(uint64_t us, EmuTime::param time)
Check that there is enough real time left before we reach as certain point in emulated time.
Definition: RealTime.cc:65
RealTime(MSXMotherBoard &motherBoard, GlobalSettings &globalSettings, EventDelay &eventDelay)
Definition: RealTime.cc:19
EmuDuration getEmuDuration(double realDur)
Convert RealTime to EmuTime.
Definition: RealTime.cc:60
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:34
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: Schedulable.cc:49
double getSpeed() const
Return the desired ratio between emutime and real time.
Definition: SpeedManager.hh:26
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void attach(Observer< T > &observer)
Definition: Subject.hh:50
bool isThrottled() const
Ask if throttling is enabled.
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:7
void sleep(uint64_t us)
Sleep for the specified amount of time (in us).
Definition: Timer.cc:27
This file implemented 3 utility functions:
Definition: Autofire.cc:9
auto visit(Visitor &&visitor, const Event &event)
Definition: Event.hh:653
const double SYNC_INTERVAL
Definition: RealTime.cc:15
const uint64_t ALLOWED_LAG
Definition: RealTime.cc:17
const int64_t MAX_LAG
Definition: RealTime.cc:16
#define UNREACHABLE
Definition: unreachable.hh:38