-
Notifications
You must be signed in to change notification settings - Fork 6
/
Engine.cpp
130 lines (112 loc) · 3.89 KB
/
Engine.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "Engine.h"
#include "EngineWindow.h"
#include "MyActions.h"
#include "PlaybackTracer.h"
#include "TextureCache.h"
#include "config.h"
#include "utils.h"
#include "world_state.h"
HighTimerResolution::HighTimerResolution() {
timeBeginPeriod(get());
}
HighTimerResolution::~HighTimerResolution() {
timeEndPeriod(get());
}
int HighTimerResolution::get() {
static int resolution = [] {
TIMECAPS tc;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != MMSYSERR_NOERROR)
throw(std::runtime_error("timeGetDevCaps failed"));
return std::min(std::max(tc.wPeriodMin, 1u), tc.wPeriodMax);
}();
return resolution;
}
Engine::Engine(EngineThread& thread, EngineWindow& window, StyleManager& styleManager)
: window(window), thread(thread), styleManager(styleManager), glContext(window),
findAsYouType(*this), coverPos(sessionCompiledCPInfo.get()), worldState(db),
texCache(thread, db, coverPos), renderer(*this), playbackTracer(thread) {}
void Engine::mainLoop() {
updateRefreshRate();
EM::ReloadCollection().execute(*this);
double lastSwapEnd = 0;
double swapEstimate = 1;
std::optional<HighTimerResolution> timerResolution;
while (!shouldStop) {
if (!windowDirty && !cacheDirty)
thread.messageQueue.wait();
if (auto msg = thread.messageQueue.popMaybe()) {
msg.value()->execute(*this);
} else if (cacheDirty) {
GLContext::checkGraphicsReset();
texCache.startLoading(worldState.getTarget());
cacheDirty = false;
} else if (windowDirty) {
fpsCounter.startFrame();
GLContext::checkGraphicsReset();
if (!timerResolution)
timerResolution.emplace();
texCache.setPriority(true);
// Housekeeping
texCache.uploadTextures();
texCache.trimCache();
// Update state
worldState.update();
// Render
renderer.drawFrame();
GLContext::checkGraphicsReset();
glFinish();
fpsCounter.endFrame();
windowDirty = worldState.isMoving() || renderer.wasMissingTextures || reloadWorker;
// Handle V-Sync
renderer.ensureVSync(cfgVSyncMode != VSYNC_SLEEP_ONLY);
if (cfgVSyncMode == VSYNC_AND_SLEEP || cfgVSyncMode == VSYNC_SLEEP_ONLY) {
double currentTime = time();
double sleepTime =
(1.0 / refreshRate) - (currentTime - lastSwapEnd) - swapEstimate;
sleepTime -= 0.002 * timerResolution->get();
if (sleepTime >= 0.001 * timerResolution->get()) {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
Sleep(int(1000 * std::min(sleepTime, 1.0)));
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
}
}
double swapStart = time();
GLContext::checkGraphicsReset();
window.swapBuffers();
GLContext::checkGraphicsReset();
glFinish(); // Wait for GPU
lastSwapEnd = time();
swapEstimate = 0.2 * (lastSwapEnd - swapStart) + 0.8 * swapEstimate;
texCache.setPriority(windowDirty);
if (!windowDirty) {
timerResolution.reset();
fpsCounter.flush();
}
}
}
// Synchronize with GPU before shutdown to avoid crashes
GLContext::checkGraphicsReset();
glFinish();
}
void Engine::updateRefreshRate() {
DEVMODE dispSettings;
ZeroMemory(&dispSettings, sizeof(dispSettings));
dispSettings.dmSize = sizeof(dispSettings);
if (0 != EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &dispSettings)) {
refreshRate = dispSettings.dmDisplayFrequency;
}
}
void Engine::setTarget(DBPos target, bool userInitiated) {
if (auto dbIter = db.iterFromPos(target)) {
metadb_handle_list tracks;
db.getTracks(dbIter.value(), tracks);
thread.runInMainThread([tracks = std::move(tracks), &engineWindow = window] {
engineWindow.setSelection(tracks);
});
}
worldState.setTarget(target);
cacheDirty = true;
if (userInitiated)
playbackTracer.delay(1);
thread.invalidateWindow();
}