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