Project
Loading...
Searching...
No Matches
GPUDisplayFrontendConfig.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 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#include "frontend/GPUDisplayInfo.inc"
18#include "GPULogging.h"
19#include "GPUCommonMath.h"
21#include "GPUDisplayConsole.h"
22#define GPUSETTINGS_ALL
23#include "GPUSettings.h"
24#include "utils/qconfig_rtti.h"
26
27#include <algorithm>
28#include <charconv>
29#include <cmath>
30#include <concepts>
31#include <cstdarg>
32#include <cstdio>
33#include <cstring>
34#include <limits>
35#include <optional>
36#include <span>
37#include <sstream>
38#include <array>
39#include <cctype>
40#include <fstream>
41#include <functional>
42#include <iomanip>
43#include <stdexcept>
44#include <string>
45#include <string_view>
46#include <tuple>
47#include <unordered_map>
48#include <utility>
49#include <variant>
50#include <vector>
51
52using namespace o2::gpu;
55
56using configKeyCallback = std::function<void()>;
57
59 static constexpr std::tuple configStructs{&GPUDisplay::mCfgL, &GPUDisplay::mCfgH, &GPUDisplay::mCfgR, &GPUDisplay::mCfgO, &GPUDisplay::mCfgC}; // Config structs with RTTI, as members of GPUDisplay
60
61 std::vector<configRTTIExt> rttiEntries; // qconfig RTTI of all options of configStructs (struct, name, type, offset, default, min / max, description, message), the index identifies an option
62 std::vector<configKeyCallback> configInfos; // SetInfo callback for an option, indexed like rttiEntries, called after the callback of a key bound to it
63 std::vector<std::vector<uint32_t>> rttiReverseId; // [configStructIndex][offset] -> index in rttiEntries, INVALID_INDEX if no member at that offset
64 static constexpr uint32_t INVALID_INDEX = std::numeric_limits<uint32_t>::max(); // No entry: option index (rttiEntries) or, for keyAction / guiElements, no option
65 static constexpr int32_t KEY_UNBOUND = -1; // configKeys entry of a key without binding
66 static constexpr int32_t KEY_MAP_INDEX = 1 << 24; // configKeys entry of keys mapped to mKeyDownMap[entry - KEY_MAP_INDEX], entries below are indices in keyActions
67 static bool isKeyMap(int32_t binding) { return binding >= KEY_MAP_INDEX; } // configKeys entry of a key down mapping
68 using keyDownMapEntry = std::tuple<uint8_t, uint8_t>; // KEY_DOWN_MAP: key, KEY_MAP_ id
69 using keyLambdaEntry = std::tuple<uint16_t, std::string, uint32_t, configKeyCallback>; // KEY_LAMBDA / KEY_ACTION: key, actionName, index in rttiEntries (INVALID_INDEX for KEY_ACTION), callback
70 struct keyAction {
71 std::string actionName; // Unique, UPPER_SNAKE, used by bindKey
72 uint32_t rttiIndex; // Option changed by the action, INVALID_INDEX if none
73 configKeyCallback callback; // Lambda of KEY_LAMBDA / KEY_ACTION
74 };
75 std::vector<keyAction> keyActions; // Key functions of KEY_LAMBDA / KEY_ACTION, keys can be bound to them by name (bindKey)
76 std::unordered_map<std::string, uint32_t> keyActionsNames; // actionName -> index in keyActions
77 std::vector<std::string> configStructNames; // Names of the configStructs, by configStructIndex
78 std::vector<std::vector<int32_t>> currentActionKeys; // Reverse lookup of configKeys: keys bound to each action, by index in keyActions, in the order of binding
79 std::vector<int32_t> currentMappedKeys; // Reverse lookup of configKeys: key of each key down mapping, by id, -1 if none
80 std::array<int32_t, N_TOTAL_KEYS> configKeys; // By key code, special keys with SHIFT at code + SPECIAL_KEYS_SHIFTED: index in keyActions, KEY_MAP_INDEX + id, or KEY_UNBOUND (filled in buildConfig)
81 std::vector<std::string> keyNames; // Names of special keys (KEY_*) by key code < N_SPECIAL_KEYS, empty for other codes; for getKeyName and key names in console commands
82 std::vector<std::string> keyMapNames; // Names of the key down mapping ids (KEY_MAP_*)
83 std::vector<std::tuple<std::string, std::string, std::vector<int32_t>>> helpTextEntries; // Keys, text, and bindings (configKeys entries) of each add_key_binding call
84 std::vector<std::string> helpText; // Aligned lines from helpTextEntries, by buildHelpText; use getHelpText, which rebuilds them if needed
85 bool helpTextUpToDate = false; // Set by buildHelpText, reset when a key binding changes: getHelpText calls rebuildHelpText
87 uint32_t rttiIndex; // Option of the element
88 std::function<void(const GPUDisplayGUIValue&)> set; // GUI_LAMBDA setter, applies a value from the GUI
89 };
90 std::vector<guiElementCallbacks> guiElements; // By element id
91 std::vector<GPUDisplayGUIChannel::Frame> guiLayout; // Frames of the add_gui_binding calls
92 static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames
93
94 template <class T, size_t I = 0>
95 static constexpr uint32_t configStructIndex() // Index of struct T in configStructs, compile-time error if not present
96 {
97 if constexpr (std::is_same_v<std::tuple_element_t<I, std::remove_cv_t<decltype(configStructs)>>, T GPUDisplay::*>) {
98 return I;
99 } else {
100 return configStructIndex<T, I + 1>();
101 }
102 }
103 template <class T>
105 {
106 return display.*std::get<configStructIndex<T>()>(configStructs);
107 }
108 static char* configStructPtr(GPUDisplay& display, uint32_t index) // Runtime version of configStruct by index
109 {
110 char* retVal = nullptr;
111 uint32_t i = 0;
112 std::apply([&](auto... s) { ((retVal = i++ == index ? reinterpret_cast<char*>(&(display.*s)) : retVal), ...); }, configStructs);
113 return retVal;
114 }
115 std::string setValue(GPUDisplay& display, uint32_t index, const GPUDisplayConsole::consoleArg& value); // Converted to the type of the option, empty string on success, otherwise the error
116 std::optional<std::string> valueString(GPUDisplay& display, uint32_t index, bool precise = false) const; // Current value of an option, printed with print_type as in qconfig.cxx, nullopt for unsupported types; precise: floats with all digits, to read them back exactly
117 template <class T, class M>
118 uint32_t getRTTIIndex(GPUDisplay& display, M T::* member) // RTTI index of a direct member of a config struct
119 {
120 const T& obj = configStruct<T>(display);
121 return rttiReverseId[configStructIndex<T>()][(const char*)&(obj.*member) - (const char*)&obj];
122 }
123 void buildHelpText() // Keys left-aligned, the ':' aligned after the longest keys
124 {
125 size_t width = 0;
126 for (const auto& [keys, text, bindings] : helpTextEntries) {
127 width = std::max(width, keys.size());
128 }
129 helpText.clear();
130 for (const auto& [keys, text, bindings] : helpTextEntries) {
131 helpText.emplace_back(keys + std::string(width - keys.size(), ' ') + ": " + text);
132 }
133 helpTextUpToDate = true;
134 }
135 void rebuildHelpText() // Keys of each entry from the current bindings, in the order of add_key_binding: [N/A] if unbound, [a|b] for several keys; entries without bindings (mouse) keep their text
136 {
137 for (auto& [keys, text, bindings] : helpTextEntries) {
138 if (bindings.empty()) {
139 continue;
140 }
141 keys.clear();
142 for (int32_t binding : bindings) {
143 std::string bound;
144 std::vector<int32_t> keyCodes = boundKeys(binding);
145 std::sort(keyCodes.begin(), keyCodes.end()); // By key code, independent of the order of binding
146 for (int32_t k : keyCodes) {
147 bound += (bound.empty() ? "" : "|") + getKeyName(k);
148 }
149 keys += (keys.empty() ? "[" : " [") + (bound.empty() ? "N/A" : bound) + "]";
150 }
151 }
153 }
154 std::vector<int32_t> boundKeys(int32_t binding) const // Keys bound to the action or key down mapping
155 {
156 if (isKeyMap(binding)) {
158 return key < 0 ? std::vector<int32_t>() : std::vector<int32_t>{key};
159 }
161 }
162 void setKeyBinding(uint32_t key, int32_t binding) // Binds the unbound key, also in the reverse lookups (mKeyDownMap is set by the caller)
163 {
165 if (isKeyMap(binding)) {
167 } else {
168 currentActionKeys[binding].emplace_back(key);
169 }
170 helpTextUpToDate = false;
171 }
172 int32_t removeKeyBinding(uint32_t key) // Unbinds the key, also in the reverse lookups (mKeyDownMap is reset by the caller), returns the previous binding
173 {
174 const int32_t binding = configKeys[key];
175 if (isKeyMap(binding)) {
177 } else if (binding != KEY_UNBOUND) {
178 std::erase(currentActionKeys[binding], key);
179 }
181 helpTextUpToDate = false;
182 return binding;
183 }
184 void unbindAllKeys() // Unbinds all keys, also in the reverse lookups (mKeyDownMap is reset by the caller)
185 {
187 currentMappedKeys.assign(KEY_MAP_MAX, -1);
188 for (auto& keys : currentActionKeys) {
189 keys.clear();
190 }
191 helpTextUpToDate = false;
192 }
193 std::string bindingName(int32_t binding) const // Name of the action or key down mapping, empty if unbound
194 {
195 return isKeyMap(binding) ? keyMapNames[binding - KEY_MAP_INDEX] : (binding >= 0 ? keyActions[binding].actionName : "");
196 }
197 uint32_t keyRTTIIndex(uint32_t key) const // Option changed by the action bound to the key, INVALID_INDEX if none
198 {
199 const int32_t binding = configKeys[key];
200 return binding != KEY_UNBOUND && !isKeyMap(binding) ? keyActions[binding].rttiIndex : INVALID_INDEX;
201 }
202 std::string getKeyName(uint16_t key) const // Name of a special key (KEY_*), the character otherwise, the code if neither, SHIFT_ for shifted special keys
203 {
204 if (key >= 256) {
205 return "SHIFT_" + getKeyName(key - SPECIAL_KEYS_SHIFTED);
206 }
207 return key < keyNames.size() && !keyNames[key].empty() ? keyNames[key] : (key < 32 ? std::to_string(key) : std::string(1, (char)key));
208 }
209};
210
211namespace
212{
213#include "utils/qconfig_genrtti.inc"
214} // namespace
215
217{
218 const auto& [oldCfgH, oldCfgL, oldCfgR] = mOldCfg;
219 const bool changedH = memcmp((void*)&oldCfgH, (void*)&mCfgH, sizeof(mCfgH)) != 0, changedL = memcmp((void*)&oldCfgL, (void*)&mCfgL, sizeof(mCfgL)) != 0, changedR = memcmp((void*)&oldCfgR, (void*)&mCfgR, sizeof(mCfgR)) != 0;
220 if (mAnimate && (changedH || changedL || changedR || mCfgC.resetScene.load())) {
221 StopAnimation();
222 }
223 if (changedH) {
224 mUpdateEventData = true;
225 }
226 if (mCfgL.propagateTracks != 0 && !mTracksArePropagated) {
227 mUpdateVertexLists = true;
228 }
229 if (changedL || changedR) {
230 mUpdateDrawCommands = true;
231 }
232 if (oldCfgR.drawQualityMSAA != mCfgR.drawQualityMSAA || oldCfgR.drawQualityDownsampleFSAA != mCfgR.drawQualityDownsampleFSAA || oldCfgL.depthBuffer != mCfgL.depthBuffer || oldCfgR.screenshotScaleFactor != mCfgR.screenshotScaleFactor) {
233 mUpdateRenderPipeline = true;
234 }
235 if (oldCfgL.depthBuffer != mCfgL.depthBuffer) { // Also for set / scripts, not only key and GUI
236 mBackend->setDepthBuffer();
237 }
238
239 if (oldCfgR.drawQualityVSync != mCfgR.drawQualityVSync) {
240 mFrontend->SetVSync(mCfgR.drawQualityVSync);
241 mBackend->SetVSync(mCfgR.drawQualityVSync);
242 }
243 if (oldCfgR.fullScreen != mCfgR.fullScreen) {
244 mFrontend->requestFullScreen(mCfgR.fullScreen);
245 }
246 if (oldCfgR.maximized != mCfgR.maximized) {
247 mFrontend->requestMaximized(mCfgR.maximized);
248 }
249 if (oldCfgR.useGLIndirectDraw != mCfgR.useGLIndirectDraw) {
250 mUpdateEventData = true;
251 }
252 if (oldCfgH.trackFilter != mCfgH.trackFilter) {
253 mUpdateTrackFilter = true;
254 }
255 mCfgO.rotateModel |= mCfgO.rotateAroundZ;
256 if (mCfgC.showConsole != mConsoleShown) {
257 mConsoleShown = mCfgC.showConsole;
258 const double start = mConsoleTimer.IsRunning() ? std::max(0., mConfig.internal.consoleDelay - mConsoleTimer.GetCurrentElapsedTime()) : 0.; // Reverse a running animation from its current position
259 mConsoleTimer.Reset();
260 mConsoleTimer.AddTime(start);
261 mConsoleTimer.Start();
262 if (mCfgC.showConsole) {
263 const bool shift = mFrontend->mKeys[mFrontend->KEY_SHIFT]; // Kept while the console is shown (scrolling, shifted keys), frontends still update it
264 std::fill(std::begin(mFrontend->mKeys), std::end(mFrontend->mKeys), false);
265 mFrontend->mKeys[mFrontend->KEY_SHIFT] = shift;
266 }
267 }
268 if (mCfgO.applyToAllPoints && oldCfgL.animationMode != mCfgL.animationMode) { // Also for keys; the animation's own changes are in mOldCfg, so this is only user input
269 for (auto& p : mAnimationVectors) {
270 p.cfgL.animationMode = mCfgL.animationMode;
271 }
272 }
273 if (mCfgC.animationPoints.selected >= 0 && mCfgC.animationPoints.selected < (int32_t)mAnimationVectors.size()) { // Update animation settings of selected point
274 mAnimationVectors[mCfgC.animationPoints.selected].cfgL = mCfgL;
275 }
276 updateAnimationPointsGUI();
277}
278
279void GPUDisplay::HandleExternalGUIKey(int32_t key, bool bypassConsole)
280{
281 bool shifted = key >= 'A' && key <= 'Z';
282 if (key >= 256) { // Special key with SHIFT, from translateExternalKey
283 shifted = true;
284 key -= mFrontend->SPECIAL_KEYS_SHIFTED;
285 }
286 bool oldShift = mFrontend->mKeys[mFrontend->KEY_SHIFT];
287 mFrontend->mKeys[mFrontend->KEY_SHIFT] = shifted;
288 HandleKey(key, true, bypassConsole);
289 mFrontend->mKeys[mFrontend->KEY_SHIFT] = oldShift;
290}
291
292void GPUDisplayFrontend::HandleExternalGUIKeys()
293{
294 std::lock_guard lock(mExternalGUIKeyMutex);
295 for (auto key : mExternalGUIKeys) {
297 }
298 mExternalGUIKeys.clear();
299}
300
301template <typename... Args>
302void GPUDisplayFrontend::add_key_binding(Args&&... args)
303{
304 constexpr size_t nKeys = (0 + ... + (std::is_same_v<std::remove_cvref_t<Args>, GPUDisplayConfiguration::keyLambdaEntry> || std::is_same_v<std::remove_cvref_t<Args>, GPUDisplayConfiguration::keyDownMapEntry>));
305 static_assert(nKeys >= 1 && (sizeof...(Args) == nKeys + 1 || sizeof...(Args) == nKeys + 2), "add_key_binding needs at least one key, the help text, and optionally the keys to show in the help text");
306 auto argTuple = std::forward_as_tuple(args...);
307 std::string keys;
308 std::vector<int32_t> bindings;
309 auto addKey = [&](auto& key) {
310 const uint16_t character = std::get<0>(key);
311 const std::string name = mConfiguration->getKeyName(character);
313 if (mConfiguration->configKeys[character] != GPUDisplayConfiguration::KEY_UNBOUND) {
314 throw std::runtime_error("Key [" + name + "] assigned twice in display frontend");
315 }
316 if constexpr (std::is_same_v<std::remove_cvref_t<decltype(key)>, GPUDisplayConfiguration::keyDownMapEntry>) {
317 const uint8_t id = std::get<1>(key);
318 if (mKeyDownMap[id]) {
319 throw std::runtime_error("Key down mapping " + mConfiguration->keyMapNames[id] + " assigned twice in display frontend");
320 }
321 mKeyDownMap[id] = character >= 'a' && character <= 'z' ? character + 'A' - 'a' : character; // mKeys stores letters in upper case
323 } else if constexpr (std::is_same_v<std::remove_cvref_t<decltype(key)>, GPUDisplayConfiguration::keyLambdaEntry>) {
324 const std::string& actionName = std::get<1>(key);
325 const auto& mapNames = mConfiguration->keyMapNames;
326 if (mConfiguration->keyActionsNames.contains(actionName) || std::find(mapNames.begin(), mapNames.end(), actionName) != mapNames.end()) {
327 throw std::runtime_error("Key action " + actionName + " defined twice in display frontend, or name of a key down mapping");
328 }
329 binding = mConfiguration->keyActions.size();
330 mConfiguration->keyActionsNames.emplace(actionName, binding);
331 mConfiguration->keyActions.push_back({actionName, std::get<2>(key), std::move(std::get<3>(key))});
332 mConfiguration->currentActionKeys.emplace_back();
333 } else {
334 static_assert(false, "invalid code path");
335 }
336 mConfiguration->setKeyBinding(character, binding);
337 bindings.emplace_back(binding);
338 keys += (keys.empty() ? "[" : " [") + name + "]";
339 };
340 [&]<size_t... I>(std::index_sequence<I...>) { (addKey(std::get<I>(argTuple)), ...); }(std::make_index_sequence<nKeys>());
341 if constexpr (sizeof...(Args) == nKeys + 2) {
342 keys = std::get<nKeys + 1>(argTuple);
343 }
344 mConfiguration->helpTextEntries.emplace_back(std::move(keys), std::get<nKeys>(argTuple), std::move(bindings));
345}
346
347struct guiType { // Element type and choices, GUI_CHECKBOX, GUI_DROPDOWN(...), etc. in GPUDisplayFrontendConfigQtGUI.inc
348 guiType(GPUDisplayGUIChannel::ElementType t, std::vector<std::pair<std::string, GPUDisplayGUIValue>> c = {}, std::function<GPUDisplayGUIChannel::Options()> o = {}, uint32_t l = 0, int64_t b = 0, double s = 0) : type(t), choices(std::move(c)), options(std::move(o)), lines(l), bitMask(b), step(s) {}
350 std::vector<std::pair<std::string, GPUDisplayGUIValue>> choices;
351 std::function<GPUDisplayGUIChannel::Options()> options; // GUI_SLIDER_DYNAMIC / GUI_DROPDOWN_DYNAMIC
352 uint32_t lines = 0; // GUI_TEXTEDIT
353 int64_t bitMask = 0; // GUI_CHECKBOX_BIT
354 double step = 0; // GUI_SLIDER_STEP / GUI_SLIDER_DYNAMIC_STEP / GUI_MULTI_SLIDER
355};
356
357static std::vector<std::pair<std::string, GPUDisplayGUIValue>> guiSliderValues(std::initializer_list<GPUDisplayGUIValue> values) // Choices of GUI_SLIDER_VALUES, named by their value
358{
359 auto name = [](const auto& x) -> std::string {
360 if constexpr (std::is_same_v<std::remove_cvref_t<decltype(x)>, std::string>) {
361 return x;
362 } else if constexpr (std::is_same_v<std::remove_cvref_t<decltype(x)>, GPUDisplayMultiSliderValue>) {
363 return "";
364 } else {
365 return qConfig::print_type(x);
366 }
367 };
368 std::vector<std::pair<std::string, GPUDisplayGUIValue>> retVal;
369 for (const auto& v : values) {
370 retVal.emplace_back(std::visit(name, v), v);
371 }
372 return retVal;
373}
374
375template <typename T>
376static std::vector<std::pair<std::string, GPUDisplayGUIValue>> guiNamedChoices(const T& names) // Choices of GUI_DROPDOWN_NAMES
377{
378 std::vector<std::pair<std::string, GPUDisplayGUIValue>> retVal;
379 for (const auto& name : names) {
380 retVal.emplace_back(name, (int64_t)retVal.size());
381 }
382 return retVal;
383}
384
385static GPUDisplayGUIChannel::Options guiRange(double min, double max) { return {.min = min, .max = max, .choices = {}}; } // For the lambda of GUI_SLIDER_DYNAMIC
386
387struct guiElement { // GUI_LAMBDA(...)
388 guiElement(std::string n, uint32_t i, guiType t, std::function<void(const GPUDisplayGUIValue&)> s, std::function<GPUDisplayGUIValue(const GPUDisplayGUIChannel::Settings&)> g) : name(std::move(n)), rttiIndex(i), type(std::move(t)), set(std::move(s)), get(std::move(g)) {}
389 std::string name;
392 std::function<void(const GPUDisplayGUIValue&)> set;
394};
395
396template <class T>
397static void guiAssign(T& dst, const GPUDisplayGUIValue& value) // Default GUI_LAMBDA setter: assign the GUI value, converted to the type of the option
398{
399 std::visit([&](const auto& v) {
400 using V = std::remove_cvref_t<decltype(v)>;
401 if constexpr (std::is_same_v<T, V>) {
402 dst = v;
403 } else if constexpr (std::is_same_v<T, GPUDisplayMultiSliderValue> || std::is_same_v<V, GPUDisplayMultiSliderValue>) {
404 throw std::runtime_error("GUI value of a multi slider for another element type, or vice versa");
405 } else if constexpr (std::is_same_v<T, std::string>) {
406 if constexpr (std::is_same_v<V, std::string>) {
407 dst = v;
408 } else {
410 }
411 } else if constexpr (std::is_same_v<V, std::string>) {
412 dst = static_cast<T>(std::stod(v)); // Throws for invalid text
413 } else {
414 dst = static_cast<T>(v);
415 }
416 },
417 value);
418}
419
420template <class T>
421static GPUDisplayGUIValue toGUIValue(const T& v)
422{
423 if constexpr (std::is_same_v<T, std::string>) {
424 return v;
425 } else if constexpr (std::is_integral_v<T>) {
426 return (int64_t)v;
427 } else {
428 static_assert(std::is_floating_point_v<T>, "Unsupported option type for the Qt GUI");
429 return (double)v;
430 }
431}
432
433static GPUDisplayGUIValue toGUIValue(const GPUDisplayMultiSliderValue& v) { return v; }
434
435template <class T>
436static GPUDisplayGUIValue toGUIValue(const GPUDisplayCopyableAtomic<T>& v)
437{
438 return toGUIValue(v.load());
439}
440
441struct guiVertical { // Argument between the elements of add_gui_binding: alignment of the following elements
443};
444
445template <typename... Args>
446void GPUDisplayFrontend::add_gui_binding(Args&&... args)
447{
448 constexpr size_t nElements = (0 + ... + std::is_same_v<std::remove_cvref_t<Args>, guiElement>);
449 constexpr size_t nHorizontal = (0 + ... + std::is_same_v<std::remove_cvref_t<Args>, bool>);
450 constexpr size_t nVertical = (0 + ... + std::is_same_v<std::remove_cvref_t<Args>, guiVertical>);
451 static_assert(nElements >= 1 && nHorizontal <= 1 && sizeof...(Args) <= nElements + nHorizontal + nVertical + 1, "add_gui_binding needs at least one GUI element, and optionally the description, the horizontal flag, and guiVertical between elements");
453 bool horizontal = false; // Initially the flag of the frame, changed by guiVertical
454 auto getHorizontal = [&](auto& arg) {
455 if constexpr (std::is_same_v<std::remove_cvref_t<decltype(arg)>, bool>) {
456 horizontal = arg;
457 }
458 };
459 (getHorizontal(args), ...);
460 auto addElement = [&](auto& element) {
462 if (element.type.bitMask) { // The element sets / shows only these bits of the option
463 element.set = [this, set = element.set, get = element.get, mask = element.type.bitMask](const GPUDisplayGUIValue& v) {
464 const int64_t current = std::get<int64_t>(get({mDisplay->mCfgH, mDisplay->mCfgL, mDisplay->mCfgR, mDisplay->mCfgO, mDisplay->mCfgC}));
465 set(std::get<int64_t>(v) ? (current | mask) : (current & ~mask));
466 };
467 element.get = [get = element.get, mask = element.type.bitMask](const GPUDisplayGUIChannel::Settings& cfg) -> GPUDisplayGUIValue { return (int64_t)((std::get<int64_t>(get(cfg)) & mask) != 0); };
468 }
469 static const GPUDisplayGUIChannel::Settings defaults; // Only for the types of the values
470 const uint32_t id = mConfiguration->guiElements.size();
471 auto& el = frame.elements.emplace_back(GPUDisplayGUIChannel::Element{.name = element.name, .description = element.rttiIndex < mConfiguration->rttiEntries.size() ? mConfiguration->rttiEntries[element.rttiIndex].description : "", .rttiIndex = element.rttiIndex, .type = element.type.type, .id = id, .isInteger = std::holds_alternative<int64_t>(element.get(defaults)), .isString = std::holds_alternative<std::string>(element.get(defaults)), .lines = element.type.lines, .step = element.type.step, .choices = std::move(element.type.choices), .dynamicOptions = std::move(element.type.options), .get = element.get});
472 if (el.dynamicOptions && !((el.type == Type::SLIDER && el.choices.empty()) || el.type == Type::DROPDOWN)) {
473 throw std::runtime_error("GUI element " + element.name + ": dynamic options only for sliders with range and dropdown menus");
474 }
475 if (el.type == Type::SLIDER && !el.dynamicOptions) {
476 if (element.rttiIndex >= mConfiguration->rttiEntries.size()) {
477 throw std::runtime_error("GUI slider " + element.name + " needs an option with RTTI for min and max values");
478 }
479 const auto& e = mConfiguration->rttiEntries[element.rttiIndex];
480 if (e.min.empty() || e.max.empty()) {
481 throw std::runtime_error("GUI slider " + element.name + ": option " + e.name + " needs min and max values");
482 }
483 el.min = std::stod(e.min);
484 el.max = std::stod(e.max);
485 }
486 if (el.type == Type::TEXTBOX && element.rttiIndex < mConfiguration->rttiEntries.size() && !mConfiguration->rttiEntries[element.rttiIndex].min.empty() && !mConfiguration->rttiEntries[element.rttiIndex].max.empty()) {
487 el.min = std::stod(mConfiguration->rttiEntries[element.rttiIndex].min);
488 el.max = std::stod(mConfiguration->rttiEntries[element.rttiIndex].max);
489 el.hasRange = true;
490 }
491 if (el.step > 0 && !(el.type == Type::MULTI_SLIDER || (el.type == Type::SLIDER && el.choices.empty()))) {
492 throw std::runtime_error("GUI element " + element.name + ": a step only for sliders with a range and multi sliders");
493 }
494 if (el.type == Type::MULTI_SLIDER && (!std::holds_alternative<GPUDisplayMultiSliderValue>(element.get(defaults)) || el.step <= 0)) {
495 throw std::runtime_error("GUI multi slider " + element.name + " needs an option of type GPUDisplayMultiSliderValue and a step > 0");
496 }
497 if ((el.type == Type::RADIO_BUTTONS || el.type == Type::DROPDOWN) ? (el.choices.empty() && !el.dynamicOptions) : (!el.choices.empty() && el.type != Type::SLIDER)) {
498 throw std::runtime_error("GUI element " + element.name + ": choices are needed for radio buttons and dropdown menus, and only allowed for them and sliders");
499 }
500 el.horizontal = horizontal;
501 mConfiguration->guiElements.emplace_back(GPUDisplayConfiguration::guiElementCallbacks{element.rttiIndex, std::move(element.set)});
502 };
503 auto addArg = [&](auto& arg) {
504 if constexpr (std::is_same_v<std::remove_cvref_t<decltype(arg)>, guiElement>) {
505 addElement(arg);
506 } else if constexpr (std::is_same_v<std::remove_cvref_t<decltype(arg)>, guiVertical>) {
507 horizontal = !arg.vertical;
508 } else if constexpr (!std::is_same_v<std::remove_cvref_t<decltype(arg)>, bool>) {
509 frame.description = arg;
510 }
511 };
512 (addArg(args), ...);
513 mConfiguration->guiLayout.emplace_back(std::move(frame));
514}
515
516void GPUDisplayFrontend::setGUILayout(GPUDisplayGUIChannel& channel) const { channel.layout = mConfiguration->guiLayout; }
517
518void GPUDisplayFrontend::applyGUIValues(const GPUDisplayGUIUpdate& update)
519{
520 for (const auto& [id, value] : update.values) {
521 if (id >= mConfiguration->guiElements.size()) {
522 continue;
523 }
524 const auto& e = mConfiguration->guiElements[id];
525 try {
526 e.set(value);
527 } catch (const std::exception& ex) {
528 GPUError("Invalid value from the GUI for element %u: %s", id, ex.what());
529 continue;
530 }
531 if (mDisplay->mCfgO.guiPrintInfo && e.rttiIndex < mConfiguration->configInfos.size()) {
532 showConfigInfo(e.rttiIndex);
533 }
534 }
535}
536
537const std::vector<std::string>& GPUDisplayFrontend::getHelpText()
538{
539 if (!mConfiguration->helpTextUpToDate) {
540 mConfiguration->rebuildHelpText();
541 }
542 return mConfiguration->helpText;
543}
544
545static std::pair<int32_t, uint16_t> keyOrder(uint16_t k) // 0-9, a-z, A-Z, other characters, special keys, special keys with SHIFT
546{
547 return {k >= '0' && k <= '9' ? 0
548 : k >= 'a' && k <= 'z' ? 1
549 : k >= 'A' && k <= 'Z' ? 2
550 : k >= 256 ? 5
551 : k > 32 ? 3
552 : 4,
553 k};
554}
555
556void GPUDisplayFrontend::listKeys(const std::function<void(const std::string&)>& print)
557{
558 const auto& rtti = *mConfiguration;
559 std::vector<uint16_t> boundKeys;
560 for (uint32_t k = 1; k < rtti.configKeys.size(); k++) {
561 if (rtti.configKeys[k] != GPUDisplayConfiguration::KEY_UNBOUND) {
562 boundKeys.emplace_back(k);
563 }
564 }
565 std::sort(boundKeys.begin(), boundKeys.end(), [](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); });
566 std::vector<std::pair<std::string, int32_t>> rows; // Key, binding
567 for (uint16_t k : boundKeys) {
568 rows.emplace_back("[" + rtti.getKeyName(k) + "]", rtti.configKeys[k]);
569 }
570 auto addUnbound = [&](int32_t binding) {
571 if (rtti.boundKeys(binding).empty()) {
572 rows.emplace_back("[N/A]", binding);
573 }
574 };
575 for (uint32_t i = 0; i < rtti.keyActions.size(); i++) {
576 addUnbound(i);
577 }
578 for (uint32_t i = 0; i < KEY_MAP_MAX; i++) {
580 }
581 size_t keyWidth = 0, nameWidth = 0;
582 for (const auto& [key, binding] : rows) {
583 keyWidth = std::max(keyWidth, key.size());
584 nameWidth = std::max(nameWidth, rtti.bindingName(binding).size());
585 }
586 print("Key bindings (key, action or key down mapping, option):");
587 for (const auto& [key, binding] : rows) {
588 const std::string name = rtti.bindingName(binding);
590 const std::string option = GPUDisplayConfiguration::isKeyMap(binding) ? "(key down map)" : (index == GPUDisplayConfiguration::INVALID_INDEX ? "(no config option)" : rtti.rttiEntries[index].configStruct + " " + rtti.rttiEntries[index].name);
591 print(" " + key + std::string(keyWidth - key.size() + 1, ' ') + name + std::string(nameWidth - name.size() + 1, ' ') + option);
592 }
593 print("");
594 print("Key down mappings (id, name, key):");
595 for (uint32_t i = 0; i < KEY_MAP_MAX; i++) {
596 char line[128];
597 snprintf(line, sizeof(line), " %2u %-28s %s", i, rtti.keyMapNames[i].c_str(), rtti.currentMappedKeys[i] < 0 ? "[N/A]" : ("[" + rtti.getKeyName(rtti.currentMappedKeys[i]) + "]").c_str());
598 print(line);
599 }
600}
601
602void GPUDisplayFrontend::listConfig()
603{
604 const auto& rtti = *mConfiguration;
605 std::vector<uint16_t> boundKeys, availableKeys;
606 for (uint32_t k = 1; k < rtti.configKeys.size(); k++) {
607 const uint32_t base = k >= 256 ? k - SPECIAL_KEYS_SHIFTED : k; // A shifted special key is possible if the special key is, SHIFT itself is the modifier
608 const bool possible = base != KEY_SHIFT && ((base < N_SPECIAL_KEYS && !rtti.keyNames[base].empty()) || (k < 256 && rtti.possibleKeys.find((char)k) != std::string_view::npos));
609 if (rtti.configKeys[k] != GPUDisplayConfiguration::KEY_UNBOUND) {
610 boundKeys.emplace_back(k);
611 } else if (possible) {
612 availableKeys.emplace_back(k);
613 }
614 }
615 std::sort(boundKeys.begin(), boundKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); });
616 std::sort(availableKeys.begin(), availableKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); });
617
618 GPUInfo("");
619 GPUInfo("Display config options with RTTI (struct, option, type, default, in Qt GUI, keys):");
620 uint32_t nOptionsWithKey = 0, nOptionsInGUI = 0;
621 int32_t structWidth = 0, nameWidth = 0, typeWidth = 0, defaultWidth = 0; // Columns aligned to the longest entry
622 for (const auto& e : rtti.rttiEntries) {
623 structWidth = std::max<int32_t>(structWidth, e.configStruct.size());
624 nameWidth = std::max<int32_t>(nameWidth, e.name.size());
625 typeWidth = std::max<int32_t>(typeWidth, e.type.size());
626 defaultWidth = std::max<int32_t>(defaultWidth, e.defaultValue.size());
627 }
628 for (uint32_t i = 0; i < rtti.rttiEntries.size(); i++) {
629 const auto& e = rtti.rttiEntries[i];
630 std::string keys;
631 for (uint16_t k : boundKeys) {
632 if (rtti.keyRTTIIndex(k) == i) {
633 keys += (keys.empty() ? "[" : " [") + rtti.getKeyName(k) + "]";
634 }
635 }
636 const bool inGUI = std::any_of(rtti.guiElements.begin(), rtti.guiElements.end(), [i](const auto& g) { return g.rttiIndex == i; });
637 nOptionsWithKey += !keys.empty();
638 nOptionsInGUI += inGUI;
639 GPUInfo(" %-*s %-*s %-*s %-*s %-3s %s", structWidth, e.configStruct.c_str(), nameWidth, e.name.c_str(), typeWidth, e.type.c_str(), defaultWidth, e.defaultValue.c_str(), inGUI ? "GUI" : "---", keys.c_str());
640 }
641 GPUInfo("");
642 listKeys([](const std::string& line) { GPUInfo("%s", line.c_str()); });
643 GPUInfo("");
644 static constexpr std::string_view availablePrefix = "Available keys: ";
645 static constexpr size_t MAX_LINE_LENGTH = 240;
646 std::string available(availablePrefix);
647 for (uint16_t k : availableKeys) {
648 const std::string key = "[" + rtti.getKeyName(k) + "]";
649 if (available.size() > availablePrefix.size() && available.size() + 1 + key.size() > MAX_LINE_LENGTH) { // Wrap, without splitting a key
650 GPUInfo("%s", available.c_str());
651 available.assign(availablePrefix.size(), ' ');
652 } else if (available.size() > availablePrefix.size()) {
653 available += ' ';
654 }
655 available += key;
656 }
657 GPUInfo("%s", available.c_str());
658 GPUInfo("");
659 GPUInfo("Help text:");
660 for (const auto& line : getHelpText()) {
661 GPUInfo(" %s", line.c_str());
662 }
663 GPUInfo("");
664 mConsole->printCommands([](const std::string& line) { GPUInfo("%s", line.c_str()); });
665 GPUInfo("");
666 GPUInfo("%zu options with RTTI, %u of them with a key, %u in the Qt GUI, %zu bound keys, %zu available keys, %zu console commands", rtti.rttiEntries.size(), nOptionsWithKey, nOptionsInGUI, boundKeys.size(), availableKeys.size(), mConsole->mConsoleCommands.size());
667 GPUInfo("");
668}
669
670#include "GPUDisplayFrontendConfigKeys.inc"
671#include "GPUDisplayFrontendConfigInfo.inc"
672#include "GPUDisplayFrontendConfigQtGUI.inc"
673#include "GPUDisplayFrontendConfigConsole.inc"
674
675void GPUDisplay::buildConfig()
676{
677 auto& rtti = mFrontend->mConfiguration;
679
680 rtti->unbindAllKeys();
681 mFrontend->mKeyDownMap.fill(0); // Set again by buildConfigKeys, also when the display is started again
682 rtti->keyNames.resize(rtti->configKeys.size());
683 rtti->keyMapNames.resize(GPUDisplayFrontend::KEY_MAP_MAX + 1); // Including MAX
684#define GPUDisplayFrontendAddKey(code, key) rtti->keyNames[code] = #key
685#define GPUDisplayFrontendKeyDownMap(code, key) rtti->keyMapNames[code] = #key
687
688 std::tie(rtti->rttiEntries, rtti->configStructNames) = std::apply([this](auto... s) { return qConfigGenRTTI<GPUDisplayFrontend::configRTTIExt, std::remove_reference_t<decltype(this->*s)>...>(); }, rtti->configStructs);
689 std::apply([&](auto... s) { (rtti->rttiReverseId.emplace_back(sizeof(this->*s), GPUDisplayFrontend::GPUDisplayConfiguration::INVALID_INDEX), ...); }, rtti->configStructs);
690 for (uint32_t i = 0; i < rtti->rttiEntries.size(); i++) {
691 const auto& entry = rtti->rttiEntries[i];
692 rtti->rttiReverseId[entry.configStructIndex][entry.offset] = i;
693 }
694
695 buildConfigKeys();
696
697 rtti->configInfos.resize(rtti->rttiEntries.size());
698 for (uint32_t i = 0; i < rtti->rttiEntries.size(); i++) { // Info from the qConfig message, with the value as %s as in qconfig.cxx
699 const auto& e = rtti->rttiEntries[i];
700 if (e.message.empty()) {
701 continue;
702 }
703 if (!rtti->valueString(*this, i)) {
704 GPUError("No info message support for type %s of %s", e.type.c_str(), e.name.c_str());
705 continue;
706 }
707 rtti->configInfos[i] = [this, i, message = e.message]() {
708 char buffer[256];
709 const int32_t n = snprintf(buffer, sizeof(buffer), message.c_str(), mFrontend->mConfiguration->valueString(*this, i)->c_str());
710 if (n < 0 || n >= (int32_t)sizeof(buffer)) {
711 GPUError("Info message too long: %s", message.c_str());
712 return;
713 }
714 SetInfo("%s", buffer);
715 };
716 }
717 buildConfigInfo();
718 buildConfigQtGUI();
719 mFrontend->buildConsoleCommands();
720 mFrontend->mConsole->mPrintToTerminal = mConfig.internal.consolePrintToTerminal;
721 rtti->buildHelpText();
722}
723
725
727{
728 key = shiftedKey(key);
729 const int32_t binding = key < mConfiguration->configKeys.size() ? mConfiguration->configKeys[key] : GPUDisplayConfiguration::KEY_UNBOUND;
731 const auto& action = mConfiguration->keyActions[binding];
732 action.callback();
733 if (action.rttiIndex < mConfiguration->configInfos.size()) {
734 showConfigInfo(action.rttiIndex);
735 }
736 return true;
737 }
738 return false;
739}
740
742{
743 if (mConfiguration->configInfos[index]) {
744 mConfiguration->configInfos[index]();
745 } else if (const auto value = mConfiguration->valueString(*mDisplay, index)) { // Generic info for options without info callback
746 const auto& e = mConfiguration->rttiEntries[index];
747 mDisplay->SetInfo("%s.%s changed to %s", mConfiguration->configStructNames[e.configStructIndex].c_str(), e.name.c_str(), value->c_str());
748 }
749}
750
751void GPUDisplay::HandleKey(uint8_t key, bool external, bool bypassConsole)
752{
753 if (mCfgC.showConsole && !bypassConsole && mFrontend->mConfiguration->keyRTTIIndex(mFrontend->shiftedKey(key)) != mFrontend->mConfiguration->getRTTIIndex(*this, &GPUDisplayControl::showConsole)) { // All keys except the one toggling the console go to the console
754 if (!external) {
755 mFrontend->mConsole->handleConsoleKey(key, mFrontend->mKeys[mFrontend->KEY_SHIFT]);
756 }
757 return;
758 }
759 const uint8_t originKey = mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIGIN], upKey = mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIENT_UP];
760 if (originKey && upKey && (key >= 'a' && key <= 'z' ? key + 'A' - 'a' : key) == originKey) { // mKeyDownMap has letters in upper case
761 mFrontend->mKeys[upKey] = false; // Release the orient up key (CTRL) with the focus origin key (ALT), to avoid orienting along y automatically!
762 }
763 mFrontend->processKeyLambda(key);
764}
765
767{
768 const auto& e = rttiEntries[index];
769 char* ptr = configStructPtr(display, e.configStructIndex) + e.offset;
770 if (e.type == "std::string") {
771 const std::string* str = std::get_if<std::string>(&value);
772 if (!str) {
773 return "string expected";
774 }
775 *(std::string*)ptr = *str;
776 return "";
777 }
778 if (e.isVector || e.arraySize) {
779 return "unsupported type " + e.type;
780 }
781 if (e.type == "float") { // Floating point: also from an integer
782 const double v = std::holds_alternative<int64_t>(value) ? (double)std::get<int64_t>(value) : (std::holds_alternative<double>(value) ? std::get<double>(value) : std::nan(""));
783 if (std::isnan(v)) {
784 return "number expected";
785 } else if ((e.min.size() && v < std::stod(e.min)) || (e.max.size() && v > std::stod(e.max))) {
786 return "value out of range [" + e.min + ", " + e.max + "]";
787 }
788 *(float*)ptr = v;
789 return "";
790 }
791 int64_t v; // Integer types and bool, exactly, not through double
792 if (const std::string* str = std::get_if<std::string>(&value)) {
793 if (e.type != "bool" || (*str != "true" && *str != "false")) {
794 return "number expected";
795 }
796 v = *str == "true";
797 } else if (const int64_t* i = std::get_if<int64_t>(&value)) {
798 v = *i;
799 } else {
800 return "integer expected";
801 }
802 if ((e.min.size() && v < std::stoll(e.min)) || (e.max.size() && v > std::stoll(e.max))) {
803 return "value out of range [" + e.min + ", " + e.max + "]";
804 }
805 auto store = [&]<class T>(auto&& assign) -> std::string { // Within the range of the type T
806 if (v < (int64_t)std::numeric_limits<T>::min() || (v > 0 && (uint64_t)v > (uint64_t)std::numeric_limits<T>::max())) {
807 return "value out of range of " + e.type;
808 }
809 assign((T)v);
810 return "";
811 };
812 if (e.type == "bool") {
813 return store.template operator()<bool>([&](bool x) { *(bool*)ptr = x; });
814 } else if (e.type == "int8_t") {
815 return store.template operator()<int8_t>([&](int8_t x) { *(int8_t*)ptr = x; });
816 } else if (e.type == "uint8_t") {
817 return store.template operator()<uint8_t>([&](uint8_t x) { *(uint8_t*)ptr = x; });
818 } else if (e.type == "int32_t") {
819 return store.template operator()<int32_t>([&](int32_t x) { *(int32_t*)ptr = x; });
820 } else if (e.type == "uint32_t") {
821 return store.template operator()<uint32_t>([&](uint32_t x) { *(uint32_t*)ptr = x; });
822 } else if (e.type == "int64_t") {
823 return store.template operator()<int64_t>([&](int64_t x) { *(int64_t*)ptr = x; });
824 } else if (e.type == "uint64_t") {
825 return store.template operator()<uint64_t>([&](uint64_t x) { *(uint64_t*)ptr = x; });
826 } else if (e.type == "GPUDisplayCopyableAtomic<int32_t>") {
827 return store.template operator()<int32_t>([&](int32_t x) { ((GPUDisplayCopyableAtomic<int32_t>*)ptr)->store(x); });
828 } else if (e.type == "GPUDisplayCopyableAtomic<bool>") {
829 return store.template operator()<bool>([&](bool x) { ((GPUDisplayCopyableAtomic<bool>*)ptr)->store(x); });
830 }
831 return "unsupported type " + e.type;
832}
833
835{
836 const auto& e = rttiEntries[index];
837 const char* ptr = configStructPtr(display, e.configStructIndex) + e.offset;
838 if (e.isVector || e.arraySize) {
839 return std::nullopt;
840 } else if (e.type == "bool") {
841 return qConfig::print_type(*(const bool*)ptr);
842 } else if (e.type == "float") {
843 if (precise) {
844 std::ostringstream s;
845 s << std::setprecision(std::numeric_limits<float>::max_digits10) << *(const float*)ptr;
846 return s.str();
847 }
848 return qConfig::print_type(*(const float*)ptr);
849 } else if (e.type == "int8_t") {
850 return qConfig::print_type(*(const int8_t*)ptr);
851 } else if (e.type == "uint8_t") {
852 return qConfig::print_type(*(const uint8_t*)ptr);
853 } else if (e.type == "int32_t") {
854 return qConfig::print_type(*(const int32_t*)ptr);
855 } else if (e.type == "uint32_t") {
856 return qConfig::print_type(*(const uint32_t*)ptr);
857 } else if (e.type == "int64_t") {
858 return qConfig::print_type(*(const int64_t*)ptr);
859 } else if (e.type == "uint64_t") {
860 return qConfig::print_type(*(const uint64_t*)ptr);
861 } else if (e.type == "GPUDisplayCopyableAtomic<int32_t>") {
863 } else if (e.type == "GPUDisplayCopyableAtomic<bool>") {
865 } else if (e.type == "std::string") {
866 return *(const std::string*)ptr;
867 }
868 return std::nullopt;
869}
std::string binding
std::function< void(void *, const void *)> assign
atype::type element
void print() const
unsigned char uint8_t
unsigned short int uint16_t
unsigned int uint32_t
std::vector< std::string > keys
int32_t i
std::function< void()> configKeyCallback
int32_t retVal
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font auto
configStandalone display
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 s
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 Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
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
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 Print launch bounds used for RTC code as debugging option
uint32_t c
Definition RawData.h:2
TBranch * ptr
StringRef key
void AddTime(double t)
Definition timer.cxx:135
void Reset()
Definition timer.cxx:108
void Start()
Definition timer.cxx:64
double GetCurrentElapsedTime(bool reset=false)
Definition timer.cxx:117
int32_t IsRunning()
Definition timer.h:34
std::variant< int64_t, double, std::string > consoleArg
static constexpr uint32_t N_SPECIAL_KEYS
static constexpr uint32_t SPECIAL_KEYS_SHIFTED
uint32_t shiftedKey(uint32_t key) const
std::vector< int32_t > mExternalGUIKeys
std::array< uint8_t, KEY_MAP_MAX > mKeyDownMap
virtual void SetVSync(bool enable)=0
GPUDisplayControl mCfgC
Definition GPUDisplay.h:93
GPUDisplayOptions mCfgO
Definition GPUDisplay.h:92
void HandleKey(uint8_t key, bool external=false, bool bypassConsole=false)
void HandleExternalGUIKey(int32_t key, bool bypassConsole=false)
GLboolean GLboolean g
Definition glcorearb.h:1233
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLdouble * v
Definition glcorearb.h:832
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLsizei width
Definition glcorearb.h:270
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLenum GLenum dst
Definition glcorearb.h:1767
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
GLuint id
Definition glcorearb.h:650
int32_t const char int32_t line
std::variant< int64_t, double, std::string, GPUDisplayMultiSliderValue > GPUDisplayGUIValue
auto get(const std::byte *buffer, size_t=0)
Definition DataHeader.h:457
value_T step
Definition TrackUtils.h:42
std::string print_type(T val, bool precise=false)
std::tuple< std::vector< qConfigRTTIentry >, std::vector< std::string > > qConfigGenRTTI(const std::vector< std::string > *configStructs)
Definition qconfig.cxx:441
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
void empty(int)
std::string setValue(GPUDisplay &display, uint32_t index, const GPUDisplayConsole::consoleArg &value)
static char * configStructPtr(GPUDisplay &display, uint32_t index)
std::vector< int32_t > boundKeys(int32_t binding) const
std::vector< std::tuple< std::string, std::string, std::vector< int32_t > > > helpTextEntries
std::optional< std::string > valueString(GPUDisplay &display, uint32_t index, bool precise=false) const
uint32_t getRTTIIndex(GPUDisplay &display, M T::*member)
std::tuple< uint16_t, std::string, uint32_t, configKeyCallback > keyLambdaEntry
std::unordered_map< std::string, uint32_t > keyActionsNames
std::vector< GPUDisplayGUIChannel::Frame > guiLayout
guiElement(std::string n, uint32_t i, guiType t, std::function< void(const GPUDisplayGUIValue &)> s, std::function< GPUDisplayGUIValue(const GPUDisplayGUIChannel::Settings &)> g)
std::function< void(const GPUDisplayGUIValue &)> set
std::function< GPUDisplayGUIValue(const GPUDisplayGUIChannel::Settings &)> get
std::vector< std::pair< std::string, GPUDisplayGUIValue > > choices
std::function< GPUDisplayGUIChannel::Options()> options
guiType(GPUDisplayGUIChannel::ElementType t, std::vector< std::pair< std::string, GPUDisplayGUIValue > > c={}, std::function< GPUDisplayGUIChannel::Options()> o={}, uint32_t l=0, int64_t b=0, double s=0)
GPUDisplayGUIChannel::ElementType type
constexpr size_t min
constexpr size_t max
std::vector< ReadoutWindowData > rows
const std::string str