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
13namespace openmsx {
14
15const double SYNC_INTERVAL = 0.08; // s
16const int64_t MAX_LAG = 200000; // us
17const 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
55double 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
65bool 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
74void RealTime::sync(EmuTime::param time, bool allowSleep)
75{
76 if (allowSleep) {
78 }
79 internalSync(time, allowSleep);
80 if (allowSleep) {
82 }
83}
84
85void 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
116void RealTime::executeUntil(EmuTime::param time)
117{
118 internalSync(time, true);
120}
121
122int 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*/) {
142 }
143 }, event);
144 return 0;
145}
146
147void RealTime::update(const Setting& /*setting*/) noexcept
148{
149 resync();
150}
151
152void RealTime::update(const SpeedManager& /*speedManager*/) noexcept
153{
154 resync();
155}
156
157void 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;
169 emuTime = getCurrentTime();
171}
172
174{
175 enabled = true;
176 resync();
177}
178
180{
181 enabled = false;
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:652
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