Skip to content

Commit 04a8217

Browse files
committed
GPU Display Qt GUI: Add proper passing of frames with config elements to the GUI and returning lambda functions with value updates
1 parent 48acba7 commit 04a8217

11 files changed

Lines changed: 713 additions & 67 deletions

‎GPU/GPUTracking/Definitions/GPUSettingsList.h‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,8 @@ AddOption(propagateTracks, int32_t, 0, "", 0, "Propagate final tracks further (i
456456
AddOption(showCollision, int32_t, -1, "", 0, "Show only individual collision")
457457
AddOption(colorCollisions, int32_t, 0, "", 0, "Distinguish collisions in timeframe by color")
458458
AddOption(colorClusters, int32_t, 1, "", 0, "Color clusters belonging to track objects")
459-
AddOption(pointSize, float, 2.0f, "", 0, "Set point size")
460-
AddOption(lineWidth, float, 1.4f, "", 0, "Set line width")
459+
AddOption(pointSize, float, 2.0f, "", 0, "Set point size", min(0.8f), max(20.f))
460+
AddOption(lineWidth, float, 1.4f, "", 0, "Set line width", min(0.4f), max(20.f))
461461
AddOption(drawTPC, bool, true, "", 0, "Enable drawing TPC data")
462462
AddOption(drawTRD, bool, true, "", 0, "Enabale drawing TRD data")
463463
AddOption(drawTOF, bool, true, "", 0, "Enabale drawing TOF data")
@@ -502,11 +502,11 @@ AddOption(camYUp, bool, false, "", 0, "Orient the camera such that the y-axis is
502502
AddOption(cameraMode, int32_t, 0, "", 0, "Camera mode")
503503
AddOption(fullScreen, bool, false, "", 0, "Full Screen")
504504
AddOption(maximized, bool, true, "", 0, "Maximized window")
505-
AddOption(drawQualityMSAA, int32_t, 0, "", 0, "MultiSample Anti Aliasing")
506-
AddOption(drawQualityDownsampleFSAA, int32_t, 0, "", 0, "Downsampling Anti Aliasing")
505+
AddOption(drawQualityMSAA, int32_t, 0, "", 0, "MultiSample Anti Aliasing", min(0), max(8))
506+
AddOption(drawQualityDownsampleFSAA, int32_t, 0, "", 0, "Downsampling Anti Aliasing", min(0), max(4))
507507
AddOption(drawQualityVSync, bool, true, "", 0, "Enable Vertical Sync")
508508
AddOption(fpsLimit, int32_t, 1, "", 0, "FPS limit: 0 = none (maximum rate), 1 = display refresh rate (no limit without VSync), 2 = fpsRate")
509-
AddOption(fpsRate, int32_t, 100, "", 0, "Target frame rate (frames per second) for fpsLimit 2, or if the backend cannot limit to the refresh rate (0 = no limit)")
509+
AddOption(fpsRate, int32_t, 100, "", 0, "Target frame rate (frames per second) for fpsLimit 2, or if the backend cannot limit to the refresh rate", min(1), max(9999))
510510
AddOption(useGLIndirectDraw, bool, true, "", 0, "Use OpenGL indirect draws to reduce number of draw calls")
511511
AddOption(screenshotScaleFactor, int32_t, 1, "", 0, "Resolution scale factor when taking screenshots")
512512
AddOption(fov, int32_t, 45, "", 0, "Display FOV")

‎GPU/GPUTracking/display/GPUDisplay.cxx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(bool suspended, float animateTim
244244
mFrontend->mMouseWheel = 0;
245245
bool lookOrigin = mCfgR.camLookOrigin ^ mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIGIN]];
246246
bool yUp = mCfgR.camYUp ^ mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIENT_UP]] ^ lookOrigin;
247-
bool rotateModel = mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_MODEL]] || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]];
248-
bool rotateModelTPC = mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]];
247+
bool rotateModelTPC = mCfgC.mRotateAroundZ || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]];
248+
bool rotateModel = rotateModelTPC || mCfgC.mRotateModel || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_MODEL]];
249249

250250
// Calculate rotation / translation scaling factors
251251
float scalefactor = mFrontend->mKeys[mFrontend->KEY_SHIFT] ? 0.2f : 1.0f;

‎GPU/GPUTracking/display/GPUDisplay.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,18 @@ class GPUDisplay : public GPUDisplayInterface
9595
bool mRequestScreenshot = false;
9696
int32_t mAnimateScreenshot = 0;
9797
bool mAnimationChangeConfig = true;
98-
bool dummy = false; // dummy value to reference for keys that do not change variables
98+
bool mGuiPrintInfo = false; // Show the info of options changed with Qt GUI elements (not for key presses in the GUI)
99+
bool mRotateModel = false; // Rotate the model instead of the camera, like holding RCTRL (set in the Qt GUI)
100+
bool mRotateAroundZ = false; // Rotate the TPC around the beamline, like holding RALT (set in the Qt GUI)
101+
float mGuiScale = 1.f; // Scale factor of text and elements of the Qt GUI (on top of the system scaling), Ctrl + mouse wheel / Ctrl + 0 in the GUI
102+
bool dummy = false; // dummy value to reference for keys that do not change variables
99103
} mCfgC;
100104

101105
private:
102106
void buildConfig();
103107
void buildConfigKeys();
104108
void buildConfigInfo();
109+
void buildConfigQtGUI();
105110

106111
static constexpr int32_t NSECTORS = o2::tpc::constants::MAXSECTOR;
107112
static constexpr float GL_SCALE_FACTOR = (1.f / 100.f);

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,19 @@ void GPUDisplayFrontend::HandleExternalGUISignals()
123123
for (int32_t key : update->keys) {
124124
mDisplay->HandleExternalGUIKey(key);
125125
}
126-
if (update->lineWidth) {
127-
mDisplay->mCfgL.lineWidth = *update->lineWidth;
126+
applyGUIValues(update->values);
127+
if (update->guiScale) {
128+
mDisplay->mCfgC.mGuiScale = *update->guiScale;
128129
}
129130
}
130131

131132
if (gui) {
133+
auto values = getGUIValues();
132134
{
133135
std::lock_guard lock(mGUIChannel->mutex);
134136
mGUIChannel->state.cfg = {mDisplay->mCfgH, mDisplay->mCfgL, mDisplay->mCfgR};
137+
mGUIChannel->state.values = std::move(values);
138+
mGUIChannel->state.guiScale = mDisplay->mCfgC.mGuiScale;
135139
if (update) {
136140
mGUIChannel->state.appliedUpdate = update->id;
137141
}
@@ -205,6 +209,7 @@ int32_t GPUDisplayFrontend::startGUI()
205209
mGUIChannel->replyTimeout = std::chrono::milliseconds(mDisplay->cfg().guiReplyTimeout);
206210
mGUIChannel->maxUpdatesAhead = std::max<uint32_t>(mDisplay->cfg().guiMaxUpdatesAhead, 1); // 0 would block all updates
207211
mGUIChannel->windowName = DISPLAY_WINDOW_NAME;
212+
setGUILayout(*mGUIChannel);
208213
}
209214
mGUI->start();
210215
} else {

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
#include <mutex>
2323
#include <condition_variable>
2424
#include <string>
25+
#include <utility>
26+
#include <variant>
2527
#include <vector>
2628

2729
namespace o2::gpu
2830
{
31+
using GPUDisplayGUIValue = std::variant<int64_t, double, std::string>; // Value of a Qt GUI element
2932
class GPUReconstruction;
3033
class GPUDisplay;
3134
class GPUDisplayBackend;
@@ -158,6 +161,11 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface
158161
bool mWindowFullScreen = false, mWindowMaximized = false; // Window state last requested or reported by the window system
159162
template <typename... Args>
160163
void add_key_binding(Args&&... args); // KEY_LAMBDA(key, ...) entries, the help text, and optionally the keys to show in the help text, only used in GPUDisplayFrontendConfig.cxx
164+
template <typename... Args>
165+
void add_gui_binding(Args&&... args); // GUI_LAMBDA(name, ...) entries shown in one frame of the Qt GUI, and optionally its description and a flag to place the elements horizontally (default vertically), only used in GPUDisplayFrontendConfig.cxx
166+
void setGUILayout(GPUDisplayGUIChannel& channel) const; // Copy the GUI elements of the add_gui_binding calls to the channel
167+
std::vector<GPUDisplayGUIValue> getGUIValues(); // Current values of the options of the GUI elements, by element id
168+
void applyGUIValues(const std::vector<std::pair<uint32_t, GPUDisplayGUIValue>>& values); // Call the setter of the GUI elements changed in the GUI (element id, value), and show the info of their options
161169
void listConfig();
162170
const std::vector<std::string>& getHelpText() const; // Help text of all key bindings, one line per binding
163171
};

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx‎

Lines changed: 167 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "GPUDisplay.h"
1717
#include "frontend/GPUDisplayInfo.inc"
1818
#include "GPULogging.h"
19+
#include "GPUDisplayGUIChannel.h"
1920
#include "utils/qconfig_rtti.h"
2021
#include "utils/qconfig_internal_helpers.h"
2122

@@ -31,6 +32,7 @@
3132
#include <string_view>
3233
#include <tuple>
3334
#include <utility>
35+
#include <variant>
3436

3537
using namespace o2::gpu;
3638
struct GPUDisplayFrontend::configRTTIExt : public qConfig::qConfigRTTIentry {
@@ -57,6 +59,13 @@ struct GPUDisplayFrontend::configRTTIInfo {
5759
std::vector<std::string> keyMapNames; // Names of the key down mapping ids (KEY_MAP_*)
5860
std::vector<std::pair<std::string, std::string>> helpTextEntries; // Keys and text of each add_key_binding call
5961
std::vector<std::string> helpText;
62+
struct guiElementCallbacks {
63+
uint32_t rttiIndex;
64+
std::function<void(const GPUDisplayGUIValue&)> set; // GUI_LAMBDA setter, applies a value from the GUI
65+
std::function<GPUDisplayGUIValue()> get; // Current value of the option of the element
66+
};
67+
std::vector<guiElementCallbacks> guiElements; // By element id
68+
std::vector<GPUDisplayGUIChannel::Frame> guiLayout; // Frames of the add_gui_binding calls
6069
static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames
6170

6271
template <class T, size_t I = 0>
@@ -232,6 +241,148 @@ void GPUDisplayFrontend::add_key_binding(Args&&... args)
232241
mConfigRTTI->helpTextEntries.emplace_back(std::move(keys), std::get<nKeys>(argTuple));
233242
}
234243

244+
struct guiType { // Element type and choices, GUI_CHECKBOX, GUI_DROPDOWN(...), etc. in GPUDisplayFrontendConfigQtGUI.inc
245+
guiType(GPUDisplayGUIChannel::ElementType t, std::vector<std::pair<std::string, GPUDisplayGUIValue>> c = {}) : type(t), choices(std::move(c)) {}
246+
GPUDisplayGUIChannel::ElementType type;
247+
std::vector<std::pair<std::string, GPUDisplayGUIValue>> choices;
248+
};
249+
250+
static std::vector<std::pair<std::string, GPUDisplayGUIValue>> guiSliderValues(std::initializer_list<GPUDisplayGUIValue> values) // Choices of GUI_SLIDER_VALUES, named by their value
251+
{
252+
auto name = [](const auto& x) -> std::string {
253+
if constexpr (std::is_same_v<std::remove_cvref_t<decltype(x)>, std::string>) {
254+
return x;
255+
} else {
256+
return qConfig::print_type(x);
257+
}
258+
};
259+
std::vector<std::pair<std::string, GPUDisplayGUIValue>> retVal;
260+
for (const auto& v : values) {
261+
retVal.emplace_back(std::visit(name, v), v);
262+
}
263+
return retVal;
264+
}
265+
266+
struct guiElement { // GUI_LAMBDA(...)
267+
guiElement(std::string n, uint32_t i, guiType t, std::function<void(const GPUDisplayGUIValue&)> s, std::function<GPUDisplayGUIValue()> g) : name(std::move(n)), rttiIndex(i), type(std::move(t)), set(std::move(s)), get(std::move(g)) {}
268+
std::string name;
269+
uint32_t rttiIndex;
270+
guiType type;
271+
std::function<void(const GPUDisplayGUIValue&)> set;
272+
std::function<GPUDisplayGUIValue()> get;
273+
};
274+
275+
template <class T>
276+
static void guiAssign(T& dst, const GPUDisplayGUIValue& value) // Default GUI_LAMBDA setter: assign the GUI value, converted to the type of the option
277+
{
278+
std::visit([&](const auto& v) {
279+
using V = std::remove_cvref_t<decltype(v)>;
280+
if constexpr (std::is_same_v<T, std::string>) {
281+
if constexpr (std::is_same_v<V, std::string>) {
282+
dst = v;
283+
} else {
284+
dst = std::to_string(v);
285+
}
286+
} else if constexpr (std::is_same_v<V, std::string>) {
287+
dst = static_cast<T>(std::stod(v)); // Throws for invalid text
288+
} else {
289+
dst = static_cast<T>(v);
290+
}
291+
},
292+
value);
293+
}
294+
295+
template <class T>
296+
static GPUDisplayGUIValue toGUIValue(const T& v)
297+
{
298+
if constexpr (requires { v.load(); }) { // std::atomic in GPUDisplay::control
299+
return toGUIValue(v.load());
300+
} else if constexpr (std::is_same_v<T, std::string>) {
301+
return v;
302+
} else if constexpr (std::is_integral_v<T>) {
303+
return (int64_t)v;
304+
} else {
305+
static_assert(std::is_floating_point_v<T>, "Unsupported option type for the Qt GUI");
306+
return (double)v;
307+
}
308+
}
309+
310+
template <typename... Args>
311+
void GPUDisplayFrontend::add_gui_binding(Args&&... args)
312+
{
313+
constexpr size_t nElements = (0 + ... + std::is_same_v<std::remove_cvref_t<Args>, guiElement>);
314+
constexpr size_t nHorizontal = (0 + ... + std::is_same_v<std::remove_cvref_t<Args>, bool>);
315+
static_assert(nElements >= 1 && nHorizontal <= 1 && sizeof...(Args) <= nElements + nHorizontal + 1, "add_gui_binding needs at least one GUI element, and optionally the description and the horizontal flag");
316+
GPUDisplayGUIChannel::Frame frame;
317+
auto addElement = [&](auto& element) {
318+
using Type = GPUDisplayGUIChannel::ElementType;
319+
const uint32_t id = mConfigRTTI->guiElements.size();
320+
auto& el = frame.elements.emplace_back(GPUDisplayGUIChannel::Element{.name = element.name, .rttiIndex = element.rttiIndex, .type = element.type.type, .id = id, .isInteger = std::holds_alternative<int64_t>(element.get()), .choices = std::move(element.type.choices)});
321+
if (el.type == Type::SLIDER) {
322+
if (element.rttiIndex >= mConfigRTTI->rttiEntries.size()) {
323+
throw std::runtime_error("GUI slider " + element.name + " needs an option with RTTI for min and max values");
324+
}
325+
const auto& e = mConfigRTTI->rttiEntries[element.rttiIndex];
326+
if (e.min.empty() || e.max.empty()) {
327+
throw std::runtime_error("GUI slider " + element.name + ": option " + e.name + " needs min and max values");
328+
}
329+
el.min = std::stod(e.min);
330+
el.max = std::stod(e.max);
331+
}
332+
if (el.type == Type::TEXTBOX && element.rttiIndex < mConfigRTTI->rttiEntries.size() && !mConfigRTTI->rttiEntries[element.rttiIndex].min.empty() && !mConfigRTTI->rttiEntries[element.rttiIndex].max.empty()) {
333+
el.min = std::stod(mConfigRTTI->rttiEntries[element.rttiIndex].min);
334+
el.max = std::stod(mConfigRTTI->rttiEntries[element.rttiIndex].max);
335+
el.hasRange = true;
336+
}
337+
if ((el.type == Type::RADIO_BUTTONS || el.type == Type::DROPDOWN) ? el.choices.empty() : (!el.choices.empty() && el.type != Type::SLIDER)) {
338+
throw std::runtime_error("GUI element " + element.name + ": choices are needed for radio buttons and dropdown menus, and only allowed for them and sliders");
339+
}
340+
mConfigRTTI->guiElements.emplace_back(configRTTIInfo::guiElementCallbacks{element.rttiIndex, std::move(element.set), std::move(element.get)});
341+
};
342+
auto addArg = [&](auto& arg) {
343+
if constexpr (std::is_same_v<std::remove_cvref_t<decltype(arg)>, guiElement>) {
344+
addElement(arg);
345+
} else if constexpr (std::is_same_v<std::remove_cvref_t<decltype(arg)>, bool>) {
346+
frame.horizontal = arg;
347+
} else {
348+
frame.description = arg;
349+
}
350+
};
351+
(addArg(args), ...);
352+
mConfigRTTI->guiLayout.emplace_back(std::move(frame));
353+
}
354+
355+
void GPUDisplayFrontend::setGUILayout(GPUDisplayGUIChannel& channel) const { channel.layout = mConfigRTTI->guiLayout; }
356+
357+
std::vector<GPUDisplayGUIValue> GPUDisplayFrontend::getGUIValues()
358+
{
359+
std::vector<GPUDisplayGUIValue> retVal;
360+
retVal.reserve(mConfigRTTI->guiElements.size());
361+
for (const auto& e : mConfigRTTI->guiElements) {
362+
retVal.emplace_back(e.get());
363+
}
364+
return retVal;
365+
}
366+
367+
void GPUDisplayFrontend::applyGUIValues(const std::vector<std::pair<uint32_t, GPUDisplayGUIValue>>& values)
368+
{
369+
for (const auto& [id, value] : values) {
370+
if (id >= mConfigRTTI->guiElements.size()) {
371+
continue;
372+
}
373+
const auto& e = mConfigRTTI->guiElements[id];
374+
try {
375+
e.set(value);
376+
} catch (const std::exception& ex) {
377+
GPUError("Invalid value from the GUI for element %u: %s", id, ex.what());
378+
continue;
379+
}
380+
if (mDisplay->mCfgC.mGuiPrintInfo && e.rttiIndex < mConfigRTTI->configInfos.size()) {
381+
showConfigInfo(e.rttiIndex);
382+
}
383+
}
384+
}
385+
235386
const std::vector<std::string>& GPUDisplayFrontend::getHelpText() const { return mConfigRTTI->helpText; }
236387

237388
void GPUDisplayFrontend::listConfig()
@@ -256,8 +407,8 @@ void GPUDisplayFrontend::listConfig()
256407
std::sort(boundKeys.begin(), boundKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); });
257408
std::sort(availableKeys.begin(), availableKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); });
258409

259-
GPUInfo("Display config options with RTTI (struct, option, type, keys):");
260-
uint32_t nOptionsWithKey = 0;
410+
GPUInfo("Display config options with RTTI (struct, option, type, in Qt GUI, keys):");
411+
uint32_t nOptionsWithKey = 0, nOptionsInGUI = 0;
261412
for (uint32_t i = 0; i < rtti.rttiEntries.size(); i++) {
262413
const auto& e = rtti.rttiEntries[i];
263414
std::string keys;
@@ -266,9 +417,12 @@ void GPUDisplayFrontend::listConfig()
266417
keys += (keys.empty() ? "[" : " [") + rtti.getKeyName(k) + "]";
267418
}
268419
}
420+
const bool inGUI = std::any_of(rtti.guiElements.begin(), rtti.guiElements.end(), [i](const auto& g) { return g.rttiIndex == i; });
269421
nOptionsWithKey += !keys.empty();
270-
GPUInfo(" %-28s %-32s %-12s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), keys.c_str());
422+
nOptionsInGUI += inGUI;
423+
GPUInfo(" %-28s %-32s %-12s %-3s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), inGUI ? "GUI" : "---", keys.c_str());
271424
}
425+
GPUInfo("");
272426
GPUInfo("Bound keys (key, struct, option):");
273427
std::vector<std::string> mappedKeys(KEY_MAP_MAX);
274428
int32_t keyWidth = 0;
@@ -285,20 +439,28 @@ void GPUDisplayFrontend::listConfig()
285439
GPUInfo(" %-*s %-28s %s", keyWidth, ("[" + rtti.getKeyName(k) + "]").c_str(), hasRTTI ? rtti.rttiEntries[index].configStruct.c_str() : "(no config option)", hasRTTI ? rtti.rttiEntries[index].name.c_str() : "");
286440
}
287441
}
442+
GPUInfo("");
288443
GPUInfo("Key down mappings (id, name, key):");
289444
for (uint32_t i = 0; i < KEY_MAP_MAX; i++) {
290445
GPUInfo(" %2u %-28s %s", i, rtti.keyMapNames[i].c_str(), mappedKeys[i].c_str());
291446
}
447+
GPUInfo("");
292448
std::string available;
293449
for (uint16_t k : availableKeys) {
294450
available += (available.empty() ? "[" : " [") + rtti.getKeyName(k) + "]";
295451
}
296452
GPUInfo("Available keys: %s", available.c_str());
297-
GPUInfo("%zu options with RTTI, %u of them with a key, %zu bound keys, %zu available keys", rtti.rttiEntries.size(), nOptionsWithKey, boundKeys.size(), availableKeys.size());
453+
GPUInfo("Help text:");
454+
for (const auto& line : rtti.helpText) {
455+
GPUInfo(" %s", line.c_str());
456+
}
457+
GPUInfo("");
458+
GPUInfo("%zu options with RTTI, %u of them with a key, %u in the Qt GUI, %zu bound keys, %zu available keys", rtti.rttiEntries.size(), nOptionsWithKey, nOptionsInGUI, boundKeys.size(), availableKeys.size());
298459
}
299460

300461
#include "GPUDisplayFrontendConfigKeys.inc"
301462
#include "GPUDisplayFrontendConfigInfo.inc"
463+
#include "GPUDisplayFrontendConfigQtGUI.inc"
302464

303465
void GPUDisplay::buildConfig()
304466
{
@@ -341,6 +503,7 @@ void GPUDisplay::buildConfig()
341503
};
342504
}
343505
buildConfigInfo();
506+
buildConfigQtGUI();
344507
rtti->buildHelpText();
345508
}
346509

0 commit comments

Comments
 (0)