Project
Loading...
Searching...
No Matches
GPUDisplayFrontend.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15#include "GPUDisplayFrontend.h"
16#include "GPUDisplayConsole.h"
17#include "GPUDisplay.h"
18
19#ifdef _WIN32
21#endif
22#include <system_error>
23#include <thread>
24
25#ifdef GPUCA_BUILD_EVENT_DISPLAY_X11
27#endif
28#ifdef GPUCA_BUILD_EVENT_DISPLAY_GLFW
30#endif
31#ifdef GPUCA_BUILD_EVENT_DISPLAY_GLUT
33#endif
34#ifdef GPUCA_BUILD_EVENT_DISPLAY_WAYLAND
35namespace o2::gpu
36{
38} // namespace o2::gpu
39#endif
41
43#include "GPULogging.h"
44#include <cstring>
45#include <exception>
46#include <algorithm>
47#include <iterator>
48#include <utility>
49
50#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
52#else
53namespace o2::gpu
54{
56{
57};
58} // namespace o2::gpu
59#endif
60
61using namespace o2::gpu;
62
63GPUDisplayFrontend::GPUDisplayFrontend() : mConsole(std::make_unique<GPUDisplayConsole>(std::string(DISPLAY_WINDOW_NAME) + " Console")) {}
65{
66 if (mFrontendThread.joinable()) { // Started but not stopped, as the detached thread before
67 mFrontendThread.detach();
68 }
69}
70
72{
74 try {
76 } catch (const std::exception& e) {
77 GPUError("Error in display frontend: %s", e.what());
78 retVal = -1;
79 }
80 std::unique_lock lock(mExitStateMutex);
81 mDisplayRunning = false; // Also after an error / exception in the main loop, StopDisplay waits for it
82 mDisplayStoppedCV.notify_all();
83 if (retVal) {
84 if (mDisplay->mInitResult == 0) { // Failed before InitDisplay was called, report failure to StartDisplay(), which otherwise waits forever
85 lock.unlock();
86 InitDisplay(true);
87 } else {
88 mDisplay->mInitResult = -1; // Error after the initialization: StopDisplay does not reset the state for a new start
89 }
90 }
91}
92
94{
95 const auto now = std::chrono::steady_clock::now();
96 const auto& cfg = mDisplay->cfgR();
97 if (mBackend->fpsLimitMode() != GPUDisplayBackend::FPS_LIMIT_RATE || cfg.fpsRate <= 0) {
98 mNextFrameTime = now;
99 return;
100 }
101 const auto period = std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<double>(1. / cfg.fpsRate));
102 mNextFrameTime += period;
103 if (mNextFrameTime < now - period) { // More than one frame behind (slow rendering, first frame), do not try to catch up
104 mNextFrameTime = now;
105 }
106 std::unique_lock lock(mConfigUpdateMutex);
107 mConfigUpdateCV.wait_until(lock, mNextFrameTime, [this] { return mConfigUpdatePending; });
108}
109
111{
112 {
113 std::lock_guard lock(mConfigUpdateMutex);
115 }
116 mConfigUpdateCV.notify_one();
117}
118
120{
121 {
122 std::lock_guard lock(mConfigUpdateMutex);
123 mConfigUpdatePending = false; // Before fetching the updates, so that updates sent in the meantime signal again
124 }
125 HandleExternalGUIKeys();
126
127 const bool gui = isGUIRunning();
128 std::optional<GPUDisplayGUIChannel::Update> update;
129 if (gui) {
130 std::lock_guard lock(mGUIChannel->mutex);
131 update = std::exchange(mGUIChannel->update, std::nullopt);
132 mGUIChannel->displayProcessingCurrentUpdate = update && mGUIChannel->guiSleeping; // GUI waits for the reply when it wakes up
133 }
134 if (update) {
135 for (int32_t key : update->keys) {
137 }
138 applyGUIValues(*update);
139 if (update->guiScale) {
140 mDisplay->mCfgO.guiScale = *update->guiScale;
141 }
142 if (update->guiDarkMode) {
143 mDisplay->mCfgO.guiDarkMode = *update->guiDarkMode;
144 }
145 }
146
147 if (gui) {
149 {
150 std::lock_guard lock(mGUIChannel->mutex);
151 mGUIChannel->state.cfg = std::move(cfg);
152 mGUIChannel->state.infoStatus = mDisplay->mCfgO.printInfoTextGUI ? mDisplay->mGUIInfoStatus : "";
153 mGUIChannel->state.infoText = mDisplay->mCfgO.printInfoTextGUI ? mDisplay->mGUIInfoText : "";
154 mGUIChannel->state.infoTextCounter = mDisplay->mGUIInfoTextCounter;
155 if (mDisplay->mUpdateGUIOptions) {
156 mGUIChannel->state.optionsRevision++;
157 mDisplay->mUpdateGUIOptions = false;
158 }
159 if (update) {
160 mGUIChannel->state.appliedUpdate = update->id;
161 }
162 mGUIChannel->state.counter++;
163 mGUIChannel->displayProcessingCurrentUpdate = false;
164 }
165 mGUIChannel->stateUpdated.notify_all();
166 std::unique_lock lock(mGUIChannel->mutex);
167 if (mGUIChannel->optionsReadRevision != mGUIChannel->state.optionsRevision) { // The GUI calls the dynamic option lambdas (reading display data) only while we wait here, short timeout, retried in the next frame
168 mGUIChannel->displayWaitingForOptions = true;
169 mGUIChannel->optionsRead.wait_for(lock, std::chrono::milliseconds(20), [this] { return mGUIChannel->optionsReadRevision == mGUIChannel->state.optionsRevision || mGUIChannel->stop; });
170 mGUIChannel->displayWaitingForOptions = false;
171 }
172 }
173}
174
175void GPUDisplayFrontend::sendExternalGUIKey(int32_t v, uint32_t translationMode, uint64_t extendedKey)
176{
177 if (translationMode && (v = translateExternalKey(translationMode, extendedKey)) < 0) {
178 return;
179 }
180 {
181 std::lock_guard lock(mExternalGUIKeyMutex);
182 mExternalGUIKeys.emplace_back(v);
183 }
185}
186
187bool GPUDisplayFrontend::consoleShown() const { return mDisplay->mCfgC.showConsole; }
188bool GPUDisplayFrontend::keyMapDown(uint32_t id) const { return mKeyDownMap[id] && mKeys[mKeyDownMap[id]]; } // Not mKeys[0] for unbound mappings: frontends may set it for unknown keys
189
190int32_t GPUDisplayFrontend::getDisplayControl() const { return mDisplay->mCfgC.displayControl.load(); }
191void GPUDisplayFrontend::setDisplayControl(int32_t v) { mDisplay->mCfgC.displayControl.store(v); }
192
195{
198 const bool startupScript = !mStartupScriptDone && !mDisplay->cfg().runScript.empty();
199 const int32_t retVal = mDisplay->DrawGLScene(suspended || startupScript); // Before the startup script, the first frame only loads the event, it is not rendered
200 mDisplay->mOldCfg = {mDisplay->mCfgH, mDisplay->mCfgL, mDisplay->mCfgR}; // Also includes the changes during drawing (animation, scene reset, ...), which set their update flags themselves
201 if (startupScript) {
202 mStartupScriptDone = true;
203 mConsole->runScriptFile(mDisplay->cfg().runScript); // Settings it changes are applied with the next frame
204 } else {
205 mConsole->processScripts(true); // Waiting script lines, wait / benchmark
206 }
207 return retVal;
208}
209
216int32_t GPUDisplayFrontend::InitDisplay(bool initFailure) { return mDisplay->InitDisplay(initFailure); }
218{
220 stopGUI();
221 mGUI.reset(nullptr);
222}
223
225{
226#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
227 if (mGUI) {
228 mGUI->stop();
229 }
230#endif
231}
232
234{
235 int32_t retVal = 1;
236#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
237 if (!mGUI) {
238 mGUIChannel = std::make_unique<GPUDisplayGUIChannel>();
239 mGUIChannel->updatePosted = [this]() { signalConfigUpdate(); };
240 mGUI = std::make_unique<GPUDisplayGUIWrapper>(*mGUIChannel);
241 }
242 if (!mGUI->isRunning()) {
243 {
244 std::lock_guard lock(mGUIChannel->mutex);
245 mGUIChannel->update.reset();
246 mGUIChannel->state = {};
247 mGUIChannel->guiSleeping = mGUIChannel->displayProcessingCurrentUpdate = mGUIChannel->stop = mGUIChannel->displayWaitingForOptions = false;
248 mGUIChannel->optionsReadRevision = 0;
249 mDisplay->mUpdateGUIOptions = true;
250 mGUIChannel->replyTimeout = std::chrono::milliseconds(mDisplay->cfg().internal.guiReplyTimeout);
251 mGUIChannel->maxUpdatesAhead = std::max<uint32_t>(mDisplay->cfg().internal.guiMaxUpdatesAhead, 1); // 0 would block all updates
252 mGUIChannel->infoTextTime = std::chrono::milliseconds(std::lround((mDisplay->cfg().internal.setInfoTime + mDisplay->cfg().internal.setInfoTransition) * 1000.f));
253 mGUIChannel->windowName = DISPLAY_WINDOW_NAME;
254 setGUILayout(*mGUIChannel);
255 }
256 mGUI->start();
257 } else {
258 mGUI->focus();
259 }
260#endif
261 return retVal;
262}
263
265{
266 bool retVal = false;
267#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
268 retVal = mGUI && mGUI->isRunning();
269#endif
270 return retVal;
271}
272
274{ // clang-format off
275#if !defined(GPUCA_STANDALONE) && defined(GPUCA_BUILD_EVENT_DISPLAY_GLFW)
276 if (strcmp(type, "glfw") == 0 || strcmp(type, "auto") == 0) {
277 return new GPUDisplayFrontendGlfw;
278 } else
279#endif
280#ifdef _WIN32
281 if (strcmp(type, "windows") == 0 || strcmp(type, "auto") == 0) {
282 return new GPUDisplayFrontendWindows;
283 } else
284#elif defined(GPUCA_BUILD_EVENT_DISPLAY_X11)
285 if (strcmp(type, "x11") == 0 || strcmp(type, "auto") == 0) {
286 return new GPUDisplayFrontendX11;
287 } else
288#endif
289#if defined(GPUCA_STANDALONE) && defined(GPUCA_BUILD_EVENT_DISPLAY_GLFW)
290 if (strcmp(type, "glfw") == 0 || strcmp(type, "auto") == 0) {
291 return new GPUDisplayFrontendGlfw;
292 } else
293#endif
294#ifdef GPUCA_BUILD_EVENT_DISPLAY_WAYLAND
295 if (strcmp(type, "wayland") == 0 || (strcmp(type, "auto") == 0 && getenv("XDG_SESSION_TYPE") && strcmp(getenv("XDG_SESSION_TYPE"), "wayland") == 0)) {
297 } else
298#endif
299#ifdef GPUCA_BUILD_EVENT_DISPLAY_GLUT
300 if (strcmp(type, "glut") == 0 || strcmp(type, "auto") == 0) {
301 return new GPUDisplayFrontendGlut;
302 } else
303#endif
304 if (strcmp(type, "none") == 0) {
305 return new GPUDisplayFrontendNone;
306 } else {
307 GPUError("Requested frontend not available");
308 }
309 return nullptr;
310} // clang-format on
311
316
317void GPUDisplayFrontend::setWindowState(bool fullScreen, bool maximized)
318{
319 mDisplay->mCfgR.fullScreen = mWindowFullScreen = fullScreen;
320 mDisplay->mCfgR.maximized = mWindowMaximized = maximized;
321}
322
324{
325 if (set != std::exchange(mWindowFullScreen, set)) {
326 SwitchFullscreen(set);
327 }
328}
329
331{
332 if (set != std::exchange(mWindowMaximized, set)) {
333 ToggleMaximized(set);
334 }
335}
336
338{
339 std::fill(std::begin(mKeys), std::end(mKeys), false);
340 mMouseDn = mMouseDnR = mMouseDnM = false;
341}
342
344{
345 if (mDisplay->cfgR().fullScreen) {
346 requestFullScreen(true);
347 } else if (mDisplay->cfgR().maximized) {
348 requestMaximized(true);
349 }
350}
351
353{
354 return mDisplay->cfgR().drawQualityVSync;
355}
356
361
363{
364 try {
366 } catch (const std::system_error& e) {
367 GPUError("Could not create frontend thread: %s", e.what());
368 return (1);
369 }
370 std::unique_lock lock(mExitStateMutex);
371 mStartDisplayDone = true;
372 mInitCV.notify_all();
373 mInitCV.wait(lock, [this] { return mDisplay->mInitResult != 0; }); // Both threads have finished their initialization
374 return (mDisplay->mInitResult != 1);
375}
376
378{
379 {
380 std::unique_lock lock(mExitStateMutex);
381 if (mDisplayRunning) {
382 mDisplay->mCfgC.displayControl.store(2);
383 StopDisplayA();
385 }
386 mDisplayStoppedCV.wait(lock, [this] { return !mDisplayRunning; });
387 }
388 if (mFrontendThread.joinable()) { // Also the wrapper has finished (it sets mInitResult = -1 for errors after the initialization), not holding mExitStateMutex, which the wrapper uses
389 mFrontendThread.join();
390 }
391 std::lock_guard lock(mExitStateMutex);
392 if (mDisplay->mInitResult == 1) { // For a new StartDisplay, not after a failed initialization or an error (undefined state)
394 mDisplay->mInitResult = 0;
395 }
396}
397
int32_t retVal
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc Replace constant variables static by constexpr expressions Compile RTC in deterministic with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define Use RTC to optimize GPU code h configStandalone Processing proc_rtctech Use a file lock to serialize access to the cache folder int32_t
StringRef key
std::unique_ptr< GPUDisplayGUIWrapper > mGUI
int32_t getDisplayControl() const override
std::unique_ptr< GPUDisplayGUIChannel > mGUIChannel
virtual int32_t FrontendMain()=0
void setDisplayControl(int32_t v) override
virtual void ToggleMaximized(bool set)=0
int32_t HandleUpdatesAndDrawGLScene(bool suspended=false)
static GPUDisplayFrontend * getFrontend(const char *type)
void ResizeScene(int32_t width, int32_t height)
std::condition_variable mDisplayStoppedCV
static constexpr const char * DISPLAY_WINDOW_NAME
void setWindowState(bool fullScreen, bool maximized)
std::vector< int32_t > mExternalGUIKeys
void sendExternalGUIKey(int32_t v, uint32_t translationMode=0, uint64_t extendedKey=0) override
bool keyMapDown(uint32_t id) const
int32_t InitDisplay(bool initFailure=false)
std::array< uint8_t, KEY_MAP_MAX > mKeyDownMap
std::condition_variable mConfigUpdateCV
std::chrono::steady_clock::time_point mNextFrameTime
virtual void SwitchFullscreen(bool set)=0
std::atomic< bool > mDisplayRunning
std::condition_variable mInitCV
GPUDisplayControl mCfgC
Definition GPUDisplay.h:93
const GPUSettingsDisplayRenderer & cfgR() const
Definition GPUDisplay.h:72
int32_t DrawGLScene(bool suspended=false)
int32_t & drawTextFontSize()
Definition GPUDisplay.h:90
const GPUSettingsDisplay & cfg() const
Definition GPUDisplay.h:75
int32_t InitDisplay(bool initFailure=false)
GPUDisplayOptions mCfgO
Definition GPUDisplay.h:92
void HandleKey(uint8_t key, bool external=false, bool bypassConsole=false)
GPUDisplayBackend * backend() const
Definition GPUDisplay.h:79
void HandleExternalGUIKey(int32_t key, bool bypassConsole=false)
void ResizeScene(int32_t width, int32_t height, bool init=false)
GLsizei const GLchar *const * string
Definition glcorearb.h:809
const GLdouble * v
Definition glcorearb.h:832
GLint GLsizei GLsizei height
Definition glcorearb.h:270
GLint GLsizei width
Definition glcorearb.h:270
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLuint id
Definition glcorearb.h:650
GPUDisplayFrontend * createGPUDisplayFrontendWayland()