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 "GPUDisplay.h"
17
18#ifdef _WIN32
20#else
21#include <pthread.h>
22#endif
23
24#ifdef GPUCA_BUILD_EVENT_DISPLAY_X11
26#endif
27#ifdef GPUCA_BUILD_EVENT_DISPLAY_GLFW
29#endif
30#ifdef GPUCA_BUILD_EVENT_DISPLAY_GLUT
32#endif
33#ifdef GPUCA_BUILD_EVENT_DISPLAY_WAYLAND
34namespace o2::gpu
35{
37} // namespace o2::gpu
38#endif
40
42#include "GPULogging.h"
43#include <cstring>
44#include <exception>
45#include <algorithm>
46#include <iterator>
47#include <thread>
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
65
67{
68 GPUDisplayFrontend* me = reinterpret_cast<GPUDisplayFrontend*>(ptr);
69 int32_t retVal;
70 try {
71 retVal = me->FrontendMain();
72 } catch (const std::exception& e) {
73 GPUError("Error in display frontend: %s", e.what());
74 retVal = -1;
75 }
76 if (retVal && me->mDisplay->mInitResult == 0) { // Failed before InitDisplay was called, report failure to StartDisplay(), which otherwise waits forever
77 me->InitDisplay(true);
78 }
79 return ((void*)(size_t)retVal);
80}
81
83{
84 mNeedUpdate = 0;
85 const auto now = std::chrono::steady_clock::now();
86 const auto& cfg = mDisplay->cfgR();
87 if (mBackend->fpsLimitMode() != GPUDisplayBackend::FPS_LIMIT_RATE || cfg.fpsRate <= 0) {
88 mNextFrameTime = now;
89 return;
90 }
91 const auto period = std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<double>(1. / cfg.fpsRate)); // TODO: wake up on HasExternalGUISignals
92 mNextFrameTime += period;
93 if (mNextFrameTime < now - period) { // More than one frame behind (slow rendering, first frame), do not try to catch up
94 mNextFrameTime = now;
95 }
96 std::this_thread::sleep_until(mNextFrameTime);
97}
98
100{
101 const GPUSettingsDisplayHeavy oldCfgH = mDisplay->mCfgH;
102 const GPUSettingsDisplayLight oldCfgL = mDisplay->mCfgL;
103 const GPUSettingsDisplayRenderer oldCfgR = mDisplay->mCfgR;
104 bool changes = HandleExternalGUIKeys();
105
106 const bool gui = isGUIRunning();
107 std::optional<GPUDisplayGUIChannel::Update> update;
108 if (gui) {
109 std::lock_guard lock(mGUIChannel->mutex);
110 update = std::exchange(mGUIChannel->update, std::nullopt);
111 mGUIChannel->displayProcessingCurrentUpdate = update && mGUIChannel->guiSleeping; // GUI waits for the reply when it wakes up
112 }
113 if (update) {
114 for (int32_t key : update->keys) {
116 }
117 if (update->lineWidth) {
118 mDisplay->mCfgL.lineWidth = *update->lineWidth;
119 }
120 changes = true;
121 }
122
123 if (changes) {
124 mDisplay->checkAndApplyConfigChanges(oldCfgH, oldCfgL, oldCfgR);
125 }
126 if (gui) {
127 {
128 std::lock_guard lock(mGUIChannel->mutex);
129 mGUIChannel->state.cfg = {mDisplay->mCfgH, mDisplay->mCfgL, mDisplay->mCfgR};
130 if (update) {
131 mGUIChannel->state.appliedUpdate = update->id;
132 }
133 mGUIChannel->state.counter++;
134 mGUIChannel->displayProcessingCurrentUpdate = false;
135 }
136 mGUIChannel->stateUpdated.notify_all();
137 }
138}
139
141{
142 return mExternalGUIKeys.size();
143}
144
145bool GPUDisplayFrontend::HandleExternalGUIKeys()
146{
147 std::lock_guard lock(mExternalGUIKeyMutex);
148 for (auto key : mExternalGUIKeys) {
150 }
151 return !std::exchange(mExternalGUIKeys, {}).empty();
152}
153
155{
156 std::lock_guard lock(mExternalGUIKeyMutex);
157 mExternalGUIKeys.emplace_back(v);
158}
159
162
169int32_t GPUDisplayFrontend::InitDisplay(bool initFailure) { return mDisplay->InitDisplay(initFailure); }
171{
173 stopGUI();
174 mGUI.reset(nullptr);
175}
176
178{
179#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
180 if (mGUI) {
181 mGUI->stop();
182 }
183#endif
184}
185
187{
188 int32_t retVal = 1;
189#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
190 if (!mGUI) {
191 mGUIChannel = std::make_unique<GPUDisplayGUIChannel>();
192 mGUI = std::make_unique<GPUDisplayGUIWrapper>(*mGUIChannel);
193 }
194 if (!mGUI->isRunning()) {
195 {
196 std::lock_guard lock(mGUIChannel->mutex);
197 mGUIChannel->update.reset();
198 mGUIChannel->state = {};
199 mGUIChannel->guiSleeping = mGUIChannel->displayProcessingCurrentUpdate = mGUIChannel->stop = false;
200 mGUIChannel->replyTimeout = std::chrono::milliseconds(mDisplay->cfg().guiReplyTimeout);
201 mGUIChannel->maxUpdatesAhead = std::max<uint32_t>(mDisplay->cfg().guiMaxUpdatesAhead, 1); // 0 would block all updates
202 mGUIChannel->windowName = DISPLAY_WINDOW_NAME;
203 }
204 mGUI->start();
205 } else {
206 mGUI->focus();
207 }
208#endif
209 return retVal;
210}
211
213{
214 bool retVal = false;
215#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
216 retVal = mGUI && mGUI->isRunning();
217#endif
218 return retVal;
219}
220
222{ // clang-format off
223#if !defined(GPUCA_STANDALONE) && defined(GPUCA_BUILD_EVENT_DISPLAY_GLFW)
224 if (strcmp(type, "glfw") == 0 || strcmp(type, "auto") == 0) {
225 return new GPUDisplayFrontendGlfw;
226 } else
227#endif
228#ifdef _WIN32
229 if (strcmp(type, "windows") == 0 || strcmp(type, "auto") == 0) {
230 return new GPUDisplayFrontendWindows;
231 } else
232#elif defined(GPUCA_BUILD_EVENT_DISPLAY_X11)
233 if (strcmp(type, "x11") == 0 || strcmp(type, "auto") == 0) {
234 return new GPUDisplayFrontendX11;
235 } else
236#endif
237#if defined(GPUCA_STANDALONE) && defined(GPUCA_BUILD_EVENT_DISPLAY_GLFW)
238 if (strcmp(type, "glfw") == 0 || strcmp(type, "auto") == 0) {
239 return new GPUDisplayFrontendGlfw;
240 } else
241#endif
242#ifdef GPUCA_BUILD_EVENT_DISPLAY_WAYLAND
243 if (strcmp(type, "wayland") == 0 || (strcmp(type, "auto") == 0 && getenv("XDG_SESSION_TYPE") && strcmp(getenv("XDG_SESSION_TYPE"), "wayland") == 0)) {
245 } else
246#endif
247#ifdef GPUCA_BUILD_EVENT_DISPLAY_GLUT
248 if (strcmp(type, "glut") == 0 || strcmp(type, "auto") == 0) {
249 return new GPUDisplayFrontendGlut;
250 } else
251#endif
252 if (strcmp(type, "none") == 0) {
253 return new GPUDisplayFrontendNone;
254 } else {
255 GPUError("Requested frontend not available");
256 }
257 return nullptr;
258} // clang-format on
259
264
265void GPUDisplayFrontend::setWindowState(bool fullScreen, bool maximized)
266{
267 mDisplay->mCfgR.fullScreen = fullScreen;
268 mDisplay->mCfgR.maximized = maximized;
269}
270
272{
273 std::fill(std::begin(mKeys), std::end(mKeys), false);
274 std::fill(std::begin(mKeysShift), std::end(mKeysShift), false);
275 mMouseDn = mMouseDnR = mMouseDnM = false;
276}
277
279{
280 if (mDisplay->cfgR().fullScreen) {
281 SwitchFullscreen(true);
282 } else if (mDisplay->cfgR().maximized) {
283 ToggleMaximized(true);
284 }
285}
286
288{
289 return mDisplay->cfgR().drawQualityVSync;
290}
291
296
298{
299#ifndef _WIN32
300 static pthread_t hThread;
301 if (pthread_create(&hThread, nullptr, FrontendThreadWrapper, this)) {
302 GPUError("Coult not Create frontend Thread...");
303 return (1);
304 }
305#else
306 HANDLE hThread;
307 if ((hThread = CreateThread(nullptr, nullptr, &OpenGLWrapper, this, nullptr, nullptr)) == nullptr) {
308 GPUError("Coult not Create GL Thread...");
309 return (1);
310 }
311#endif
312 return (0);
313}
314
316{
317 {
318 std::lock_guard lock(mExitStateMutex);
319 if (mDisplayRunning) {
320 mDisplayControl = 2;
321 StopDisplayA();
323 }
324 }
325 while (mDisplayRunning) {
326 std::this_thread::sleep_for(std::chrono::microseconds(10000)); // TODO: Signal by semaphore
327 }
328}
329
int32_t retVal
TBranch * ptr
StringRef key
std::unique_ptr< GPUDisplayGUIWrapper > mGUI
static void * FrontendThreadWrapper(void *)
std::unique_ptr< GPUDisplayGUIChannel > mGUIChannel
virtual void ToggleMaximized(bool set)=0
static GPUDisplayFrontend * getFrontend(const char *type)
void ResizeScene(int32_t width, int32_t height)
static constexpr const char * DISPLAY_WINDOW_NAME
void setWindowState(bool fullScreen, bool maximized)
void sendExternalGUIKey(int32_t v) override
std::vector< int32_t > mExternalGUIKeys
int32_t InitDisplay(bool initFailure=false)
std::chrono::steady_clock::time_point mNextFrameTime
virtual void SwitchFullscreen(bool set)=0
void checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy &oldCfgH, const GPUSettingsDisplayLight &oldCfgL, const GPUSettingsDisplayRenderer &oldCfgR)
const GPUSettingsDisplayRenderer & cfgR() const
Definition GPUDisplay.h:68
int32_t & drawTextFontSize()
Definition GPUDisplay.h:87
void HandleExternalGUIKey(int32_t key)
const GPUSettingsDisplay & cfg() const
Definition GPUDisplay.h:71
void HandleKey(uint8_t key, bool doNotHandleConfigChanges=false)
int32_t InitDisplay(bool initFailure=false)
GPUDisplayBackend * backend() const
Definition GPUDisplay.h:75
void ResizeScene(int32_t width, int32_t height, bool init=false)
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
GPUDisplayFrontend * createGPUDisplayFrontendWayland()
void empty(int)