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 "narrow.hh"
12#include "unreachable.hh"
13
14namespace openmsx {
15
16const double SYNC_INTERVAL = 0.08; // s
17const int64_t MAX_LAG = 200000; // us
18const uint64_t ALLOWED_LAG = 20000; // us
19
21 MSXMotherBoard& motherBoard_, GlobalSettings& globalSettings,
22 EventDelay& eventDelay_)
23 : Schedulable(motherBoard_.getScheduler())
24 , motherBoard(motherBoard_)
25 , eventDistributor(motherBoard.getReactor().getEventDistributor())
26 , eventDelay(eventDelay_)
27 , speedManager (globalSettings.getSpeedManager())
28 , throttleManager(globalSettings.getThrottleManager())
29 , pauseSetting (globalSettings.getPauseSetting())
30 , powerSetting (globalSettings.getPowerSetting())
31{
32 speedManager.attach(*this);
33 throttleManager.attach(*this);
34 pauseSetting.attach(*this);
35 powerSetting.attach(*this);
36
37 resync();
38
39 eventDistributor.registerEventListener(EventType::FINISH_FRAME, *this);
40 eventDistributor.registerEventListener(EventType::FRAME_DRAWN, *this);
41}
42
44{
45 eventDistributor.unregisterEventListener(EventType::FRAME_DRAWN, *this);
46 eventDistributor.unregisterEventListener(EventType::FINISH_FRAME, *this);
47
48 powerSetting.detach(*this);
49 pauseSetting.detach(*this);
50 throttleManager.detach(*this);
51 speedManager.detach(*this);
52}
53
54double RealTime::getRealDuration(EmuTime::param time1, EmuTime::param time2)
55{
56 return (time2 - time1).toDouble() / speedManager.getSpeed();
57}
58
60{
61 return EmuDuration(realDur * speedManager.getSpeed());
62}
63
64bool RealTime::timeLeft(uint64_t us, EmuTime::param time)
65{
66 auto realDuration = static_cast<uint64_t>(
67 getRealDuration(emuTime, time) * 1000000ULL);
68 auto currentRealTime = Timer::getTime();
69 return (currentRealTime + us) <
70 (idealRealTime + realDuration + ALLOWED_LAG);
71}
72
73void RealTime::sync(EmuTime::param time, bool allowSleep)
74{
75 if (allowSleep) {
77 }
78 internalSync(time, allowSleep);
79 if (allowSleep) {
81 }
82}
83
84void RealTime::internalSync(EmuTime::param time, bool allowSleep)
85{
86 if (throttleManager.isThrottled()) {
87 auto realDuration = static_cast<uint64_t>(
88 getRealDuration(emuTime, time) * 1000000ULL);
89 idealRealTime += realDuration;
90 auto currentRealTime = Timer::getTime();
91 auto sleep = narrow_cast<int64_t>(idealRealTime - currentRealTime);
92 if (allowSleep) {
93 // want to sleep for 'sleep' us
94 sleep += narrow_cast<int64_t>(sleepAdjust);
95 int64_t delta = 0;
96 if (sleep > 0) {
97 Timer::sleep(sleep); // request to sleep for 'sleep+sleepAdjust'
98 auto slept = narrow<int64_t>(Timer::getTime() - currentRealTime);
99 delta = sleep - slept; // actually slept for 'slept' us
100 }
101 const double ALPHA = 0.2;
102 sleepAdjust = sleepAdjust * (1 - ALPHA) + narrow_cast<double>(delta) * ALPHA;
103 }
104 if (-sleep > MAX_LAG) {
105 idealRealTime = currentRealTime - MAX_LAG / 2;
106 }
107 }
108 if (allowSleep) {
109 eventDelay.sync(time);
110 }
111
112 emuTime = time;
113}
114
115void RealTime::executeUntil(EmuTime::param time)
116{
117 internalSync(time, true);
119}
120
121int RealTime::signalEvent(const Event& event)
122{
123 if (!motherBoard.isActive() || !enabled) {
124 // these are global events, only the active machine should
125 // synchronize with real time
126 return 0;
127 }
129 [&](const FinishFrameEvent& ffe) {
130 if (!ffe.needRender()) {
131 // sync but don't sleep
132 sync(getCurrentTime(), false);
133 }
134 },
135 [&](const FrameDrawnEvent&) {
136 // sync and possibly sleep
137 sync(getCurrentTime(), true);
138 },
139 [&](const EventBase /*e*/) {
141 }
142 }, event);
143 return 0;
144}
145
146void RealTime::update(const Setting& /*setting*/) noexcept
147{
148 resync();
149}
150
151void RealTime::update(const SpeedManager& /*speedManager*/) noexcept
152{
153 resync();
154}
155
156void RealTime::update(const ThrottleManager& /*throttleManager*/) noexcept
157{
158 resync();
159}
160
162{
163 if (!enabled) return;
164
165 idealRealTime = Timer::getTime();
166 sleepAdjust = 0.0;
168 emuTime = getCurrentTime();
170}
171
173{
174 enabled = true;
175 resync();
176}
177
179{
180 enabled = false;
182}
183
184} // 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:87
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:54
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:64
RealTime(MSXMotherBoard &motherBoard, GlobalSettings &globalSettings, EventDelay &eventDelay)
Definition: RealTime.cc:20
EmuDuration getEmuDuration(double realDur)
Convert RealTime to EmuTime.
Definition: RealTime.cc:59
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:655
const double SYNC_INTERVAL
Definition: RealTime.cc:16
const uint64_t ALLOWED_LAG
Definition: RealTime.cc:18
const int64_t MAX_LAG
Definition: RealTime.cc:17
#define UNREACHABLE
Definition: unreachable.hh:38