From 3361b5c46d4b8843807c67bbf5ab3628d55d47c6 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 17 Oct 2026 00:34:05 +0200 Subject: [PATCH 01/27] GPU: remove obsolete config code --- GPU/GPUTracking/utils/qconfig_internal.h | 4 ---- GPU/GPUTracking/utils/qconfig_rtc.h | 9 --------- 2 files changed, 13 deletions(-) diff --git a/GPU/GPUTracking/utils/qconfig_internal.h b/GPU/GPUTracking/utils/qconfig_internal.h index 04ecced0cb34a..72fb6fbe023ef 100644 --- a/GPU/GPUTracking/utils/qconfig_internal.h +++ b/GPU/GPUTracking/utils/qconfig_internal.h @@ -18,8 +18,6 @@ #include "qconfig_general.h" #include "qconfig_helpers.h" -#if !defined(QCONFIG_HEADER_GUARD_NO_INCLUDE) || defined(QCONFIG_GENRTC) - #ifdef QCONFIG_PARSE #define QCONFIG_COMPARE(name, optname, optnameshort) \ @@ -269,5 +267,3 @@ void qConfigPrint(const T& qconfig_tmp_object, const std::string& blockName); // #endif #include "qconfig_wrapinclude.h" - -#endif // QCONFIG_HEADER_GUARD_NO_INCLUDE diff --git a/GPU/GPUTracking/utils/qconfig_rtc.h b/GPU/GPUTracking/utils/qconfig_rtc.h index 7e8090004a8bd..c98f1f8cf309a 100644 --- a/GPU/GPUTracking/utils/qconfig_rtc.h +++ b/GPU/GPUTracking/utils/qconfig_rtc.h @@ -18,15 +18,6 @@ #include "qconfig.h" #include "qconfig_helpers.h" -#ifndef qon_mxstr -#define qon_mstr(a) #a -#define qon_mxstr(a) qon_mstr(a) -#endif -#ifndef qon_mxcat -#define qon_mcat(a, b) a##b -#define qon_mxcat(a, b) qon_mcat(a, b) -#endif - template static std::string qConfigPrintRtc(const T& tSrc, bool useConstexpr) { From e84c169e3d46060a709b67e50c98e35e1a6ce7c9 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 22 Oct 2026 07:49:57 +0200 Subject: [PATCH 02/27] GPU Display: Fix FPE with display width = 0, e.g. with none backend --- GPU/GPUTracking/display/GPUDisplay.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 2db90ea9a5fed..991b9b4736ef9 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -673,7 +673,8 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) // Draw Event nextViewMatrix = nextViewMatrix * *mModelMatrix; const float zFar = ((mParam->par.continuousTracking ? (mMaxClusterZ * GL_SCALE_FACTOR) : 8.f) + 50.f) * 2.f; - const hmm_mat4 proj = HMM_Perspective(mCfgR.fov, (float)mBackend->mRenderWidth / (float)mBackend->mRenderHeight, 0.1f, zFar); + const float aspect = mBackend->mRenderHeight ? (float)mBackend->mRenderWidth / (float)mBackend->mRenderHeight : 1.f; // No render size with the None backend + const hmm_mat4 proj = HMM_Perspective(mCfgR.fov, aspect, 0.1f, zFar); if (!mBackend->prepareDraw(proj, nextViewMatrix, doScreenshot || mRequestScreenshot, renderToMixBuffer, mixSlaveImage)) { return; // No image available (e.g. hidden window), retry with the next frame } From 6e0a21db5dd16a3efa9b9cbf6c7ec233737fe53b Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 16 Oct 2026 10:36:03 +0200 Subject: [PATCH 03/27] GPU: Possibility to generate run time type information about config structure --- GPU/GPUTracking/DataTypes/GPUConfigDump.cxx | 5 +- .../display/frontend/GPUDisplayFrontend.cxx | 11 +++ GPU/GPUTracking/utils/qconfig.cxx | 83 ++++--------------- GPU/GPUTracking/utils/qconfig_general.h | 11 ++- GPU/GPUTracking/utils/qconfig_genrtti.inc | 78 +++++++++++++++++ GPU/GPUTracking/utils/qconfig_internal.h | 22 +++++ .../utils/qconfig_internal_helpers.h | 72 ++++++++++++++++ GPU/GPUTracking/utils/qconfig_print.inc | 26 ++++++ GPU/GPUTracking/utils/qconfig_rtti.h | 43 ++++++++++ 9 files changed, 275 insertions(+), 76 deletions(-) create mode 100644 GPU/GPUTracking/utils/qconfig_genrtti.inc create mode 100644 GPU/GPUTracking/utils/qconfig_print.inc create mode 100644 GPU/GPUTracking/utils/qconfig_rtti.h diff --git a/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx b/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx index 9353e3664d05a..3aab0270671f7 100644 --- a/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx +++ b/GPU/GPUTracking/DataTypes/GPUConfigDump.cxx @@ -27,10 +27,7 @@ using namespace o2::gpu; namespace { GPUSettingsStandalone configStandalone; -std::vector> qprint_global; -#define QCONFIG_PRINT -#include "utils/qconfig_internal.h" -#undef QCONFIG_PRINT +#include "utils/qconfig_print.inc" } // namespace void GPUConfigDump::dumpConfig(const GPUSettingsRec* rec, const GPUSettingsProcessing* proc, const GPUSettingsQA* qa, const GPUSettingsDisplay* display, const GPUSettingsDeviceBackend* device, const GPURecoStepConfiguration* workflow) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index b48d7f6c59d5c..3bf8011eddf43 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -40,11 +40,17 @@ GPUDisplayFrontend* createGPUDisplayFrontendWayland(); #include "GPUDisplayGUIChannel.h" #include "GPULogging.h" +#include "utils/qconfig_rtti.h" +#include "utils/qconfig_internal_helpers.h" #include #include #include #include +#include +#include +#include #include +#include #include #ifdef GPUCA_BUILD_EVENT_DISPLAY_QT @@ -60,6 +66,11 @@ class GPUDisplayGUIWrapper using namespace o2::gpu; +namespace +{ +#include "utils/qconfig_genrtti.inc" +} // namespace + GPUDisplayFrontend::GPUDisplayFrontend() = default; GPUDisplayFrontend::~GPUDisplayFrontend() = default; diff --git a/GPU/GPUTracking/utils/qconfig.cxx b/GPU/GPUTracking/utils/qconfig.cxx index 8a15a6ecdd9ef..6186c8950ca6f 100644 --- a/GPU/GPUTracking/utils/qconfig.cxx +++ b/GPU/GPUTracking/utils/qconfig.cxx @@ -17,11 +17,16 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include "qconfig.h" #include "qconfig_helpers.h" +#include "qconfig_rtti.h" #include "qconfig_internal_helpers.h" // Create config instances @@ -41,19 +46,6 @@ static inline const char* getOptName(const char** argv, int32_t i) return (argv[i]); } -template -struct qConfigSettings { - qConfigSettings() : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(), max(), set(), message(nullptr), allowEmpty(false) {}; - template - qConfigSettings(const qConfigSettings v) : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(), max(), set(), message(v.message), allowEmpty(v.allowEmpty){}; - bool checkMin, checkMax; - bool doSet, doDefault; - T min, max; - T set; - const char* message; - bool allowEmpty; -}; - template static int32_t qAddOptionType(qConfigSettings& settings, T& ref, int32_t& i, const char** argv, const int argc, T def); template @@ -61,15 +53,6 @@ static void qAddOptionMessage(qConfigSettings& settings, T& ref); template static int32_t qAddOptionMinMax(qConfigSettings& settings, T& ref, const char* arg); -template -struct qSettingsType { - typedef T settingsType; -}; -template -struct qSettingsType> { - typedef int32_t settingsType; -}; - template struct qConfigTypeSpecialized { static inline int32_t qAddOptionMain(qConfigSettings& settings, T& ref, int32_t& i, const char** argv, const int argc, T def); @@ -130,41 +113,8 @@ inline int32_t qConfigTypeSpecialized>::qAddOptionMain(qConfigS } template -struct qConfigType { - // Recursive handling of additional settings - static inline void qProcessSetting(qConfigSettings& settings, qmin_t minval) - { - static_assert(!std::is_same_v, "min option not supported for boolean settings"); - settings.checkMin = true; - settings.min = minval.v; - } - static inline void qProcessSetting(qConfigSettings& settings, qmax_t maxval) - { - static_assert(!std::is_same_v, "max option not supported for boolean settings"); - settings.checkMax = true; - settings.max = maxval.v; - } - static inline void qProcessSetting(qConfigSettings& settings, qmessage_t msg) { settings.message = msg.v; } - template - static inline void qProcessSetting(qConfigSettings& settings, qset_t set) - { - settings.doSet = true; - settings.set = set.v; - } - template - static inline void qProcessSetting(qConfigSettings& settings, qdef_t set) - { - settings.doDefault = true; - settings.set = set.v; - } - - static inline void qAddOptionSettings(qConfigSettings& /*settings*/) {} - template - static inline void qAddOptionSettings(qConfigSettings& settings, Arg1&& arg1, Args&&... args) - { - qProcessSetting(settings, arg1); - qAddOptionSettings(settings, args...); - } +struct qConfigType : public qConfigSettingsParser { + using qConfigSettingsParser::qConfigGetSettings; template static inline int32_t qAddOption(T& ref, int32_t& i, const char** argv, const int argc, T def, const char* /*help*/, Args&&... args) @@ -264,14 +214,6 @@ struct qConfigType { } std::cout << "\n"; } - - template - static inline auto qConfigGetSettings(Args&... args) - { - qConfigSettings::settingsType> settings; - qConfigType::settingsType>::qAddOptionSettings(settings, args...); - return (settings); - } }; static inline const char* getArg(int32_t& i, const char** argv, const int argc, bool allowOption = false) @@ -480,10 +422,7 @@ int32_t qConfigParse(int argc, const char** argv, const char* /*filename*/) return (0); } -std::vector> qprint_global; -#define QCONFIG_PRINT -#include "qconfig_internal.h" -#undef QCONFIG_PRINT +#include "qconfig_print.inc" void qConfigPrint() { @@ -491,4 +430,10 @@ void qConfigPrint() f(); } } +namespace internal +{ +#include "qconfig_genrtti.inc" +} // namespace internal + +std::tuple, std::vector> qConfigGenRTTI(const std::vector* configStructs) { return internal::qConfigGenRTTI(configStructs); } } // end namespace qConfig diff --git a/GPU/GPUTracking/utils/qconfig_general.h b/GPU/GPUTracking/utils/qconfig_general.h index 225808107a4cf..6804a91666ef0 100644 --- a/GPU/GPUTracking/utils/qconfig_general.h +++ b/GPU/GPUTracking/utils/qconfig_general.h @@ -17,16 +17,21 @@ #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include +#include #endif namespace qConfig { +struct qConfigRTTIentry; #ifndef GPUCA_GPUCODE_DEVICE -int32_t qConfigParse(int argc, const char** argv, const char* filename = nullptr); -void qConfigPrint(); // Print all settings +int32_t qConfigParse(int argc, const char** argv, const char* filename = nullptr); // Parse qConfig options +void qConfigPrint(); // Print all settings template -void qConfigPrint(const T& qconfig_tmp_object, const std::string& blockName = ""); // Print one sub config, specializations created in qconfig.cxx +void qConfigPrint(const T& qconfig_tmp_object, const std::string& blockName = ""); // Print one sub config, specializations created in qconfig.cxx +std::tuple, std::vector> qConfigGenRTTI(const std::vector* configStructs = nullptr); // Entries and list of config structs, for the given config structs (all if nullptr) #endif + enum qConfigRetVal { qcrOK = 0, qcrError = 1, qcrMinFailure = 2, diff --git a/GPU/GPUTracking/utils/qconfig_genrtti.inc b/GPU/GPUTracking/utils/qconfig_genrtti.inc new file mode 100644 index 0000000000000..c0a06920375a1 --- /dev/null +++ b/GPU/GPUTracking/utils/qconfig_genrtti.inc @@ -0,0 +1,78 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file qconfig_genrtti.inc +/// \author David Rohr + +// Provides static inline qConfigGenRTTI() in the namespace it is included in. +// Requires , , , , , , , "qconfig_rtti.h", "qconfig_internal_helpers.h" to be included before (outside of the namespace), and the config structs to be visible unqualified. + +#ifndef QCONFIG_GENRTTI_INC +#define QCONFIG_GENRTTI_INC + +template +static inline T qConfigRTTICheckedCast(size_t v, const char* what) +{ + if (v > std::numeric_limits::max()) { + throw std::runtime_error(std::string("qConfig RTTI: ") + what + " " + std::to_string(v) + " exceeds the range of the entry"); + } + return static_cast(v); +} + +static inline std::optional qConfigRTTIStructIndex(std::vector& configStructs, const char* name, bool filter) // Index of name in configStructs, appended if not filtering, nullopt if filtered out +{ + if (auto it = std::find(configStructs.begin(), configStructs.end(), name); it != configStructs.end()) { + return it - configStructs.begin(); + } + if (filter) { + return std::nullopt; + } + configStructs.emplace_back(name); + return configStructs.size() - 1; +} + +template +static inline void qConfigRTTIEntry(std::vector<::qConfig::qConfigRTTIentry>& retVal, ::qConfig::qConfigRTTIentry entry, size_t arraySize, size_t configStructIndex, std::string defaultValue, const char* help, Args&&... args) // Complete the entry, check the narrow members for overflow +{ + auto settings = ::qConfig::qConfigSettingsParser::qConfigGetSettings(args...); + entry.size = qConfigRTTICheckedCast(sizeof(T), "size"); + entry.arraySize = qConfigRTTICheckedCast(arraySize, "arraySize"); + entry.configStructIndex = qConfigRTTICheckedCast(configStructIndex, "configStructIndex"); + entry.defaultValue = std::move(defaultValue); + entry.description = help; + if (settings.checkMin) { + entry.min = ::qConfig::print_type(settings.min); + } + if (settings.checkMax) { + entry.max = ::qConfig::print_type(settings.max); + } + if (settings.message) { + entry.message = settings.message; + } + retVal.emplace_back(std::move(entry)); +} + +static inline std::tuple, std::vector> qConfigGenRTTI(const std::vector* configStructs) // Entries and list of config structs, for the given config structs (all if nullptr) +{ + using namespace ::qConfig; // For the settings helpers (min, max, ...) and print_type used in GPUSettingsList.h / qconfig_internal.h + std::vector retVal; + std::vector retStructs; + if (configStructs) { + retStructs = *configStructs; + } +#define QCONFIG_GEN_RTTI +#include "qconfig_internal.h" +#undef QCONFIG_GEN_RTTI +#undef QCONFIG_RTTI + return {std::move(retVal), std::move(retStructs)}; +} + +#endif // QCONFIG_GENRTTI_INC diff --git a/GPU/GPUTracking/utils/qconfig_internal.h b/GPU/GPUTracking/utils/qconfig_internal.h index 72fb6fbe023ef..cb646efe5d7c5 100644 --- a/GPU/GPUTracking/utils/qconfig_internal.h +++ b/GPU/GPUTracking/utils/qconfig_internal.h @@ -260,6 +260,28 @@ void qConfigPrint(const T& qconfig_tmp_object, const std::string& blockName); // #define AddOptionSet(...) // End QCONFIG_PRINT_RTC +#elif defined(QCONFIG_GEN_RTTI) +#define QCONFIG_RTTI(vName, vType, rtc, variable, vector, count, defaultValue, ...) \ + qConfigRTTIEntry(retVal, \ + {.name = qon_mxstr(vName), .type = qon_mxstr(vType), .isRTC = rtc, .isVariable = variable, .isVector = vector, .offset = static_cast(reinterpret_cast(&qConfigObject.vName) - reinterpret_cast(&qConfigObject)), .configStruct = qConfigStruct}, \ + count, *qConfigStructIndex, defaultValue, __VA_ARGS__); +#define AddOption(name, type, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, false, false, false, 0, print_type(static_cast(default)), __VA_ARGS__) +#define AddOptionRTC(name, type, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, true, false, false, 0, print_type(static_cast(default)), __VA_ARGS__) +#define AddVariable(name, type, default) QCONFIG_RTTI(name, type, false, true, false, 0, print_type(static_cast(default)), "") +#define AddVariableRTC(name, type, default) QCONFIG_RTTI(name, type, true, true, false, 0, print_type(static_cast(default)), "") +#define AddOptionVec(name, type, optname, optnameshort, ...) QCONFIG_RTTI(name, type, false, false, true, 0, "", __VA_ARGS__) +#define AddOptionArray(name, type, count, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, false, false, false, count, qon_mxstr(default), __VA_ARGS__) +#define AddOptionArrayRTC(name, type, count, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, true, false, false, count, qon_mxstr(default), __VA_ARGS__) +#define AddOptionSet(...) +#define AddSubConfig(name, instance) +#define BeginConfig(name, instance) \ + if (const auto qConfigStructIndex = qConfigRTTIStructIndex(retStructs, qon_mxstr(name), configStructs != nullptr)) { \ + [[maybe_unused]] constexpr const char* qConfigStruct = qon_mxstr(name); \ + [[maybe_unused]] const name qConfigObject{}; // Default-constructed instance to compute the member offsets +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, ...) BeginConfig(name, instance) +#define EndConfig() } + +// End QCONFIG_GEN_RTTI #else // Define structures #error Internal error, qconfig_internal called with action type diff --git a/GPU/GPUTracking/utils/qconfig_internal_helpers.h b/GPU/GPUTracking/utils/qconfig_internal_helpers.h index c3702e819256b..89ef328e9de4f 100644 --- a/GPU/GPUTracking/utils/qconfig_internal_helpers.h +++ b/GPU/GPUTracking/utils/qconfig_internal_helpers.h @@ -15,6 +15,10 @@ #ifndef QCONFIG_INTERNAL_HELPERS_H #define QCONFIG_INTERNAL_HELPERS_H +#include "qconfig_helpers.h" +#include +#include + namespace qConfig { #define QCONFIG_SETTING(name, type) \ @@ -41,6 +45,74 @@ QCONFIG_SETTING_TEMPLATE(min) QCONFIG_SETTING_TEMPLATE(max) QCONFIG_SETTING_TEMPLATE(set) QCONFIG_SETTING_TEMPLATE(def) + +template +struct qConfigSettings { + qConfigSettings() : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(), max(), set(), message(nullptr), allowEmpty(false) {}; + template + qConfigSettings(const qConfigSettings v) : checkMin(false), checkMax(false), doSet(false), doDefault(false), min(), max(), set(), message(v.message), allowEmpty(v.allowEmpty) {}; + bool checkMin, checkMax; + bool doSet, doDefault; + T min, max; + T set; + const char* message; + bool allowEmpty; +}; + +template +struct qSettingsType { + typedef T settingsType; +}; +template +struct qSettingsType> { + typedef int32_t settingsType; +}; + +template +struct qConfigSettingsParser { // Parse the additional settings (min, max, set, def, message) of an option + // Recursive handling of additional settings + static inline void qProcessSetting(qConfigSettings& settings, qmin_t minval) + { + static_assert(!std::is_same_v, "min option not supported for boolean settings"); + settings.checkMin = true; + settings.min = minval.v; + } + static inline void qProcessSetting(qConfigSettings& settings, qmax_t maxval) + { + static_assert(!std::is_same_v, "max option not supported for boolean settings"); + settings.checkMax = true; + settings.max = maxval.v; + } + static inline void qProcessSetting(qConfigSettings& settings, qmessage_t msg) { settings.message = msg.v; } + template + static inline void qProcessSetting(qConfigSettings& settings, qset_t set) + { + settings.doSet = true; + settings.set = set.v; + } + template + static inline void qProcessSetting(qConfigSettings& settings, qdef_t set) + { + settings.doDefault = true; + settings.set = set.v; + } + + static inline void qAddOptionSettings(qConfigSettings& /*settings*/) {} + template + static inline void qAddOptionSettings(qConfigSettings& settings, Arg1&& arg1, Args&&... args) + { + qProcessSetting(settings, arg1); + qAddOptionSettings(settings, args...); + } + + template + static inline auto qConfigGetSettings(Args&... args) + { + qConfigSettings::settingsType> settings; + qConfigSettingsParser::settingsType>::qAddOptionSettings(settings, args...); + return (settings); + } +}; } // namespace qConfig #endif diff --git a/GPU/GPUTracking/utils/qconfig_print.inc b/GPU/GPUTracking/utils/qconfig_print.inc new file mode 100644 index 0000000000000..0928bae1a1592 --- /dev/null +++ b/GPU/GPUTracking/utils/qconfig_print.inc @@ -0,0 +1,26 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file qconfig_print.inc +/// \author David Rohr + +// Generates qConfigPrint for all config structs in the namespace it is included in, after the instances of the top-level configs (e.g. configStandalone). +// Requires , , , , "qconfig_helpers.h" to be included before (outside of the namespace). + +#ifndef QCONFIG_PRINT_INC +#define QCONFIG_PRINT_INC + +std::vector> qprint_global; +#define QCONFIG_PRINT +#include "qconfig_internal.h" +#undef QCONFIG_PRINT + +#endif // QCONFIG_PRINT_INC diff --git a/GPU/GPUTracking/utils/qconfig_rtti.h b/GPU/GPUTracking/utils/qconfig_rtti.h new file mode 100644 index 0000000000000..63e58bd96341c --- /dev/null +++ b/GPU/GPUTracking/utils/qconfig_rtti.h @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file qconfig_rtti.h +/// \author David Rohr + +#ifndef QCONFIG_RTTI_H +#define QCONFIG_RTTI_H + +#include "qconfig.h" +#include "qconfig_helpers.h" +#include + +namespace qConfig +{ +struct qConfigRTTIentry { + std::string name = ""; // Name of option + std::string type = ""; // Data type of option + uint8_t size = 0; // sizeof(type) + bool isRTC = false; // enable RTC constexpr optimization + bool isVariable = false; // is a variable, not settable at command line + bool isVector = false; // is std::vector + uint16_t arraySize = 0; // size of array if std::array, 0 = no array + uint16_t configStructIndex = 0; // Index in configStructVector + size_t offset = 0; // Offset address in configStruct + std::string configStruct = ""; // Name of config struct that stores the option + std::string max = ""; // Maximum Value + std::string min = ""; // Minimum Value + std::string defaultValue = ""; // Default Value + std::string description = ""; // Description of option + std::string message = ""; // printf format string, printed with the value when the option is set +}; +} // namespace qConfig + +#endif // QCONFIG_RTTI_H From 452b665eb564a8703629617edd1f4f89476a7c62 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 17 Oct 2026 00:53:38 +0200 Subject: [PATCH 04/27] GPU Display: Reorganize frontend to have GPUSettingsFrontendConfig.cxx file, with all code for configuration updates --- GPU/GPUTracking/display/CMakeLists.txt | 3 +- .../display/frontend/GPUDisplayFrontend.cxx | 25 ----- .../frontend/GPUDisplayFrontendConfig.cxx | 91 +++++++++++++++++++ .../display/frontend/GPUDisplayKeys.cxx | 45 --------- GPU/GPUTracking/utils/qconfig_genrtti.inc | 2 +- 5 files changed, 94 insertions(+), 72 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index 9edefc19a072c..a274c481d0365 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -113,7 +113,8 @@ set(SRCS_NO_H helpers/GPUDisplayLoader.cxx helpers/GPUDisplayQuaternion.cxx helpers/GPUDisplayInterpolation.cxx helpers/GPUDisplayROOT.cxx - frontend/GPUDisplayKeys.cxx) + frontend/GPUDisplayKeys.cxx + frontend/GPUDisplayFrontendConfig.cxx) set(HDRS_INSTALL helpers/GPUDisplayMagneticFieldConstants.h) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index 3bf8011eddf43..43983b688de8a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -40,17 +40,11 @@ GPUDisplayFrontend* createGPUDisplayFrontendWayland(); #include "GPUDisplayGUIChannel.h" #include "GPULogging.h" -#include "utils/qconfig_rtti.h" -#include "utils/qconfig_internal_helpers.h" #include #include #include #include -#include -#include -#include #include -#include #include #ifdef GPUCA_BUILD_EVENT_DISPLAY_QT @@ -66,11 +60,6 @@ class GPUDisplayGUIWrapper using namespace o2::gpu; -namespace -{ -#include "utils/qconfig_genrtti.inc" -} // namespace - GPUDisplayFrontend::GPUDisplayFrontend() = default; GPUDisplayFrontend::~GPUDisplayFrontend() = default; @@ -148,20 +137,6 @@ void GPUDisplayFrontend::HandleExternalGUISignals() } } -bool GPUDisplayFrontend::HasExternalGUISignals() const -{ - return mExternalGUIKeys.size(); -} - -bool GPUDisplayFrontend::HandleExternalGUIKeys() -{ - std::lock_guard lock(mExternalGUIKeyMutex); - for (auto key : mExternalGUIKeys) { - mDisplay->HandleExternalGUIKey(key); - } - return !std::exchange(mExternalGUIKeys, {}).empty(); -} - void GPUDisplayFrontend::sendExternalGUIKey(int32_t v) { std::lock_guard lock(mExternalGUIKeyMutex); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx new file mode 100644 index 0000000000000..73fe498a17a64 --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -0,0 +1,91 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayFrontendConfig.cxx +/// \author David Rohr + +#include "GPUDisplayFrontend.h" +#include "GPUDisplay.h" +#include "utils/qconfig_rtti.h" +#include "utils/qconfig_internal_helpers.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::gpu; + +namespace +{ +#include "utils/qconfig_genrtti.inc" +} // namespace + +void GPUDisplay::checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCfgH, const GPUSettingsDisplayLight& oldCfgL, const GPUSettingsDisplayRenderer& oldCfgR) +{ + if (memcmp((void*)&oldCfgH, (void*)&mCfgH, sizeof(mCfgH)) != 0) { + mUpdateEventData = true; + } + if (mCfgL.propagateTracks != 0 && !mTracksArePropagated) { + mUpdateVertexLists = true; + } + if (memcmp((void*)&oldCfgL, (void*)&mCfgL, sizeof(mCfgL)) != 0 || memcmp((void*)&oldCfgR, (void*)&mCfgR, sizeof(mCfgR)) != 0) { + mUpdateDrawCommands = true; + } + if (oldCfgR.drawQualityMSAA != mCfgR.drawQualityMSAA || oldCfgR.drawQualityDownsampleFSAA != mCfgR.drawQualityDownsampleFSAA || oldCfgL.depthBuffer != mCfgL.depthBuffer || oldCfgR.screenshotScaleFactor != mCfgR.screenshotScaleFactor) { + mUpdateRenderPipeline = true; + } + + if (oldCfgR.drawQualityVSync != mCfgR.drawQualityVSync) { + mFrontend->SetVSync(mCfgR.drawQualityVSync); + mBackend->SetVSync(mCfgR.drawQualityVSync); + } + if (oldCfgR.fullScreen != mCfgR.fullScreen) { + mFrontend->SwitchFullscreen(mCfgR.fullScreen); + } + if (oldCfgR.maximized != mCfgR.maximized) { + mFrontend->ToggleMaximized(mCfgR.maximized); + } + if (oldCfgR.useGLIndirectDraw != mCfgR.useGLIndirectDraw) { + mUpdateEventData = true; + } +} + +void GPUDisplay::HandleExternalGUIKey(int32_t key) +{ + // GPUError("key %d '%c'", key, (char) key); + + bool shifted = key >= 'A' && key <= 'Z'; + int32_t press = key; + if (press >= 'a' && press <= 'z') { + press += 'A' - 'a'; + } + bool oldShift = mFrontend->mKeysShift[press]; + mFrontend->mKeysShift[press] = shifted; + HandleKey(key, true); + mFrontend->mKeysShift[press] = oldShift; +} + +bool GPUDisplayFrontend::HasExternalGUISignals() const +{ + return mExternalGUIKeys.size(); +} + +bool GPUDisplayFrontend::HandleExternalGUIKeys() +{ + std::lock_guard lock(mExternalGUIKeyMutex); + for (auto key : mExternalGUIKeys) { + mDisplay->HandleExternalGUIKey(key); + } + return !std::exchange(mExternalGUIKeys, {}).empty(); +} diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx index b7c5c55cfdf22..5b2e8599c5838 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx @@ -511,51 +511,6 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) } } -void GPUDisplay::checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCfgH, const GPUSettingsDisplayLight& oldCfgL, const GPUSettingsDisplayRenderer& oldCfgR) -{ - if (memcmp((void*)&oldCfgH, (void*)&mCfgH, sizeof(mCfgH)) != 0) { - mUpdateEventData = true; - } - if (mCfgL.propagateTracks != 0 && !mTracksArePropagated) { - mUpdateVertexLists = true; - } - if (memcmp((void*)&oldCfgL, (void*)&mCfgL, sizeof(mCfgL)) != 0 || memcmp((void*)&oldCfgR, (void*)&mCfgR, sizeof(mCfgR)) != 0) { - mUpdateDrawCommands = true; - } - if (oldCfgR.drawQualityMSAA != mCfgR.drawQualityMSAA || oldCfgR.drawQualityDownsampleFSAA != mCfgR.drawQualityDownsampleFSAA || oldCfgL.depthBuffer != mCfgL.depthBuffer || oldCfgR.screenshotScaleFactor != mCfgR.screenshotScaleFactor) { - mUpdateRenderPipeline = true; - } - - if (oldCfgR.drawQualityVSync != mCfgR.drawQualityVSync) { - mFrontend->SetVSync(mCfgR.drawQualityVSync); - mBackend->SetVSync(mCfgR.drawQualityVSync); - } - if (oldCfgR.fullScreen != mCfgR.fullScreen) { - mFrontend->SwitchFullscreen(mCfgR.fullScreen); - } - if (oldCfgR.maximized != mCfgR.maximized) { - mFrontend->ToggleMaximized(mCfgR.maximized); - } - if (oldCfgR.useGLIndirectDraw != mCfgR.useGLIndirectDraw) { - mUpdateEventData = true; - } -} - -void GPUDisplay::HandleExternalGUIKey(int32_t key) -{ - // GPUError("key %d '%c'", key, (char) key); - - bool shifted = key >= 'A' && key <= 'Z'; - int32_t press = key; - if (press >= 'a' && press <= 'z') { - press += 'A' - 'a'; - } - bool oldShift = mFrontend->mKeysShift[press]; - mFrontend->mKeysShift[press] = shifted; - HandleKey(key, true); - mFrontend->mKeysShift[press] = oldShift; -} - void GPUDisplay::PrintGLHelpText(float colorValue) { for (uint32_t i = 0; i < sizeof(HelpText) / sizeof(HelpText[0]); i++) { diff --git a/GPU/GPUTracking/utils/qconfig_genrtti.inc b/GPU/GPUTracking/utils/qconfig_genrtti.inc index c0a06920375a1..5d817f5a26b93 100644 --- a/GPU/GPUTracking/utils/qconfig_genrtti.inc +++ b/GPU/GPUTracking/utils/qconfig_genrtti.inc @@ -13,7 +13,7 @@ /// \author David Rohr // Provides static inline qConfigGenRTTI() in the namespace it is included in. -// Requires , , , , , , , "qconfig_rtti.h", "qconfig_internal_helpers.h" to be included before (outside of the namespace), and the config structs to be visible unqualified. +// Requires , , , , "qconfig_rtti.h" (brings , , ), "qconfig_internal_helpers.h" to be included before (outside of the namespace), and the config structs to be visible unqualified. #ifndef QCONFIG_GENRTTI_INC #define QCONFIG_GENRTTI_INC From 2f24f3f84e11f474d88860ab0116c47af6af1595 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 24 Oct 2026 18:20:55 +0200 Subject: [PATCH 05/27] GPU Display Frontend: move key binding constants to separate header --- GPU/GPUTracking/display/CMakeLists.txt | 2 +- .../display/frontend/GPUDisplayFrontend.h | 32 ++----------- .../display/frontend/GPUDisplayFrontendKeys.h | 46 +++++++++++++++++++ 3 files changed, 50 insertions(+), 30 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index a274c481d0365..5f770ddf7b914 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -116,7 +116,7 @@ set(SRCS_NO_H helpers/GPUDisplayLoader.cxx frontend/GPUDisplayKeys.cxx frontend/GPUDisplayFrontendConfig.cxx) -set(HDRS_INSTALL helpers/GPUDisplayMagneticFieldConstants.h) +set(HDRS_INSTALL helpers/GPUDisplayMagneticFieldConstants.h frontend/GPUDisplayFrontendKeys.h) if(GPUCA_EVENT_DISPLAY_VULKAN) set(SRCS ${SRCS} backend/GPUDisplayBackendVulkan.cxx) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 1bfbc36883f41..fb4e8dc73b3aa 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -89,35 +89,9 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface static constexpr int32_t MIN_WIDTH = 200, MIN_HEIGHT = 150; // Minimum window size static constexpr const char* DISPLAY_WINDOW_NAME = "GPU CA Standalone Event Display"; // Title of event display set by backend // Constant key codes for special mKeys (to unify different treatment in X11 / Windows / GLUT / etc.) - static constexpr int32_t KEY_UP = 1; - static constexpr int32_t KEY_DOWN = 2; - static constexpr int32_t KEY_LEFT = 3; - static constexpr int32_t KEY_RIGHT = 4; - static constexpr int32_t KEY_PAGEUP = 5; - static constexpr int32_t KEY_PAGEDOWN = 6; - static constexpr int32_t KEY_SHIFT = 8; - static constexpr int32_t KEY_ALT = 9; - static constexpr int32_t KEY_RALT = 10; - static constexpr int32_t KEY_CTRL = 11; - static constexpr int32_t KEY_RCTRL = 12; - static constexpr int32_t KEY_ENTER = 13; // fixed at 13 - static constexpr int32_t KEY_F1 = 14; - static constexpr int32_t KEY_F2 = 15; - static constexpr int32_t KEY_F3 = 16; - static constexpr int32_t KEY_F4 = 17; - static constexpr int32_t KEY_F5 = 18; - static constexpr int32_t KEY_F6 = 19; - static constexpr int32_t KEY_F7 = 20; - static constexpr int32_t KEY_F8 = 21; - static constexpr int32_t KEY_F9 = 22; - static constexpr int32_t KEY_F10 = 23; - static constexpr int32_t KEY_F11 = 24; - static constexpr int32_t KEY_F12 = 25; - static constexpr int32_t KEY_INSERT = 26; - static constexpr int32_t KEY_ESCAPE = 27; // fixed at 27 - static constexpr int32_t KEY_HOME = 28; - static constexpr int32_t KEY_END = 29; - static constexpr int32_t KEY_SPACE = 32; // fixed at 32 +#define GPUDisplayFrontendAddKey(code, key) static constexpr int32_t KEY_##key = code +#include "GPUDisplayFrontendKeys.h" +#undef GPUDisplayFrontendAddKey // volatile variables to exchange control informations between display and backend volatile int32_t mDisplayControl = 0; // Control for next event (=1) or quit (=2) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h new file mode 100644 index 0000000000000..a22eaeb5fbcd8 --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h @@ -0,0 +1,46 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayFrontendKeys.h +/// \author David Rohr + +// Key codes for special keys (to unify different treatment in X11 / Windows / GLUT / etc.), as GPUDisplayFrontendAddKey(code, name) X-macros. +// No include guard: included multiple times with different definitions of GPUDisplayFrontendAddKey. + +GPUDisplayFrontendAddKey(1, UP); +GPUDisplayFrontendAddKey(2, DOWN); +GPUDisplayFrontendAddKey(3, LEFT); +GPUDisplayFrontendAddKey(4, RIGHT); +GPUDisplayFrontendAddKey(5, PAGEUP); +GPUDisplayFrontendAddKey(6, PAGEDOWN); +GPUDisplayFrontendAddKey(8, SHIFT); +GPUDisplayFrontendAddKey(9, ALT); +GPUDisplayFrontendAddKey(10, RALT); +GPUDisplayFrontendAddKey(11, CTRL); +GPUDisplayFrontendAddKey(12, RCTRL); +GPUDisplayFrontendAddKey(13, ENTER); // fixed at 13 +GPUDisplayFrontendAddKey(14, F1); +GPUDisplayFrontendAddKey(15, F2); +GPUDisplayFrontendAddKey(16, F3); +GPUDisplayFrontendAddKey(17, F4); +GPUDisplayFrontendAddKey(18, F5); +GPUDisplayFrontendAddKey(19, F6); +GPUDisplayFrontendAddKey(20, F7); +GPUDisplayFrontendAddKey(21, F8); +GPUDisplayFrontendAddKey(22, F9); +GPUDisplayFrontendAddKey(23, F10); +GPUDisplayFrontendAddKey(24, F11); +GPUDisplayFrontendAddKey(25, F12); +GPUDisplayFrontendAddKey(26, INSERT); +GPUDisplayFrontendAddKey(27, ESCAPE); // fixed at 27 +GPUDisplayFrontendAddKey(28, HOME); +GPUDisplayFrontendAddKey(29, END); +GPUDisplayFrontendAddKey(32, SPACE); // fixed at 32 From 115b35c2c7579b8f3d66d3f1b7aee9f631ab8c87 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 22 Oct 2026 07:23:35 +0200 Subject: [PATCH 06/27] GPU Display: Build config RTTI Structs --- GPU/GPUTracking/display/GPUDisplay.h | 2 + .../display/frontend/GPUDisplayFrontend.cxx | 1 + .../display/frontend/GPUDisplayFrontend.h | 7 ++ .../frontend/GPUDisplayFrontendConfig.cxx | 106 ++++++++++++++++++ GPU/GPUTracking/utils/qconfig.cxx | 1 + GPU/GPUTracking/utils/qconfig_genrtti.inc | 34 ++++-- GPU/GPUTracking/utils/qconfig_internal.h | 19 ++-- 7 files changed, 154 insertions(+), 16 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 67ab1a10334c9..0fb99ada77b7b 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -87,6 +87,8 @@ class GPUDisplay : public GPUDisplayInterface int32_t& drawTextFontSize() { return mDrawTextFontSize; } private: + void buildConfig(); // Key bindings / config RTTI of the frontend, in GPUDisplayFrontendConfig.cxx + static constexpr int32_t NSECTORS = o2::tpc::constants::MAXSECTOR; static constexpr float GL_SCALE_FACTOR = (1.f / 100.f); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index 43983b688de8a..aafe316160d82 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -295,6 +295,7 @@ int32_t GPUDisplayFrontend::StartDisplay() return (1); } #endif + mDisplay->buildConfig(); return (0); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index fb4e8dc73b3aa..badf42242e106 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -138,7 +138,14 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface void focusLost(); // Release all keys and mouse buttons when the window loses focus, since the release events go to another window void applyInitialWindowState(); // Switch to fullscreen / maximize at startup according to the settings private: + struct configRTTIInfo; + struct configRTTIExt; + struct configKey; + std::unique_ptr mConfigRTTI{nullptr, nullptr}; // Deleter set in GPUDisplayFrontendConfig.cxx, the only place where configRTTIInfo is defined + bool HandleExternalGUIKeys(); // Returns whether keys were handled, config changes are not handled + template + 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 }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 73fe498a17a64..ae8567d8a6d64 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -18,13 +18,77 @@ #include "utils/qconfig_internal_helpers.h" #include +#include #include #include #include +#include +#include #include +#include +#include #include using namespace o2::gpu; +struct GPUDisplayFrontend::configRTTIExt : public qConfig::qConfigRTTIentry { +}; + +using configKeyCallback = std::function; + +struct GPUDisplayFrontend::configKey { + uint32_t configRTTIIndex; + configKeyCallback callback; +}; + +#define KEY_LAMBDA(character, obj, value, ...) std::tuple((uint8_t)(character), mFrontend->mConfigRTTI->getRTTIIndex(*this, &std::remove_cvref_t::value), configKeyCallback([this]() { [[maybe_unused]] auto& val = obj.value; __VA_ARGS__ })) + +struct GPUDisplayFrontend::configRTTIInfo { + static constexpr std::tuple configStructs{&GPUDisplay::mCfgL, &GPUDisplay::mCfgH, &GPUDisplay::mCfgR}; // Config structs with RTTI, as members of GPUDisplay + std::vector rttiEntries; + std::vector> rttiReverseId; // [configStructIndex][offset] -> index in rttiEntries, INVALID_INDEX if no member at that offset + static constexpr uint32_t INVALID_INDEX = std::numeric_limits::max(); + std::vector configStructNames; + std::array configKeys; + std::vector keyNames; + std::vector> helpTextEntries; // Keys and text of each add_key_binding call + std::string helpText; // Built from helpTextEntries by buildHelpText + + template + static constexpr uint32_t configStructIndex() // Index of struct T in configStructs, compile-time error if not present + { + if constexpr (std::is_same_v>, T GPUDisplay::*>) { + return I; + } else { + return configStructIndex(); + } + } + template + static T& configStruct(GPUDisplay& display) + { + return display.*std::get()>(configStructs); + } + template + uint32_t getRTTIIndex(GPUDisplay& display, M T::* member) // RTTI index of a direct member of a config struct + { + const T& obj = configStruct(display); + return rttiReverseId[configStructIndex()][(const char*)&(obj.*member) - (const char*)&obj]; + } + void buildHelpText() // Keys left-aligned, the ':' aligned after the longest keys + { + size_t width = 0; + for (const auto& e : helpTextEntries) { + width = std::max(width, e.first.size()); + } + helpText.clear(); + for (const auto& e : helpTextEntries) { + helpText += e.first + std::string(width - e.first.size(), ' ') + ": " + e.second + "\n"; + } + } + std::string getKeyName(uint8_t key) const // Name of a special key (KEY_*), the character otherwise, the code if neither + { + return key < keyNames.size() && !keyNames[key].empty() ? keyNames[key] : (key < 32 ? std::to_string(key) : std::string(1, (char)key)); + } +}; namespace { @@ -89,3 +153,45 @@ bool GPUDisplayFrontend::HandleExternalGUIKeys() } return !std::exchange(mExternalGUIKeys, {}).empty(); } + +template +void GPUDisplayFrontend::add_key_binding(Args&&... args) +{ + constexpr size_t nKeys = (0 + ... + std::is_same_v, std::tuple>); + 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"); + auto argTuple = std::forward_as_tuple(args...); + std::string keys; + auto addKey = [&](std::tuple& key) { + const std::string name = mConfigRTTI->getKeyName(std::get<0>(key)); + auto& entry = mConfigRTTI->configKeys[std::get<0>(key)]; + if (entry.callback) { + throw std::runtime_error("Key [" + name + "] assigned twice in display frontend"); + } + entry.configRTTIIndex = std::get<1>(key); + entry.callback = std::move(std::get<2>(key)); + keys += (keys.empty() ? "[" : " [") + name + "]"; + }; + [&](std::index_sequence) { (addKey(std::get(argTuple)), ...); }(std::make_index_sequence()); + if constexpr (sizeof...(Args) == nKeys + 2) { + keys = std::get(argTuple); + } + mConfigRTTI->helpTextEntries.emplace_back(std::move(keys), std::get(argTuple)); +} + +void GPUDisplay::buildConfig() +{ + auto& rtti = mFrontend->mConfigRTTI; + rtti = {new GPUDisplayFrontend::configRTTIInfo, [](GPUDisplayFrontend::configRTTIInfo* p) { delete p; }}; + + rtti->keyNames.resize(rtti->configKeys.size()); +#define GPUDisplayFrontendAddKey(code, key) rtti->keyNames[code] = #key +#include "frontend/GPUDisplayFrontendKeys.h" +#undef GPUDisplayFrontendAddKey + + std::tie(rtti->rttiEntries, rtti->configStructNames) = std::apply([this](auto... s) { return qConfigGenRTTI*s)>...>(); }, rtti->configStructs); + std::apply([&](auto... s) { (rtti->rttiReverseId.emplace_back(sizeof(this->*s), GPUDisplayFrontend::configRTTIInfo::INVALID_INDEX), ...); }, rtti->configStructs); + for (uint32_t i = 0; i < rtti->rttiEntries.size(); i++) { + const auto& entry = rtti->rttiEntries[i]; + rtti->rttiReverseId[entry.configStructIndex][entry.offset] = i; + } +} diff --git a/GPU/GPUTracking/utils/qconfig.cxx b/GPU/GPUTracking/utils/qconfig.cxx index 6186c8950ca6f..ddfe56ae78cb6 100644 --- a/GPU/GPUTracking/utils/qconfig.cxx +++ b/GPU/GPUTracking/utils/qconfig.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/GPU/GPUTracking/utils/qconfig_genrtti.inc b/GPU/GPUTracking/utils/qconfig_genrtti.inc index 5d817f5a26b93..b309727799f9e 100644 --- a/GPU/GPUTracking/utils/qconfig_genrtti.inc +++ b/GPU/GPUTracking/utils/qconfig_genrtti.inc @@ -12,8 +12,8 @@ /// \file qconfig_genrtti.inc /// \author David Rohr -// Provides static inline qConfigGenRTTI() in the namespace it is included in. -// Requires , , , , "qconfig_rtti.h" (brings , , ), "qconfig_internal_helpers.h" to be included before (outside of the namespace), and the config structs to be visible unqualified. +// Provides static inline qConfigGenRTTI() in the namespace it is included in. +// Requires , , , , , "qconfig_rtti.h" (brings , , ), "qconfig_internal_helpers.h" to be included before (outside of the namespace), and the config structs to be visible unqualified. #ifndef QCONFIG_GENRTTI_INC #define QCONFIG_GENRTTI_INC @@ -27,8 +27,17 @@ static inline T qConfigRTTICheckedCast(size_t v, const char* what) return static_cast(v); } -static inline std::optional qConfigRTTIStructIndex(std::vector& configStructs, const char* name, bool filter) // Index of name in configStructs, appended if not filtering, nullopt if filtered out +template +constexpr bool qConfigRTTISelected = sizeof...(Structs) == 0 || (std::is_same_v || ...); // Compile-time filter, all structs if none given + +template +static inline std::optional qConfigRTTIStructIndex(std::vector& configStructs, const char* name, bool filter) // Index of struct S, position in Structs if given, else of name in configStructs (appended if not filtering, nullopt if filtered out) { + if constexpr (sizeof...(Structs) > 0) { + constexpr size_t index = [] { size_t i = 0; ((std::is_same_v ? false : (++i, true)) && ...); return i; }(); + configStructs[index] = name; + return index; + } if (auto it = std::find(configStructs.begin(), configStructs.end(), name); it != configStructs.end()) { return it - configStructs.begin(); } @@ -39,8 +48,11 @@ static inline std::optional qConfigRTTIStructIndex(std::vector -static inline void qConfigRTTIEntry(std::vector<::qConfig::qConfigRTTIentry>& retVal, ::qConfig::qConfigRTTIentry entry, size_t arraySize, size_t configStructIndex, std::string defaultValue, const char* help, Args&&... args) // Complete the entry, check the narrow members for overflow +template +concept qConfigRTTIentryType = std::derived_from && std::is_aggregate_v; // Aggregate, so it can be initialized from the base entry; extra members need default initializers + +template +static inline void qConfigRTTIEntry(std::vector& retVal, R entry, size_t arraySize, size_t configStructIndex, std::string defaultValue, const char* help, Args&&... args) // Complete the entry, check the narrow members for overflow { auto settings = ::qConfig::qConfigSettingsParser::qConfigGetSettings(args...); entry.size = qConfigRTTICheckedCast(sizeof(T), "size"); @@ -60,18 +72,24 @@ static inline void qConfigRTTIEntry(std::vector<::qConfig::qConfigRTTIentry>& re retVal.emplace_back(std::move(entry)); } -static inline std::tuple, std::vector> qConfigGenRTTI(const std::vector* configStructs) // Entries and list of config structs, for the given config structs (all if nullptr) +template +static inline std::tuple, std::vector> qConfigGenRTTI(const std::vector* configStructs = nullptr) // Entries (of type T, derived from qConfigRTTIentry) and list of config structs. Filter: struct types Structs at compile time (code only generated for those), else the names in configStructs at runtime (all if nullptr) { using namespace ::qConfig; // For the settings helpers (min, max, ...) and print_type used in GPUSettingsList.h / qconfig_internal.h - std::vector retVal; + std::vector retVal; std::vector retStructs; - if (configStructs) { + if constexpr (sizeof...(Structs) > 0) { + retStructs.resize(sizeof...(Structs)); + } else if (configStructs) { retStructs = *configStructs; } #define QCONFIG_GEN_RTTI #include "qconfig_internal.h" #undef QCONFIG_GEN_RTTI #undef QCONFIG_RTTI + if (std::find(retStructs.begin(), retStructs.end(), "") != retStructs.end()) { + throw std::runtime_error("qConfigGenRTTI: requested type is not a config struct"); + } return {std::move(retVal), std::move(retStructs)}; } diff --git a/GPU/GPUTracking/utils/qconfig_internal.h b/GPU/GPUTracking/utils/qconfig_internal.h index cb646efe5d7c5..17ac6b55cb530 100644 --- a/GPU/GPUTracking/utils/qconfig_internal.h +++ b/GPU/GPUTracking/utils/qconfig_internal.h @@ -261,9 +261,9 @@ void qConfigPrint(const T& qconfig_tmp_object, const std::string& blockName); // // End QCONFIG_PRINT_RTC #elif defined(QCONFIG_GEN_RTTI) -#define QCONFIG_RTTI(vName, vType, rtc, variable, vector, count, defaultValue, ...) \ - qConfigRTTIEntry(retVal, \ - {.name = qon_mxstr(vName), .type = qon_mxstr(vType), .isRTC = rtc, .isVariable = variable, .isVector = vector, .offset = static_cast(reinterpret_cast(&qConfigObject.vName) - reinterpret_cast(&qConfigObject)), .configStruct = qConfigStruct}, \ +#define QCONFIG_RTTI(vName, vType, rtc, variable, vector, count, defaultValue, ...) \ + qConfigRTTIEntry(retVal, \ + T{::qConfig::qConfigRTTIentry{.name = qon_mxstr(vName), .type = qon_mxstr(vType), .isRTC = rtc, .isVariable = variable, .isVector = vector, .offset = static_cast(reinterpret_cast(&qConfigObject.vName) - reinterpret_cast(&qConfigObject)), .configStruct = qConfigStruct}}, \ count, *qConfigStructIndex, defaultValue, __VA_ARGS__); #define AddOption(name, type, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, false, false, false, 0, print_type(static_cast(default)), __VA_ARGS__) #define AddOptionRTC(name, type, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, true, false, false, 0, print_type(static_cast(default)), __VA_ARGS__) @@ -274,12 +274,15 @@ void qConfigPrint(const T& qconfig_tmp_object, const std::string& blockName); // #define AddOptionArrayRTC(name, type, count, default, optname, optnameshort, ...) QCONFIG_RTTI(name, type, true, false, false, count, qon_mxstr(default), __VA_ARGS__) #define AddOptionSet(...) #define AddSubConfig(name, instance) -#define BeginConfig(name, instance) \ - if (const auto qConfigStructIndex = qConfigRTTIStructIndex(retStructs, qon_mxstr(name), configStructs != nullptr)) { \ - [[maybe_unused]] constexpr const char* qConfigStruct = qon_mxstr(name); \ - [[maybe_unused]] const name qConfigObject{}; // Default-constructed instance to compute the member offsets +#define BeginConfig(name, instance) \ + if constexpr (qConfigRTTISelected) { \ + if (const auto qConfigStructIndex = qConfigRTTIStructIndex(retStructs, qon_mxstr(name), configStructs != nullptr)) { \ + [[maybe_unused]] constexpr const char* qConfigStruct = qon_mxstr(name); \ + [[maybe_unused]] const name qConfigObject{}; // Default-constructed instance to compute the member offsets #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, ...) BeginConfig(name, instance) -#define EndConfig() } +#define EndConfig() \ + } \ + } // End QCONFIG_GEN_RTTI #else // Define structures From f291280c1c3603f0011a587974d9919cf23d2b52 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 24 Oct 2026 18:56:40 +0200 Subject: [PATCH 07/27] GPU Display: Remove mKeysShifted helper array --- GPU/GPUTracking/display/GPUDisplay.cxx | 6 +- .../display/frontend/GPUDisplayFrontend.cxx | 1 - .../display/frontend/GPUDisplayFrontend.h | 1 - .../frontend/GPUDisplayFrontendConfig.cxx | 14 ++-- .../frontend/GPUDisplayFrontendGlfw.cxx | 4 -- .../frontend/GPUDisplayFrontendGlut.cxx | 4 -- .../frontend/GPUDisplayFrontendWayland.cxx | 4 +- .../frontend/GPUDisplayFrontendWindows.cxx | 2 - .../frontend/GPUDisplayFrontendX11.cxx | 2 - .../display/frontend/GPUDisplayKeys.cxx | 70 +++++++++---------- 10 files changed, 43 insertions(+), 65 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 991b9b4736ef9..af2eb8def6b6e 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -305,7 +305,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla rotPitch += rotatescalefactor * mouseScale * ((float)mFrontend->mMouseMvY - (float)mFrontend->mMouseDnY); } - if (mFrontend->mKeys[(uint8_t)'<'] && !mFrontend->mKeysShift[(uint8_t)'<']) { + if (mFrontend->mKeys[(uint8_t)'<']) { mAnimationDelay += moveX; if (mAnimationDelay < 0.05f) { mAnimationDelay = 0.05f; @@ -395,7 +395,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla // Graphichs Options float minSize = 0.4f / (mCfgR.drawQualityDownsampleFSAA > 1 ? mCfgR.drawQualityDownsampleFSAA : 1); - int32_t deltaLine = mFrontend->mKeys[(uint8_t)'+'] * mFrontend->mKeysShift[(uint8_t)'+'] - mFrontend->mKeys[(uint8_t)'-'] * mFrontend->mKeysShift[(uint8_t)'-']; + int32_t deltaLine = (mFrontend->mKeys[(uint8_t)'+'] - mFrontend->mKeys[(uint8_t)'-']) * mFrontend->mKeys[mFrontend->KEY_SHIFT]; mCfgL.lineWidth += (float)deltaLine * mFPSScale * 0.02f * mCfgL.lineWidth; if (mCfgL.lineWidth < minSize) { mCfgL.lineWidth = minSize; @@ -405,7 +405,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla mUpdateDrawCommands = true; } minSize *= 2; - int32_t deltaPoint = mFrontend->mKeys[(uint8_t)'+'] * (!mFrontend->mKeysShift[(uint8_t)'+']) - mFrontend->mKeys[(uint8_t)'-'] * (!mFrontend->mKeysShift[(uint8_t)'-']); + int32_t deltaPoint = (mFrontend->mKeys[(uint8_t)'+'] - mFrontend->mKeys[(uint8_t)'-']) * !mFrontend->mKeys[mFrontend->KEY_SHIFT]; mCfgL.pointSize += (float)deltaPoint * mFPSScale * 0.02f * mCfgL.pointSize; if (mCfgL.pointSize < minSize) { mCfgL.pointSize = minSize; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index aafe316160d82..2e271bada2b00 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -257,7 +257,6 @@ void GPUDisplayFrontend::setWindowState(bool fullScreen, bool maximized) void GPUDisplayFrontend::focusLost() { std::fill(std::begin(mKeys), std::end(mKeys), false); - std::fill(std::begin(mKeysShift), std::end(mKeysShift), false); mMouseDn = mMouseDnR = mMouseDnM = false; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index badf42242e106..67af4ec11e92a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -108,7 +108,6 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface float mMouseMvX, mMouseMvY; // Current mouse pointer position int32_t mMouseWheel = 0; // Incremental value of mouse wheel, ca +/- 100 per wheel tick bool mKeys[256] = {false}; // Array of mKeys currently pressed - bool mKeysShift[256] = {false}; // Array whether shift was held during key-press int32_t mDisplayHeight = INIT_HEIGHT; int32_t mDisplayWidth = INIT_WIDTH; int32_t mCanDrawText = 0; // 1 = in compat mode, 2 = with shader diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index ae8567d8a6d64..9d6f861e669ae 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -14,6 +14,7 @@ #include "GPUDisplayFrontend.h" #include "GPUDisplay.h" +#include "GPULogging.h" #include "utils/qconfig_rtti.h" #include "utils/qconfig_internal_helpers.h" @@ -123,6 +124,9 @@ void GPUDisplay::checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCf if (oldCfgR.useGLIndirectDraw != mCfgR.useGLIndirectDraw) { mUpdateEventData = true; } + if (oldCfgL.animationMode != mCfgL.animationMode) { + resetAnimation(); + } } void GPUDisplay::HandleExternalGUIKey(int32_t key) @@ -130,14 +134,10 @@ void GPUDisplay::HandleExternalGUIKey(int32_t key) // GPUError("key %d '%c'", key, (char) key); bool shifted = key >= 'A' && key <= 'Z'; - int32_t press = key; - if (press >= 'a' && press <= 'z') { - press += 'A' - 'a'; - } - bool oldShift = mFrontend->mKeysShift[press]; - mFrontend->mKeysShift[press] = shifted; + bool oldShift = mFrontend->mKeys[mFrontend->KEY_SHIFT]; + mFrontend->mKeys[mFrontend->KEY_SHIFT] = shifted; HandleKey(key, true); - mFrontend->mKeysShift[press] = oldShift; + mFrontend->mKeys[mFrontend->KEY_SHIFT] = oldShift; } bool GPUDisplayFrontend::HasExternalGUISignals() const diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx index ac61ddacdd0f6..1e0e9bafe0d7d 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx @@ -163,11 +163,9 @@ void GPUDisplayFrontendGlfw::key_callback(GLFWwindow* window, int32_t key, int32 if (handleKey < 32) { if (action == GLFW_PRESS) { me->mKeys[keyPress] = true; - me->mKeysShift[keyPress] = mods & GLFW_MOD_SHIFT; me->HandleKey(handleKey); } else if (action == GLFW_RELEASE) { me->mKeys[keyPress] = false; - me->mKeysShift[keyPress] = false; } } else if (handleKey < 256) { if (action == GLFW_PRESS) { @@ -175,7 +173,6 @@ void GPUDisplayFrontendGlfw::key_callback(GLFWwindow* window, int32_t key, int32 } else if (action == GLFW_RELEASE) { keyPress = (uint8_t)me->mKeyDownMap[handleKey]; me->mKeys[keyPress] = false; - me->mKeysShift[keyPress] = false; } } } @@ -190,7 +187,6 @@ void GPUDisplayFrontendGlfw::char_callback(GLFWwindow* window, uint32_t codepoin } me->mKeyDownMap[me->mLastKeyDown] = keyPress; me->mKeys[keyPress] = true; - me->mKeysShift[keyPress] = me->mKeys[KEY_SHIFT]; me->HandleKey(codepoint); } } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index 368a86413194c..1abf2c578d030 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -149,7 +149,6 @@ void GPUDisplayFrontendGlut::keyboardDownFunc(uint8_t key, int32_t x, int32_t y) { int32_t handleKey = 0, keyPress = 0; GetKey(key, handleKey, keyPress, false); - me->mKeysShift[keyPress] = glutGetModifiers() & GLUT_ACTIVE_SHIFT; me->mKeys[keyPress] = true; me->HandleKey(handleKey); } @@ -159,14 +158,12 @@ void GPUDisplayFrontendGlut::keyboardUpFunc(uint8_t key, int32_t x, int32_t y) int32_t handleKey = 0, keyPress = 0; GetKey(key, handleKey, keyPress, false); me->mKeys[keyPress] = false; - me->mKeysShift[keyPress] = false; } void GPUDisplayFrontendGlut::specialDownFunc(int32_t key, int32_t x, int32_t y) { int32_t handleKey = 0, keyPress = 0; GetKey(key, handleKey, keyPress, true); - me->mKeysShift[keyPress] = glutGetModifiers() & GLUT_ACTIVE_SHIFT; me->mKeys[keyPress] = true; me->HandleKey(handleKey); } @@ -176,7 +173,6 @@ void GPUDisplayFrontendGlut::specialUpFunc(int32_t key, int32_t x, int32_t y) int32_t handleKey = 0, keyPress = 0; GetKey(key, handleKey, keyPress, true); me->mKeys[keyPress] = false; - me->mKeysShift[keyPress] = false; } void GPUDisplayFrontendGlut::ResizeSceneWrapper(int32_t width, int32_t height) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx index 66701557a8068..f8c1190e978a2 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx @@ -95,7 +95,7 @@ int32_t GPUDisplayFrontendWayland::GetKey(uint32_t key, uint32_t state) return (KEY_HOME); } else if (sym == XKB_KEY_Insert) { return (KEY_INSERT); - } else if (sym == XKB_KEY_F1) { + } else if (sym == XKB_KEY_F1) { // TODO: Fix F* keys not working return (KEY_F1); } else if (sym == XKB_KEY_F2) { return (KEY_F2); @@ -259,7 +259,6 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() int32_t keyPress = (symbol >= 'a' && symbol <= 'z') ? symbol + 'A' - 'a' : symbol; if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { me->mKeys[keyPress] = true; - me->mKeysShift[keyPress] = me->mKeys[KEY_SHIFT]; me->HandleKey(symbol); if (symbol && me->mRepeatRate > 0 && me->mXKBkeymap && xkb_keymap_key_repeats(me->mXKBkeymap, key + 8)) { // Wayland compositors do not repeat keys, the client must do it me->mRepeatKeyCode = key; @@ -268,7 +267,6 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() } } else { me->mKeys[keyPress] = false; - me->mKeysShift[keyPress] = false; if ((int64_t)key == me->mRepeatKeyCode) { me->mRepeatKeyCode = -1; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx index 80537c15c4700..9a641b5c6ef05 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx @@ -260,13 +260,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_KEYDOWN: // Is A Key Being Held Down? wParam = GetKey(wParam); mKeys[wParam] = TRUE; - mKeysShift[wParam] = mKeys[KEY_SHIFT]; HandleKey(wParam); return 0; case WM_KEYUP: wParam = GetKey(wParam); - mKeysShift[wParam] = false; mKeys[wParam] = false; GPUInfo("Key: %d", wParam); return 0; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx index a06f75d094cda..23de92432cfd1 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx @@ -399,7 +399,6 @@ int32_t GPUDisplayFrontendX11::FrontendMain() case KeyPress: { int32_t handleKey = 0, keyPress = 0; GetKey(event, handleKey, keyPress); - mKeysShift[keyPress] = mKeys[KEY_SHIFT]; mKeys[keyPress] = true; HandleKey(handleKey); break; @@ -409,7 +408,6 @@ int32_t GPUDisplayFrontendX11::FrontendMain() int32_t handleKey = 0, keyPress = 0; GetKey(event, handleKey, keyPress); mKeys[keyPress] = false; - mKeysShift[keyPress] = false; break; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx index 5b2e8599c5838..a498553a9e7de 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx @@ -85,17 +85,17 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) if (key == 'n') { mFrontend->mDisplayControl = 1; SetInfo("Showing next event", 1); - } else if (key == 27 || key == mFrontend->KEY_ESCAPE) { + } else if (key == mFrontend->KEY_ESCAPE) { mFrontend->mDisplayControl = 2; SetInfo("Exiting", 1); } else if (key == 'r') { mResetScene = true; SetInfo("View reset", 1); - } else if (key == mFrontend->KEY_ALT && mFrontend->mKeysShift[mFrontend->KEY_ALT]) { + } else if (key == mFrontend->KEY_ALT && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { mCfgR.camLookOrigin ^= 1; mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * mCfgR.camYUp; SetInfo("Camera locked on origin: %s", mCfgR.camLookOrigin ? "enabled" : "disabled"); - } else if (key == mFrontend->KEY_CTRL && mFrontend->mKeysShift[mFrontend->KEY_CTRL]) { + } else if (key == mFrontend->KEY_CTRL && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { mCfgR.camYUp ^= 1; mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * mCfgR.camYUp; SetInfo("Camera locked on y-axis facing upwards: %s", mCfgR.camYUp ? "enabled" : "disabled"); @@ -330,39 +330,31 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) mCfgL.drawExtrapolatedTracks ^= 1; } else if (key == '8') { mCfgL.drawFinal ^= 1; - } else if (key == mFrontend->KEY_F1) { - if (mFrontend->mKeysShift[mFrontend->KEY_F1]) { - mCfgH.drawTPCTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else { - mCfgL.drawTPC ^= 1; - SetInfo("Showing TPC Clusters: %d", (int32_t)mCfgL.drawTPC); - } - } else if (key == mFrontend->KEY_F2) { - if (mFrontend->mKeysShift[mFrontend->KEY_F2]) { - mCfgH.drawTRDTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else { - mCfgL.drawTRD ^= 1; - SetInfo("Showing TRD Tracklets: %d", (int32_t)mCfgL.drawTRD); - } - } else if (key == mFrontend->KEY_F3) { - if (mFrontend->mKeysShift[mFrontend->KEY_F3]) { - mCfgH.drawTOFTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else { - mCfgL.drawTOF ^= 1; - SetInfo("Showing TOF Hits: %d", (int32_t)mCfgL.drawTOF); - } - } else if (key == mFrontend->KEY_F4) { - if (mFrontend->mKeysShift[mFrontend->KEY_F4]) { - mCfgH.drawITSTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else { - mCfgL.drawITS ^= 1; - SetInfo("Showing ITS Clusters: %d", (int32_t)mCfgL.drawITS); - } - } else if (key == mFrontend->KEY_F12 && mFrontend->mKeysShift[mFrontend->KEY_F12]) { + } else if (key == mFrontend->KEY_F1 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgH.drawTPCTracks ^= 1; + SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); + } else if (key == mFrontend->KEY_F1 && !mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgL.drawTPC ^= 1; + SetInfo("Showing TPC Clusters: %d", (int32_t)mCfgL.drawTPC); + } else if (key == mFrontend->KEY_F2 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgH.drawTRDTracks ^= 1; + SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); + } else if (key == mFrontend->KEY_F2 && !mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgL.drawTRD ^= 1; + SetInfo("Showing TRD Tracklets: %d", (int32_t)mCfgL.drawTRD); + } else if (key == mFrontend->KEY_F3 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgH.drawTOFTracks ^= 1; + SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); + } else if (key == mFrontend->KEY_F3 && !mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgL.drawTOF ^= 1; + SetInfo("Showing TOF Hits: %d", (int32_t)mCfgL.drawTOF); + } else if (key == mFrontend->KEY_F4 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgH.drawITSTracks ^= 1; + SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); + } else if (key == mFrontend->KEY_F4 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + mCfgL.drawITS ^= 1; + SetInfo("Showing ITS Clusters: %d", (int32_t)mCfgL.drawITS); + } else if (key == mFrontend->KEY_F12 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { mCfgH.drawTracksAndFilter ^= 1; SetInfo("Track filter: %s", mCfgH.drawTracksAndFilter ? "AND" : "OR"); } else if (key == 't') { @@ -378,7 +370,10 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) mCfgR.screenshotScaleFactor = 1; } SetInfo("Screenshot scaling factor set to %d", mCfgR.screenshotScaleFactor); - } else if (key == 'y' || key == 'T') { + } else if (key == 'T') { + mAnimateScreenshot ^= 1; + SetInfo("Writing screenshots from animation set to %d", (int32_t)mAnimateScreenshot); + } else if (key == 'y') { if (mAnimateVectors[0].size() > 1) { if ((mAnimateScreenshot = (key == 'T'))) { mAnimationExport++; @@ -405,7 +400,6 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) if (mCfgL.animationMode == 7) { mCfgL.animationMode = 0; } - resetAnimation(); if (mCfgL.animationMode == 6) { SetInfo("Animation mode %d - Centered on origin", mCfgL.animationMode); } else { From 59148a984ae7a5185911b0649cc0a63a21b5b100 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 25 Oct 2026 17:34:34 +0100 Subject: [PATCH 08/27] GPU Display: Assemble all variables controlling the display in a control struct --- GPU/GPUTracking/display/GPUDisplay.cxx | 35 +++++++++---------- GPU/GPUTracking/display/GPUDisplay.h | 18 ++++++---- .../display/frontend/GPUDisplayFrontend.cxx | 6 ++-- .../display/frontend/GPUDisplayFrontend.h | 7 ++-- .../frontend/GPUDisplayFrontendConfig.cxx | 11 ++++-- .../frontend/GPUDisplayFrontendGlfw.cxx | 2 +- .../frontend/GPUDisplayFrontendGlut.cxx | 2 +- .../frontend/GPUDisplayFrontendNone.cxx | 2 +- .../frontend/GPUDisplayFrontendWayland.cxx | 6 ++-- .../frontend/GPUDisplayFrontendX11.cxx | 6 ++-- .../display/frontend/GPUDisplayKeys.cxx | 31 ++++++++-------- .../display/helpers/GPUDisplayAnimation.cxx | 6 ++-- .../display/helpers/GPUDisplayHelpers.cxx | 2 +- 13 files changed, 72 insertions(+), 62 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index af2eb8def6b6e..e2102660e6a29 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -148,7 +148,7 @@ void GPUDisplay::ResizeScene(int32_t width, int32_t height, bool init) mBackend->resizeScene(width, height); if (init) { - mResetScene = true; + mCfgC.mResetScene.store(true); *mViewMatrix = MY_HMM_IDENTITY; *mModelMatrix = MY_HMM_IDENTITY; } @@ -268,7 +268,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla if (animateCamera(animateTime, mixSlaveImage, nextViewMatrix)) { // Do nothing else - } else if (mResetScene) { + } else if (mCfgC.mResetScene.load()) { const float initialZpos = mCfgH.projectXY ? 16 : (mParam->par.continuousTracking ? (mMaxClusterZ * GL_SCALE_FACTOR + 8) : 8); nextViewMatrix = nextViewMatrix * HMM_Translate({{0, 0, -initialZpos}}); *mViewMatrix = MY_HMM_IDENTITY; @@ -284,7 +284,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla mCfgR.fov = 45.f; mUpdateDrawCommands = true; - mResetScene = false; + mCfgC.mResetScene.store(false); } else { float moveZ = scalefactor * ((float)mMouseWheelTmp / 150 + (float)(mFrontend->mKeys[(uint8_t)'W'] - mFrontend->mKeys[(uint8_t)'S']) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]) * 0.2f * mFPSScale); float moveY = scalefactor * ((float)(mFrontend->mKeys[mFrontend->KEY_PAGEDOWN] - mFrontend->mKeys[mFrontend->KEY_PAGEUP]) * 0.2f * mFPSScale); @@ -629,18 +629,18 @@ void GPUDisplay::DrawGLScene_drawCommands() void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) // negative time = no mixing { - if (mRequestScreenshot || mAnimateScreenshot) { + if (mCfgC.mRequestScreenshot || mCfgC.mAnimateScreenshot) { const uint32_t factor = std::max(1, mCfgR.drawQualityDownsampleFSAA) * std::max(1, mCfgR.screenshotScaleFactor); if (std::max(mBackend->mScreenWidth, mBackend->mScreenHeight) * factor > mBackend->mMaxRenderSize) { GPUError("Screenshot not taken: requested render size %u x %u (window x FSAA x screenshot scale) exceeds the maximum size %u x %u", mBackend->mScreenWidth * factor, mBackend->mScreenHeight * factor, mBackend->mMaxRenderSize, mBackend->mMaxRenderSize); - mRequestScreenshot = false; - mAnimateScreenshot = 0; + mCfgC.mRequestScreenshot = false; + mCfgC.mAnimateScreenshot = 0; } } - bool doScreenshot = (mRequestScreenshot || mAnimateScreenshot) && animateTime < 0; + bool doScreenshot = (mCfgC.mRequestScreenshot || mCfgC.mAnimateScreenshot) && animateTime < 0; updateOptions(); - if (animateTime < 0 && (mUpdateEventData || mResetScene || mUpdateVertexLists) && mIOPtrs) { + if (animateTime < 0 && (mUpdateEventData || mCfgC.mResetScene.load() || mUpdateVertexLists) && mIOPtrs) { disableUnsupportedOptions(); } if (mUpdateEventData || mUpdateVertexLists) { @@ -648,7 +648,7 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) } HighResTimer timerDraw(mUpdateVertexLists); - if (animateTime < 0 && (mUpdateEventData || mResetScene) && mIOPtrs) { + if (animateTime < 0 && (mUpdateEventData || mCfgC.mResetScene.load()) && mIOPtrs) { timerDraw.ResetStart(); DrawGLScene_updateEventData(); mTimerFPS.ResetStart(); @@ -675,7 +675,7 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) const float zFar = ((mParam->par.continuousTracking ? (mMaxClusterZ * GL_SCALE_FACTOR) : 8.f) + 50.f) * 2.f; const float aspect = mBackend->mRenderHeight ? (float)mBackend->mRenderWidth / (float)mBackend->mRenderHeight : 1.f; // No render size with the None backend const hmm_mat4 proj = HMM_Perspective(mCfgR.fov, aspect, 0.1f, zFar); - if (!mBackend->prepareDraw(proj, nextViewMatrix, doScreenshot || mRequestScreenshot, renderToMixBuffer, mixSlaveImage)) { + if (!mBackend->prepareDraw(proj, nextViewMatrix, doScreenshot || mCfgC.mRequestScreenshot, renderToMixBuffer, mixSlaveImage)) { return; // No image available (e.g. hidden window), retry with the next frame } mBackend->pointSizeFactor(1); @@ -706,7 +706,7 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) fps, mCfgL.drawSector, mCfgL.drawClusters, mCfgL.drawInitLinks, mCfgL.drawLinks, mCfgL.drawSeeds, mCfgL.drawTracklets, mCfgL.drawTracks, mCfgL.drawExtrapolatedTracks, mCfgL.drawFinal, mFramesDone, mNDrawCalls, mXYZ[0], mXYZ[1], mXYZ[2], mRPhiTheta[0], mRPhiTheta[1] * 180 / CAMath::Pi(), mRPhiTheta[2] * 180 / CAMath::Pi(), mAngle[1] * 180 / CAMath::Pi(), mAngle[0] * 180 / CAMath::Pi(), mAngle[2] * 180 / CAMath::Pi()); if (fpstime > 1.) { - if (mPrintInfoText & 2) { + if (mCfgC.mPrintInfoText & 2) { GPUInfo("%s", info); } if (mFPSScaleadjust++) { @@ -716,20 +716,20 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) mFramesDoneFPS = 0; } - if (mPrintInfoText & 1) { + if (mCfgC.mPrintInfoText & 1) { showInfo(info); } } - mBackend->finishFrame(mRequestScreenshot, renderToMixBuffer, mixSlaveImage); + mBackend->finishFrame(mCfgC.mRequestScreenshot, renderToMixBuffer, mixSlaveImage); if (doScreenshot) { - mRequestScreenshot = false; + mCfgC.mRequestScreenshot = false; std::vector pixels = mBackend->getPixels(); char tmpFileName[48]; - if (mAnimateScreenshot) { + if (mCfgC.mAnimateScreenshot) { snprintf(tmpFileName, 48, "mAnimation%d_%05d.bmp", mAnimationExport, mAnimationFrame); } - DoScreenshot(mAnimateScreenshot ? tmpFileName : mScreenshotFile.c_str(), pixels); + DoScreenshot(mCfgC.mAnimateScreenshot ? tmpFileName : mScreenshotFile.c_str(), pixels); } } @@ -739,9 +739,8 @@ void GPUDisplay::ShowNextEvent(const GPUTrackingInOutPointers* ptrs) mIOPtrs = ptrs; } if (mMaxClusterZ <= 0) { - mResetScene = true; + mCfgC.mResetScene.store(true); } - mFrontend->mNeedUpdate = 1; mUpdateEventData = true; mLoadAndShowEvent = true; mSemLockDisplay.Unlock(); diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 0fb99ada77b7b..fb78a1219b51c 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -21,6 +21,7 @@ #include "GPUSettings.h" #include "DataFormatsTPC/Constants.h" +#include #include #include #include @@ -86,6 +87,17 @@ class GPUDisplay : public GPUDisplayInterface bool drawTextInCompatMode() const { return mDrawTextInCompatMode; } int32_t& drawTextFontSize() { return mDrawTextFontSize; } + struct control { + std::atomic mDisplayControl = 0; // Control for next event (=1) or quit (=2) + std::atomic mResetScene = false; + int32_t mPrintInfoText = 1; + bool mPrintInfoTextAlways = false; + bool mRequestScreenshot = false; + int32_t mAnimateScreenshot = 0; + bool mAnimationChangeConfig = true; + bool dummy = false; // dummy value to reference for keys that do not change variables + } mCfgC; + private: void buildConfig(); // Key bindings / config RTTI of the frontend, in GPUDisplayFrontendConfig.cxx @@ -292,7 +304,6 @@ class GPUDisplay : public GPUDisplayInterface volatile bool mUpdateEventData = false; volatile bool mUpdateDrawCommands = true; volatile bool mUpdateRenderPipeline = false; - volatile bool mResetScene = false; volatile bool mLoadAndShowEvent = false; bool mTracksArePropagated = false; @@ -301,16 +312,12 @@ class GPUDisplay : public GPUDisplayInterface int32_t mAnimationFrame = 0; int32_t mAnimationLastBase = 0; int32_t mAnimationMixBase = -1; // Target keyframe of the current config blend (mixing), -1 = none - int32_t mAnimateScreenshot = 0; int32_t mAnimationExport = 0; - bool mAnimationChangeConfig = true; float mAnimationDelay = 2.f; vecpod mAnimateVectors[9]; vecpod mAnimateConfig; opengl_spline mAnimationSplines[8]; - int32_t mPrintInfoText = 1; - bool mPrintInfoTextAlways = 0; char mInfoText2[1024]; HighResTimer mInfoText2Timer, mInfoHelpTimer; @@ -328,7 +335,6 @@ class GPUDisplay : public GPUDisplayInterface vboList mGlDLGrid[NSECTORS]; vboList mGlDLGridTRD[NSECTORS / 2]; - bool mRequestScreenshot = false; std::string mScreenshotFile; float mYFactor = 1.0f; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index 2e271bada2b00..7cd9878fc43c7 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -81,7 +81,6 @@ void* GPUDisplayFrontend::FrontendThreadWrapper(void* ptr) void GPUDisplayFrontend::waitForFPSLimit() { - mNeedUpdate = 0; const auto now = std::chrono::steady_clock::now(); const auto& cfg = mDisplay->cfgR(); if (mBackend->fpsLimitMode() != GPUDisplayBackend::FPS_LIMIT_RATE || cfg.fpsRate <= 0) { @@ -143,6 +142,9 @@ void GPUDisplayFrontend::sendExternalGUIKey(int32_t v) mExternalGUIKeys.emplace_back(v); } +int32_t GPUDisplayFrontend::getDisplayControl() const { return mDisplay->mCfgC.mDisplayControl.load(); } +void GPUDisplayFrontend::setDisplayControl(int32_t v) { mDisplay->mCfgC.mDisplayControl.store(v); } + void GPUDisplayFrontend::HandleKey(uint8_t key) { mDisplay->HandleKey(key); } int32_t GPUDisplayFrontend::DrawGLScene() { return mDisplay->DrawGLScene(); } @@ -303,7 +305,7 @@ void GPUDisplayFrontend::StopDisplay() { std::lock_guard lock(mExitStateMutex); if (mDisplayRunning) { - mDisplayControl = 2; + mDisplay->mCfgC.mDisplayControl.store(2); StopDisplayA(); mDisplay->UnblockDisplay(); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 67af4ec11e92a..6345a1aaf47b5 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -68,8 +68,8 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface virtual int32_t getVulkanSurface(void* instance, void* surface) { return 1; } virtual uint32_t getReqVulkanExtensions(const char**& p) { return 0; }; - int32_t getDisplayControl() const override { return mDisplayControl; } - void setDisplayControl(int32_t v) override { mDisplayControl = v; } + int32_t getDisplayControl() const override; + void setDisplayControl(int32_t v) override; void sendExternalGUIKey(int32_t v) override; frontendTypes frontendType() const { return mFrontendType; } @@ -93,9 +93,6 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface #include "GPUDisplayFrontendKeys.h" #undef GPUDisplayFrontendAddKey - // volatile variables to exchange control informations between display and backend - volatile int32_t mDisplayControl = 0; // Control for next event (=1) or quit (=2) - volatile int32_t mNeedUpdate = 0; // flag that backend shall update the GL window, and call DrawGLScene std::mutex mExternalGUIKeyMutex; std::vector mExternalGUIKeys; // Keys sent by external entity (usually console), may be ignored by backend. std::mutex mExitStateMutex; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 9d6f861e669ae..c8db5c2b42b90 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -71,8 +71,12 @@ struct GPUDisplayFrontend::configRTTIInfo { template uint32_t getRTTIIndex(GPUDisplay& display, M T::* member) // RTTI index of a direct member of a config struct { - const T& obj = configStruct(display); - return rttiReverseId[configStructIndex()][(const char*)&(obj.*member) - (const char*)&obj]; + if constexpr (std::is_same_v) { + return INVALID_INDEX; + } else { + const T& obj = configStruct(display); + return rttiReverseId[configStructIndex()][(const char*)&(obj.*member) - (const char*)&obj]; + } } void buildHelpText() // Keys left-aligned, the ':' aligned after the longest keys { @@ -127,6 +131,9 @@ void GPUDisplay::checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCf if (oldCfgL.animationMode != mCfgL.animationMode) { resetAnimation(); } + if (oldCfgH.trackFilter != mCfgH.trackFilter) { + mUpdateTrackFilter = true; + } } void GPUDisplay::HandleExternalGUIKey(int32_t key) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx index 1e0e9bafe0d7d..176d2b916040e 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx @@ -344,7 +344,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain() ExitDisplay(); { std::lock_guard lock(mExitStateMutex); - mDisplayControl = 2; + setDisplayControl(2); glfwDestroyWindow(mWindow); glfwTerminate(); mDisplayRunning = false; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index 1abf2c578d030..90391fff43bf0 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -273,7 +273,7 @@ int32_t GPUDisplayFrontendGlut::FrontendMain() glutMainLoop(); { std::lock_guard lock(mExitStateMutex); - mDisplayControl = 2; + setDisplayControl(2); mDisplayRunning = false; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx index 5d5748e13349a..e0b6f96933612 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx @@ -31,6 +31,6 @@ int32_t GPUDisplayFrontendNone::FrontendMain() HandleExternalGUISignals(); DrawGLScene(); waitForFPSLimit(); - } while (mDisplayControl != 2); + } while (getDisplayControl() != 2); return 0; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx index f8c1190e978a2..efee3ca538973 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx @@ -509,7 +509,7 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() }; auto xdg_toplevel_handle_close = [](void* data, xdg_toplevel* toplevel) { - ((GPUDisplayFrontendWayland*)data)->mDisplayControl = 2; + ((GPUDisplayFrontendWayland*)data)->mDisplay->mCfgC.mDisplayControl.store(2); }; #ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION auto xdg_toplevel_handle_configure_bounds = [](void* data, xdg_toplevel* toplevel, int32_t width, int32_t height) { @@ -583,7 +583,7 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() mDisplayRunning = true; } - while (mDisplayControl != 2) { + while (mDisplay->mCfgC.mDisplayControl.load() != 2) { bool error = false; while (!error && wl_display_prepare_read(mWayland) != 0) { error = wl_display_dispatch_pending(mWayland) == -1; @@ -606,7 +606,7 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() reportError("dispatching events"); break; } - if (mDisplayControl == 2) { + if (mDisplay->mCfgC.mDisplayControl.load() == 2) { break; } handleKeyRepeat(); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx index 23de92432cfd1..50cdd13fd9d19 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx @@ -355,7 +355,7 @@ int32_t GPUDisplayFrontendX11::FrontendMain() while (1) { do { - if (mDisplayControl == 2) { + if (getDisplayControl() == 2) { break; } if (!XPending(mXDisplay)) { @@ -442,13 +442,13 @@ int32_t GPUDisplayFrontendX11::FrontendMain() if (event.xclient.message_type == XInternAtom(mXDisplay, "_NET_WM_STATE", False)) { XFlush(mXDisplay); } else { - mDisplayControl = 2; + setDisplayControl(2); } break; } } } while (XPending(mXDisplay)); // Loop to compress events - if (mDisplayControl == 2) { + if (getDisplayControl() == 2) { break; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx index a498553a9e7de..70a3083d23906 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx @@ -83,13 +83,13 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) const GPUSettingsDisplayLight oldCfgL = mCfgL; const GPUSettingsDisplayRenderer oldCfgR = mCfgR; if (key == 'n') { - mFrontend->mDisplayControl = 1; + mCfgC.mDisplayControl.store(1); SetInfo("Showing next event", 1); } else if (key == mFrontend->KEY_ESCAPE) { - mFrontend->mDisplayControl = 2; + mCfgC.mDisplayControl.store(2); SetInfo("Exiting", 1); } else if (key == 'r') { - mResetScene = true; + mCfgC.mResetScene.store(true); SetInfo("View reset", 1); } else if (key == mFrontend->KEY_ALT && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { mCfgR.camLookOrigin ^= 1; @@ -178,9 +178,9 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) SetInfo("FPS limit: none"); } } else if (key == 'H') { - mPrintInfoText += 1; - mPrintInfoText &= 3; - SetInfo("Info text display - console: %s, onscreen %s", (mPrintInfoText & 2) ? "enabled" : "disabled", (mPrintInfoText & 1) ? "enabled" : "disabled"); + mCfgC.mPrintInfoText += 1; + mCfgC.mPrintInfoText &= 3; + SetInfo("Info text display - console: %s, onscreen %s", (mCfgC.mPrintInfoText & 2) ? "enabled" : "disabled", (mCfgC.mPrintInfoText & 1) ? "enabled" : "disabled"); } else if (key == 'j') { if (mCfgH.separateExtrapolatedTracks) { mCfgH.splitCETracks ^= 1; @@ -361,7 +361,7 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) static int32_t nScreenshot = 1; char fname[32]; snprintf(fname, 32, "screenshot%d.bmp", nScreenshot++); - mRequestScreenshot = true; + mCfgC.mRequestScreenshot = true; mScreenshotFile = fname; SetInfo("Taking screenshot (%s)", fname); } else if (key == 'Z') { @@ -371,11 +371,11 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) } SetInfo("Screenshot scaling factor set to %d", mCfgR.screenshotScaleFactor); } else if (key == 'T') { - mAnimateScreenshot ^= 1; - SetInfo("Writing screenshots from animation set to %d", (int32_t)mAnimateScreenshot); + mCfgC.mAnimateScreenshot ^= 1; + SetInfo("Writing screenshots from animation set to %d", mCfgC.mAnimateScreenshot); } else if (key == 'y') { if (mAnimateVectors[0].size() > 1) { - if ((mAnimateScreenshot = (key == 'T'))) { + if (mCfgC.mAnimateScreenshot) { mAnimationExport++; } startAnimation(); @@ -384,8 +384,8 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) SetInfo("Insufficient Animation points to start Animation", 1); } } else if (key == '>') { - mAnimationChangeConfig ^= 1; - SetInfo("Interpolating visualization settings during Animation %s", mAnimationChangeConfig ? "enabled" : "disabled"); + mCfgC.mAnimationChangeConfig ^= 1; + SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled"); } else if (key == 'Y') { setAnimationPoint(); SetInfo("Added Animation point (%d points, %6.2f seconds)", (int32_t)mAnimateVectors[0].size(), mAnimateVectors[0].back()); @@ -407,7 +407,6 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) } } else if (key == 'u') { mCfgH.trackFilter = (mCfgH.trackFilter + 1) % (mConfig.filterMacros.size() + 1); - mUpdateTrackFilter = true; SetInfo("Track filter: %s", mCfgH.trackFilter == 0 ? "None" : mConfig.filterMacros[mCfgH.trackFilter - 1].c_str()); } else if (key == 'o') { FILE* ftmp = fopen("glpos.tmp", "w+b"); @@ -472,11 +471,11 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) } SetInfo("Animation path loaded from file %s", "glanimation.tmp"); } else if (key == 'h') { - if (mPrintInfoTextAlways) { - mPrintInfoTextAlways = false; + if (mCfgC.mPrintInfoTextAlways) { + mCfgC.mPrintInfoTextAlways = false; SetInfo("Showing help text disabled", 1); } else if (mInfoHelpTimer.IsRunning()) { - mPrintInfoTextAlways = true; + mCfgC.mPrintInfoTextAlways = true; mInfoHelpTimer.Reset(); SetInfo("Showing help text until disabled", 1); } else { diff --git a/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx b/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx index bb2bacfc08434..7ef55de0113e4 100644 --- a/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx +++ b/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx @@ -109,7 +109,7 @@ int32_t GPUDisplay::animateCamera(float& animateTime, float& mixSlaveImage, hmm_ { float time = animateTime; if (mAnimate && time < 0) { - if (mAnimateScreenshot) { + if (mCfgC.mAnimateScreenshot) { time = mAnimationFrame / 30.f; } else { time = mAnimationTimer.GetCurrentElapsedTime(); @@ -118,7 +118,7 @@ int32_t GPUDisplay::animateCamera(float& animateTime, float& mixSlaveImage, hmm_ float maxTime = mAnimateVectors[0].back(); if (time >= maxTime) { time = maxTime; - mAnimate = mAnimateScreenshot = 0; + mAnimate = mCfgC.mAnimateScreenshot = 0; SetInfo("Animation finished. (%1.2f seconds, %d frames)", time, mAnimationFrame); } else { SetInfo("Running mAnimation: time %1.2f/%1.2f, frames %d", time, maxTime, mAnimationFrame); @@ -132,7 +132,7 @@ int32_t GPUDisplay::animateCamera(float& animateTime, float& mixSlaveImage, hmm_ for (int32_t i = 0; i < 8; i++) { vals[i] = mAnimationSplines[i].evaluate(time); } - if (mAnimationChangeConfig && animateTime < 0) { + if (mCfgC.mAnimationChangeConfig && animateTime < 0) { int32_t base = 0; int32_t k = mAnimateVectors[0].size() - 1; while (base < k && time > mAnimateVectors[0][base]) { diff --git a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx index d0a6685efc742..99a14a8319c24 100644 --- a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx +++ b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx @@ -110,7 +110,7 @@ void GPUDisplay::showInfo(const char* info) OpenGLPrint(mInfoText2, 40.f, 20.f, colorValue, colorValue, colorValue, 6 - mInfoText2Timer.GetCurrentElapsedTime()); } } - if (mInfoHelpTimer.IsRunning() || mPrintInfoTextAlways) { + if (mInfoHelpTimer.IsRunning() || mCfgC.mPrintInfoTextAlways) { if (mInfoHelpTimer.GetCurrentElapsedTime() >= 6) { mInfoHelpTimer.Reset(); } else { From adae1dc3318bf09b0fb1a2f0b4b3ccb0ab4884f5 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 24 Oct 2026 18:23:28 +0200 Subject: [PATCH 09/27] GPU Display: Add option to list configuration bindings for keyboard and GUI --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + .../display/frontend/GPUDisplayFrontend.cxx | 3 ++ .../display/frontend/GPUDisplayFrontend.h | 1 + .../frontend/GPUDisplayFrontendConfig.cxx | 53 ++++++++++++++++++- 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index fa560ec535313..553f0304abe24 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -534,6 +534,7 @@ AddOption(displayFrontend, std::string, "auto", "displayFrontend", 0, "Frontend AddOptionVec(filterMacros, std::string, "", 0, "ROOT macros used as track filter") AddOption(guiReplyTimeout, uint32_t, 100, "", 0, "Time in ms the Qt GUI waits at most for the display to process its update") AddOption(guiMaxUpdatesAhead, uint32_t, 3, "", 0, "Number of updates the Qt GUI may send ahead of the last one the display has processed, before it waits for the display") +AddOption(listFrontendOptionHandling, bool, false, "", 0, "List all assigned keys in frontend, Configurable settings in Qt GUI, and missing configuration options") AddSubConfig(GPUSettingsDisplayLight, light) AddSubConfig(GPUSettingsDisplayHeavy, heavy) AddSubConfig(GPUSettingsDisplayRenderer, renderer) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index 7cd9878fc43c7..af36e54479f04 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -297,6 +297,9 @@ int32_t GPUDisplayFrontend::StartDisplay() } #endif mDisplay->buildConfig(); + if (mDisplay->cfg().listFrontendOptionHandling) { + listConfig(); + } return (0); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 6345a1aaf47b5..5bafc1c289a2b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -142,6 +142,7 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface bool HandleExternalGUIKeys(); // Returns whether keys were handled, config changes are not handled template 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 + void listConfig(); }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index c8db5c2b42b90..8e44d761add7b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,10 @@ struct GPUDisplayFrontend::configRTTIInfo { std::array configKeys; std::vector keyNames; std::vector> helpTextEntries; // Keys and text of each add_key_binding call - std::string helpText; // Built from helpTextEntries by buildHelpText + std::string helpText; + static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames + static constexpr char reservedKeysData[] = {'W', 'A', 'S', 'D', 'E', 'F', 'w', 'a', 's', 'd', 'e', 'f', '+', '-', '<', KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_PAGEUP, KEY_PAGEDOWN, KEY_SHIFT, KEY_ALT, KEY_RALT, KEY_CTRL, KEY_RCTRL}; + static constexpr std::string_view reservedKeys{reservedKeysData, sizeof(reservedKeysData)}; // Keys queried continuously as held keys (camera control, modifiers), not available for bindings template static constexpr uint32_t configStructIndex() // Index of struct T in configStructs, compile-time error if not present @@ -185,6 +189,53 @@ void GPUDisplayFrontend::add_key_binding(Args&&... args) mConfigRTTI->helpTextEntries.emplace_back(std::move(keys), std::get(argTuple)); } +void GPUDisplayFrontend::listConfig() +{ + const auto& rtti = *mConfigRTTI; + auto keyOrder = [](uint8_t k) { return std::pair(k >= '0' && k <= '9' ? 0 : k >= 'a' && k <= 'z' ? 1 + : k >= 'A' && k <= 'Z' ? 2 + : k > 32 ? 3 + : 4, + k); }; // 0-9, a-z, A-Z, other characters, special keys + std::vector boundKeys, availableKeys; + for (uint32_t k = 1; k < rtti.configKeys.size(); k++) { + const bool possible = (k < 32 && !rtti.keyNames[k].empty()) || rtti.possibleKeys.find((char)k) != std::string_view::npos; + if (rtti.configKeys[k].callback) { + boundKeys.emplace_back(k); + } else if (possible && rtti.reservedKeys.find((char)k) == std::string_view::npos) { + availableKeys.emplace_back(k); + } + } + std::sort(boundKeys.begin(), boundKeys.end(), [&](uint8_t a, uint8_t b) { return keyOrder(a) < keyOrder(b); }); + std::sort(availableKeys.begin(), availableKeys.end(), [&](uint8_t a, uint8_t b) { return keyOrder(a) < keyOrder(b); }); + + GPUInfo("Display config options with RTTI (struct, option, type, keys):"); + uint32_t nOptionsWithKey = 0; + for (uint32_t i = 0; i < rtti.rttiEntries.size(); i++) { + const auto& e = rtti.rttiEntries[i]; + std::string keys; + for (uint8_t k : boundKeys) { + if (rtti.configKeys[k].configRTTIIndex == i) { + keys += (keys.empty() ? "[" : " [") + rtti.getKeyName(k) + "]"; + } + } + nOptionsWithKey += !keys.empty(); + GPUInfo(" %-28s %-32s %-12s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), keys.c_str()); + } + GPUInfo("Bound keys (key, struct, option):"); + for (uint8_t k : boundKeys) { + const uint32_t index = rtti.configKeys[k].configRTTIIndex; + const bool hasRTTI = index != configRTTIInfo::INVALID_INDEX; + GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), hasRTTI ? rtti.rttiEntries[index].configStruct.c_str() : "(no config option)", hasRTTI ? rtti.rttiEntries[index].name.c_str() : ""); + } + std::string available; + for (uint8_t k : availableKeys) { + available += (available.empty() ? "[" : " [") + rtti.getKeyName(k) + "]"; + } + GPUInfo("Available keys: %s", available.c_str()); + 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()); +} + void GPUDisplay::buildConfig() { auto& rtti = mFrontend->mConfigRTTI; From b101516113b75fe810e779ec0f1570b541e638f0 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 24 Oct 2026 18:24:03 +0200 Subject: [PATCH 10/27] GPU Display Frontend: move key handling to RTTI callbacks instead of long if/else if --- GPU/GPUTracking/display/GPUDisplay.h | 3 +- .../display/frontend/GPUDisplayFrontend.h | 4 + .../frontend/GPUDisplayFrontendConfig.cxx | 65 ++- .../frontend/GPUDisplayFrontendConfigKeys.inc | 414 +++++++++++++++++ .../display/frontend/GPUDisplayKeys.cxx | 425 +----------------- 5 files changed, 468 insertions(+), 443 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index fb78a1219b51c..8a48d5a3fbf3b 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -99,7 +99,8 @@ class GPUDisplay : public GPUDisplayInterface } mCfgC; private: - void buildConfig(); // Key bindings / config RTTI of the frontend, in GPUDisplayFrontendConfig.cxx + void buildConfig(); + void buildConfigKeys(); static constexpr int32_t NSECTORS = o2::tpc::constants::MAXSECTOR; static constexpr float GL_SCALE_FACTOR = (1.f / 100.f); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 5bafc1c289a2b..54a5c3e332f64 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -92,6 +92,9 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface #define GPUDisplayFrontendAddKey(code, key) static constexpr int32_t KEY_##key = code #include "GPUDisplayFrontendKeys.h" #undef GPUDisplayFrontendAddKey + static constexpr uint32_t N_SPECIAL_KEYS = 32; + static constexpr uint32_t SPECIAL_KEYS_SHIFTED = 255; + static constexpr uint32_t N_TOTAL_KEYS = 256 + N_SPECIAL_KEYS; std::mutex mExternalGUIKeyMutex; std::vector mExternalGUIKeys; // Keys sent by external entity (usually console), may be ignored by backend. @@ -125,6 +128,7 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface int32_t DrawGLScene(); // Callback to draw the GL scene void HandleExternalGUISignals(); // Callback to handle signals from external GUIs bool HasExternalGUISignals() const; // Returns whether there are currently pending signals from external GUIs to be handled + bool processKeyLambda(uint32_t key); // Process key lambda callback void ResizeScene(int32_t width, int32_t height); // Callback when GL window is resized int32_t InitDisplay(bool initFailure = false); // Callback to initialize the GL Display (to be called in StartDisplay) void ExitDisplay(); // Callback to clean up the GL Display diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 8e44d761add7b..8ba89e6d5bbfe 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -14,6 +14,7 @@ #include "GPUDisplayFrontend.h" #include "GPUDisplay.h" +#include "frontend/GPUDisplayInfo.inc" #include "GPULogging.h" #include "utils/qconfig_rtti.h" #include "utils/qconfig_internal_helpers.h" @@ -42,7 +43,11 @@ struct GPUDisplayFrontend::configKey { configKeyCallback callback; }; -#define KEY_LAMBDA(character, obj, value, ...) std::tuple((uint8_t)(character), mFrontend->mConfigRTTI->getRTTIIndex(*this, &std::remove_cvref_t::value), configKeyCallback([this]() { [[maybe_unused]] auto& val = obj.value; __VA_ARGS__ })) +#ifdef GPUCA_DISPLAY_OPENGL_CORE +static constexpr bool displayOpenGLCore = true; // OpenGL compat profile not available +#else +static constexpr bool displayOpenGLCore = false; +#endif struct GPUDisplayFrontend::configRTTIInfo { static constexpr std::tuple configStructs{&GPUDisplay::mCfgL, &GPUDisplay::mCfgH, &GPUDisplay::mCfgR}; // Config structs with RTTI, as members of GPUDisplay @@ -50,9 +55,9 @@ struct GPUDisplayFrontend::configRTTIInfo { std::vector> rttiReverseId; // [configStructIndex][offset] -> index in rttiEntries, INVALID_INDEX if no member at that offset static constexpr uint32_t INVALID_INDEX = std::numeric_limits::max(); std::vector configStructNames; - std::array configKeys; + std::array configKeys; // By key code, special keys with SHIFT at code + SPECIAL_KEYS_SHIFTED std::vector keyNames; - std::vector> helpTextEntries; // Keys and text of each add_key_binding call + std::vector> helpTextEntries; // Keys and text of each add_key_binding call std::string helpText; static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames static constexpr char reservedKeysData[] = {'W', 'A', 'S', 'D', 'E', 'F', 'w', 'a', 's', 'd', 'e', 'f', '+', '-', '<', KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_PAGEUP, KEY_PAGEDOWN, KEY_SHIFT, KEY_ALT, KEY_RALT, KEY_CTRL, KEY_RCTRL}; @@ -93,8 +98,11 @@ struct GPUDisplayFrontend::configRTTIInfo { helpText += e.first + std::string(width - e.first.size(), ' ') + ": " + e.second + "\n"; } } - std::string getKeyName(uint8_t key) const // Name of a special key (KEY_*), the character otherwise, the code if neither + 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 { + if (key >= 256) { + return "SHIFT+" + getKeyName(key - SPECIAL_KEYS_SHIFTED); + } return key < keyNames.size() && !keyNames[key].empty() ? keyNames[key] : (key < 32 ? std::to_string(key) : std::string(1, (char)key)); } }; @@ -168,11 +176,11 @@ bool GPUDisplayFrontend::HandleExternalGUIKeys() template void GPUDisplayFrontend::add_key_binding(Args&&... args) { - constexpr size_t nKeys = (0 + ... + std::is_same_v, std::tuple>); + constexpr size_t nKeys = (0 + ... + std::is_same_v, std::tuple>); 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"); auto argTuple = std::forward_as_tuple(args...); std::string keys; - auto addKey = [&](std::tuple& key) { + auto addKey = [&](std::tuple& key) { const std::string name = mConfigRTTI->getKeyName(std::get<0>(key)); auto& entry = mConfigRTTI->configKeys[std::get<0>(key)]; if (entry.callback) { @@ -192,29 +200,31 @@ void GPUDisplayFrontend::add_key_binding(Args&&... args) void GPUDisplayFrontend::listConfig() { const auto& rtti = *mConfigRTTI; - auto keyOrder = [](uint8_t k) { return std::pair(k >= '0' && k <= '9' ? 0 : k >= 'a' && k <= 'z' ? 1 - : k >= 'A' && k <= 'Z' ? 2 - : k > 32 ? 3 - : 4, - k); }; // 0-9, a-z, A-Z, other characters, special keys - std::vector boundKeys, availableKeys; + auto keyOrder = [](uint16_t k) { return std::pair(k >= '0' && k <= '9' ? 0 : k >= 'a' && k <= 'z' ? 1 + : k >= 'A' && k <= 'Z' ? 2 + : k >= 256 ? 5 + : k > 32 ? 3 + : 4, + k); }; // 0-9, a-z, A-Z, other characters, special keys, special keys with SHIFT + std::vector boundKeys, availableKeys; for (uint32_t k = 1; k < rtti.configKeys.size(); k++) { - const bool possible = (k < 32 && !rtti.keyNames[k].empty()) || rtti.possibleKeys.find((char)k) != std::string_view::npos; + const uint32_t base = k >= 256 ? k - SPECIAL_KEYS_SHIFTED : k; // A shifted special key is possible if the special key is + const bool possible = (base < N_SPECIAL_KEYS && !rtti.keyNames[base].empty()) || (k < 256 && rtti.possibleKeys.find((char)k) != std::string_view::npos); if (rtti.configKeys[k].callback) { boundKeys.emplace_back(k); - } else if (possible && rtti.reservedKeys.find((char)k) == std::string_view::npos) { + } else if (possible && rtti.reservedKeys.find((char)base) == std::string_view::npos) { availableKeys.emplace_back(k); } } - std::sort(boundKeys.begin(), boundKeys.end(), [&](uint8_t a, uint8_t b) { return keyOrder(a) < keyOrder(b); }); - std::sort(availableKeys.begin(), availableKeys.end(), [&](uint8_t a, uint8_t b) { return keyOrder(a) < keyOrder(b); }); + std::sort(boundKeys.begin(), boundKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); }); + std::sort(availableKeys.begin(), availableKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); }); GPUInfo("Display config options with RTTI (struct, option, type, keys):"); uint32_t nOptionsWithKey = 0; for (uint32_t i = 0; i < rtti.rttiEntries.size(); i++) { const auto& e = rtti.rttiEntries[i]; std::string keys; - for (uint8_t k : boundKeys) { + for (uint16_t k : boundKeys) { if (rtti.configKeys[k].configRTTIIndex == i) { keys += (keys.empty() ? "[" : " [") + rtti.getKeyName(k) + "]"; } @@ -223,19 +233,21 @@ void GPUDisplayFrontend::listConfig() GPUInfo(" %-28s %-32s %-12s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), keys.c_str()); } GPUInfo("Bound keys (key, struct, option):"); - for (uint8_t k : boundKeys) { + for (uint16_t k : boundKeys) { const uint32_t index = rtti.configKeys[k].configRTTIIndex; const bool hasRTTI = index != configRTTIInfo::INVALID_INDEX; GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), hasRTTI ? rtti.rttiEntries[index].configStruct.c_str() : "(no config option)", hasRTTI ? rtti.rttiEntries[index].name.c_str() : ""); } std::string available; - for (uint8_t k : availableKeys) { + for (uint16_t k : availableKeys) { available += (available.empty() ? "[" : " [") + rtti.getKeyName(k) + "]"; } GPUInfo("Available keys: %s", available.c_str()); 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()); } +#include "GPUDisplayFrontendConfigKeys.inc" + void GPUDisplay::buildConfig() { auto& rtti = mFrontend->mConfigRTTI; @@ -252,4 +264,19 @@ void GPUDisplay::buildConfig() const auto& entry = rtti->rttiEntries[i]; rtti->rttiReverseId[entry.configStructIndex][entry.offset] = i; } + + buildConfigKeys(); + rtti->buildHelpText(); +} + +bool GPUDisplayFrontend::processKeyLambda(uint32_t key) +{ + if (key < N_SPECIAL_KEYS && mKeys[KEY_SHIFT]) { + key += SPECIAL_KEYS_SHIFTED; + } + if (key < mConfigRTTI->configKeys.size() && mConfigRTTI->configKeys[key].callback) { + mConfigRTTI->configKeys[key].callback(); + return true; + } + return false; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc new file mode 100644 index 0000000000000..6417d700b6470 --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc @@ -0,0 +1,414 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayFrontendConfigKeys.inc +/// \author David Rohr + +#ifndef GPUDISPLAYFRONTENDCONFIGKEYS_INC +#define GPUDISPLAYFRONTENDCONFIGKEYS_INC + +#define KEY_LAMBDA(character, obj, value, ...) std::tuple((uint16_t)(character), mFrontend->mConfigRTTI->getRTTIIndex(*this, &std::remove_cvref_t::value), configKeyCallback([this]() { [[maybe_unused]] auto& val = obj.value; __VA_ARGS__ })) + +void GPUDisplay::buildConfigKeys() +{ + mFrontend->add_key_binding( + KEY_LAMBDA('1', mCfgL, drawClusters, val ^= 1;), + KEY_LAMBDA('2', mCfgL, drawInitLinks, val ^= 1;), + KEY_LAMBDA('3', mCfgL, drawLinks, val ^= 1;), + KEY_LAMBDA('4', mCfgL, drawSeeds, val ^= 1;), + KEY_LAMBDA('5', mCfgL, drawTracklets, val ^= 1;), + KEY_LAMBDA('6', mCfgL, drawTracks, val ^= 1;), + KEY_LAMBDA('7', mCfgL, drawExtrapolatedTracks, val ^= 1;), + KEY_LAMBDA('8', mCfgL, drawFinal, val ^= 1;), + KEY_LAMBDA('N', mCfgL, colorClusters, val ^= 1; SetInfo("Color coding for seed / trrack attachmend %s", mCfgL.colorClusters ? "enabled" : "disabled");), // + "Enable display of clusters, preseeds, seeds, starthits, tracklets, tracks, extrapolated tracks, merged tracks / Show assigned clusters in colors", + "[1] ... [8] / [N]"); + + mFrontend->add_key_binding( + KEY_LAMBDA(mFrontend->KEY_ESCAPE, mCfgC, mDisplayControl, val.store(2); SetInfo("Exiting", 1);), + KEY_LAMBDA('n', mCfgC, mDisplayControl, val.store(1);), + KEY_LAMBDA('r', mCfgC, mResetScene, val.store(true); SetInfo("View reset", 1);), // + KEY_LAMBDA('q', mCfgC, dummy, { + if (mFrontend->isGUIRunning()) { + SetInfo("Stopping GUI", 1); + mFrontend->stopGUI(); + } else { + SetInfo("Starting GUI", 1); + mFrontend->startGUI(); + } + }), // + "Quit / Next event / Reset Display Settings / Start / Stop Qt GUI"); + + mFrontend->add_key_binding( + KEY_LAMBDA('l', mCfgL, drawSector, { + if (val >= (mCfgL.drawRelatedSectors ? (NSECTORS / 4 - 1) : (NSECTORS - 1))) { + val = -1; + SetInfo("Showing all sectors", 1); + } else { + val++; + SetInfo("Showing sector %d", val); + } + }), + KEY_LAMBDA('k', mCfgL, drawSector, { + if (val <= -1) { + val = mCfgL.drawRelatedSectors ? (NSECTORS / 4 - 1) : (NSECTORS - 1); + } else { + val--; + } + if (val == -1) { + SetInfo("Showing all sectors", 1); + } else { + SetInfo("Showing sector %d", val); + } + }), + KEY_LAMBDA('J', mCfgL, drawRelatedSectors, val ^= 1; SetInfo("Drawing of related sectors %s", mCfgL.drawRelatedSectors ? "enabled" : "disabled");), // + KEY_LAMBDA(';', mCfgH, xAdd, { + mCfgH.xAdd += 60; + mCfgH.zAdd += 60; + SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd); + }), + KEY_LAMBDA(':', mCfgH, xAdd, { + mCfgH.xAdd -= 60; + mCfgH.zAdd -= 60; + if (mCfgH.zAdd < 0 || mCfgH.xAdd < 0) { + mCfgH.zAdd = mCfgH.xAdd = 0; + } + SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd); + }), // + "Draw single sector (next / previous sector), draw related sectors (same plane in phi) / Show splitting of TPC in sectors by extruding volume, [:] resets"); + + mFrontend->add_key_binding( + KEY_LAMBDA('y', mCfgC, dummy, { + if (mAnimateVectors[0].size() > 1) { + if (mCfgC.mAnimateScreenshot) { + mAnimationExport++; + } + startAnimation(); + SetInfo("Starting Animation", 1); + } else { + SetInfo("Insufficient Animation points to start Animation", 1); + } + }), + KEY_LAMBDA('Y', mCfgC, dummy, setAnimationPoint(); SetInfo("Added Animation point (%d points, %6.2f seconds)", (int32_t)mAnimateVectors[0].size(), mAnimateVectors[0].back());), // + KEY_LAMBDA('\'', mCfgC, dummy, removeAnimationPoint(); SetInfo("Removed Animation point", 1);), // + KEY_LAMBDA('X', mCfgC, dummy, resetAnimation(); SetInfo("Reset Animation points", 1);), // + KEY_LAMBDA('M', mCfgL, animationMode, { + val++; + if (val == 7) { + val = 0; + } + if (val == 6) { + SetInfo("Animation mode %d - Centered on origin", val); + } else { + SetInfo("Animation mode %d - Position: %s, Direction: %s", val, (val & 2) ? "Spherical (spherical rotation)" : (val & 4) ? "Spherical (Euler angles)" : "Cartesian", (val & 1) ? "Euler angles" : "Quaternion"); + } + }), // + "Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)"); + + mFrontend->add_key_binding( + KEY_LAMBDA('#', mCfgL, invertColors, val ^= 1;), + KEY_LAMBDA('g', mCfgL, drawGrid, val ^= 1; SetInfo("Fast Cluster Search Grid %s", mCfgL.drawGrid ? "shown" : "hidden");), + KEY_LAMBDA('i', mCfgH, projectXY, val ^= 1; SetInfo("Projection onto xy plane %s", mCfgH.projectXY ? "enabled" : "disabled");), // + "Invert colors / Draw Grid / Project onto XY-plane"); + + mFrontend->add_key_binding( + KEY_LAMBDA('x', mCfgL, excludeClusters, val = (val + 1) % 3; SetInfo(mCfgL.excludeClusters ? "Clusters of selected category are excluded from display (%d)" : "Clusters are shown", mCfgL.excludeClusters);), + KEY_LAMBDA('.', mCfgH, hideRejectedTracks, val ^= 1; SetInfo("Rejected tracks are %s", mCfgH.hideRejectedTracks ? "hidden" : "shown");), // + "Exclude Clusters used in the tracking steps enabled for visualization ([1]-[8]) / Exclude rejected tracks"); + + mFrontend->add_key_binding( + KEY_LAMBDA('c', mCfgH, markClusters, { + if (val == 0) { + val = 1; + } else if (val >= 0x20) { + val = 0; + } else { + val <<= 1; + } + SetInfo("Cluster flag highlight mask set to %d (%s)", val, + val == 0 ? "off" : val == 1 ? "split pad" : val == 2 ? "split time" : val == 4 ? "edge" : val == 8 ? "singlePad" : val == 0x10 ? "reject distance" : "reject error"); + }), + KEY_LAMBDA('z', mCfgH, markFakeClusters, val ^= 1; SetInfo("Marking fake clusters: %s", mCfgH.markFakeClusters ? "on" : "off");), // + KEY_LAMBDA('B', mCfgH, markAdjacentClusters, { + val++; + if (val == 5) { + val = 7; + } + if (val == 9) { + val = 15; + } + if (val == 17) { + val = 31; + } + if (val == 34) { + val = 0; + } + if (val == 33) { + SetInfo("Marking protected clusters (%d)", val); + } else if (val == 32) { + SetInfo("Marking removable clusters (%d)", val); + } else { + SetInfo("Marking adjacent clusters (%d): rejected %s, tube %s, looper leg %s, low Pt %s, high incl %s", val, (val & 1) ? "yes" : " no", (val & 2) ? "yes" : " no", (val & 4) ? "yes" : " no", (val & 8) ? "yes" : " no", (val & 16) ? "yes" : " no"); + } + }), // + "Mark flagged clusters (splitPad = 0x1, splitTime = 0x2, edge = 0x4, singlePad = 0x8, rejectDistance = 0x10, rejectErr = 0x20 / Mark fake attached clusters / Mark clusters attached as adjacent"); + + mFrontend->add_key_binding( + KEY_LAMBDA('L', mCfgL, showCollision, { + if (val >= mNCollissions - 1) { + val = -1; + SetInfo("Showing all collisions", 1); + } else { + val++; + SetInfo("Showing collision %d / %d", val, mNCollissions); + } + }), + KEY_LAMBDA('K', mCfgL, showCollision, { + if (val <= -1) { + val = mNCollissions - 1; + } else { + val--; + } + if (val == -1) { + SetInfo("Showing all collisions", 1); + } else { + SetInfo("Showing collision %d / %d", val, mNCollissions); + } + }), + KEY_LAMBDA('C', mCfgL, colorCollisions, val ^= 1; SetInfo("Color coding of collisions %s", mCfgL.colorCollisions ? "enabled" : "disabled");), // + "Draw single collisions (next / previous) / Colorcode clusters of different collisions"); + + mFrontend->add_key_binding( + KEY_LAMBDA('v', mCfgH, hideRejectedClusters, val ^= 1; SetInfo("Rejected clusters are %s", mCfgH.hideRejectedClusters ? "hidden" : "shown");), // + KEY_LAMBDA('j', mCfgH, separateExtrapolatedTracks, { + if (val) { + mCfgH.splitCETracks ^= 1; + } + val ^= 1; + SetInfo("Seperated display of tracks propagated to adjacent sectors %s / of CE tracks %s", val ? "enabled" : "disabled", mCfgH.splitCETracks ? "enabled" : "disabled"); + }), + KEY_LAMBDA('u', mCfgH, trackFilter, val = (val + 1) % (mConfig.filterMacros.size() + 1); SetInfo("Track filter: %s", mCfgH.trackFilter == 0 ? "None" : mConfig.filterMacros[mCfgH.trackFilter - 1].c_str());), // + "Hide rejected clusters from tracks / Show tracks segments propagated to adjacent sector in different color / splt CE tracks / Cycle through track filter"); + + mFrontend->add_key_binding( + KEY_LAMBDA('E', mCfgL, propagateTracks, { + val += 1; + if (val == 4) { + val = 0; + } + const char* infoText[] = {"Hits connected", "Hits connected and propagated to vertex", "Reconstructed track propagated inwards and outwards", "Monte Carlo track"}; + SetInfo("Display of propagated tracks: %s", infoText[val]); + }), + KEY_LAMBDA('G', mCfgH, propagateLoopers, val ^= 1; SetInfo("Propagation of loopers %s", mCfgH.propagateLoopers ? "enabled" : "disabled");), // + "Extrapolate tracks / loopers"); + + mFrontend->add_key_binding( + KEY_LAMBDA('t', mCfgC, mRequestScreenshot, { + static int32_t nScreenshot = 1; + char fname[32]; + snprintf(fname, 32, "screenshot%d.bmp", nScreenshot++); + val = true; + mScreenshotFile = fname; + SetInfo("Taking screenshot (%s)", fname); + }), + KEY_LAMBDA('T', mCfgC, mAnimateScreenshot, val ^= 1; SetInfo("Writing screenshots from animation set to %d", mCfgC.mAnimateScreenshot);), // + KEY_LAMBDA('Z', mCfgR, screenshotScaleFactor, { + val += 1; + if (val == 5) { + val = 1; + } + SetInfo("Screenshot scaling factor set to %d", val); + }), // + "Take Screenshot / Record Animation to pictures / Change screenshot resolution (scaling factor)"); + + mFrontend->add_key_binding( + KEY_LAMBDA('S', mCfgL, smoothPoints, val ^= true; SetInfo("Smoothing of points %s", mCfgL.smoothPoints ? "enabled" : "disabled");), // + KEY_LAMBDA('A', mCfgL, smoothLines, val ^= true; SetInfo("Smoothing of lines %s", mCfgL.smoothLines ? "enabled" : "disabled");), // + KEY_LAMBDA('D', mCfgL, depthBuffer, { + val ^= true; + SetInfo("Depth buffer (z-buffer, %u bits) %s", mBackend->DepthBits(), val ? "enabled" : "disabled"); + mBackend->setDepthBuffer(); + }), // + "Enable or disable smoothing of points / smoothing of lines / depth buffer"); + + mFrontend->add_key_binding( + KEY_LAMBDA('W', mCfgR, drawQualityMSAA, { + val *= 2; + if (val < 2) { + val = 2; + } + if (val > 16 || val > mBackend->getMaxMSAA()) { + val = 0; + } + SetInfo("Multisampling anti-aliasing factor set to %d", val); + }), + KEY_LAMBDA('U', mCfgR, drawQualityDownsampleFSAA, { + val++; + if (val == 1) { + val = 2; + } + if (val == 5) { + val = 0; + } + SetInfo("Downsampling anti-aliasing factor set to %d", val); + }), + KEY_LAMBDA('V', mCfgR, drawQualityVSync, val ^= true; SetInfo("VSync: %s", mCfgR.drawQualityVSync ? "enabled" : "disabled");), // + KEY_LAMBDA('R', mCfgR, fpsLimit, { + val = (val + 1) % 3; + if (val == GPUDisplayBackend::FPS_LIMIT_RATE) { + SetInfo("FPS limit: %d FPS", mCfgR.fpsRate); + } else if (val == GPUDisplayBackend::FPS_LIMIT_VSYNC) { + char rate[32] = "unknown rate"; + if (mFrontend->getRefreshRate() > 0.f) { + snprintf(rate, sizeof(rate), "%.1f Hz", mFrontend->getRefreshRate()); + } + const int32_t mode = mBackend->fpsLimitMode(); + if (mode == GPUDisplayBackend::FPS_LIMIT_NONE) { + SetInfo("FPS limit: refresh rate (%s) - VSync disabled, no limit", rate); + } else if (mode == GPUDisplayBackend::FPS_LIMIT_RATE) { + SetInfo("FPS limit: refresh rate (%s) - not supported by the backend, fallback to %d FPS", rate, mCfgR.fpsRate); + } else { + SetInfo("FPS limit: refresh rate (%s)", rate); + } + } else { + SetInfo("FPS limit: none"); + } + }), // + "Toggle anti-aliasing (MSAA at raster level / change downsampling FSAA factor) / toggle VSync / FPS limit (none, refresh rate, fpsRate)"); + + mFrontend->add_key_binding( + KEY_LAMBDA('F', mCfgR, fullScreen, val ^= 1; SetInfo("Toggling full screen (%d)", (int32_t)mCfgR.fullScreen);), // + KEY_LAMBDA('_', mCfgR, maximized, val ^= 1; SetInfo("Toggling Maximized window (%d)", (int32_t)mCfgR.maximized);), // + KEY_LAMBDA('b', mCfgR, fov, { + if ((val += 5) > 175) { + val = 5; + } + SetInfo("Set FOV to %f", val); + }), + KEY_LAMBDA('m', mCfgR, rendererCompatSmoothing, { + if (displayOpenGLCore && backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { + SetInfo("OpenGL compat profile not available, using core profile", 1); + } else { + val ^= 1; + SetInfo("Using renderer path for %s profile", val ? "compat" : "core (shader)"); + } + }), + KEY_LAMBDA('I', mCfgR, useGLIndirectDraw, val ^= true; SetInfo("OpenGL Indirect Draw %s", mCfgR.useGLIndirectDraw ? "enabled" : "disabled");), // + "Switch mFullScreen / Maximized window / Change FOV (field of view) / Switch between core / compat renderer path (OpenGL profile, Vulkan smooth lines) / Enable / disable GL indirect draw"); + + mFrontend->add_key_binding( + KEY_LAMBDA('o', mCfgC, dummy, { + FILE* ftmp = fopen("glpos.tmp", "w+b"); + if (ftmp) { + int32_t retval = fwrite(&mViewMatrix, sizeof(mViewMatrix), 1, ftmp); + if (retval != 1) { + GPUError("Error writing position to file"); + } else { + GPUInfo("Position stored to file"); + } + fclose(ftmp); + } else { + GPUError("Error opening file"); + } + SetInfo("Camera position stored to file", 1); + }), + KEY_LAMBDA('p', mCfgC, dummy, { + FILE* ftmp = fopen("glpos.tmp", "rb"); + if (ftmp) { + int32_t retval = fread(&mViewMatrix, 1, sizeof(mViewMatrix), ftmp); + if (retval == sizeof(mViewMatrix)) { + GPUInfo("Position read from file"); + } else { + GPUError("Error reading position from file"); + } + fclose(ftmp); + } else { + GPUError("Error opening file"); + } + SetInfo("Camera position loaded from file", 1); + }), + KEY_LAMBDA('O', mCfgC, dummy, { + FILE* ftmp = fopen("glanimation.tmp", "w+b"); + if (ftmp) { + fwrite(&mCfgL, sizeof(mCfgL), 1, ftmp); + int32_t size = mAnimateVectors[0].size(); + fwrite(&size, sizeof(size), 1, ftmp); + for (int32_t i = 0; i < 9; i++) { + fwrite(mAnimateVectors[i].data(), sizeof(mAnimateVectors[i][0]), size, ftmp); + } + fwrite(mAnimateConfig.data(), sizeof(mAnimateConfig[0]), size, ftmp); + fclose(ftmp); + } else { + GPUError("Error opening file"); + } + SetInfo("Animation path stored to file %s", "glanimation.tmp"); + }), + KEY_LAMBDA('P', mCfgC, dummy, { + FILE* ftmp = fopen("glanimation.tmp", "rb"); + if (ftmp) { + int32_t retval = fread(&mCfgL, sizeof(mCfgL), 1, ftmp); + int32_t size; + retval += fread(&size, sizeof(size), 1, ftmp); + for (int32_t i = 0; i < 9; i++) { + mAnimateVectors[i].resize(size); + retval += fread(mAnimateVectors[i].data(), sizeof(mAnimateVectors[i][0]), size, ftmp); + } + mAnimateConfig.resize(size); + retval += fread(mAnimateConfig.data(), sizeof(mAnimateConfig[0]), size, ftmp); + (void)retval; // disable unused warning + fclose(ftmp); + updateConfig(); + } else { + GPUError("Error opening file"); + } + SetInfo("Animation path loaded from file %s", "glanimation.tmp"); + }), // + "Save / restore current camera position / Animation path"); + + mFrontend->add_key_binding( + KEY_LAMBDA(mFrontend->KEY_F1, mCfgL, drawTPC, val ^= 1; SetInfo("Showing TPC Clusters: %d", (int32_t)mCfgL.drawTPC);), + KEY_LAMBDA(mFrontend->KEY_F2, mCfgL, drawTRD, val ^= 1; SetInfo("Showing TRD Tracklets: %d", (int32_t)mCfgL.drawTRD);), + KEY_LAMBDA(mFrontend->KEY_F3, mCfgL, drawTOF, val ^= 1; SetInfo("Showing TOF Hits: %d", (int32_t)mCfgL.drawTOF);), + KEY_LAMBDA(mFrontend->KEY_F4, mCfgL, drawITS, val ^= 1; SetInfo("Showing ITS Clusters: %d", (int32_t)mCfgL.drawITS);), // + "Enable / disable drawing of TPC / TRD / TOF / ITS"); + + mFrontend->add_key_binding( + KEY_LAMBDA(mFrontend->KEY_F1 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTPCTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), + KEY_LAMBDA(mFrontend->KEY_F2 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTRDTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), + KEY_LAMBDA(mFrontend->KEY_F3 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTOFTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), + KEY_LAMBDA(mFrontend->KEY_F4 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawITSTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), + KEY_LAMBDA(mFrontend->KEY_F12 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTracksAndFilter, val ^= 1; SetInfo("Track filter: %s", mCfgH.drawTracksAndFilter ? "AND" : "OR");), // + "Enable / disable track detector filter / SHIFT F12: Switch track detector filter between AND and OR mode", + "[SHIFT] + [F1] to [F4] / [F12]"); + + mFrontend->add_key_binding( + KEY_LAMBDA('h', mCfgC, mPrintInfoTextAlways, { + if (val) { + val = false; + SetInfo("Showing help text disabled", 1); + } else if (mInfoHelpTimer.IsRunning()) { + val = true; + mInfoHelpTimer.Reset(); + SetInfo("Showing help text until disabled", 1); + } else { + PrintHelp(); + SetInfo("Showing help text", 1); + } + }), + KEY_LAMBDA('H', mCfgC, mPrintInfoText, { + val += 1; + val &= 3; + SetInfo("Info text display - console: %s, onscreen %s", (val & 2) ? "enabled" : "disabled", (val & 1) ? "enabled" : "disabled"); + }), // + "Print Help / Show info texts"); +} + +#endif // GPUDISPLAYFRONTENDCONFIGKEYS_INC diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx index 70a3083d23906..162a738318588 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx @@ -18,53 +18,17 @@ using namespace o2::gpu; const char* HelpText[] = { - "[ESC] Quit", - "[n] Next event", - "[r] Reset Display Settings", - "[l] / [k] / [J] Draw single sector (next / previous sector), draw related sectors (same plane in phi)", - "[;] / [:] Show splitting of TPC in sectors by extruding volume, [:] resets", - "[#] Invert colors", - "[y] / [Y] / ['] / [X] / [M] Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)", "[>] / [<] Toggle config interpolation during Animation / change Animation interval (via movement)", - "[g] Draw Grid", - "[i] Project onto XY-plane", - "[x] Exclude Clusters used in the tracking steps enabled for visualization ([1]-[8])", - "[.] Exclude rejected tracks", - "[c] Mark flagged clusters (splitPad = 0x1, splitTime = 0x2, edge = 0x4, singlePad = 0x8, rejectDistance = 0x10, rejectErr = 0x20", - "[z] Mark fake attached clusters", - "[B] Mark clusters attached as adjacent", - "[L] / [K] Draw single collisions (next / previous)", - "[C] Colorcode clusters of different collisions", - "[v] Hide rejected clusters from tracks", - "[j] Show tracks segments propagated to adjacent sector in different color / splt CE tracks", - "[u] Cycle through track filter", - "[E] / [G] Extrapolate tracks / loopers", - "[t] / [T] Take Screenshot / Record Animation to pictures", - "[Z] Change screenshot resolution (scaling factor)", - "[S] / [A] / [D] Enable or disable smoothing of points / smoothing of lines / depth buffer", - "[W] / [U] / [V] Toggle anti-aliasing (MSAA at raster level / change downsampling FSAA factor) / toggle VSync", - "[F] / [_] / [R] Switch mFullScreen / Maximized window / FPS limit (none, refresh rate, fpsRate)", - "[I] Enable / disable GL indirect draw", - "[o] / [p] / [O] / [P] Save / restore current camera position / Animation path", - "[h] Print Help", - "[H] Show info texts", - "[q] Start / Stop Qt GUI", "[w] / [s] / [a] / [d] Zoom / Strafe Left and Right", "[pgup] / [pgdn] Strafe up / down", "[e] / [f] Rotate left / right", "[+] / [-] Increase / decrease point size (Hold SHIFT for lines)", - "[b] Change FOV (field of view)", - "[m] Switch between core / compat renderer path (OpenGL profile, Vulkan smooth lines)", "[MOUSE 1] Look around", "[MOUSE 2] Strafe camera", "[MOUSE 1+2] Zoom / Rotate", "[SHIFT] Slow Zoom / Move / Rotate", "[ALT] / [CTRL] / [ENTER] Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", "[RCTRL] / [RALT] Rotate model instead of camera / rotate TPC around beamline", - "[1] ... [8] / [N] Enable display of clusters, preseeds, seeds, starthits, tracklets, tracks, extrapolated tracks, merged tracks / Show assigned clusters in colors", - "[F1] / [F2] / [F3] / [F4] Enable / disable drawing of TPC / TRD / TOF / ITS", - "[SHIFT] + [F1] to [F4] Enable / disable track detector filter", - "[SHIFT] + [F12] Switch track detector filter between AND and OR mode" // FREE: [SPACE] [Q] // Test setting: ^ }; @@ -82,16 +46,7 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) const GPUSettingsDisplayHeavy oldCfgH = mCfgH; const GPUSettingsDisplayLight oldCfgL = mCfgL; const GPUSettingsDisplayRenderer oldCfgR = mCfgR; - if (key == 'n') { - mCfgC.mDisplayControl.store(1); - SetInfo("Showing next event", 1); - } else if (key == mFrontend->KEY_ESCAPE) { - mCfgC.mDisplayControl.store(2); - SetInfo("Exiting", 1); - } else if (key == 'r') { - mCfgC.mResetScene.store(true); - SetInfo("View reset", 1); - } else if (key == mFrontend->KEY_ALT && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { + if (key == mFrontend->KEY_ALT && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { mCfgR.camLookOrigin ^= 1; mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * mCfgR.camYUp; SetInfo("Camera locked on origin: %s", mCfgR.camLookOrigin ? "enabled" : "disabled"); @@ -110,386 +65,10 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) SetInfo("Camera mode %d: %s", mCfgR.cameraMode, modeText[mCfgR.cameraMode]); } else if (key == mFrontend->KEY_ALT) { mFrontend->mKeys[mFrontend->KEY_CTRL] = false; // Release CTRL with alt, to avoid orienting along y automatically! - } else if (key == 'l') { - if (mCfgL.drawSector >= (mCfgL.drawRelatedSectors ? (NSECTORS / 4 - 1) : (NSECTORS - 1))) { - mCfgL.drawSector = -1; - SetInfo("Showing all sectors", 1); - } else { - mCfgL.drawSector++; - SetInfo("Showing sector %d", mCfgL.drawSector); - } - } else if (key == 'k') { - if (mCfgL.drawSector <= -1) { - mCfgL.drawSector = mCfgL.drawRelatedSectors ? (NSECTORS / 4 - 1) : (NSECTORS - 1); - } else { - mCfgL.drawSector--; - } - if (mCfgL.drawSector == -1) { - SetInfo("Showing all sectors", 1); - } else { - SetInfo("Showing sector %d", mCfgL.drawSector); - } - } else if (key == 'J') { - mCfgL.drawRelatedSectors ^= 1; - SetInfo("Drawing of related sectors %s", mCfgL.drawRelatedSectors ? "enabled" : "disabled"); - } else if (key == 'L') { - if (mCfgL.showCollision >= mNCollissions - 1) { - mCfgL.showCollision = -1; - SetInfo("Showing all collisions", 1); - } else { - mCfgL.showCollision++; - SetInfo("Showing collision %d / %d", mCfgL.showCollision, mNCollissions); - } - } else if (key == 'K') { - if (mCfgL.showCollision <= -1) { - mCfgL.showCollision = mNCollissions - 1; - } else { - mCfgL.showCollision--; - } - if (mCfgL.showCollision == -1) { - SetInfo("Showing all collisions", 1); - } else { - SetInfo("Showing collision %d / %d", mCfgL.showCollision, mNCollissions); - } - } else if (key == 'F') { - mCfgR.fullScreen ^= 1; - SetInfo("Toggling full screen (%d)", (int32_t)mCfgR.fullScreen); - } else if (key == '_') { - mCfgR.maximized ^= 1; - SetInfo("Toggling Maximized window (%d)", (int32_t)mCfgR.maximized); - } else if (key == 'R') { - mCfgR.fpsLimit = (mCfgR.fpsLimit + 1) % 3; - if (mCfgR.fpsLimit == GPUDisplayBackend::FPS_LIMIT_RATE) { - SetInfo("FPS limit: %d FPS", mCfgR.fpsRate); - } else if (mCfgR.fpsLimit == GPUDisplayBackend::FPS_LIMIT_VSYNC) { - char rate[32] = "unknown rate"; - if (mFrontend->getRefreshRate() > 0.f) { - snprintf(rate, sizeof(rate), "%.1f Hz", mFrontend->getRefreshRate()); - } - const int32_t mode = mBackend->fpsLimitMode(); - if (mode == GPUDisplayBackend::FPS_LIMIT_NONE) { - SetInfo("FPS limit: refresh rate (%s) - VSync disabled, no limit", rate); - } else if (mode == GPUDisplayBackend::FPS_LIMIT_RATE) { - SetInfo("FPS limit: refresh rate (%s) - not supported by the backend, fallback to %d FPS", rate, mCfgR.fpsRate); - } else { - SetInfo("FPS limit: refresh rate (%s)", rate); - } - } else { - SetInfo("FPS limit: none"); - } - } else if (key == 'H') { - mCfgC.mPrintInfoText += 1; - mCfgC.mPrintInfoText &= 3; - SetInfo("Info text display - console: %s, onscreen %s", (mCfgC.mPrintInfoText & 2) ? "enabled" : "disabled", (mCfgC.mPrintInfoText & 1) ? "enabled" : "disabled"); - } else if (key == 'j') { - if (mCfgH.separateExtrapolatedTracks) { - mCfgH.splitCETracks ^= 1; - } - mCfgH.separateExtrapolatedTracks ^= 1; - SetInfo("Seperated display of tracks propagated to adjacent sectors %s / of CE tracks %s", mCfgH.separateExtrapolatedTracks ? "enabled" : "disabled", mCfgH.splitCETracks ? "enabled" : "disabled"); - } else if (key == 'c') { - if (mCfgH.markClusters == 0) { - mCfgH.markClusters = 1; - } else if (mCfgH.markClusters >= 0x20) { - mCfgH.markClusters = 0; - } else { - mCfgH.markClusters <<= 1; - } - SetInfo("Cluster flag highlight mask set to %d (%s)", mCfgH.markClusters, - mCfgH.markClusters == 0 ? "off" : mCfgH.markClusters == 1 ? "split pad" : mCfgH.markClusters == 2 ? "split time" : mCfgH.markClusters == 4 ? "edge" : mCfgH.markClusters == 8 ? "singlePad" : mCfgH.markClusters == 0x10 ? "reject distance" : "reject error"); - } else if (key == 'z') { - mCfgH.markFakeClusters ^= 1; - SetInfo("Marking fake clusters: %s", mCfgH.markFakeClusters ? "on" : "off"); - } else if (key == 'b') { - if ((mCfgR.fov += 5) > 175) { - mCfgR.fov = 5; - } - SetInfo("Set FOV to %f", mCfgR.fov); - } else if (key == 'm') { -#ifdef GPUCA_DISPLAY_OPENGL_CORE - if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { - SetInfo("OpenGL compat profile not available, using core profile", 1); - } else -#endif - { - mCfgR.rendererCompatSmoothing ^= 1; - SetInfo("Using renderer path for %s profile", mCfgR.rendererCompatSmoothing ? "compat" : "core (shader)"); - } - } else if (key == 'B') { - mCfgH.markAdjacentClusters++; - if (mCfgH.markAdjacentClusters == 5) { - mCfgH.markAdjacentClusters = 7; - } - if (mCfgH.markAdjacentClusters == 9) { - mCfgH.markAdjacentClusters = 15; - } - if (mCfgH.markAdjacentClusters == 17) { - mCfgH.markAdjacentClusters = 31; - } - if (mCfgH.markAdjacentClusters == 34) { - mCfgH.markAdjacentClusters = 0; - } - if (mCfgH.markAdjacentClusters == 33) { - SetInfo("Marking protected clusters (%d)", mCfgH.markAdjacentClusters); - } else if (mCfgH.markAdjacentClusters == 32) { - SetInfo("Marking removable clusters (%d)", mCfgH.markAdjacentClusters); - } else { - SetInfo("Marking adjacent clusters (%d): rejected %s, tube %s, looper leg %s, low Pt %s, high incl %s", mCfgH.markAdjacentClusters, (mCfgH.markAdjacentClusters & 1) ? "yes" : " no", (mCfgH.markAdjacentClusters & 2) ? "yes" : " no", (mCfgH.markAdjacentClusters & 4) ? "yes" : " no", (mCfgH.markAdjacentClusters & 8) ? "yes" : " no", (mCfgH.markAdjacentClusters & 16) ? "yes" : " no"); - } - } else if (key == 'C') { - mCfgL.colorCollisions ^= 1; - SetInfo("Color coding of collisions %s", mCfgL.colorCollisions ? "enabled" : "disabled"); - } else if (key == 'N') { - mCfgL.colorClusters ^= 1; - SetInfo("Color coding for seed / trrack attachmend %s", mCfgL.colorClusters ? "enabled" : "disabled"); - } else if (key == 'E') { - mCfgL.propagateTracks += 1; - if (mCfgL.propagateTracks == 4) { - mCfgL.propagateTracks = 0; - } - const char* infoText[] = {"Hits connected", "Hits connected and propagated to vertex", "Reconstructed track propagated inwards and outwards", "Monte Carlo track"}; - SetInfo("Display of propagated tracks: %s", infoText[mCfgL.propagateTracks]); - } else if (key == 'G') { - mCfgH.propagateLoopers ^= 1; - SetInfo("Propagation of loopers %s", mCfgH.propagateLoopers ? "enabled" : "disabled"); - } else if (key == 'v') { - mCfgH.hideRejectedClusters ^= 1; - SetInfo("Rejected clusters are %s", mCfgH.hideRejectedClusters ? "hidden" : "shown"); - } else if (key == 'i') { - mCfgH.projectXY ^= 1; - SetInfo("Projection onto xy plane %s", mCfgH.projectXY ? "enabled" : "disabled"); - } else if (key == 'S') { - mCfgL.smoothPoints ^= true; - SetInfo("Smoothing of points %s", mCfgL.smoothPoints ? "enabled" : "disabled"); - } else if (key == 'A') { - mCfgL.smoothLines ^= true; - SetInfo("Smoothing of lines %s", mCfgL.smoothLines ? "enabled" : "disabled"); - } else if (key == 'D') { - mCfgL.depthBuffer ^= true; - SetInfo("Depth buffer (z-buffer, %u bits) %s", mBackend->DepthBits(), mCfgL.depthBuffer ? "enabled" : "disabled"); - mBackend->setDepthBuffer(); - } else if (key == 'W') { - mCfgR.drawQualityMSAA *= 2; - if (mCfgR.drawQualityMSAA < 2) { - mCfgR.drawQualityMSAA = 2; - } - if (mCfgR.drawQualityMSAA > 16 || mCfgR.drawQualityMSAA > mBackend->getMaxMSAA()) { - mCfgR.drawQualityMSAA = 0; - } - SetInfo("Multisampling anti-aliasing factor set to %d", mCfgR.drawQualityMSAA); - } else if (key == 'U') { - mCfgR.drawQualityDownsampleFSAA++; - if (mCfgR.drawQualityDownsampleFSAA == 1) { - mCfgR.drawQualityDownsampleFSAA = 2; - } - if (mCfgR.drawQualityDownsampleFSAA == 5) { - mCfgR.drawQualityDownsampleFSAA = 0; - } - SetInfo("Downsampling anti-aliasing factor set to %d", mCfgR.drawQualityDownsampleFSAA); - } else if (key == 'V') { - mCfgR.drawQualityVSync ^= true; - SetInfo("VSync: %s", mCfgR.drawQualityVSync ? "enabled" : "disabled"); - } else if (key == 'I') { - mCfgR.useGLIndirectDraw ^= true; - SetInfo("OpenGL Indirect Draw %s", mCfgR.useGLIndirectDraw ? "enabled" : "disabled"); - } else if (key == ';') { - mCfgH.xAdd += 60; - mCfgH.zAdd += 60; - SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd); - } else if (key == ':') { - mCfgH.xAdd -= 60; - mCfgH.zAdd -= 60; - if (mCfgH.zAdd < 0 || mCfgH.xAdd < 0) { - mCfgH.zAdd = mCfgH.xAdd = 0; - } - SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd); - } else if (key == '#') { - mCfgL.invertColors ^= 1; - } else if (key == 'g') { - mCfgL.drawGrid ^= 1; - SetInfo("Fast Cluster Search Grid %s", mCfgL.drawGrid ? "shown" : "hidden"); - } else if (key == 'x') { - mCfgL.excludeClusters = (mCfgL.excludeClusters + 1) % 3; - SetInfo(mCfgL.excludeClusters ? "Clusters of selected category are excluded from display (%d)" : "Clusters are shown", mCfgL.excludeClusters); - } else if (key == '.') { - mCfgH.hideRejectedTracks ^= 1; - SetInfo("Rejected tracks are %s", mCfgH.hideRejectedTracks ? "hidden" : "shown"); - } else if (key == '1') { - mCfgL.drawClusters ^= 1; - } else if (key == '2') { - mCfgL.drawInitLinks ^= 1; - } else if (key == '3') { - mCfgL.drawLinks ^= 1; - } else if (key == '4') { - mCfgL.drawSeeds ^= 1; - } else if (key == '5') { - mCfgL.drawTracklets ^= 1; - } else if (key == '6') { - mCfgL.drawTracks ^= 1; - } else if (key == '7') { - mCfgL.drawExtrapolatedTracks ^= 1; - } else if (key == '8') { - mCfgL.drawFinal ^= 1; - } else if (key == mFrontend->KEY_F1 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgH.drawTPCTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else if (key == mFrontend->KEY_F1 && !mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgL.drawTPC ^= 1; - SetInfo("Showing TPC Clusters: %d", (int32_t)mCfgL.drawTPC); - } else if (key == mFrontend->KEY_F2 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgH.drawTRDTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else if (key == mFrontend->KEY_F2 && !mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgL.drawTRD ^= 1; - SetInfo("Showing TRD Tracklets: %d", (int32_t)mCfgL.drawTRD); - } else if (key == mFrontend->KEY_F3 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgH.drawTOFTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else if (key == mFrontend->KEY_F3 && !mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgL.drawTOF ^= 1; - SetInfo("Showing TOF Hits: %d", (int32_t)mCfgL.drawTOF); - } else if (key == mFrontend->KEY_F4 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgH.drawITSTracks ^= 1; - SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); - } else if (key == mFrontend->KEY_F4 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgL.drawITS ^= 1; - SetInfo("Showing ITS Clusters: %d", (int32_t)mCfgL.drawITS); - } else if (key == mFrontend->KEY_F12 && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgH.drawTracksAndFilter ^= 1; - SetInfo("Track filter: %s", mCfgH.drawTracksAndFilter ? "AND" : "OR"); - } else if (key == 't') { - static int32_t nScreenshot = 1; - char fname[32]; - snprintf(fname, 32, "screenshot%d.bmp", nScreenshot++); - mCfgC.mRequestScreenshot = true; - mScreenshotFile = fname; - SetInfo("Taking screenshot (%s)", fname); - } else if (key == 'Z') { - mCfgR.screenshotScaleFactor += 1; - if (mCfgR.screenshotScaleFactor == 5) { - mCfgR.screenshotScaleFactor = 1; - } - SetInfo("Screenshot scaling factor set to %d", mCfgR.screenshotScaleFactor); - } else if (key == 'T') { - mCfgC.mAnimateScreenshot ^= 1; - SetInfo("Writing screenshots from animation set to %d", mCfgC.mAnimateScreenshot); - } else if (key == 'y') { - if (mAnimateVectors[0].size() > 1) { - if (mCfgC.mAnimateScreenshot) { - mAnimationExport++; - } - startAnimation(); - SetInfo("Starting Animation", 1); - } else { - SetInfo("Insufficient Animation points to start Animation", 1); - } } else if (key == '>') { mCfgC.mAnimationChangeConfig ^= 1; SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled"); - } else if (key == 'Y') { - setAnimationPoint(); - SetInfo("Added Animation point (%d points, %6.2f seconds)", (int32_t)mAnimateVectors[0].size(), mAnimateVectors[0].back()); - } else if (key == 'X') { - resetAnimation(); - SetInfo("Reset Animation points", 1); - } else if (key == '\'') { - removeAnimationPoint(); - SetInfo("Removed Animation point", 1); - } else if (key == 'M') { - mCfgL.animationMode++; - if (mCfgL.animationMode == 7) { - mCfgL.animationMode = 0; - } - if (mCfgL.animationMode == 6) { - SetInfo("Animation mode %d - Centered on origin", mCfgL.animationMode); - } else { - SetInfo("Animation mode %d - Position: %s, Direction: %s", mCfgL.animationMode, (mCfgL.animationMode & 2) ? "Spherical (spherical rotation)" : (mCfgL.animationMode & 4) ? "Spherical (Euler angles)" : "Cartesian", (mCfgL.animationMode & 1) ? "Euler angles" : "Quaternion"); - } - } else if (key == 'u') { - mCfgH.trackFilter = (mCfgH.trackFilter + 1) % (mConfig.filterMacros.size() + 1); - SetInfo("Track filter: %s", mCfgH.trackFilter == 0 ? "None" : mConfig.filterMacros[mCfgH.trackFilter - 1].c_str()); - } else if (key == 'o') { - FILE* ftmp = fopen("glpos.tmp", "w+b"); - if (ftmp) { - int32_t retval = fwrite(&mViewMatrix, sizeof(mViewMatrix), 1, ftmp); - if (retval != 1) { - GPUError("Error writing position to file"); - } else { - GPUInfo("Position stored to file"); - } - fclose(ftmp); - } else { - GPUError("Error opening file"); - } - SetInfo("Camera position stored to file", 1); - } else if (key == 'p') { - FILE* ftmp = fopen("glpos.tmp", "rb"); - if (ftmp) { - int32_t retval = fread(&mViewMatrix, 1, sizeof(mViewMatrix), ftmp); - if (retval == sizeof(mViewMatrix)) { - GPUInfo("Position read from file"); - } else { - GPUError("Error reading position from file"); - } - fclose(ftmp); - } else { - GPUError("Error opening file"); - } - SetInfo("Camera position loaded from file", 1); - } else if (key == 'O') { - FILE* ftmp = fopen("glanimation.tmp", "w+b"); - if (ftmp) { - fwrite(&mCfgL, sizeof(mCfgL), 1, ftmp); - int32_t size = mAnimateVectors[0].size(); - fwrite(&size, sizeof(size), 1, ftmp); - for (int32_t i = 0; i < 9; i++) { - fwrite(mAnimateVectors[i].data(), sizeof(mAnimateVectors[i][0]), size, ftmp); - } - fwrite(mAnimateConfig.data(), sizeof(mAnimateConfig[0]), size, ftmp); - fclose(ftmp); - } else { - GPUError("Error opening file"); - } - SetInfo("Animation path stored to file %s", "glanimation.tmp"); - } else if (key == 'P') { - FILE* ftmp = fopen("glanimation.tmp", "rb"); - if (ftmp) { - int32_t retval = fread(&mCfgL, sizeof(mCfgL), 1, ftmp); - int32_t size; - retval += fread(&size, sizeof(size), 1, ftmp); - for (int32_t i = 0; i < 9; i++) { - mAnimateVectors[i].resize(size); - retval += fread(mAnimateVectors[i].data(), sizeof(mAnimateVectors[i][0]), size, ftmp); - } - mAnimateConfig.resize(size); - retval += fread(mAnimateConfig.data(), sizeof(mAnimateConfig[0]), size, ftmp); - (void)retval; // disable unused warning - fclose(ftmp); - updateConfig(); - } else { - GPUError("Error opening file"); - } - SetInfo("Animation path loaded from file %s", "glanimation.tmp"); - } else if (key == 'h') { - if (mCfgC.mPrintInfoTextAlways) { - mCfgC.mPrintInfoTextAlways = false; - SetInfo("Showing help text disabled", 1); - } else if (mInfoHelpTimer.IsRunning()) { - mCfgC.mPrintInfoTextAlways = true; - mInfoHelpTimer.Reset(); - SetInfo("Showing help text until disabled", 1); - } else { - PrintHelp(); - SetInfo("Showing help text", 1); - } - } else if (key == 'q') { - if (mFrontend->isGUIRunning()) { - SetInfo("Stopping GUI", 1); - mFrontend->stopGUI(); - } else { - SetInfo("Starting GUI", 1); - mFrontend->startGUI(); - } + } else if (mFrontend->processKeyLambda(key)) { } /* else if (key == '^') From 65bd0ffde85ea1de052c56dd831a356e7c9299eb Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 27 Oct 2026 06:35:33 +0100 Subject: [PATCH 11/27] GPU Display Frontend: Add a map for key down actions, and assign keys to them via add_key_binding --- GPU/GPUTracking/display/GPUDisplay.cxx | 26 ++++---- .../display/frontend/GPUDisplayFrontend.h | 3 +- .../frontend/GPUDisplayFrontendConfig.cxx | 56 +++++++++++----- .../frontend/GPUDisplayFrontendConfigKeys.inc | 66 +++++++++++++++++++ .../frontend/GPUDisplayFrontendGlfw.cxx | 4 +- .../display/frontend/GPUDisplayFrontendGlfw.h | 2 +- .../display/frontend/GPUDisplayFrontendKeys.h | 28 ++++++++ .../display/frontend/GPUDisplayKeys.cxx | 29 +------- 8 files changed, 152 insertions(+), 62 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index e2102660e6a29..988e0a2509a84 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -243,10 +243,10 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla HighResTimer timer(mUpdateVertexLists && mChain->GetProcessingSettings().debugLevel >= 2); int32_t mMouseWheelTmp = mFrontend->mMouseWheel; mFrontend->mMouseWheel = 0; - bool lookOrigin = mCfgR.camLookOrigin ^ mFrontend->mKeys[mFrontend->KEY_ALT]; - bool yUp = mCfgR.camYUp ^ mFrontend->mKeys[mFrontend->KEY_CTRL] ^ lookOrigin; - bool rotateModel = mFrontend->mKeys[mFrontend->KEY_RCTRL] || mFrontend->mKeys[mFrontend->KEY_RALT]; - bool rotateModelTPC = mFrontend->mKeys[mFrontend->KEY_RALT]; + bool lookOrigin = mCfgR.camLookOrigin ^ mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIGIN]]; + bool yUp = mCfgR.camYUp ^ mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIENT_UP]] ^ lookOrigin; + bool rotateModel = mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_MODEL]] || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]]; + bool rotateModelTPC = mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]]; // Calculate rotation / translation scaling factors float scalefactor = mFrontend->mKeys[mFrontend->KEY_SHIFT] ? 0.2f : 1.0f; @@ -286,12 +286,12 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla mCfgC.mResetScene.store(false); } else { - float moveZ = scalefactor * ((float)mMouseWheelTmp / 150 + (float)(mFrontend->mKeys[(uint8_t)'W'] - mFrontend->mKeys[(uint8_t)'S']) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]) * 0.2f * mFPSScale); - float moveY = scalefactor * ((float)(mFrontend->mKeys[mFrontend->KEY_PAGEDOWN] - mFrontend->mKeys[mFrontend->KEY_PAGEUP]) * 0.2f * mFPSScale); - float moveX = scalefactor * ((float)(mFrontend->mKeys[(uint8_t)'A'] - mFrontend->mKeys[(uint8_t)'D']) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]) * 0.2f * mFPSScale); - float rotRoll = rotatescalefactor * mFPSScale * 2 * (mFrontend->mKeys[(uint8_t)'E'] - mFrontend->mKeys[(uint8_t)'F']) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]); - float rotYaw = rotatescalefactor * mFPSScale * 2 * (mFrontend->mKeys[mFrontend->KEY_RIGHT] - mFrontend->mKeys[mFrontend->KEY_LEFT]); - float rotPitch = rotatescalefactor * mFPSScale * 2 * (mFrontend->mKeys[mFrontend->KEY_DOWN] - mFrontend->mKeys[mFrontend->KEY_UP]); + float moveZ = scalefactor * ((float)mMouseWheelTmp / 150 + (float)(mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FORWARD]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_BACKWARD]]) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]) * 0.2f * mFPSScale); + float moveY = scalefactor * ((float)(mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_STRAFE_DOWN]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_STRAFE_UP]]) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]) * 0.2f * mFPSScale); + float moveX = scalefactor * ((float)(mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_STRAFE_LEFT]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_STRAFE_RIGHT]]) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]) * 0.2f * mFPSScale); + float rotRoll = rotatescalefactor * mFPSScale * 2 * (mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_LEFT]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_RIGHT]]) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]); + float rotYaw = rotatescalefactor * mFPSScale * 2 * (mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_YAW_RIGHT]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_YAW_LEFT]]) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]); + float rotPitch = rotatescalefactor * mFPSScale * 2 * (mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_PITCH_DOWN]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_PITCH_UP]]) * (!mFrontend->mKeys[mFrontend->KEY_SHIFT]); float mouseScale = 1920.f / std::max(1920.f, mBackend->mScreenWidth); if (mFrontend->mMouseDnR && mFrontend->mMouseDn) { @@ -305,7 +305,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla rotPitch += rotatescalefactor * mouseScale * ((float)mFrontend->mMouseMvY - (float)mFrontend->mMouseDnY); } - if (mFrontend->mKeys[(uint8_t)'<']) { + if (mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_CHANGE_ANIMATION_INTERVAL]]) { mAnimationDelay += moveX; if (mAnimationDelay < 0.05f) { mAnimationDelay = 0.05f; @@ -395,7 +395,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla // Graphichs Options float minSize = 0.4f / (mCfgR.drawQualityDownsampleFSAA > 1 ? mCfgR.drawQualityDownsampleFSAA : 1); - int32_t deltaLine = (mFrontend->mKeys[(uint8_t)'+'] - mFrontend->mKeys[(uint8_t)'-']) * mFrontend->mKeys[mFrontend->KEY_SHIFT]; + int32_t deltaLine = (mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_INCREASE_WIDTH]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_DECREASE_WIDTH]]) * mFrontend->mKeys[mFrontend->KEY_SHIFT]; mCfgL.lineWidth += (float)deltaLine * mFPSScale * 0.02f * mCfgL.lineWidth; if (mCfgL.lineWidth < minSize) { mCfgL.lineWidth = minSize; @@ -405,7 +405,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla mUpdateDrawCommands = true; } minSize *= 2; - int32_t deltaPoint = (mFrontend->mKeys[(uint8_t)'+'] - mFrontend->mKeys[(uint8_t)'-']) * !mFrontend->mKeys[mFrontend->KEY_SHIFT]; + int32_t deltaPoint = (mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_INCREASE_WIDTH]] - mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_DECREASE_WIDTH]]) * !mFrontend->mKeys[mFrontend->KEY_SHIFT]; mCfgL.pointSize += (float)deltaPoint * mFPSScale * 0.02f * mCfgL.pointSize; if (mCfgL.pointSize < minSize) { mCfgL.pointSize = minSize; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 54a5c3e332f64..b75035155d66b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -90,8 +90,8 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface static constexpr const char* DISPLAY_WINDOW_NAME = "GPU CA Standalone Event Display"; // Title of event display set by backend // Constant key codes for special mKeys (to unify different treatment in X11 / Windows / GLUT / etc.) #define GPUDisplayFrontendAddKey(code, key) static constexpr int32_t KEY_##key = code +#define GPUDisplayFrontendKeyDownMap(code, key) static constexpr int32_t KEY_MAP_##key = code #include "GPUDisplayFrontendKeys.h" -#undef GPUDisplayFrontendAddKey static constexpr uint32_t N_SPECIAL_KEYS = 32; static constexpr uint32_t SPECIAL_KEYS_SHIFTED = 255; static constexpr uint32_t N_TOTAL_KEYS = 256 + N_SPECIAL_KEYS; @@ -99,6 +99,7 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface std::mutex mExternalGUIKeyMutex; std::vector mExternalGUIKeys; // Keys sent by external entity (usually console), may be ignored by backend. std::mutex mExitStateMutex; + std::array mKeyDownMap = {}; // Keyboard / Mouse actions bool mMouseDn = false; // Mouse button down diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 8ba89e6d5bbfe..90949c85bca29 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -39,7 +39,7 @@ struct GPUDisplayFrontend::configRTTIExt : public qConfig::qConfigRTTIentry { using configKeyCallback = std::function; struct GPUDisplayFrontend::configKey { - uint32_t configRTTIIndex; + uint32_t configRTTIIndex = std::numeric_limits::max(); // Index in rttiEntries, KEY_MAP_INDEX + id for a key down mapping, INVALID_INDEX otherwise configKeyCallback callback; }; @@ -54,14 +54,15 @@ struct GPUDisplayFrontend::configRTTIInfo { std::vector rttiEntries; std::vector> rttiReverseId; // [configStructIndex][offset] -> index in rttiEntries, INVALID_INDEX if no member at that offset static constexpr uint32_t INVALID_INDEX = std::numeric_limits::max(); + static constexpr uint32_t KEY_MAP_INDEX = 0x80000000; // configRTTIIndex of keys mapped to mKeyDownMap[configRTTIIndex - KEY_MAP_INDEX] + static bool isKeyMap(uint32_t index) { return index >= KEY_MAP_INDEX && index != INVALID_INDEX; } std::vector configStructNames; std::array configKeys; // By key code, special keys with SHIFT at code + SPECIAL_KEYS_SHIFTED std::vector keyNames; + std::vector keyMapNames; // Names of the key down mapping ids (KEY_MAP_*) std::vector> helpTextEntries; // Keys and text of each add_key_binding call std::string helpText; static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames - static constexpr char reservedKeysData[] = {'W', 'A', 'S', 'D', 'E', 'F', 'w', 'a', 's', 'd', 'e', 'f', '+', '-', '<', KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_PAGEUP, KEY_PAGEDOWN, KEY_SHIFT, KEY_ALT, KEY_RALT, KEY_CTRL, KEY_RCTRL}; - static constexpr std::string_view reservedKeys{reservedKeysData, sizeof(reservedKeysData)}; // Keys queried continuously as held keys (camera control, modifiers), not available for bindings template static constexpr uint32_t configStructIndex() // Index of struct T in configStructs, compile-time error if not present @@ -176,18 +177,28 @@ bool GPUDisplayFrontend::HandleExternalGUIKeys() template void GPUDisplayFrontend::add_key_binding(Args&&... args) { - constexpr size_t nKeys = (0 + ... + std::is_same_v, std::tuple>); + constexpr size_t nKeys = (0 + ... + (std::is_same_v, std::tuple> || std::is_same_v, std::tuple>)); 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"); auto argTuple = std::forward_as_tuple(args...); std::string keys; - auto addKey = [&](std::tuple& key) { - const std::string name = mConfigRTTI->getKeyName(std::get<0>(key)); - auto& entry = mConfigRTTI->configKeys[std::get<0>(key)]; - if (entry.callback) { + auto addKey = [&](auto& key) { + const uint16_t character = std::get<0>(key); + const std::string name = mConfigRTTI->getKeyName(character); + auto& entry = mConfigRTTI->configKeys[character]; + if (entry.callback || entry.configRTTIIndex != configRTTIInfo::INVALID_INDEX) { throw std::runtime_error("Key [" + name + "] assigned twice in display frontend"); } - entry.configRTTIIndex = std::get<1>(key); - entry.callback = std::move(std::get<2>(key)); + if constexpr (std::is_same_v, std::tuple>) { + const uint8_t id = std::get<1>(key); + if (mKeyDownMap[id]) { + throw std::runtime_error("Key down mapping " + mConfigRTTI->keyMapNames[id] + " assigned twice in display frontend"); + } + mKeyDownMap[id] = character >= 'a' && character <= 'z' ? character + 'A' - 'a' : character; // mKeys stores letters in upper case + entry.configRTTIIndex = configRTTIInfo::KEY_MAP_INDEX + id; + } else { + entry.configRTTIIndex = std::get<1>(key); + entry.callback = std::move(std::get<2>(key)); + } keys += (keys.empty() ? "[" : " [") + name + "]"; }; [&](std::index_sequence) { (addKey(std::get(argTuple)), ...); }(std::make_index_sequence()); @@ -208,11 +219,11 @@ void GPUDisplayFrontend::listConfig() k); }; // 0-9, a-z, A-Z, other characters, special keys, special keys with SHIFT std::vector boundKeys, availableKeys; for (uint32_t k = 1; k < rtti.configKeys.size(); k++) { - const uint32_t base = k >= 256 ? k - SPECIAL_KEYS_SHIFTED : k; // A shifted special key is possible if the special key is - const bool possible = (base < N_SPECIAL_KEYS && !rtti.keyNames[base].empty()) || (k < 256 && rtti.possibleKeys.find((char)k) != std::string_view::npos); - if (rtti.configKeys[k].callback) { + 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 + 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)); + if (rtti.configKeys[k].callback || rtti.isKeyMap(rtti.configKeys[k].configRTTIIndex)) { boundKeys.emplace_back(k); - } else if (possible && rtti.reservedKeys.find((char)base) == std::string_view::npos) { + } else if (possible) { availableKeys.emplace_back(k); } } @@ -233,10 +244,20 @@ void GPUDisplayFrontend::listConfig() GPUInfo(" %-28s %-32s %-12s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), keys.c_str()); } GPUInfo("Bound keys (key, struct, option):"); + std::vector mappedKeys(KEY_MAP_MAX); for (uint16_t k : boundKeys) { const uint32_t index = rtti.configKeys[k].configRTTIIndex; - const bool hasRTTI = index != configRTTIInfo::INVALID_INDEX; - GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), hasRTTI ? rtti.rttiEntries[index].configStruct.c_str() : "(no config option)", hasRTTI ? rtti.rttiEntries[index].name.c_str() : ""); + if (rtti.isKeyMap(index)) { + mappedKeys[index - configRTTIInfo::KEY_MAP_INDEX] = "[" + rtti.getKeyName(k) + "]"; + GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), "(key down map)", rtti.keyMapNames[index - configRTTIInfo::KEY_MAP_INDEX].c_str()); + } else { + const bool hasRTTI = index != configRTTIInfo::INVALID_INDEX; + GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), hasRTTI ? rtti.rttiEntries[index].configStruct.c_str() : "(no config option)", hasRTTI ? rtti.rttiEntries[index].name.c_str() : ""); + } + } + GPUInfo("Key down mappings (id, name, key):"); + for (uint32_t i = 0; i < KEY_MAP_MAX; i++) { + GPUInfo(" %2u %-28s %s", i, rtti.keyMapNames[i].c_str(), mappedKeys[i].c_str()); } std::string available; for (uint16_t k : availableKeys) { @@ -254,9 +275,10 @@ void GPUDisplay::buildConfig() rtti = {new GPUDisplayFrontend::configRTTIInfo, [](GPUDisplayFrontend::configRTTIInfo* p) { delete p; }}; rtti->keyNames.resize(rtti->configKeys.size()); + rtti->keyMapNames.resize(GPUDisplayFrontend::KEY_MAP_MAX + 1); // Including MAX #define GPUDisplayFrontendAddKey(code, key) rtti->keyNames[code] = #key +#define GPUDisplayFrontendKeyDownMap(code, key) rtti->keyMapNames[code] = #key #include "frontend/GPUDisplayFrontendKeys.h" -#undef GPUDisplayFrontendAddKey std::tie(rtti->rttiEntries, rtti->configStructNames) = std::apply([this](auto... s) { return qConfigGenRTTI*s)>...>(); }, rtti->configStructs); std::apply([&](auto... s) { (rtti->rttiReverseId.emplace_back(sizeof(this->*s), GPUDisplayFrontend::configRTTIInfo::INVALID_INDEX), ...); }, rtti->configStructs); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc index 6417d700b6470..069c00fc696dc 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc @@ -15,6 +15,7 @@ #ifndef GPUDISPLAYFRONTENDCONFIGKEYS_INC #define GPUDISPLAYFRONTENDCONFIGKEYS_INC +#define KEY_DOWN_MAP(character, id) std::tuple((uint8_t)(character), (uint8_t)GPUDisplayFrontend::KEY_MAP_##id) // Key queried as held key: GPUDisplay uses mKeys[mKeyDownMap[KEY_MAP_...]] #define KEY_LAMBDA(character, obj, value, ...) std::tuple((uint16_t)(character), mFrontend->mConfigRTTI->getRTTIIndex(*this, &std::remove_cvref_t::value), configKeyCallback([this]() { [[maybe_unused]] auto& val = obj.value; __VA_ARGS__ })) void GPUDisplay::buildConfigKeys() @@ -409,6 +410,71 @@ void GPUDisplay::buildConfigKeys() SetInfo("Info text display - console: %s, onscreen %s", (val & 2) ? "enabled" : "disabled", (val & 1) ? "enabled" : "disabled"); }), // "Print Help / Show info texts"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP('w', FORWARD), + KEY_DOWN_MAP('s', BACKWARD), + KEY_DOWN_MAP('a', STRAFE_LEFT), + KEY_DOWN_MAP('d', STRAFE_RIGHT), // + "Zoom / Strafe Left and Right"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_PAGEUP, STRAFE_UP), + KEY_DOWN_MAP(mFrontend->KEY_PAGEDOWN, STRAFE_DOWN), // + "Strafe up / down"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP('e', ROTATE_LEFT), + KEY_DOWN_MAP('f', ROTATE_RIGHT), // + "Rotate left / right"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_LEFT, ROTATE_YAW_LEFT), + KEY_DOWN_MAP(mFrontend->KEY_RIGHT, ROTATE_YAW_RIGHT), + KEY_DOWN_MAP(mFrontend->KEY_UP, ROTATE_PITCH_UP), + KEY_DOWN_MAP(mFrontend->KEY_DOWN, ROTATE_PITCH_DOWN), // + "Turn camera left / right / up / down"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP('+', INCREASE_WIDTH), + KEY_DOWN_MAP('-', DECREASE_WIDTH), // + "Increase / decrease point size (Hold SHIFT for lines)"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_ALT, FOCUS_ORIGIN), + KEY_DOWN_MAP(mFrontend->KEY_CTRL, FOCUS_ORIENT_UP), // + KEY_LAMBDA(mFrontend->KEY_ALT + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camLookOrigin, { + val ^= 1; + mCfgR.cameraMode = val + 2 * mCfgR.camYUp; + SetInfo("Camera locked on origin: %s", val ? "enabled" : "disabled"); + }), + KEY_LAMBDA(mFrontend->KEY_CTRL + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camYUp, { + val ^= 1; + mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * val; + SetInfo("Camera locked on y-axis facing upwards: %s", val ? "enabled" : "disabled"); + }), + KEY_LAMBDA(mFrontend->KEY_ENTER, mCfgR, cameraMode, { + val++; + if (val == 4) { + val = 0; + } + mCfgR.camLookOrigin = val & 1; + mCfgR.camYUp = val & 2; + const char* modeText[] = {"Descent (free movement)", "Focus locked on origin (y-axis forced upwards)", "Spectator (y-axis forced upwards)", "Focus locked on origin (with free rotation)"}; + SetInfo("Camera mode %d: %s", val, modeText[val]); + }), + "Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", // + "[ALT] / [CTRL] / [ENTER]"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_RCTRL, ROTATE_MODEL), + KEY_DOWN_MAP(mFrontend->KEY_RALT, ROTATE_AROUND_Z), // + "Rotate model instead of camera / rotate TPC around beamline"); + + mFrontend->add_key_binding( + KEY_LAMBDA('>', mCfgC, mAnimationChangeConfig, val ^= 1; SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled");), + KEY_DOWN_MAP('<', CHANGE_ANIMATION_INTERVAL), // + "Toggle config interpolation during Animation / change Animation interval (via movement)"); } #endif // GPUDISPLAYFRONTENDCONFIGKEYS_INC diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx index 176d2b916040e..336161216b351 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx @@ -171,7 +171,7 @@ void GPUDisplayFrontendGlfw::key_callback(GLFWwindow* window, int32_t key, int32 if (action == GLFW_PRESS) { me->mLastKeyDown = handleKey; } else if (action == GLFW_RELEASE) { - keyPress = (uint8_t)me->mKeyDownMap[handleKey]; + keyPress = (uint8_t)me->mGLFWKeyDownMap[handleKey]; me->mKeys[keyPress] = false; } } @@ -185,7 +185,7 @@ void GPUDisplayFrontendGlfw::char_callback(GLFWwindow* window, uint32_t codepoin if (keyPress >= 'a' && keyPress <= 'z') { keyPress += 'A' - 'a'; } - me->mKeyDownMap[me->mLastKeyDown] = keyPress; + me->mGLFWKeyDownMap[me->mLastKeyDown] = keyPress; me->mKeys[keyPress] = true; me->HandleKey(codepoint); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h index f079c87852e13..58de6b0990aa7 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h @@ -67,7 +67,7 @@ class GPUDisplayFrontendGlfw : public GPUDisplayFrontend int32_t mWindowY = 0; int32_t mWindowWidth = INIT_WIDTH; int32_t mWindowHeight = INIT_HEIGHT; - uint8_t mKeyDownMap[256] = {0}; + uint8_t mGLFWKeyDownMap[256] = {0}; uint8_t mLastKeyDown = 0; }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h index a22eaeb5fbcd8..4fbe81054c1d2 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendKeys.h @@ -44,3 +44,31 @@ GPUDisplayFrontendAddKey(27, ESCAPE); // fixed at 27 GPUDisplayFrontendAddKey(28, HOME); GPUDisplayFrontendAddKey(29, END); GPUDisplayFrontendAddKey(32, SPACE); // fixed at 32 + +#ifndef GPUDisplayFrontendKeyDownMap +#define GPUDisplayFrontendKeyDownMap(...) +#endif + +GPUDisplayFrontendKeyDownMap(0, FORWARD); +GPUDisplayFrontendKeyDownMap(1, BACKWARD); +GPUDisplayFrontendKeyDownMap(2, STRAFE_LEFT); +GPUDisplayFrontendKeyDownMap(3, STRAFE_RIGHT); +GPUDisplayFrontendKeyDownMap(4, STRAFE_UP); +GPUDisplayFrontendKeyDownMap(5, STRAFE_DOWN); +GPUDisplayFrontendKeyDownMap(6, ROTATE_LEFT); +GPUDisplayFrontendKeyDownMap(7, ROTATE_RIGHT); +GPUDisplayFrontendKeyDownMap(8, INCREASE_WIDTH); +GPUDisplayFrontendKeyDownMap(9, DECREASE_WIDTH); +GPUDisplayFrontendKeyDownMap(10, FOCUS_ORIGIN); +GPUDisplayFrontendKeyDownMap(11, FOCUS_ORIENT_UP); +GPUDisplayFrontendKeyDownMap(12, ROTATE_MODEL); +GPUDisplayFrontendKeyDownMap(13, ROTATE_AROUND_Z); +GPUDisplayFrontendKeyDownMap(14, CHANGE_ANIMATION_INTERVAL); +GPUDisplayFrontendKeyDownMap(15, ROTATE_YAW_LEFT); +GPUDisplayFrontendKeyDownMap(16, ROTATE_YAW_RIGHT); +GPUDisplayFrontendKeyDownMap(17, ROTATE_PITCH_UP); +GPUDisplayFrontendKeyDownMap(18, ROTATE_PITCH_DOWN); +GPUDisplayFrontendKeyDownMap(19, MAX); // Must be the maximum value! + +#undef GPUDisplayFrontendAddKey +#undef GPUDisplayFrontendKeyDownMap diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx index 162a738318588..0ea230c0625a9 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx @@ -18,17 +18,10 @@ using namespace o2::gpu; const char* HelpText[] = { - "[>] / [<] Toggle config interpolation during Animation / change Animation interval (via movement)", - "[w] / [s] / [a] / [d] Zoom / Strafe Left and Right", - "[pgup] / [pgdn] Strafe up / down", - "[e] / [f] Rotate left / right", - "[+] / [-] Increase / decrease point size (Hold SHIFT for lines)", "[MOUSE 1] Look around", "[MOUSE 2] Strafe camera", "[MOUSE 1+2] Zoom / Rotate", "[SHIFT] Slow Zoom / Move / Rotate", - "[ALT] / [CTRL] / [ENTER] Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", - "[RCTRL] / [RALT] Rotate model instead of camera / rotate TPC around beamline", // FREE: [SPACE] [Q] // Test setting: ^ }; @@ -46,28 +39,8 @@ void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) const GPUSettingsDisplayHeavy oldCfgH = mCfgH; const GPUSettingsDisplayLight oldCfgL = mCfgL; const GPUSettingsDisplayRenderer oldCfgR = mCfgR; - if (key == mFrontend->KEY_ALT && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgR.camLookOrigin ^= 1; - mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * mCfgR.camYUp; - SetInfo("Camera locked on origin: %s", mCfgR.camLookOrigin ? "enabled" : "disabled"); - } else if (key == mFrontend->KEY_CTRL && mFrontend->mKeys[mFrontend->KEY_SHIFT]) { - mCfgR.camYUp ^= 1; - mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * mCfgR.camYUp; - SetInfo("Camera locked on y-axis facing upwards: %s", mCfgR.camYUp ? "enabled" : "disabled"); - } else if (key == mFrontend->KEY_ENTER) { - mCfgR.cameraMode++; - if (mCfgR.cameraMode == 4) { - mCfgR.cameraMode = 0; - } - mCfgR.camLookOrigin = mCfgR.cameraMode & 1; - mCfgR.camYUp = mCfgR.cameraMode & 2; - const char* modeText[] = {"Descent (free movement)", "Focus locked on origin (y-axis forced upwards)", "Spectator (y-axis forced upwards)", "Focus locked on origin (with free rotation)"}; - SetInfo("Camera mode %d: %s", mCfgR.cameraMode, modeText[mCfgR.cameraMode]); - } else if (key == mFrontend->KEY_ALT) { + if (key == mFrontend->KEY_ALT) { mFrontend->mKeys[mFrontend->KEY_CTRL] = false; // Release CTRL with alt, to avoid orienting along y automatically! - } else if (key == '>') { - mCfgC.mAnimationChangeConfig ^= 1; - SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled"); } else if (mFrontend->processKeyLambda(key)) { } /* From 2afcc388854c9aec1156719f2d4b15433198e67f Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 27 Oct 2026 07:10:15 +0100 Subject: [PATCH 12/27] GPU Display: Switch help-text to auto-generated help text from key bindings --- GPU/GPUTracking/display/CMakeLists.txt | 2 +- GPU/GPUTracking/display/GPUDisplay.h | 2 - .../display/frontend/GPUDisplayFrontend.h | 2 + .../frontend/GPUDisplayFrontendConfig.cxx | 29 +++++++-- .../frontend/GPUDisplayFrontendConfigKeys.inc | 2 + .../display/frontend/GPUDisplayHelp.cxx | 34 ++++++++++ .../display/frontend/GPUDisplayKeys.cxx | 64 ------------------- 7 files changed, 64 insertions(+), 71 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx delete mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index 5f770ddf7b914..592483293303c 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -113,7 +113,7 @@ set(SRCS_NO_H helpers/GPUDisplayLoader.cxx helpers/GPUDisplayQuaternion.cxx helpers/GPUDisplayInterpolation.cxx helpers/GPUDisplayROOT.cxx - frontend/GPUDisplayKeys.cxx + frontend/GPUDisplayHelp.cxx frontend/GPUDisplayFrontendConfig.cxx) set(HDRS_INSTALL helpers/GPUDisplayMagneticFieldConstants.h frontend/GPUDisplayFrontendKeys.h) diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 8a48d5a3fbf3b..92d8d194e8eef 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -259,8 +259,6 @@ class GPUDisplay : public GPUDisplayInterface std::array mDrawColor = {1.f, 1.f, 1.f, 1.f}; - int32_t mTestSetting = 0; - float mAngleRollOrigin = -1e9; float mMaxClusterZ = -1; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index b75035155d66b..3b26f22eea4c3 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace o2::gpu @@ -148,6 +149,7 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface template 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 void listConfig(); + const std::vector& getHelpText() const; // Help text of all key bindings, one line per binding }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 90949c85bca29..3fd6dd81d99b6 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -61,7 +61,7 @@ struct GPUDisplayFrontend::configRTTIInfo { std::vector keyNames; std::vector keyMapNames; // Names of the key down mapping ids (KEY_MAP_*) std::vector> helpTextEntries; // Keys and text of each add_key_binding call - std::string helpText; + std::vector helpText; static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames template @@ -96,7 +96,7 @@ struct GPUDisplayFrontend::configRTTIInfo { } helpText.clear(); for (const auto& e : helpTextEntries) { - helpText += e.first + std::string(width - e.first.size(), ' ') + ": " + e.second + "\n"; + helpText.emplace_back(e.first + std::string(width - e.first.size(), ' ') + ": " + e.second); } } 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 @@ -208,6 +208,8 @@ void GPUDisplayFrontend::add_key_binding(Args&&... args) mConfigRTTI->helpTextEntries.emplace_back(std::move(keys), std::get(argTuple)); } +const std::vector& GPUDisplayFrontend::getHelpText() const { return mConfigRTTI->helpText; } + void GPUDisplayFrontend::listConfig() { const auto& rtti = *mConfigRTTI; @@ -245,14 +247,18 @@ void GPUDisplayFrontend::listConfig() } GPUInfo("Bound keys (key, struct, option):"); std::vector mappedKeys(KEY_MAP_MAX); + int32_t keyWidth = 0; + for (uint16_t k : boundKeys) { + keyWidth = std::max(keyWidth, rtti.getKeyName(k).size() + 2); + } for (uint16_t k : boundKeys) { const uint32_t index = rtti.configKeys[k].configRTTIIndex; if (rtti.isKeyMap(index)) { mappedKeys[index - configRTTIInfo::KEY_MAP_INDEX] = "[" + rtti.getKeyName(k) + "]"; - GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), "(key down map)", rtti.keyMapNames[index - configRTTIInfo::KEY_MAP_INDEX].c_str()); + GPUInfo(" %-*s %-28s %s", keyWidth, ("[" + rtti.getKeyName(k) + "]").c_str(), "(key down map)", rtti.keyMapNames[index - configRTTIInfo::KEY_MAP_INDEX].c_str()); } else { const bool hasRTTI = index != configRTTIInfo::INVALID_INDEX; - GPUInfo(" %-10s %-28s %s", ("[" + rtti.getKeyName(k) + "]").c_str(), hasRTTI ? rtti.rttiEntries[index].configStruct.c_str() : "(no config option)", hasRTTI ? rtti.rttiEntries[index].name.c_str() : ""); + 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() : ""); } } GPUInfo("Key down mappings (id, name, key):"); @@ -302,3 +308,18 @@ bool GPUDisplayFrontend::processKeyLambda(uint32_t key) } return false; } + +void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) +{ + const GPUSettingsDisplayHeavy oldCfgH = mCfgH; + const GPUSettingsDisplayLight oldCfgL = mCfgL; + const GPUSettingsDisplayRenderer oldCfgR = mCfgR; + if (key == mFrontend->KEY_ALT) { + mFrontend->mKeys[mFrontend->KEY_CTRL] = false; // Release CTRL with alt, to avoid orienting along y automatically! + } else if (mFrontend->processKeyLambda(key)) { + } + + if (!doNotHandleConfigChanges) { + checkAndApplyConfigChanges(oldCfgH, oldCfgL, oldCfgR); + } +} diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc index 069c00fc696dc..86925d5c8c3bf 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc @@ -475,6 +475,8 @@ void GPUDisplay::buildConfigKeys() KEY_LAMBDA('>', mCfgC, mAnimationChangeConfig, val ^= 1; SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled");), KEY_DOWN_MAP('<', CHANGE_ANIMATION_INTERVAL), // "Toggle config interpolation during Animation / change Animation interval (via movement)"); + + mFrontend->mConfigRTTI->helpTextEntries.emplace_back("[MOUSE 1] / [MOUSE 2] / [MOUSE 1+2] / [SHIFT]", "Look around / Strafe camera / Zoom / Rotate / Slow Zoom / Move / Rotate"); } #endif // GPUDISPLAYFRONTENDCONFIGKEYS_INC diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx new file mode 100644 index 0000000000000..275ac348653d8 --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayHelp.cxx +/// \author David Rohr + +#include "GPUDisplay.h" +#include "GPULogging.h" + +using namespace o2::gpu; + +void GPUDisplay::PrintHelp() +{ + mInfoHelpTimer.ResetStart(); + for (const auto& line : mFrontend->getHelpText()) { + GPUInfo("%s", line.c_str()); + } +} + +void GPUDisplay::PrintGLHelpText(float colorValue) +{ + const auto& lines = mFrontend->getHelpText(); + for (uint32_t i = 0; i < lines.size(); i++) { + OpenGLPrint(lines[i].c_str(), 40.f, 35 + std::max(20, mDrawTextFontSize + 4) * (1 + i), colorValue, colorValue, colorValue, mInfoHelpTimer.GetCurrentElapsedTime() >= 5 ? (6 - mInfoHelpTimer.GetCurrentElapsedTime()) : 1, false); + } +} diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx deleted file mode 100644 index 0ea230c0625a9..0000000000000 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file GPUDisplayKeys.cxx -/// \author David Rohr - -#include "GPUDisplay.h" -#include "frontend/GPUDisplayInfo.inc" - -using namespace o2::gpu; - -const char* HelpText[] = { - "[MOUSE 1] Look around", - "[MOUSE 2] Strafe camera", - "[MOUSE 1+2] Zoom / Rotate", - "[SHIFT] Slow Zoom / Move / Rotate", - // FREE: [SPACE] [Q] - // Test setting: ^ -}; - -void GPUDisplay::PrintHelp() -{ - mInfoHelpTimer.ResetStart(); - for (uint32_t i = 0; i < sizeof(HelpText) / sizeof(HelpText[0]); i++) { - GPUInfo("%s", HelpText[i]); - } -} - -void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) -{ - const GPUSettingsDisplayHeavy oldCfgH = mCfgH; - const GPUSettingsDisplayLight oldCfgL = mCfgL; - const GPUSettingsDisplayRenderer oldCfgR = mCfgR; - if (key == mFrontend->KEY_ALT) { - mFrontend->mKeys[mFrontend->KEY_CTRL] = false; // Release CTRL with alt, to avoid orienting along y automatically! - } else if (mFrontend->processKeyLambda(key)) { - } - /* - else if (key == '^') - { - mTestSetting++; - SetInfo("Debug test variable set to %d", mTestSetting); - } - */ - - if (!doNotHandleConfigChanges) { - checkAndApplyConfigChanges(oldCfgH, oldCfgL, oldCfgR); - } -} - -void GPUDisplay::PrintGLHelpText(float colorValue) -{ - for (uint32_t i = 0; i < sizeof(HelpText) / sizeof(HelpText[0]); i++) { - OpenGLPrint(HelpText[i], 40.f, 35 + std::max(20, mDrawTextFontSize + 4) * (1 + i), colorValue, colorValue, colorValue, mInfoHelpTimer.GetCurrentElapsedTime() >= 5 ? (6 - mInfoHelpTimer.GetCurrentElapsedTime()) : 1, false); - } -} From b8e35cf201fe827571a50c1f1640a05a01d40609 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 27 Oct 2026 07:38:04 +0100 Subject: [PATCH 13/27] GPU Display: Info messages per configuration value, not per key, so they can be reused for the GUI --- GPU/GPUTracking/display/GPUDisplay.h | 1 + .../frontend/GPUDisplayFrontendConfig.cxx | 51 +++++++ .../frontend/GPUDisplayFrontendConfigInfo.inc | 129 ++++++++++++++++++ .../frontend/GPUDisplayFrontendConfigKeys.inc | 121 ++++------------ 4 files changed, 209 insertions(+), 93 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 92d8d194e8eef..896fef6f3893b 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -101,6 +101,7 @@ class GPUDisplay : public GPUDisplayInterface private: void buildConfig(); void buildConfigKeys(); + void buildConfigInfo(); static constexpr int32_t NSECTORS = o2::tpc::constants::MAXSECTOR; static constexpr float GL_SCALE_FACTOR = (1.f / 100.f); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 3fd6dd81d99b6..23070ec813baf 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -52,6 +52,7 @@ static constexpr bool displayOpenGLCore = false; struct GPUDisplayFrontend::configRTTIInfo { static constexpr std::tuple configStructs{&GPUDisplay::mCfgL, &GPUDisplay::mCfgH, &GPUDisplay::mCfgR}; // Config structs with RTTI, as members of GPUDisplay std::vector rttiEntries; + std::vector configInfos; // SetInfo callback for an option, indexed like rttiEntries, called after the callback of a key bound to it std::vector> rttiReverseId; // [configStructIndex][offset] -> index in rttiEntries, INVALID_INDEX if no member at that offset static constexpr uint32_t INVALID_INDEX = std::numeric_limits::max(); static constexpr uint32_t KEY_MAP_INDEX = 0x80000000; // configRTTIIndex of keys mapped to mKeyDownMap[configRTTIIndex - KEY_MAP_INDEX] @@ -78,6 +79,13 @@ struct GPUDisplayFrontend::configRTTIInfo { { return display.*std::get()>(configStructs); } + static char* configStructPtr(GPUDisplay& display, uint32_t index) // Runtime version of configStruct by index + { + char* retVal = nullptr; + uint32_t i = 0; + std::apply([&](auto... s) { ((retVal = i++ == index ? reinterpret_cast(&(display.*s)) : retVal), ...); }, configStructs); + return retVal; + } template uint32_t getRTTIIndex(GPUDisplay& display, M T::* member) // RTTI index of a direct member of a config struct { @@ -274,6 +282,7 @@ void GPUDisplayFrontend::listConfig() } #include "GPUDisplayFrontendConfigKeys.inc" +#include "GPUDisplayFrontendConfigInfo.inc" void GPUDisplay::buildConfig() { @@ -294,6 +303,45 @@ void GPUDisplay::buildConfig() } buildConfigKeys(); + + rtti->configInfos.resize(rtti->rttiEntries.size()); + auto messageInfo = [this](const std::string& message, const T* value) { // Info from the qConfig message, value printed with print_type as in qconfig.cxx + return configKeyCallback([this, message, value]() { + char buffer[256]; + const int32_t n = snprintf(buffer, sizeof(buffer), message.c_str(), qConfig::print_type(*value).c_str()); + if (n < 0 || n >= (int32_t)sizeof(buffer)) { + GPUError("Info message too long: %s", message.c_str()); + return; + } + SetInfo("%s", buffer); + }); + }; + for (uint32_t i = 0; i < rtti->rttiEntries.size(); i++) { + const auto& e = rtti->rttiEntries[i]; + if (e.message.empty() || e.isVector || e.arraySize) { + continue; + } + const char* ptr = GPUDisplayFrontend::configRTTIInfo::configStructPtr(*this, e.configStructIndex) + e.offset; + configKeyCallback info; + if (e.type == "bool") { + info = messageInfo(e.message, (const bool*)ptr); + } else if (e.type == "float") { + info = messageInfo(e.message, (const float*)ptr); + } else if (e.type == "int8_t") { + info = messageInfo(e.message, (const int8_t*)ptr); + } else if (e.type == "uint8_t") { + info = messageInfo(e.message, (const uint8_t*)ptr); + } else if (e.type == "int32_t" || e.type == "int") { + info = messageInfo(e.message, (const int32_t*)ptr); + } else if (e.type == "uint32_t") { + info = messageInfo(e.message, (const uint32_t*)ptr); + } else { + GPUError("No info message support for type %s of %s", e.type.c_str(), e.name.c_str()); + continue; + } + rtti->configInfos[i] = std::move(info); + } + buildConfigInfo(); rtti->buildHelpText(); } @@ -304,6 +352,9 @@ bool GPUDisplayFrontend::processKeyLambda(uint32_t key) } if (key < mConfigRTTI->configKeys.size() && mConfigRTTI->configKeys[key].callback) { mConfigRTTI->configKeys[key].callback(); + if (const uint32_t index = mConfigRTTI->configKeys[key].configRTTIIndex; index < mConfigRTTI->configInfos.size() && mConfigRTTI->configInfos[index]) { + mConfigRTTI->configInfos[index](); + } return true; } return false; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc new file mode 100644 index 0000000000000..2644f4e87949b --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc @@ -0,0 +1,129 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayFrontendConfigInfo.inc +/// \author David Rohr + +#ifndef GPUDISPLAYFRONTENDCONFIGINFO_INC +#define GPUDISPLAYFRONTENDCONFIGINFO_INC + +#define INFO_LAMBDA(obj, value, ...) std::pair(mFrontend->mConfigRTTI->getRTTIIndex(*this, &std::remove_cvref_t::value), configKeyCallback([=, this]() { [[maybe_unused]] auto& val = obj.value; __VA_ARGS__ })) // Info printed after the callback of a key bound to the option, same val as in KEY_LAMBDA, local helpers captured by copy + +void GPUDisplay::buildConfigInfo() +{ + auto addInfo = [this](std::pair&& info) { mFrontend->mConfigRTTI->configInfos[info.first] = std::move(info.second); }; + addInfo(INFO_LAMBDA(mCfgL, colorClusters, SetInfo("Color coding for seed / trrack attachmend %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgL, drawSector, { + if (val == -1) { + SetInfo("Showing all sectors", 1); + } else { + SetInfo("Showing sector %d", val); + } + })); + addInfo(INFO_LAMBDA(mCfgL, drawRelatedSectors, SetInfo("Drawing of related sectors %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgH, xAdd, SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd);)); + addInfo(INFO_LAMBDA(mCfgL, animationMode, { + if (val == 6) { + SetInfo("Animation mode %d - Centered on origin", val); + } else { + SetInfo("Animation mode %d - Position: %s, Direction: %s", val, (val & 2) ? "Spherical (spherical rotation)" : ((val & 4) ? "Spherical (Euler angles)" : "Cartesian"), (val & 1) ? "Euler angles" : "Quaternion"); + } + })); + addInfo(INFO_LAMBDA(mCfgL, drawGrid, SetInfo("Fast Cluster Search Grid %s", val ? "shown" : "hidden");)); + addInfo(INFO_LAMBDA(mCfgH, projectXY, SetInfo("Projection onto xy plane %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgL, excludeClusters, SetInfo(val ? "Clusters of selected category are excluded from display (%d)" : "Clusters are shown", val);)); + addInfo(INFO_LAMBDA(mCfgH, hideRejectedTracks, SetInfo("Rejected tracks are %s", val ? "hidden" : "shown");)); + addInfo(INFO_LAMBDA(mCfgH, markClusters, { + SetInfo("Cluster flag highlight mask set to %d (%s)", val, val == 0 ? "off" : (val == 1 ? "split pad" : (val == 2 ? "split time" : (val == 4 ? "edge" : (val == 8 ? "singlePad" : (val == 0x10 ? "reject distance" : "reject error")))))); + })); + addInfo(INFO_LAMBDA(mCfgH, markFakeClusters, SetInfo("Marking fake clusters: %s", val ? "on" : "off");)); + addInfo(INFO_LAMBDA(mCfgH, markAdjacentClusters, { + if (val == 33) { + SetInfo("Marking protected clusters (%d)", val); + } else if (val == 32) { + SetInfo("Marking removable clusters (%d)", val); + } else { + SetInfo("Marking adjacent clusters (%d): rejected %s, tube %s, looper leg %s, low Pt %s, high incl %s", val, (val & 1) ? "yes" : " no", (val & 2) ? "yes" : " no", (val & 4) ? "yes" : " no", (val & 8) ? "yes" : " no", (val & 16) ? "yes" : " no"); + } + })); + addInfo(INFO_LAMBDA(mCfgL, showCollision, { + if (val == -1) { + SetInfo("Showing all collisions", 1); + } else { + SetInfo("Showing collision %d / %d", val, mNCollissions); + } + })); + addInfo(INFO_LAMBDA(mCfgL, colorCollisions, SetInfo("Color coding of collisions %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgH, hideRejectedClusters, SetInfo("Rejected clusters are %s", val ? "hidden" : "shown");)); + addInfo(INFO_LAMBDA(mCfgH, separateExtrapolatedTracks, SetInfo("Seperated display of tracks propagated to adjacent sectors %s / of CE tracks %s", val ? "enabled" : "disabled", mCfgH.splitCETracks ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgH, trackFilter, SetInfo("Track filter: %s", val == 0 ? "None" : mConfig.filterMacros[val - 1].c_str());)); + addInfo(INFO_LAMBDA(mCfgL, propagateTracks, { + const char* infoText[] = {"Hits connected", "Hits connected and propagated to vertex", "Reconstructed track propagated inwards and outwards", "Monte Carlo track"}; + SetInfo("Display of propagated tracks: %s", infoText[val]); + })); + addInfo(INFO_LAMBDA(mCfgH, propagateLoopers, SetInfo("Propagation of loopers %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgR, screenshotScaleFactor, SetInfo("Screenshot scaling factor set to %d", val);)); + addInfo(INFO_LAMBDA(mCfgL, smoothPoints, SetInfo("Smoothing of points %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgL, smoothLines, SetInfo("Smoothing of lines %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgL, depthBuffer, SetInfo("Depth buffer (z-buffer, %u bits) %s", mBackend->DepthBits(), val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgR, drawQualityMSAA, SetInfo("Multisampling anti-aliasing factor set to %d", val);)); + addInfo(INFO_LAMBDA(mCfgR, drawQualityDownsampleFSAA, SetInfo("Downsampling anti-aliasing factor set to %d", val);)); + addInfo(INFO_LAMBDA(mCfgR, drawQualityVSync, SetInfo("VSync: %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgR, fpsLimit, { + if (val == GPUDisplayBackend::FPS_LIMIT_RATE) { + SetInfo("FPS limit: %d FPS", mCfgR.fpsRate); + } else if (val == GPUDisplayBackend::FPS_LIMIT_VSYNC) { + char rate[32] = "unknown rate"; + if (mFrontend->getRefreshRate() > 0.f) { + snprintf(rate, sizeof(rate), "%.1f Hz", mFrontend->getRefreshRate()); + } + const int32_t mode = mBackend->fpsLimitMode(); + if (mode == GPUDisplayBackend::FPS_LIMIT_NONE) { + SetInfo("FPS limit: refresh rate (%s) - VSync disabled, no limit", rate); + } else if (mode == GPUDisplayBackend::FPS_LIMIT_RATE) { + SetInfo("FPS limit: refresh rate (%s) - not supported by the backend, fallback to %d FPS", rate, mCfgR.fpsRate); + } else { + SetInfo("FPS limit: refresh rate (%s)", rate); + } + } else { + SetInfo("FPS limit: none"); + } + })); + addInfo(INFO_LAMBDA(mCfgR, fullScreen, SetInfo("Toggling full screen (%d)", (int32_t)val);)); + addInfo(INFO_LAMBDA(mCfgR, maximized, SetInfo("Toggling Maximized window (%d)", (int32_t)val);)); + addInfo(INFO_LAMBDA(mCfgR, fov, SetInfo("Set FOV to %f", val);)); + addInfo(INFO_LAMBDA(mCfgR, rendererCompatSmoothing, { + if (displayOpenGLCore && backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { + SetInfo("OpenGL compat profile not available, using core profile", 1); + } else { + SetInfo("Using renderer path for %s profile", val ? "compat" : "core (shader)"); + } + })); + addInfo(INFO_LAMBDA(mCfgR, useGLIndirectDraw, SetInfo("OpenGL Indirect Draw %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgL, drawTPC, SetInfo("Showing TPC Clusters: %d", (int32_t)val);)); + addInfo(INFO_LAMBDA(mCfgL, drawTRD, SetInfo("Showing TRD Tracklets: %d", (int32_t)val);)); + addInfo(INFO_LAMBDA(mCfgL, drawTOF, SetInfo("Showing TOF Hits: %d", (int32_t)val);)); + addInfo(INFO_LAMBDA(mCfgL, drawITS, SetInfo("Showing ITS Clusters: %d", (int32_t)val);)); + auto trackFilterMaskInfo = [this]() { SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks); }; + addInfo(INFO_LAMBDA(mCfgH, drawTPCTracks, trackFilterMaskInfo();)); + addInfo(INFO_LAMBDA(mCfgH, drawTRDTracks, trackFilterMaskInfo();)); + addInfo(INFO_LAMBDA(mCfgH, drawTOFTracks, trackFilterMaskInfo();)); + addInfo(INFO_LAMBDA(mCfgH, drawITSTracks, trackFilterMaskInfo();)); + addInfo(INFO_LAMBDA(mCfgH, drawTracksAndFilter, SetInfo("Track filter: %s", val ? "AND" : "OR");)); + addInfo(INFO_LAMBDA(mCfgR, camLookOrigin, SetInfo("Camera locked on origin: %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgR, camYUp, SetInfo("Camera locked on y-axis facing upwards: %s", val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgR, cameraMode, { + const char* modeText[] = {"Descent (free movement)", "Focus locked on origin (y-axis forced upwards)", "Spectator (y-axis forced upwards)", "Focus locked on origin (with free rotation)"}; + SetInfo("Camera mode %d: %s", val, modeText[val]); + })); +} + +#endif // GPUDISPLAYFRONTENDCONFIGINFO_INC diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc index 86925d5c8c3bf..62dbe363d38d5 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc @@ -29,7 +29,7 @@ void GPUDisplay::buildConfigKeys() KEY_LAMBDA('6', mCfgL, drawTracks, val ^= 1;), KEY_LAMBDA('7', mCfgL, drawExtrapolatedTracks, val ^= 1;), KEY_LAMBDA('8', mCfgL, drawFinal, val ^= 1;), - KEY_LAMBDA('N', mCfgL, colorClusters, val ^= 1; SetInfo("Color coding for seed / trrack attachmend %s", mCfgL.colorClusters ? "enabled" : "disabled");), // + KEY_LAMBDA('N', mCfgL, colorClusters, val ^= 1;), // "Enable display of clusters, preseeds, seeds, starthits, tracklets, tracks, extrapolated tracks, merged tracks / Show assigned clusters in colors", "[1] ... [8] / [N]"); @@ -52,10 +52,8 @@ void GPUDisplay::buildConfigKeys() KEY_LAMBDA('l', mCfgL, drawSector, { if (val >= (mCfgL.drawRelatedSectors ? (NSECTORS / 4 - 1) : (NSECTORS - 1))) { val = -1; - SetInfo("Showing all sectors", 1); } else { val++; - SetInfo("Showing sector %d", val); } }), KEY_LAMBDA('k', mCfgL, drawSector, { @@ -64,17 +62,11 @@ void GPUDisplay::buildConfigKeys() } else { val--; } - if (val == -1) { - SetInfo("Showing all sectors", 1); - } else { - SetInfo("Showing sector %d", val); - } }), - KEY_LAMBDA('J', mCfgL, drawRelatedSectors, val ^= 1; SetInfo("Drawing of related sectors %s", mCfgL.drawRelatedSectors ? "enabled" : "disabled");), // + KEY_LAMBDA('J', mCfgL, drawRelatedSectors, val ^= 1;), // KEY_LAMBDA(';', mCfgH, xAdd, { mCfgH.xAdd += 60; mCfgH.zAdd += 60; - SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd); }), KEY_LAMBDA(':', mCfgH, xAdd, { mCfgH.xAdd -= 60; @@ -82,7 +74,6 @@ void GPUDisplay::buildConfigKeys() if (mCfgH.zAdd < 0 || mCfgH.xAdd < 0) { mCfgH.zAdd = mCfgH.xAdd = 0; } - SetInfo("TPC sector separation: %f %f", mCfgH.xAdd, mCfgH.zAdd); }), // "Draw single sector (next / previous sector), draw related sectors (same plane in phi) / Show splitting of TPC in sectors by extruding volume, [:] resets"); @@ -106,23 +97,18 @@ void GPUDisplay::buildConfigKeys() if (val == 7) { val = 0; } - if (val == 6) { - SetInfo("Animation mode %d - Centered on origin", val); - } else { - SetInfo("Animation mode %d - Position: %s, Direction: %s", val, (val & 2) ? "Spherical (spherical rotation)" : (val & 4) ? "Spherical (Euler angles)" : "Cartesian", (val & 1) ? "Euler angles" : "Quaternion"); - } }), // "Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)"); mFrontend->add_key_binding( KEY_LAMBDA('#', mCfgL, invertColors, val ^= 1;), - KEY_LAMBDA('g', mCfgL, drawGrid, val ^= 1; SetInfo("Fast Cluster Search Grid %s", mCfgL.drawGrid ? "shown" : "hidden");), - KEY_LAMBDA('i', mCfgH, projectXY, val ^= 1; SetInfo("Projection onto xy plane %s", mCfgH.projectXY ? "enabled" : "disabled");), // + KEY_LAMBDA('g', mCfgL, drawGrid, val ^= 1;), + KEY_LAMBDA('i', mCfgH, projectXY, val ^= 1;), // "Invert colors / Draw Grid / Project onto XY-plane"); mFrontend->add_key_binding( - KEY_LAMBDA('x', mCfgL, excludeClusters, val = (val + 1) % 3; SetInfo(mCfgL.excludeClusters ? "Clusters of selected category are excluded from display (%d)" : "Clusters are shown", mCfgL.excludeClusters);), - KEY_LAMBDA('.', mCfgH, hideRejectedTracks, val ^= 1; SetInfo("Rejected tracks are %s", mCfgH.hideRejectedTracks ? "hidden" : "shown");), // + KEY_LAMBDA('x', mCfgL, excludeClusters, val = (val + 1) % 3;), + KEY_LAMBDA('.', mCfgH, hideRejectedTracks, val ^= 1;), // "Exclude Clusters used in the tracking steps enabled for visualization ([1]-[8]) / Exclude rejected tracks"); mFrontend->add_key_binding( @@ -134,10 +120,8 @@ void GPUDisplay::buildConfigKeys() } else { val <<= 1; } - SetInfo("Cluster flag highlight mask set to %d (%s)", val, - val == 0 ? "off" : val == 1 ? "split pad" : val == 2 ? "split time" : val == 4 ? "edge" : val == 8 ? "singlePad" : val == 0x10 ? "reject distance" : "reject error"); }), - KEY_LAMBDA('z', mCfgH, markFakeClusters, val ^= 1; SetInfo("Marking fake clusters: %s", mCfgH.markFakeClusters ? "on" : "off");), // + KEY_LAMBDA('z', mCfgH, markFakeClusters, val ^= 1;), // KEY_LAMBDA('B', mCfgH, markAdjacentClusters, { val++; if (val == 5) { @@ -152,13 +136,6 @@ void GPUDisplay::buildConfigKeys() if (val == 34) { val = 0; } - if (val == 33) { - SetInfo("Marking protected clusters (%d)", val); - } else if (val == 32) { - SetInfo("Marking removable clusters (%d)", val); - } else { - SetInfo("Marking adjacent clusters (%d): rejected %s, tube %s, looper leg %s, low Pt %s, high incl %s", val, (val & 1) ? "yes" : " no", (val & 2) ? "yes" : " no", (val & 4) ? "yes" : " no", (val & 8) ? "yes" : " no", (val & 16) ? "yes" : " no"); - } }), // "Mark flagged clusters (splitPad = 0x1, splitTime = 0x2, edge = 0x4, singlePad = 0x8, rejectDistance = 0x10, rejectErr = 0x20 / Mark fake attached clusters / Mark clusters attached as adjacent"); @@ -166,10 +143,8 @@ void GPUDisplay::buildConfigKeys() KEY_LAMBDA('L', mCfgL, showCollision, { if (val >= mNCollissions - 1) { val = -1; - SetInfo("Showing all collisions", 1); } else { val++; - SetInfo("Showing collision %d / %d", val, mNCollissions); } }), KEY_LAMBDA('K', mCfgL, showCollision, { @@ -178,25 +153,19 @@ void GPUDisplay::buildConfigKeys() } else { val--; } - if (val == -1) { - SetInfo("Showing all collisions", 1); - } else { - SetInfo("Showing collision %d / %d", val, mNCollissions); - } }), - KEY_LAMBDA('C', mCfgL, colorCollisions, val ^= 1; SetInfo("Color coding of collisions %s", mCfgL.colorCollisions ? "enabled" : "disabled");), // + KEY_LAMBDA('C', mCfgL, colorCollisions, val ^= 1;), // "Draw single collisions (next / previous) / Colorcode clusters of different collisions"); mFrontend->add_key_binding( - KEY_LAMBDA('v', mCfgH, hideRejectedClusters, val ^= 1; SetInfo("Rejected clusters are %s", mCfgH.hideRejectedClusters ? "hidden" : "shown");), // + KEY_LAMBDA('v', mCfgH, hideRejectedClusters, val ^= 1;), // KEY_LAMBDA('j', mCfgH, separateExtrapolatedTracks, { if (val) { mCfgH.splitCETracks ^= 1; } val ^= 1; - SetInfo("Seperated display of tracks propagated to adjacent sectors %s / of CE tracks %s", val ? "enabled" : "disabled", mCfgH.splitCETracks ? "enabled" : "disabled"); }), - KEY_LAMBDA('u', mCfgH, trackFilter, val = (val + 1) % (mConfig.filterMacros.size() + 1); SetInfo("Track filter: %s", mCfgH.trackFilter == 0 ? "None" : mConfig.filterMacros[mCfgH.trackFilter - 1].c_str());), // + KEY_LAMBDA('u', mCfgH, trackFilter, val = (val + 1) % (mConfig.filterMacros.size() + 1);), // "Hide rejected clusters from tracks / Show tracks segments propagated to adjacent sector in different color / splt CE tracks / Cycle through track filter"); mFrontend->add_key_binding( @@ -205,10 +174,8 @@ void GPUDisplay::buildConfigKeys() if (val == 4) { val = 0; } - const char* infoText[] = {"Hits connected", "Hits connected and propagated to vertex", "Reconstructed track propagated inwards and outwards", "Monte Carlo track"}; - SetInfo("Display of propagated tracks: %s", infoText[val]); }), - KEY_LAMBDA('G', mCfgH, propagateLoopers, val ^= 1; SetInfo("Propagation of loopers %s", mCfgH.propagateLoopers ? "enabled" : "disabled");), // + KEY_LAMBDA('G', mCfgH, propagateLoopers, val ^= 1;), // "Extrapolate tracks / loopers"); mFrontend->add_key_binding( @@ -226,16 +193,14 @@ void GPUDisplay::buildConfigKeys() if (val == 5) { val = 1; } - SetInfo("Screenshot scaling factor set to %d", val); }), // "Take Screenshot / Record Animation to pictures / Change screenshot resolution (scaling factor)"); mFrontend->add_key_binding( - KEY_LAMBDA('S', mCfgL, smoothPoints, val ^= true; SetInfo("Smoothing of points %s", mCfgL.smoothPoints ? "enabled" : "disabled");), // - KEY_LAMBDA('A', mCfgL, smoothLines, val ^= true; SetInfo("Smoothing of lines %s", mCfgL.smoothLines ? "enabled" : "disabled");), // + KEY_LAMBDA('S', mCfgL, smoothPoints, val ^= true;), // + KEY_LAMBDA('A', mCfgL, smoothLines, val ^= true;), // KEY_LAMBDA('D', mCfgL, depthBuffer, { val ^= true; - SetInfo("Depth buffer (z-buffer, %u bits) %s", mBackend->DepthBits(), val ? "enabled" : "disabled"); mBackend->setDepthBuffer(); }), // "Enable or disable smoothing of points / smoothing of lines / depth buffer"); @@ -249,7 +214,6 @@ void GPUDisplay::buildConfigKeys() if (val > 16 || val > mBackend->getMaxMSAA()) { val = 0; } - SetInfo("Multisampling anti-aliasing factor set to %d", val); }), KEY_LAMBDA('U', mCfgR, drawQualityDownsampleFSAA, { val++; @@ -259,50 +223,25 @@ void GPUDisplay::buildConfigKeys() if (val == 5) { val = 0; } - SetInfo("Downsampling anti-aliasing factor set to %d", val); }), - KEY_LAMBDA('V', mCfgR, drawQualityVSync, val ^= true; SetInfo("VSync: %s", mCfgR.drawQualityVSync ? "enabled" : "disabled");), // - KEY_LAMBDA('R', mCfgR, fpsLimit, { - val = (val + 1) % 3; - if (val == GPUDisplayBackend::FPS_LIMIT_RATE) { - SetInfo("FPS limit: %d FPS", mCfgR.fpsRate); - } else if (val == GPUDisplayBackend::FPS_LIMIT_VSYNC) { - char rate[32] = "unknown rate"; - if (mFrontend->getRefreshRate() > 0.f) { - snprintf(rate, sizeof(rate), "%.1f Hz", mFrontend->getRefreshRate()); - } - const int32_t mode = mBackend->fpsLimitMode(); - if (mode == GPUDisplayBackend::FPS_LIMIT_NONE) { - SetInfo("FPS limit: refresh rate (%s) - VSync disabled, no limit", rate); - } else if (mode == GPUDisplayBackend::FPS_LIMIT_RATE) { - SetInfo("FPS limit: refresh rate (%s) - not supported by the backend, fallback to %d FPS", rate, mCfgR.fpsRate); - } else { - SetInfo("FPS limit: refresh rate (%s)", rate); - } - } else { - SetInfo("FPS limit: none"); - } - }), // + KEY_LAMBDA('V', mCfgR, drawQualityVSync, val ^= true;), // + KEY_LAMBDA('R', mCfgR, fpsLimit, val = (val + 1) % 3;), // "Toggle anti-aliasing (MSAA at raster level / change downsampling FSAA factor) / toggle VSync / FPS limit (none, refresh rate, fpsRate)"); mFrontend->add_key_binding( - KEY_LAMBDA('F', mCfgR, fullScreen, val ^= 1; SetInfo("Toggling full screen (%d)", (int32_t)mCfgR.fullScreen);), // - KEY_LAMBDA('_', mCfgR, maximized, val ^= 1; SetInfo("Toggling Maximized window (%d)", (int32_t)mCfgR.maximized);), // + KEY_LAMBDA('F', mCfgR, fullScreen, val ^= 1;), // + KEY_LAMBDA('_', mCfgR, maximized, val ^= 1;), // KEY_LAMBDA('b', mCfgR, fov, { if ((val += 5) > 175) { val = 5; } - SetInfo("Set FOV to %f", val); }), KEY_LAMBDA('m', mCfgR, rendererCompatSmoothing, { - if (displayOpenGLCore && backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { - SetInfo("OpenGL compat profile not available, using core profile", 1); - } else { + if (!displayOpenGLCore || backend()->backendType() != GPUDisplayBackend::TYPE_OPENGL) { val ^= 1; - SetInfo("Using renderer path for %s profile", val ? "compat" : "core (shader)"); } }), - KEY_LAMBDA('I', mCfgR, useGLIndirectDraw, val ^= true; SetInfo("OpenGL Indirect Draw %s", mCfgR.useGLIndirectDraw ? "enabled" : "disabled");), // + KEY_LAMBDA('I', mCfgR, useGLIndirectDraw, val ^= true;), // "Switch mFullScreen / Maximized window / Change FOV (field of view) / Switch between core / compat renderer path (OpenGL profile, Vulkan smooth lines) / Enable / disable GL indirect draw"); mFrontend->add_key_binding( @@ -375,18 +314,18 @@ void GPUDisplay::buildConfigKeys() "Save / restore current camera position / Animation path"); mFrontend->add_key_binding( - KEY_LAMBDA(mFrontend->KEY_F1, mCfgL, drawTPC, val ^= 1; SetInfo("Showing TPC Clusters: %d", (int32_t)mCfgL.drawTPC);), - KEY_LAMBDA(mFrontend->KEY_F2, mCfgL, drawTRD, val ^= 1; SetInfo("Showing TRD Tracklets: %d", (int32_t)mCfgL.drawTRD);), - KEY_LAMBDA(mFrontend->KEY_F3, mCfgL, drawTOF, val ^= 1; SetInfo("Showing TOF Hits: %d", (int32_t)mCfgL.drawTOF);), - KEY_LAMBDA(mFrontend->KEY_F4, mCfgL, drawITS, val ^= 1; SetInfo("Showing ITS Clusters: %d", (int32_t)mCfgL.drawITS);), // + KEY_LAMBDA(mFrontend->KEY_F1, mCfgL, drawTPC, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F2, mCfgL, drawTRD, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F3, mCfgL, drawTOF, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F4, mCfgL, drawITS, val ^= 1;), // "Enable / disable drawing of TPC / TRD / TOF / ITS"); mFrontend->add_key_binding( - KEY_LAMBDA(mFrontend->KEY_F1 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTPCTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), - KEY_LAMBDA(mFrontend->KEY_F2 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTRDTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), - KEY_LAMBDA(mFrontend->KEY_F3 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTOFTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), - KEY_LAMBDA(mFrontend->KEY_F4 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawITSTracks, val ^= 1; SetInfo("Track Filter Mask: TPC:%d TRD:%d TOF:%d ITS:%d", (int32_t)mCfgH.drawTPCTracks, (int32_t)mCfgH.drawTRDTracks, (int32_t)mCfgH.drawTOFTracks, (int32_t)mCfgH.drawITSTracks);), - KEY_LAMBDA(mFrontend->KEY_F12 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTracksAndFilter, val ^= 1; SetInfo("Track filter: %s", mCfgH.drawTracksAndFilter ? "AND" : "OR");), // + KEY_LAMBDA(mFrontend->KEY_F1 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTPCTracks, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F2 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTRDTracks, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F3 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTOFTracks, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F4 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawITSTracks, val ^= 1;), + KEY_LAMBDA(mFrontend->KEY_F12 + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgH, drawTracksAndFilter, val ^= 1;), // "Enable / disable track detector filter / SHIFT F12: Switch track detector filter between AND and OR mode", "[SHIFT] + [F1] to [F4] / [F12]"); @@ -446,12 +385,10 @@ void GPUDisplay::buildConfigKeys() KEY_LAMBDA(mFrontend->KEY_ALT + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camLookOrigin, { val ^= 1; mCfgR.cameraMode = val + 2 * mCfgR.camYUp; - SetInfo("Camera locked on origin: %s", val ? "enabled" : "disabled"); }), KEY_LAMBDA(mFrontend->KEY_CTRL + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camYUp, { val ^= 1; mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * val; - SetInfo("Camera locked on y-axis facing upwards: %s", val ? "enabled" : "disabled"); }), KEY_LAMBDA(mFrontend->KEY_ENTER, mCfgR, cameraMode, { val++; @@ -460,8 +397,6 @@ void GPUDisplay::buildConfigKeys() } mCfgR.camLookOrigin = val & 1; mCfgR.camYUp = val & 2; - const char* modeText[] = {"Descent (free movement)", "Focus locked on origin (y-axis forced upwards)", "Spectator (y-axis forced upwards)", "Focus locked on origin (with free rotation)"}; - SetInfo("Camera mode %d: %s", val, modeText[val]); }), "Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", // "[ALT] / [CTRL] / [ENTER]"); From 6c4bccbeda7939eba795027e2e18ef1ef828e910 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 27 Oct 2026 17:47:02 +0100 Subject: [PATCH 14/27] GPU Display: Unify calling DrawGLScene and handling of events --- GPU/GPUTracking/display/GPUDisplay.cxx | 16 ++++++++++------ GPU/GPUTracking/display/GPUDisplay.h | 8 ++++---- .../display/frontend/GPUDisplayFrontend.cxx | 6 +++++- .../display/frontend/GPUDisplayFrontend.h | 3 ++- .../display/frontend/GPUDisplayFrontendGlfw.cxx | 3 +-- .../display/frontend/GPUDisplayFrontendGlut.cxx | 11 ++++++++--- .../display/frontend/GPUDisplayFrontendGlut.h | 1 + .../display/frontend/GPUDisplayFrontendNone.cxx | 3 +-- .../frontend/GPUDisplayFrontendWayland.cxx | 5 +---- .../frontend/GPUDisplayFrontendWindows.cxx | 12 +++++------- .../display/frontend/GPUDisplayFrontendX11.cxx | 3 +-- .../display/helpers/GPUDisplayAnimation.cxx | 4 ++-- 12 files changed, 41 insertions(+), 34 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 988e0a2509a84..59d169bc33482 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -204,7 +204,7 @@ void GPUDisplay::ExitDisplay() mBackend->ExitBackend(); } -int32_t GPUDisplay::DrawGLScene() +int32_t GPUDisplay::DrawGLScene(bool suspended) { // Make sure event gets not overwritten during display mSemLockDisplay.Lock(); @@ -220,7 +220,7 @@ int32_t GPUDisplay::DrawGLScene() mNCollissions = std::max(1u, mIOPtrs->nMCInfosTPCCol); } try { - DrawGLScene_internal(); + DrawGLScene_internal(suspended); } catch (const std::runtime_error& e) { GPUError("Runtime error %s during display", e.what()); retVal = 1; @@ -238,7 +238,7 @@ int32_t GPUDisplay::DrawGLScene() return retVal; } -void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix) +void GPUDisplay::DrawGLScene_cameraAndAnimation(bool suspended, float animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix) { HighResTimer timer(mUpdateVertexLists && mChain->GetProcessingSettings().debugLevel >= 2); int32_t mMouseWheelTmp = mFrontend->mMouseWheel; @@ -266,7 +266,7 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(float animateTime, float& mixSla // Perform new rotation / translation / animation - if (animateCamera(animateTime, mixSlaveImage, nextViewMatrix)) { + if (animateCamera(suspended, animateTime, mixSlaveImage, nextViewMatrix)) { // Do nothing else } else if (mCfgC.mResetScene.load()) { const float initialZpos = mCfgH.projectXY ? 16 : (mParam->par.continuousTracking ? (mMaxClusterZ * GL_SCALE_FACTOR + 8) : 8); @@ -627,7 +627,7 @@ void GPUDisplay::DrawGLScene_drawCommands() } } -void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) // negative time = no mixing +void GPUDisplay::DrawGLScene_internal(bool suspended, float animateTime, bool renderToMixBuffer) // negative time = no mixing { if (mCfgC.mRequestScreenshot || mCfgC.mAnimateScreenshot) { const uint32_t factor = std::max(1, mCfgR.drawQualityDownsampleFSAA) * std::max(1, mCfgR.screenshotScaleFactor); @@ -660,7 +660,7 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) hmm_mat4 nextViewMatrix = MY_HMM_IDENTITY; float mixSlaveImage = 0.f; - DrawGLScene_cameraAndAnimation(animateTime, mixSlaveImage, nextViewMatrix); + DrawGLScene_cameraAndAnimation(suspended, animateTime, mixSlaveImage, nextViewMatrix); // Prepare Event if (mUpdateVertexLists && mIOPtrs) { @@ -670,6 +670,10 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) } } + if (suspended) { + return; + } + // Draw Event nextViewMatrix = nextViewMatrix * *mModelMatrix; const float zFar = ((mParam->par.continuousTracking ? (mMaxClusterZ * GL_SCALE_FACTOR) : 8.f) + 50.f) * 2.f; diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 896fef6f3893b..25fec52afae18 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -60,7 +60,7 @@ class GPUDisplay : public GPUDisplayInterface void UpdateParam(const GPUParam* param) override { mParam = param; } void HandleKey(uint8_t key, bool doNotHandleConfigChanges = false); // With doNotHandleConfigChanges, the caller must call checkAndApplyConfigChanges - int32_t DrawGLScene(); + int32_t DrawGLScene(bool suspended = false); void HandleExternalGUIKey(int32_t key); // Does not handle config changes, see HandleKey int32_t InitDisplay(bool initFailure = false); void ResizeScene(int32_t width, int32_t height, bool init = false); @@ -170,9 +170,9 @@ class GPUDisplay : public GPUDisplayInterface static const GPUSettingsDisplay& GetConfig(GPUChainTracking* chain); static const GPUSettingsProcessing& GetProcessingConfig(GPUChainTracking* chain); - void DrawGLScene_internal(float animateTime = -1.f, bool renderToMixBuffer = false); + void DrawGLScene_internal(bool suspended, float animateTime = -1.f, bool renderToMixBuffer = false); // suspended: return before drawing void DrawGLScene_updateEventData(); - void DrawGLScene_cameraAndAnimation(float animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix); + void DrawGLScene_cameraAndAnimation(bool suspended, float animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix); size_t DrawGLScene_updateVertexList(); void DrawGLScene_drawCommands(); int32_t InitDisplay_internal(); @@ -195,7 +195,7 @@ class GPUDisplay : public GPUDisplayInterface void resetAnimation(); void removeAnimationPoint(); void startAnimation(); - int32_t animateCamera(float& animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix); + int32_t animateCamera(bool suspended, float& animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix); void showInfo(const char* info); void ActivateColor(); void SetColorTRD(); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index af36e54479f04..f4da34fab1534 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -146,7 +146,11 @@ int32_t GPUDisplayFrontend::getDisplayControl() const { return mDisplay->mCfgC.m void GPUDisplayFrontend::setDisplayControl(int32_t v) { mDisplay->mCfgC.mDisplayControl.store(v); } void GPUDisplayFrontend::HandleKey(uint8_t key) { mDisplay->HandleKey(key); } -int32_t GPUDisplayFrontend::DrawGLScene() { return mDisplay->DrawGLScene(); } +int32_t GPUDisplayFrontend::HandleUpdatesAndDrawGLScene(bool suspended) +{ + HandleExternalGUISignals(); + return mDisplay->DrawGLScene(suspended); +} void GPUDisplayFrontend::ResizeScene(int32_t width, int32_t height) { diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 3b26f22eea4c3..dd66c4bd6d085 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -126,8 +126,9 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface std::unique_ptr mGUIChannel; // Must outlive mGUI std::unique_ptr mGUI; + int32_t HandleUpdatesAndDrawGLScene(bool suspended = false); // Handle external GUI signals and config changes, then draw the scene (suspended: only update, no drawing, e.g. for a hidden window) + void HandleKey(uint8_t key); // Callback for handling key presses - int32_t DrawGLScene(); // Callback to draw the GL scene void HandleExternalGUISignals(); // Callback to handle signals from external GUIs bool HasExternalGUISignals() const; // Returns whether there are currently pending signals from external GUIs to be handled bool processKeyLambda(uint32_t key); // Process key lambda callback diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx index 336161216b351..442cc7224509a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx @@ -322,8 +322,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain() int32_t retVal = 0; while (!glfwWindowShouldClose(mWindow)) { - HandleExternalGUISignals(); - if (DrawGLScene()) { + if (HandleUpdatesAndDrawGLScene()) { fprintf(stderr, "Error drawing GL scene\n"); retVal = 1; break; // Cleanup below diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index 90391fff43bf0..db8998060117d 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -38,15 +38,20 @@ GPUDisplayFrontendGlut::GPUDisplayFrontendGlut() void GPUDisplayFrontendGlut::displayFunc() // TODO: Functions should be lambdas, to get rid of the static me variable! { - me->DrawGLScene(); + me->mRedisplayPending = false; + me->HandleUpdatesAndDrawGLScene(); glutSwapBuffers(); me->waitForFPSLimit(); } void GPUDisplayFrontendGlut::glutLoopFunc() { - me->HandleExternalGUISignals(); - displayFunc(); + if (me->mRedisplayPending) { // Last redisplay not served (window not visible): handle updates without drawing + me->HandleUpdatesAndDrawGLScene(true); + me->waitForFPSLimit(); + } + me->mRedisplayPending = true; + glutPostRedisplay(); // Drawing only in displayFunc, so that expose events do not draw twice } int32_t GPUDisplayFrontendGlut::GetKey(int32_t key) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h index 9fe8e7b44d018..ca04b093a2589 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h @@ -51,6 +51,7 @@ class GPUDisplayFrontendGlut : public GPUDisplayFrontend int32_t mWidth = INIT_WIDTH; int32_t mHeight = INIT_HEIGHT; bool mFullScreen = false; + bool mRedisplayPending = false; // glutPostRedisplay called in glutLoopFunc, not yet served by displayFunc }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx index e0b6f96933612..ce616814e208b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx @@ -28,8 +28,7 @@ int32_t GPUDisplayFrontendNone::FrontendMain() return 1; } do { - HandleExternalGUISignals(); - DrawGLScene(); + HandleUpdatesAndDrawGLScene(); waitForFPSLimit(); } while (getDisplayControl() != 2); return 0; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx index efee3ca538973..9f6a70a63faac 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx @@ -610,10 +610,7 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() break; } handleKeyRepeat(); - HandleExternalGUISignals(); - if (!mSuspended) { // Window not visible, compositor would not release swapchain images - DrawGLScene(); - } + HandleUpdatesAndDrawGLScene(mSuspended); // Window not visible, compositor would not release swapchain images waitForFPSLimit(); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx index 9a641b5c6ef05..bb72a330bba0b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx @@ -350,13 +350,11 @@ int32_t GPUDisplayFrontendWindows::FrontendMain() DispatchMessage(&msg); // Dispatch The Message } } else { - HandleExternalGUISignals(); - if (active) // Program Active? - { - if (mKeys[VK_ESCAPE]) { - done = TRUE; - } else { - DrawGLScene(); // Draw The Scene + if (active && mKeys[VK_ESCAPE]) { + done = TRUE; + } else { + HandleUpdatesAndDrawGLScene(!active); // Only draw if the program is active + if (active) { SwapBuffers(hDC); // Swap Buffers (Double Buffering) waitForFPSLimit(); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx index 50cdd13fd9d19..e44a5f5e50048 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx @@ -452,8 +452,7 @@ int32_t GPUDisplayFrontendX11::FrontendMain() break; } - HandleExternalGUISignals(); - DrawGLScene(); + HandleUpdatesAndDrawGLScene(); if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { glXSwapBuffers(mXDisplay, mWindow); } diff --git a/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx b/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx index 7ef55de0113e4..e8c1e16ff9b9e 100644 --- a/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx +++ b/GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx @@ -105,7 +105,7 @@ void GPUDisplay::startAnimation() mAnimationMixBase = -1; } -int32_t GPUDisplay::animateCamera(float& animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix) +int32_t GPUDisplay::animateCamera(bool suspended, float& animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix) { float time = animateTime; if (mAnimate && time < 0) { @@ -152,7 +152,7 @@ int32_t GPUDisplay::animateCamera(float& animateTime, float& mixSlaveImage, hmm_ if (mixSlaveImage > 0) { mCfgL = mAnimateConfig[mAnimationLastBase]; updateConfig(); - DrawGLScene_internal(time, true); + DrawGLScene_internal(suspended, time, true); } } From 4ec2eab5a13d5d4bac602e58886e07516b91af26 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 28 Oct 2026 23:27:40 +0100 Subject: [PATCH 15/27] GPU Display: Clean up full screen / maximized setting, make them idempotent --- .../display/frontend/GPUDisplayFrontend.cxx | 22 +++++++++++++++---- .../display/frontend/GPUDisplayFrontend.h | 6 ++++- .../frontend/GPUDisplayFrontendConfig.cxx | 4 ++-- .../frontend/GPUDisplayFrontendX11.cxx | 4 ++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index f4da34fab1534..4c3309cff0834 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -256,8 +256,22 @@ GPUDisplayBackend* GPUDisplayFrontend::backend() void GPUDisplayFrontend::setWindowState(bool fullScreen, bool maximized) { - mDisplay->mCfgR.fullScreen = fullScreen; - mDisplay->mCfgR.maximized = maximized; + mDisplay->mCfgR.fullScreen = mWindowFullScreen = fullScreen; + mDisplay->mCfgR.maximized = mWindowMaximized = maximized; +} + +void GPUDisplayFrontend::requestFullScreen(bool set) +{ + if (set != std::exchange(mWindowFullScreen, set)) { + SwitchFullscreen(set); + } +} + +void GPUDisplayFrontend::requestMaximized(bool set) +{ + if (set != std::exchange(mWindowMaximized, set)) { + ToggleMaximized(set); + } } void GPUDisplayFrontend::focusLost() @@ -269,9 +283,9 @@ void GPUDisplayFrontend::focusLost() void GPUDisplayFrontend::applyInitialWindowState() { if (mDisplay->cfgR().fullScreen) { - SwitchFullscreen(true); + requestFullScreen(true); } else if (mDisplay->cfgR().maximized) { - ToggleMaximized(true); + requestMaximized(true); } } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index dd66c4bd6d085..b3db83c7e83e9 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -140,13 +140,17 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface void setWindowState(bool fullScreen, bool maximized); // Sync the settings if the window manager changed the window state void focusLost(); // Release all keys and mouse buttons when the window loses focus, since the release events go to another window void applyInitialWindowState(); // Switch to fullscreen / maximize at startup according to the settings + void requestFullScreen(bool set); // Call SwitchFullscreen / ToggleMaximized only if the state differs from the last requested or reported (setWindowState) one + void requestMaximized(bool set); + private: struct configRTTIInfo; struct configRTTIExt; struct configKey; std::unique_ptr mConfigRTTI{nullptr, nullptr}; // Deleter set in GPUDisplayFrontendConfig.cxx, the only place where configRTTIInfo is defined - bool HandleExternalGUIKeys(); // Returns whether keys were handled, config changes are not handled + void HandleExternalGUIKeys(); + bool mWindowFullScreen = false, mWindowMaximized = false; // Window state last requested or reported by the window system template 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 void listConfig(); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 23070ec813baf..b7d87e87dcbfb 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -141,10 +141,10 @@ void GPUDisplay::checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCf mBackend->SetVSync(mCfgR.drawQualityVSync); } if (oldCfgR.fullScreen != mCfgR.fullScreen) { - mFrontend->SwitchFullscreen(mCfgR.fullScreen); + mFrontend->requestFullScreen(mCfgR.fullScreen); } if (oldCfgR.maximized != mCfgR.maximized) { - mFrontend->ToggleMaximized(mCfgR.maximized); + mFrontend->requestMaximized(mCfgR.maximized); } if (oldCfgR.useGLIndirectDraw != mCfgR.useGLIndirectDraw) { mUpdateEventData = true; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx index e44a5f5e50048..3f0cfcbf77b78 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx @@ -510,8 +510,8 @@ void GPUDisplayFrontendX11::SwitchFullscreen(bool set) xev.xclient.window = mWindow; xev.xclient.message_type = XInternAtom(mXDisplay, "_NET_WM_STATE", False); xev.xclient.format = 32; - xev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE - xev.xclient.data.l[1] = XInternAtom(mXDisplay, "_NET_WM_STATE_FULLSCREEN", True); + xev.xclient.data.l[0] = set ? 1 : 0; // _NET_WM_STATE_ADD / _NET_WM_STATE_REMOVE + xev.xclient.data.l[1] = XInternAtom(mXDisplay, "_NET_WM_STATE_FULLSCREEN", False); xev.xclient.data.l[2] = 0; XSendEvent(mXDisplay, DefaultRootWindow(mXDisplay), False, SubstructureNotifyMask, &xev); } From 19339bbcb29e70bcc791444e92e18a24c2bd6467 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 28 Oct 2026 23:27:54 +0100 Subject: [PATCH 16/27] GPU Display: Cleanup: check for config changes only once --- GPU/GPUTracking/display/GPUDisplay.cxx | 1 + GPU/GPUTracking/display/GPUDisplay.h | 8 +++++--- .../display/frontend/GPUDisplayFrontend.cxx | 14 +++++--------- .../frontend/GPUDisplayFrontendConfig.cxx | 18 ++++++------------ 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 59d169bc33482..4faceadf34ec1 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -177,6 +177,7 @@ int32_t GPUDisplay::InitDisplay(bool initFailure) retVal = 1; } mInitResult = retVal == 0 ? 1 : -1; + mOldCfg = {mCfgH, mCfgL, mCfgR}; return (retVal); } diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 25fec52afae18..905db94448773 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -59,12 +60,12 @@ class GPUDisplay : public GPUDisplayInterface void UpdateCalib(const GPUCalibObjectsConst* calib) override { mCalib = calib; } void UpdateParam(const GPUParam* param) override { mParam = param; } - void HandleKey(uint8_t key, bool doNotHandleConfigChanges = false); // With doNotHandleConfigChanges, the caller must call checkAndApplyConfigChanges + void HandleKey(uint8_t key); int32_t DrawGLScene(bool suspended = false); - void HandleExternalGUIKey(int32_t key); // Does not handle config changes, see HandleKey + void HandleExternalGUIKey(int32_t key); int32_t InitDisplay(bool initFailure = false); void ResizeScene(int32_t width, int32_t height, bool init = false); - void checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCfgH, const GPUSettingsDisplayLight& oldCfgL, const GPUSettingsDisplayRenderer& oldCfgR); // Set update flags / apply the settings that changed compared to the old copies + void checkAndApplyConfigChanges(); // Set update flags / apply the settings that changed compared to mOldCfg const GPUSettingsDisplayRenderer& cfgR() const { return mCfgR; } const GPUSettingsDisplayLight& cfgL() const { return mCfgL; } @@ -244,6 +245,7 @@ class GPUDisplay : public GPUDisplayInterface GPUSettingsDisplayLight mCfgL; GPUSettingsDisplayHeavy mCfgH; GPUSettingsDisplayRenderer mCfgR; + std::tuple mOldCfg; // Settings after the last draw, for checkAndApplyConfigChanges const GPUSettingsProcessing& mProcessingSettings; GPUQA* mQA; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index 4c3309cff0834..aecac676c6349 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -97,10 +97,7 @@ void GPUDisplayFrontend::waitForFPSLimit() void GPUDisplayFrontend::HandleExternalGUISignals() { - const GPUSettingsDisplayHeavy oldCfgH = mDisplay->mCfgH; - const GPUSettingsDisplayLight oldCfgL = mDisplay->mCfgL; - const GPUSettingsDisplayRenderer oldCfgR = mDisplay->mCfgR; - bool changes = HandleExternalGUIKeys(); + HandleExternalGUIKeys(); const bool gui = isGUIRunning(); std::optional update; @@ -116,12 +113,8 @@ void GPUDisplayFrontend::HandleExternalGUISignals() if (update->lineWidth) { mDisplay->mCfgL.lineWidth = *update->lineWidth; } - changes = true; } - if (changes) { - mDisplay->checkAndApplyConfigChanges(oldCfgH, oldCfgL, oldCfgR); - } if (gui) { { std::lock_guard lock(mGUIChannel->mutex); @@ -149,7 +142,10 @@ void GPUDisplayFrontend::HandleKey(uint8_t key) { mDisplay->HandleKey(key); } int32_t GPUDisplayFrontend::HandleUpdatesAndDrawGLScene(bool suspended) { HandleExternalGUISignals(); - return mDisplay->DrawGLScene(suspended); + mDisplay->checkAndApplyConfigChanges(); + const int32_t retVal = mDisplay->DrawGLScene(suspended); + mDisplay->mOldCfg = {mDisplay->mCfgH, mDisplay->mCfgL, mDisplay->mCfgR}; // Also includes the changes during drawing (animation, scene reset, ...), which set their update flags themselves + return retVal; } void GPUDisplayFrontend::ResizeScene(int32_t width, int32_t height) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index b7d87e87dcbfb..e068e5865714a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -121,8 +121,9 @@ namespace #include "utils/qconfig_genrtti.inc" } // namespace -void GPUDisplay::checkAndApplyConfigChanges(const GPUSettingsDisplayHeavy& oldCfgH, const GPUSettingsDisplayLight& oldCfgL, const GPUSettingsDisplayRenderer& oldCfgR) +void GPUDisplay::checkAndApplyConfigChanges() { + const auto& [oldCfgH, oldCfgL, oldCfgR] = mOldCfg; if (memcmp((void*)&oldCfgH, (void*)&mCfgH, sizeof(mCfgH)) != 0) { mUpdateEventData = true; } @@ -164,7 +165,7 @@ void GPUDisplay::HandleExternalGUIKey(int32_t key) bool shifted = key >= 'A' && key <= 'Z'; bool oldShift = mFrontend->mKeys[mFrontend->KEY_SHIFT]; mFrontend->mKeys[mFrontend->KEY_SHIFT] = shifted; - HandleKey(key, true); + HandleKey(key); mFrontend->mKeys[mFrontend->KEY_SHIFT] = oldShift; } @@ -173,13 +174,13 @@ bool GPUDisplayFrontend::HasExternalGUISignals() const return mExternalGUIKeys.size(); } -bool GPUDisplayFrontend::HandleExternalGUIKeys() +void GPUDisplayFrontend::HandleExternalGUIKeys() { std::lock_guard lock(mExternalGUIKeyMutex); for (auto key : mExternalGUIKeys) { mDisplay->HandleExternalGUIKey(key); } - return !std::exchange(mExternalGUIKeys, {}).empty(); + mExternalGUIKeys.clear(); } template @@ -360,17 +361,10 @@ bool GPUDisplayFrontend::processKeyLambda(uint32_t key) return false; } -void GPUDisplay::HandleKey(uint8_t key, bool doNotHandleConfigChanges) +void GPUDisplay::HandleKey(uint8_t key) { - const GPUSettingsDisplayHeavy oldCfgH = mCfgH; - const GPUSettingsDisplayLight oldCfgL = mCfgL; - const GPUSettingsDisplayRenderer oldCfgR = mCfgR; if (key == mFrontend->KEY_ALT) { mFrontend->mKeys[mFrontend->KEY_CTRL] = false; // Release CTRL with alt, to avoid orienting along y automatically! } else if (mFrontend->processKeyLambda(key)) { } - - if (!doNotHandleConfigChanges) { - checkAndApplyConfigChanges(oldCfgH, oldCfgL, oldCfgR); - } } From 3a73005ddb80d110089280203ffb21615c7c8599 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 28 Oct 2026 23:32:54 +0100 Subject: [PATCH 17/27] GPU Display backend glut: support maximizing the window --- .../frontend/GPUDisplayFrontendGlut.cxx | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index db8998060117d..7ae870dd97355 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -24,6 +24,7 @@ #ifdef GPUCA_BUILD_EVENT_DISPLAY_X11 #include #include +#include #endif #include @@ -187,6 +188,24 @@ void GPUDisplayFrontendGlut::ResizeSceneWrapper(int32_t width, int32_t height) me->mHeight = height; } me->ResizeScene(width, height); +#ifdef GPUCA_BUILD_EVENT_DISPLAY_X11 + Display* display = glXGetCurrentDisplay(); // Maximizing / unmaximizing by the window manager changes the size, read the state as freeglut does not report it (fullscreen stays as requested, freeglut tracks it itself) + Atom type; + int32_t format; + unsigned long nItems, bytesAfter; + unsigned char* data = nullptr; + if (!display || XGetWindowProperty(display, glXGetCurrentDrawable(), XInternAtom(display, "_NET_WM_STATE", False), 0, 1024, False, XA_ATOM, &type, &format, &nItems, &bytesAfter, &data) != Success || data == nullptr) { + return; + } + const Atom maxHorz = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False), maxVert = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + bool isMaxHorz = false, isMaxVert = false; + for (unsigned long i = 0; i < nItems; i++) { + isMaxHorz |= ((Atom*)data)[i] == maxHorz; + isMaxVert |= ((Atom*)data)[i] == maxVert; + } + XFree(data); + me->setWindowState(me->mFullScreen, isMaxHorz && isMaxVert); +#endif } void GPUDisplayFrontendGlut::mouseFunc(int32_t button, int32_t state, int32_t x, int32_t y) @@ -313,7 +332,27 @@ void GPUDisplayFrontendGlut::SwitchFullscreen(bool set) } } -void GPUDisplayFrontendGlut::ToggleMaximized(bool set) {} +void GPUDisplayFrontendGlut::ToggleMaximized(bool set) // GLUT has no API for maximizing, request it from the window manager via GLX / X11 directly (no-op if freeglut does not use GLX) +{ +#ifdef GPUCA_BUILD_EVENT_DISPLAY_X11 + Display* display = glXGetCurrentDisplay(); + if (!display) { + return; + } + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = glXGetCurrentDrawable(); // freeglut renders directly to the X window + xev.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False); + xev.xclient.format = 32; + xev.xclient.data.l[0] = set ? 1 : 0; // _NET_WM_STATE_ADD / _NET_WM_STATE_REMOVE + xev.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + xev.xclient.data.l[2] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + XSendEvent(display, DefaultRootWindow(display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); + XFlush(display); +#endif +} + void GPUDisplayFrontendGlut::SetVSync(bool enable) // GLUT has no swap interval API, set it via GLX directly (no-op if freeglut does not use GLX) { #ifdef GPUCA_BUILD_EVENT_DISPLAY_X11 From 8ca0feaf2f4d17ef59bcbc9518da05bd4c53fa9e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 28 Oct 2026 23:48:05 +0100 Subject: [PATCH 18/27] GPU Display: Modernize GLUT backend: Use callbacks with user-data for this ptr, update key constants --- GPU/GPUTracking/display/CMakeLists.txt | 16 ++ .../frontend/GPUDisplayFrontendGlut.cxx | 242 ++++++++---------- .../display/frontend/GPUDisplayFrontendGlut.h | 18 +- 3 files changed, 125 insertions(+), 151 deletions(-) diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index 592483293303c..b96862db705cf 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -69,6 +69,22 @@ if(NOT GPUCA_EVENT_DISPLAY_OPENGL) set(X11_FOUND 0) endif() +if(GLUT_FOUND) + include(CheckSymbolExists) + set(CMAKE_REQUIRED_INCLUDES ${GLUT_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${GLUT_glut_LIBRARY}) + check_symbol_exists(glutDisplayFuncUcall "GL/freeglut.h;GL/freeglut_ucall.h" GPUCA_GLUT_HAS_UCALL) # Callbacks with user data, freeglut >= 3.2 (modifier key codes exist since 2.8) + unset(CMAKE_REQUIRED_INCLUDES) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT GPUCA_GLUT_HAS_UCALL) + if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") + message(FATAL_ERROR "GLUT event display frontend needs freeglut >= 3.2") + endif() + message(STATUS "GLUT event display frontend disabled: needs freeglut >= 3.2") + set(GLUT_FOUND 0) + endif() +endif() + if(Vulkan_FOUND) string(FIND "${Vulkan_VERSION}" "." vulkan_dot_index) if(NOT ${Vulkan_VERSION} VERSION_GREATER_EQUAL "1.4.328" OR vulkan_dot_index EQUAL -1 OR NOT GPUCA_GLSLC) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index 7ae870dd97355..2f2dfb204fa50 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include #ifdef GPUCA_BUILD_EVENT_DISPLAY_X11 #include @@ -29,7 +30,6 @@ #include using namespace o2::gpu; -static GPUDisplayFrontendGlut* me = nullptr; GPUDisplayFrontendGlut::GPUDisplayFrontendGlut() { @@ -37,103 +37,83 @@ GPUDisplayFrontendGlut::GPUDisplayFrontendGlut() mFrontendName = "GLUT"; } -void GPUDisplayFrontendGlut::displayFunc() // TODO: Functions should be lambdas, to get rid of the static me variable! +void GPUDisplayFrontendGlut::displayFunc() { - me->mRedisplayPending = false; - me->HandleUpdatesAndDrawGLScene(); + mRedisplayPending = false; + HandleUpdatesAndDrawGLScene(); glutSwapBuffers(); - me->waitForFPSLimit(); + waitForFPSLimit(); } void GPUDisplayFrontendGlut::glutLoopFunc() { - if (me->mRedisplayPending) { // Last redisplay not served (window not visible): handle updates without drawing - me->HandleUpdatesAndDrawGLScene(true); - me->waitForFPSLimit(); + if (mRedisplayPending) { // Last redisplay not served (window not visible): handle updates without drawing + HandleUpdatesAndDrawGLScene(true); + waitForFPSLimit(); } - me->mRedisplayPending = true; + mRedisplayPending = true; glutPostRedisplay(); // Drawing only in displayFunc, so that expose events do not draw twice } int32_t GPUDisplayFrontendGlut::GetKey(int32_t key) { - if (key == GLUT_KEY_UP) { - return KEY_UP; + switch (key) { + case GLUT_KEY_UP: + return KEY_UP; + case GLUT_KEY_DOWN: + return KEY_DOWN; + case GLUT_KEY_LEFT: + return KEY_LEFT; + case GLUT_KEY_RIGHT: + return KEY_RIGHT; + case GLUT_KEY_PAGE_UP: + return KEY_PAGEUP; + case GLUT_KEY_PAGE_DOWN: + return KEY_PAGEDOWN; + case GLUT_KEY_HOME: + return KEY_HOME; + case GLUT_KEY_END: + return KEY_END; + case GLUT_KEY_INSERT: + return KEY_INSERT; + case GLUT_KEY_F1: + return KEY_F1; + case GLUT_KEY_F2: + return KEY_F2; + case GLUT_KEY_F3: + return KEY_F3; + case GLUT_KEY_F4: + return KEY_F4; + case GLUT_KEY_F5: + return KEY_F5; + case GLUT_KEY_F6: + return KEY_F6; + case GLUT_KEY_F7: + return KEY_F7; + case GLUT_KEY_F8: + return KEY_F8; + case GLUT_KEY_F9: + return KEY_F9; + case GLUT_KEY_F10: + return KEY_F10; + case GLUT_KEY_F11: + return KEY_F11; + case GLUT_KEY_F12: + return KEY_F12; + case GLUT_KEY_SHIFT_L: + case GLUT_KEY_SHIFT_R: + return KEY_SHIFT; + case GLUT_KEY_CTRL_L: + return KEY_CTRL; + case GLUT_KEY_CTRL_R: + return KEY_RCTRL; + case GLUT_KEY_ALT_L: + return KEY_ALT; + case GLUT_KEY_ALT_R: + return KEY_RALT; + default: + return 0; } - if (key == GLUT_KEY_DOWN) { - return KEY_DOWN; - } - if (key == GLUT_KEY_LEFT) { - return KEY_LEFT; - } - if (key == GLUT_KEY_RIGHT) { - return KEY_RIGHT; - } - if (key == GLUT_KEY_PAGE_UP) { - return KEY_PAGEUP; - } - if (key == GLUT_KEY_PAGE_DOWN) { - return KEY_PAGEDOWN; - } - if (key == GLUT_KEY_HOME) { - return KEY_HOME; - } - if (key == GLUT_KEY_END) { - return KEY_END; - } - if (key == GLUT_KEY_INSERT) { - return KEY_INSERT; - } - if (key == GLUT_KEY_F1) { - return KEY_F1; - } - if (key == GLUT_KEY_F2) { - return KEY_F2; - } - if (key == GLUT_KEY_F3) { - return KEY_F3; - } - if (key == GLUT_KEY_F4) { - return KEY_F4; - } - if (key == GLUT_KEY_F5) { - return KEY_F5; - } - if (key == GLUT_KEY_F6) { - return KEY_F6; - } - if (key == GLUT_KEY_F7) { - return KEY_F7; - } - if (key == GLUT_KEY_F8) { - return KEY_F8; - } - if (key == GLUT_KEY_F9) { - return KEY_F9; - } - if (key == GLUT_KEY_F10) { - return KEY_F10; - } - if (key == GLUT_KEY_F11) { - return KEY_F11; - } - if (key == GLUT_KEY_F12) { - return KEY_F12; - } - if (key == 112 || key == 113) { - return KEY_SHIFT; - } - if (key == 114) { - return KEY_CTRL; - } - if (key == 115) { - return KEY_RCTRL; - } - if (key == 116) { - return KEY_ALT; - } - - return (0); } void GPUDisplayFrontendGlut::GetKey(int32_t key, int32_t& keyOut, int32_t& keyPressOut, bool special) @@ -151,43 +131,23 @@ void GPUDisplayFrontendGlut::GetKey(int32_t key, int32_t& keyOut, int32_t& keyPr } } -void GPUDisplayFrontendGlut::keyboardDownFunc(uint8_t key, int32_t x, int32_t y) -{ - int32_t handleKey = 0, keyPress = 0; - GetKey(key, handleKey, keyPress, false); - me->mKeys[keyPress] = true; - me->HandleKey(handleKey); -} - -void GPUDisplayFrontendGlut::keyboardUpFunc(uint8_t key, int32_t x, int32_t y) +void GPUDisplayFrontendGlut::keyboardFunc(int32_t key, bool special, bool down) { int32_t handleKey = 0, keyPress = 0; - GetKey(key, handleKey, keyPress, false); - me->mKeys[keyPress] = false; -} - -void GPUDisplayFrontendGlut::specialDownFunc(int32_t key, int32_t x, int32_t y) -{ - int32_t handleKey = 0, keyPress = 0; - GetKey(key, handleKey, keyPress, true); - me->mKeys[keyPress] = true; - me->HandleKey(handleKey); -} - -void GPUDisplayFrontendGlut::specialUpFunc(int32_t key, int32_t x, int32_t y) -{ - int32_t handleKey = 0, keyPress = 0; - GetKey(key, handleKey, keyPress, true); - me->mKeys[keyPress] = false; + GetKey(key, handleKey, keyPress, special); + mKeys[keyPress] = down; + if (down) { + HandleKey(handleKey); + } } void GPUDisplayFrontendGlut::ResizeSceneWrapper(int32_t width, int32_t height) { - if (!me->mFullScreen) { - me->mWidth = width; - me->mHeight = height; + if (!mFullScreen) { + mWidth = width; + mHeight = height; } - me->ResizeScene(width, height); + ResizeScene(width, height); #ifdef GPUCA_BUILD_EVENT_DISPLAY_X11 Display* display = glXGetCurrentDisplay(); // Maximizing / unmaximizing by the window manager changes the size, read the state as freeglut does not report it (fullscreen stays as requested, freeglut tracks it itself) Atom type; @@ -204,44 +164,44 @@ void GPUDisplayFrontendGlut::ResizeSceneWrapper(int32_t width, int32_t height) isMaxVert |= ((Atom*)data)[i] == maxVert; } XFree(data); - me->setWindowState(me->mFullScreen, isMaxHorz && isMaxVert); + setWindowState(mFullScreen, isMaxHorz && isMaxVert); #endif } void GPUDisplayFrontendGlut::mouseFunc(int32_t button, int32_t state, int32_t x, int32_t y) { if (button == 3) { - me->mMouseWheel += 100; + mMouseWheel += 100; } else if (button == 4) { - me->mMouseWheel -= 100; + mMouseWheel -= 100; } else if (state == GLUT_DOWN) { if (button == GLUT_LEFT_BUTTON) { - me->mMouseDn = true; + mMouseDn = true; } else if (button == GLUT_RIGHT_BUTTON) { - me->mMouseDnR = true; + mMouseDnR = true; } else if (button == GLUT_MIDDLE_BUTTON) { - me->mMouseDnM = true; + mMouseDnM = true; } - me->mMouseDnX = x; - me->mMouseDnY = y; + mMouseDnX = x; + mMouseDnY = y; } else if (state == GLUT_UP) { if (button == GLUT_LEFT_BUTTON) { - me->mMouseDn = false; + mMouseDn = false; } else if (button == GLUT_RIGHT_BUTTON) { - me->mMouseDnR = false; + mMouseDnR = false; } else if (button == GLUT_MIDDLE_BUTTON) { - me->mMouseDnM = false; + mMouseDnM = false; } } } void GPUDisplayFrontendGlut::mouseMoveFunc(int32_t x, int32_t y) { - me->mMouseMvX = x; - me->mMouseMvY = y; + mMouseMvX = x; + mMouseMvY = y; } -void GPUDisplayFrontendGlut::mMouseWheelFunc(int32_t button, int32_t dir, int32_t x, int32_t y) { me->mMouseWheel += dir; } +void GPUDisplayFrontendGlut::mMouseWheelFunc(int32_t button, int32_t dir, int32_t x, int32_t y) { mMouseWheel += dir; } int32_t GPUDisplayFrontendGlut::FrontendMain() { @@ -249,7 +209,6 @@ int32_t GPUDisplayFrontendGlut::FrontendMain() fprintf(stderr, "Only OpenGL backend supported\n"); return 1; } - me = this; mCanDrawText = 1; if (drawTextFontSize() == 0) { drawTextFontSize() = 12; @@ -276,17 +235,18 @@ int32_t GPUDisplayFrontendGlut::FrontendMain() return (1); } - glutDisplayFunc(displayFunc); - glutIdleFunc(glutLoopFunc); - glutReshapeFunc(ResizeSceneWrapper); - glutKeyboardFunc(keyboardDownFunc); - glutKeyboardUpFunc(keyboardUpFunc); - glutSpecialFunc(specialDownFunc); - glutSpecialUpFunc(specialUpFunc); - glutMouseFunc(mouseFunc); - glutMotionFunc(mouseMoveFunc); - glutPassiveMotionFunc(mouseMoveFunc); - glutMouseWheelFunc(mMouseWheelFunc); + auto mouseMoveFunc = [](int32_t x, int32_t y, void* p) { self(p)->mouseMoveFunc(x, y); }; + glutDisplayFuncUcall([](void* p) { self(p)->displayFunc(); }, this); // Callbacks with this as user data, freeglut >= 3.2 + glutIdleFuncUcall([](void* p) { self(p)->glutLoopFunc(); }, this); + glutReshapeFuncUcall([](int32_t width, int32_t height, void* p) { self(p)->ResizeSceneWrapper(width, height); }, this); + glutKeyboardFuncUcall([](uint8_t key, int32_t x, int32_t y, void* p) { self(p)->keyboardFunc(key, false, true); }, this); + glutKeyboardUpFuncUcall([](uint8_t key, int32_t x, int32_t y, void* p) { self(p)->keyboardFunc(key, false, false); }, this); + glutSpecialFuncUcall([](int32_t key, int32_t x, int32_t y, void* p) { self(p)->keyboardFunc(key, true, true); }, this); + glutSpecialUpFuncUcall([](int32_t key, int32_t x, int32_t y, void* p) { self(p)->keyboardFunc(key, true, false); }, this); + glutMouseFuncUcall([](int32_t button, int32_t state, int32_t x, int32_t y, void* p) { self(p)->mouseFunc(button, state, x, y); }, this); + glutMotionFuncUcall(mouseMoveFunc, this); + glutPassiveMotionFuncUcall(mouseMoveFunc, this); + glutMouseWheelFuncUcall([](int32_t button, int32_t dir, int32_t x, int32_t y, void* p) { self(p)->mMouseWheelFunc(button, dir, x, y); }, this); applyInitialWindowState(); SetVSync(vsyncEnabled()); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h index ca04b093a2589..eb6d4a220d0ce 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h @@ -35,16 +35,14 @@ class GPUDisplayFrontendGlut : public GPUDisplayFrontend private: int32_t FrontendMain() override; - static void displayFunc(); - static void glutLoopFunc(); - static void keyboardUpFunc(uint8_t key, int32_t x, int32_t y); - static void keyboardDownFunc(uint8_t key, int32_t x, int32_t y); - static void specialUpFunc(int32_t key, int32_t x, int32_t y); - static void specialDownFunc(int32_t key, int32_t x, int32_t y); - static void mouseMoveFunc(int32_t x, int32_t y); - static void mMouseWheelFunc(int32_t button, int32_t dir, int32_t x, int32_t y); - static void mouseFunc(int32_t button, int32_t state, int32_t x, int32_t y); - static void ResizeSceneWrapper(int32_t width, int32_t height); + static GPUDisplayFrontendGlut* self(void* p) { return static_cast(p); } // GLUT callback user data + void displayFunc(); + void glutLoopFunc(); + void keyboardFunc(int32_t key, bool special, bool down); + void mouseMoveFunc(int32_t x, int32_t y); + void mMouseWheelFunc(int32_t button, int32_t dir, int32_t x, int32_t y); + void mouseFunc(int32_t button, int32_t state, int32_t x, int32_t y); + void ResizeSceneWrapper(int32_t width, int32_t height); static int32_t GetKey(int32_t key); static void GetKey(int32_t keyin, int32_t& keyOut, int32_t& keyPressOut, bool special); From 1f44d1735ac48c2411b7128836db36aa45f8cf52 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 04:05:35 +0100 Subject: [PATCH 19/27] GPU Display Frontend: Use condition_variable to signal stop and config updates instead of busy loop / sleep --- .../display/frontend/GPUDisplayFrontend.cxx | 43 ++++++++++++------- .../display/frontend/GPUDisplayFrontend.h | 8 +++- .../frontend/GPUDisplayFrontendGlfw.cxx | 1 + .../frontend/GPUDisplayFrontendGlut.cxx | 1 + .../frontend/GPUDisplayFrontendNone.cxx | 10 +++++ .../frontend/GPUDisplayFrontendWayland.cxx | 1 + .../frontend/GPUDisplayFrontendX11.cxx | 1 + .../display/frontend/GPUDisplayGUI.cxx | 6 ++- .../display/frontend/GPUDisplayGUIChannel.h | 2 + 9 files changed, 56 insertions(+), 17 deletions(-) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index aecac676c6349..0db41feae33e9 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -44,7 +44,6 @@ GPUDisplayFrontend* createGPUDisplayFrontendWayland(); #include #include #include -#include #include #ifdef GPUCA_BUILD_EVENT_DISPLAY_QT @@ -87,16 +86,30 @@ void GPUDisplayFrontend::waitForFPSLimit() mNextFrameTime = now; return; } - const auto period = std::chrono::duration_cast(std::chrono::duration(1. / cfg.fpsRate)); // TODO: wake up on HasExternalGUISignals + const auto period = std::chrono::duration_cast(std::chrono::duration(1. / cfg.fpsRate)); mNextFrameTime += period; if (mNextFrameTime < now - period) { // More than one frame behind (slow rendering, first frame), do not try to catch up mNextFrameTime = now; } - std::this_thread::sleep_until(mNextFrameTime); + std::unique_lock lock(mConfigUpdateMutex); + mConfigUpdateCV.wait_until(lock, mNextFrameTime, [this] { return mConfigUpdatePending; }); +} + +void GPUDisplayFrontend::signalConfigUpdate() +{ + { + std::lock_guard lock(mConfigUpdateMutex); + mConfigUpdatePending = true; + } + mConfigUpdateCV.notify_one(); } void GPUDisplayFrontend::HandleExternalGUISignals() { + { + std::lock_guard lock(mConfigUpdateMutex); + mConfigUpdatePending = false; // Before fetching the updates, so that updates sent in the meantime signal again + } HandleExternalGUIKeys(); const bool gui = isGUIRunning(); @@ -131,8 +144,11 @@ void GPUDisplayFrontend::HandleExternalGUISignals() void GPUDisplayFrontend::sendExternalGUIKey(int32_t v) { - std::lock_guard lock(mExternalGUIKeyMutex); - mExternalGUIKeys.emplace_back(v); + { + std::lock_guard lock(mExternalGUIKeyMutex); + mExternalGUIKeys.emplace_back(v); + } + signalConfigUpdate(); } int32_t GPUDisplayFrontend::getDisplayControl() const { return mDisplay->mCfgC.mDisplayControl.load(); } @@ -177,6 +193,7 @@ int32_t GPUDisplayFrontend::startGUI() #ifdef GPUCA_BUILD_EVENT_DISPLAY_QT if (!mGUI) { mGUIChannel = std::make_unique(); + mGUIChannel->updatePosted = [this]() { signalConfigUpdate(); }; mGUI = std::make_unique(*mGUIChannel); } if (!mGUI->isRunning()) { @@ -319,17 +336,13 @@ int32_t GPUDisplayFrontend::StartDisplay() void GPUDisplayFrontend::StopDisplay() { - { - std::lock_guard lock(mExitStateMutex); - if (mDisplayRunning) { - mDisplay->mCfgC.mDisplayControl.store(2); - StopDisplayA(); - mDisplay->UnblockDisplay(); - } - } - while (mDisplayRunning) { - std::this_thread::sleep_for(std::chrono::microseconds(10000)); // TODO: Signal by semaphore + std::unique_lock lock(mExitStateMutex); + if (mDisplayRunning) { + mDisplay->mCfgC.mDisplayControl.store(2); + StopDisplayA(); + mDisplay->UnblockDisplay(); } + mDisplayStoppedCV.wait(lock, [this] { return !mDisplayRunning; }); } void GPUDisplayFrontend::StopDisplayA() {} diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index b3db83c7e83e9..a18920ae0acb9 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,8 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface virtual int32_t FrontendMain() = 0; virtual void StopDisplayA(); static void* FrontendThreadWrapper(void*); - void waitForFPSLimit(); // Sleep until the next frame is due according to fpsRate (only in FPS_LIMIT_RATE mode, see GPUDisplayBackend::fpsLimitMode) + void waitForFPSLimit(); // Sleep until the next frame is due according to fpsRate (only in FPS_LIMIT_RATE mode, see GPUDisplayBackend::fpsLimitMode), or until an external config update is signalled + void signalConfigUpdate(); // Signal a config update from an external source (console keys, Qt GUI), wakes up waitForFPSLimit static constexpr int32_t INIT_WIDTH = 1024, INIT_HEIGHT = 768; // Initial window size, before maximizing static constexpr int32_t MIN_WIDTH = 200, MIN_HEIGHT = 150; // Minimum window size @@ -99,7 +101,11 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface std::mutex mExternalGUIKeyMutex; std::vector mExternalGUIKeys; // Keys sent by external entity (usually console), may be ignored by backend. + std::mutex mConfigUpdateMutex; + std::condition_variable mConfigUpdateCV; // Notified by signalConfigUpdate + bool mConfigUpdatePending = false; // Protected by mConfigUpdateMutex, set by signalConfigUpdate, cleared by HandleExternalGUISignals std::mutex mExitStateMutex; + std::condition_variable mDisplayStoppedCV; // Notified with mExitStateMutex held when the frontend sets mDisplayRunning to false, StopDisplay waits for it std::array mKeyDownMap = {}; // Keyboard / Mouse actions diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx index 442cc7224509a..255ba11828be1 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx @@ -347,6 +347,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain() glfwDestroyWindow(mWindow); glfwTerminate(); mDisplayRunning = false; + mDisplayStoppedCV.notify_all(); } return retVal; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index 2f2dfb204fa50..0d64a7f79d40a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -259,6 +259,7 @@ int32_t GPUDisplayFrontendGlut::FrontendMain() std::lock_guard lock(mExitStateMutex); setDisplayControl(2); mDisplayRunning = false; + mDisplayStoppedCV.notify_all(); } ExitDisplay(); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx index ce616814e208b..f195fce1977ce 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.cxx @@ -27,9 +27,19 @@ int32_t GPUDisplayFrontendNone::FrontendMain() if (InitDisplay()) { return 1; } + { + std::lock_guard lock(mExitStateMutex); + mDisplayRunning = true; + } do { HandleUpdatesAndDrawGLScene(); waitForFPSLimit(); } while (getDisplayControl() != 2); + ExitDisplay(); + { + std::lock_guard lock(mExitStateMutex); + mDisplayRunning = false; + mDisplayStoppedCV.notify_all(); + } return 0; } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx index 9f6a70a63faac..5d1d175a14838 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx @@ -626,6 +626,7 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() { std::lock_guard lock(mExitStateMutex); mDisplayRunning = false; + mDisplayStoppedCV.notify_all(); } if (mCursorShapeDevice) { diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx index 3f0cfcbf77b78..e90a3062a54ce 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx @@ -476,6 +476,7 @@ int32_t GPUDisplayFrontendX11::FrontendMain() { std::lock_guard lock(mExitStateMutex); mDisplayRunning = false; + mDisplayStoppedCV.notify_all(); } return (0); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx index 93a5caa2fa4dd..c2e98b4e6e754 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx @@ -65,7 +65,8 @@ void GPUDisplayGUI::timerTick() } } const bool apply = newState && mUpdate.empty() && mState.appliedUpdate == mLastSentId; // State contains all our updates, and we have no unsent changes - if (!mUpdate.empty() && mLastSentId - mLastAppliedId < mChannel.maxUpdatesAhead) { + const bool post = !mUpdate.empty() && mLastSentId - mLastAppliedId < mChannel.maxUpdatesAhead; + if (post) { if (mChannel.update) { // Previous update not yet picked up by the display, merge auto& update = *mChannel.update; update.keys.insert(update.keys.end(), mUpdate.keys.begin(), mUpdate.keys.end()); @@ -81,6 +82,9 @@ void GPUDisplayGUI::timerTick() } lock.unlock(); + if (post) { + mChannel.updatePosted(); + } if (apply) { applySettings(); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h b/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h index cf24951477d0a..0ee36a4ec213e 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ struct GPUDisplayGUIChannel { std::chrono::milliseconds replyTimeout; // Maximum time the GUI waits for a reply uint32_t maxUpdatesAhead = 0; // GUI waits for the reply if it is that many updates ahead of the last one it applied std::string windowName; // Name of the display window, set when starting the GUI + std::function updatePosted; // Set by the display, called by the GUI (without holding mutex) after posting an update, wakes up the display }; } // namespace o2::gpu From ef0ff3c9ff6f06792835b417456be9c814f9aeac Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 04:32:06 +0100 Subject: [PATCH 20/27] GPU Wayland Display: Fix handling of F keys --- GPU/GPUTracking/display/CMakeLists.txt | 6 +-- .../frontend/GPUDisplayFrontendWayland.cxx | 23 ++---------- .../frontend/GPUDisplayFrontendWayland.h | 1 + .../frontend/GPUDisplayFrontendWaylandEv.cxx | 37 +++++++++++++++++++ 4 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayFrontendWaylandEv.cxx diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index b96862db705cf..4d242b5a0930e 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -167,10 +167,10 @@ if(GPUCA_EVENT_DISPLAY_WAYLAND) list(APPEND WAYLAND_PROTOCOL_SOURCES ${source}) endforeach() - set_property(SOURCE frontend/GPUDisplayFrontendWayland.cxx ${WAYLAND_PROTOCOL_SOURCES} APPEND PROPERTY OBJECT_DEPENDS ${WAYLAND_PROTOCOL_HEADERS}) - set_property(SOURCE frontend/GPUDisplayFrontendWayland.cxx APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/xdg-gen/) + set_property(SOURCE frontend/GPUDisplayFrontendWayland.cxx frontend/GPUDisplayFrontendWaylandEv.cxx ${WAYLAND_PROTOCOL_SOURCES} APPEND PROPERTY OBJECT_DEPENDS ${WAYLAND_PROTOCOL_HEADERS}) + set_property(SOURCE frontend/GPUDisplayFrontendWayland.cxx frontend/GPUDisplayFrontendWaylandEv.cxx APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/xdg-gen/) - set(SRCS_NO_H ${SRCS_NO_H} frontend/GPUDisplayFrontendWayland.cxx ${WAYLAND_PROTOCOL_SOURCES}) + set(SRCS_NO_H ${SRCS_NO_H} frontend/GPUDisplayFrontendWayland.cxx frontend/GPUDisplayFrontendWaylandEv.cxx ${WAYLAND_PROTOCOL_SOURCES}) endif() if(X11_FOUND) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx index 5d1d175a14838..900af8061b587 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx @@ -38,7 +38,6 @@ #include "viewporter-client-protocol.h" #include "cursor-shape-client-protocol.h" #include -#include using namespace o2::gpu; @@ -95,7 +94,7 @@ int32_t GPUDisplayFrontendWayland::GetKey(uint32_t key, uint32_t state) return (KEY_HOME); } else if (sym == XKB_KEY_Insert) { return (KEY_INSERT); - } else if (sym == XKB_KEY_F1) { // TODO: Fix F* keys not working + } else if (sym == XKB_KEY_F1) { return (KEY_F1); } else if (sym == XKB_KEY_F2) { return (KEY_F2); @@ -174,24 +173,8 @@ int32_t GPUDisplayFrontendWayland::FrontendMain() }; auto pointer_button = [](void* data, wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { GPUDisplayFrontendWayland* me = (GPUDisplayFrontendWayland*)data; - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - if (button == BTN_RIGHT) { - me->mMouseDnR = true; - } else if (button == BTN_LEFT) { - me->mMouseDn = true; - } else if (button == BTN_MIDDLE) { - me->mMouseDnM = true; - } - me->mMouseDnX = me->mMouseMvX; - me->mMouseDnY = me->mMouseMvY; - } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (button == BTN_RIGHT) { - me->mMouseDnR = false; - } else if (button == BTN_LEFT) { - me->mMouseDn = false; - } else if (button == BTN_MIDDLE) { - me->mMouseDnM = false; - } + if (state == WL_POINTER_BUTTON_STATE_PRESSED || state == WL_POINTER_BUTTON_STATE_RELEASED) { + me->handlePointerButton(button, state == WL_POINTER_BUTTON_STATE_PRESSED); } }; auto pointer_frame = [](void* data, wl_pointer* wl_pointer) { ((GPUDisplayFrontendWayland*)data)->applyScroll(); }; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h index bd55aa53f4b55..068135eb2b725 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h @@ -52,6 +52,7 @@ class GPUDisplayFrontendWayland : public GPUDisplayFrontend private: int32_t FrontendMain() override; + void handlePointerButton(uint32_t button, bool pressed); // In GPUDisplayFrontendWaylandEv.cxx, which needs the evdev button codes int32_t GetKey(uint32_t key, uint32_t state); void updateScale(); // Scale from wp_fractional_scale_v1, else integer wl_surface.preferred_buffer_scale, or max scale of the outputs the surface is on (older compositors) void updateSize(); // Physical size = logical size * scale, resizes the scene if changed diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWaylandEv.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWaylandEv.cxx new file mode 100644 index 0000000000000..4e5e68be566b1 --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWaylandEv.cxx @@ -0,0 +1,37 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayFrontendWaylandEv.cxx +/// \author David Rohr + +// Functions of the Wayland frontend that need the evdev codes from linux/input-event-codes.h. +// That header defines macros KEY_UP, KEY_F1, ..., which replace GPUDisplayFrontend::KEY_* (e.g. KEY_F1 becomes 59 = ';'), so it must not be included in GPUDisplayFrontendWayland.cxx, +// and here only after all other headers, and the functions in this file must not use GPUDisplayFrontend::KEY_*. + +#include "GPUDisplayFrontendWayland.h" +#include + +using namespace o2::gpu; + +void GPUDisplayFrontendWayland::handlePointerButton(uint32_t button, bool pressed) +{ + if (button == BTN_RIGHT) { + mMouseDnR = pressed; + } else if (button == BTN_LEFT) { + mMouseDn = pressed; + } else if (button == BTN_MIDDLE) { + mMouseDnM = pressed; + } + if (pressed) { + mMouseDnX = mMouseMvX; + mMouseDnY = mMouseMvY; + } +} From fd76c949ded251eccc67a702095b2e31c19910f5 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 04:47:40 +0100 Subject: [PATCH 21/27] GPU Display: Drop / implement some simple TODOs --- .../display/backend/GPUDisplayBackend.cxx | 11 ++-- .../backend/GPUDisplayBackendOpenGL.cxx | 1 - .../backend/GPUDisplayBackendVulkan.cxx | 66 ++++++++++++------- .../display/backend/GPUDisplayBackendVulkan.h | 8 ++- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx index c1e39311b1768..bb8d1ac8176fc 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx @@ -163,13 +163,16 @@ std::vector GPUDisplayBackend::getPixels() void GPUDisplayBackend::fillIndirectCmdBuffer() { - mCmdBuffer.clear(); mIndirectSectorOffset.resize(GPUTPCGeometry::NSECTORS); - // TODO: Check if this can be parallelized + size_t total = 0; + for (uint32_t iSector = 0; iSector < GPUTPCGeometry::NSECTORS; iSector++) { + mIndirectSectorOffset[iSector] = total; + total += mDisplay->vertexBufferStart()[iSector].size(); + } + mCmdBuffer.resize(total); for (uint32_t iSector = 0; iSector < GPUTPCGeometry::NSECTORS; iSector++) { - mIndirectSectorOffset[iSector] = mCmdBuffer.size(); for (uint32_t k = 0; k < mDisplay->vertexBufferStart()[iSector].size(); k++) { - mCmdBuffer.emplace_back(mDisplay->vertexBufferCount()[iSector][k], 1, mDisplay->vertexBufferStart()[iSector][k], 0); + mCmdBuffer[mIndirectSectorOffset[iSector] + k] = DrawArraysIndirectCommand(mDisplay->vertexBufferCount()[iSector][k], 1, mDisplay->vertexBufferStart()[iSector][k], 0); } } } diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx index d5342e0ab5abb..91b431a7d07c6 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx @@ -459,7 +459,6 @@ void GPUDisplayBackendOpenGL::clearScreen(bool alphaOnly) void GPUDisplayBackendOpenGL::loadDataToGPU(size_t totalVertizes) { - // TODO: Check if this can be parallelized if (mDisplay->useMultiVBO()) { for (uint32_t i = 0; i < GPUTPCGeometry::NSECTORS; i++) { CHKERR(glNamedBufferData(mVBOId[i], mDisplay->vertexBuffer()[i].size() * sizeof(mDisplay->vertexBuffer()[i][0]), mDisplay->vertexBuffer()[i].data(), GL_STATIC_DRAW)); diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx index b0d63bfda17cf..7a9a95996b3ce 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx @@ -617,7 +617,7 @@ void GPUDisplayBackendVulkan::clearDevice() // ---------------------------- VULKAN COMMAND BUFFERS ---------------------------- -void GPUDisplayBackendVulkan::createCommandBuffers() +void GPUDisplayBackendVulkan::createCommandBuffersForImages() { vk::CommandBufferAllocateInfo allocInfo{}; allocInfo.commandPool = mCommandPool; @@ -632,7 +632,7 @@ void GPUDisplayBackendVulkan::createCommandBuffers() mCommandBuffersPresent = mDevice.allocateCommandBuffers(allocInfo); } -void GPUDisplayBackendVulkan::clearCommandBuffers() +void GPUDisplayBackendVulkan::clearCommandBuffersForImages() { mDevice.freeCommandBuffers(mCommandPool, mCommandBuffers.size(), mCommandBuffers.data()); mDevice.freeCommandBuffers(mCommandPool, mCommandBuffersText.size(), mCommandBuffersText.data()); @@ -648,18 +648,22 @@ void GPUDisplayBackendVulkan::createSemaphores() { vk::SemaphoreCreateInfo semaphoreInfo{}; mImageAvailableSemaphore.resize(mFramesInFlight); - mPresentSemaphore.resize(mImageCount); for (uint32_t i = 0; i < mFramesInFlight; i++) { mImageAvailableSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr); } - for (uint32_t i = 0; i < mImageCount; i++) { - mPresentSemaphore[i] = mDevice.createSemaphore(semaphoreInfo, nullptr); - } vk::SemaphoreTypeCreateInfo timelineTypeInfo{vk::SemaphoreType::eTimeline, 0}; semaphoreInfo.pNext = &timelineTypeInfo; mTimelineSemaphore = mDevice.createSemaphore(semaphoreInfo, nullptr); mTimelineValue = 0; mFrameTimelineValue.assign(mFramesInFlight, 0); +} + +void GPUDisplayBackendVulkan::createSemaphoresForImages() +{ + mPresentSemaphore.resize(mImageCount); + for (uint32_t i = 0; i < mImageCount; i++) { + mPresentSemaphore[i] = mDevice.createSemaphore(vk::SemaphoreCreateInfo{}, nullptr); + } mImageTimelineValue.assign(mImageCount, 0); for (uint32_t i = 0; i < mImageCount; i++) { mPresentFences.emplace_back(mDevice.createFence(vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled}, nullptr)); @@ -679,8 +683,12 @@ void GPUDisplayBackendVulkan::waitPresentFences() void GPUDisplayBackendVulkan::clearSemaphores() { clearVector(mImageAvailableSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); }); - clearVector(mPresentSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); }); mDevice.destroySemaphore(mTimelineSemaphore, nullptr); +} + +void GPUDisplayBackendVulkan::clearSemaphoresForImages() +{ + clearVector(mPresentSemaphore, [&](auto& x) { mDevice.destroySemaphore(x, nullptr); }); clearVector(mPresentFences, [&](auto& x) { mDevice.destroyFence(x, nullptr); }); } @@ -688,13 +696,6 @@ void GPUDisplayBackendVulkan::clearSemaphores() void GPUDisplayBackendVulkan::createUniformLayoutsAndBuffers() { - for (int32_t j = 0; j < 3; j++) { - mUniformBuffersMat[j].resize(mImageCount); - for (uint32_t i = 0; i < mImageCount; i++) { - mUniformBuffersMat[j][i] = createBuffer(sizeof(hmm_mat4), nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0); - } - } - vk::DescriptorSetLayoutBinding uboLayoutBindingMat{}; uboLayoutBindingMat.binding = 0; uboLayoutBindingMat.descriptorType = vk::DescriptorType::eUniformBuffer; @@ -714,7 +715,16 @@ void GPUDisplayBackendVulkan::createUniformLayoutsAndBuffers() mUniformDescriptor = mDevice.createDescriptorSetLayout(layoutInfo, nullptr); layoutInfo.bindingCount = 2; mUniformDescriptorTexture = mDevice.createDescriptorSetLayout(layoutInfo, nullptr); +} +void GPUDisplayBackendVulkan::createUniformLayoutsAndBuffersForImages() +{ + for (int32_t j = 0; j < 3; j++) { + mUniformBuffersMat[j].resize(mImageCount); + for (uint32_t i = 0; i < mImageCount; i++) { + mUniformBuffersMat[j][i] = createBuffer(sizeof(hmm_mat4), nullptr, vk::BufferUsageFlagBits::eUniformBuffer, mDisplay->cfg().vulkan.uniformBuffersInDeviceMemory ? 2 : 0); + } + } mUpdateUBOdrawMatrix = mUpdateUBOtextMatrix = mUpdateUBOmixMatrix = -1; } @@ -722,6 +732,10 @@ void GPUDisplayBackendVulkan::clearUniformLayoutsAndBuffers() { mDevice.destroyDescriptorSetLayout(mUniformDescriptor, nullptr); mDevice.destroyDescriptorSetLayout(mUniformDescriptorTexture, nullptr); +} + +void GPUDisplayBackendVulkan::clearUniformLayoutsAndBuffersForImages() +{ for (int32_t j = 0; j < 3; j++) { clearVector(mUniformBuffersMat[j], [&](auto& x) { clearBuffer(x); }); } @@ -818,22 +832,24 @@ void GPUDisplayBackendVulkan::createSwapChain(vk::SwapchainKHR oldSwapChain) } mSwapChainImages = mDevice.getSwapchainImagesKHR(mSwapChain); - uint32_t oldFramesInFlight = mFramesInFlight, oldImageCount = mImageCount; - mFramesInFlight = mDisplay->cfg().vulkan.nFramesInFlight; + uint32_t oldImageCount = mImageCount; mImageCount = mSwapChainImages.size(); if (mImageCount > sizeof(mUpdateUBOdrawMatrix) * 8) { throw std::runtime_error("Too many swapchain images"); } - if (mFramesInFlight != oldFramesInFlight || mImageCount != oldImageCount || !mCommandInfrastructureCreated) { + if (mImageCount != oldImageCount || !mCommandInfrastructureCreated) { if (mCommandInfrastructureCreated) { - clearUniformLayoutsAndBuffers(); // TODO: Check what must actually be cleaned up if frames in flight changes - clearCommandBuffers(); - clearSemaphores(); + clearUniformLayoutsAndBuffersForImages(); + clearCommandBuffersForImages(); + clearSemaphoresForImages(); + } else { + createUniformLayoutsAndBuffers(); + createSemaphores(); } - createUniformLayoutsAndBuffers(); - createCommandBuffers(); - createSemaphores(); + createUniformLayoutsAndBuffersForImages(); + createCommandBuffersForImages(); + createSemaphoresForImages(); mCommandInfrastructureCreated = true; } @@ -1397,8 +1413,10 @@ void GPUDisplayBackendVulkan::ExitBackendA() clearOffscreenBuffers(); clearSwapChain(); if (mCommandInfrastructureCreated) { + clearSemaphoresForImages(); clearSemaphores(); - clearCommandBuffers(); + clearCommandBuffersForImages(); + clearUniformLayoutsAndBuffersForImages(); clearUniformLayoutsAndBuffers(); } clearTextureSampler(); diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h index 4a621edc096d0..3e76e0a2c8360 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h @@ -116,17 +116,21 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend void createDevice(); void loadShaders(); void createTextureSampler(); - void createCommandBuffers(); + void createCommandBuffersForImages(); void createSemaphores(); + void createSemaphoresForImages(); void createUniformLayoutsAndBuffers(); + void createUniformLayoutsAndBuffersForImages(); void createSwapChain(vk::SwapchainKHR oldSwapChain = {}); void createOffscreenBuffers(bool forScreenshot = false, bool forMixing = false); void createPipeline(); void clearDevice(); void clearTextureSampler(); - void clearCommandBuffers(); + void clearCommandBuffersForImages(); void clearSemaphores(); + void clearSemaphoresForImages(); void clearUniformLayoutsAndBuffers(); + void clearUniformLayoutsAndBuffersForImages(); void clearOffscreenBuffers(); void clearSwapChain(bool keepSwapChain = false); void clearPipeline(); From e6196441b46db3cddc6260e3a04c4b85c2400bf8 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 05:09:12 +0100 Subject: [PATCH 22/27] GPU Display: Fix creating unrequested screenshot, when enabling saving of an animation without running an animation --- GPU/GPUTracking/display/GPUDisplay.cxx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 4faceadf34ec1..3e57de651e1c5 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -638,7 +638,9 @@ void GPUDisplay::DrawGLScene_internal(bool suspended, float animateTime, bool re mCfgC.mAnimateScreenshot = 0; } } - bool doScreenshot = (mCfgC.mRequestScreenshot || mCfgC.mAnimateScreenshot) && animateTime < 0; + const bool animateScreenshot = mCfgC.mAnimateScreenshot && mAnimate; // Copy, the animation resets mAnimateScreenshot in its last frame + const bool screenshotFrame = mCfgC.mRequestScreenshot || animateScreenshot; // Also for the mix pass (animateTime >= 0), which must use the same render size + const bool doScreenshot = screenshotFrame && animateTime < 0; updateOptions(); if (animateTime < 0 && (mUpdateEventData || mCfgC.mResetScene.load() || mUpdateVertexLists) && mIOPtrs) { @@ -680,7 +682,7 @@ void GPUDisplay::DrawGLScene_internal(bool suspended, float animateTime, bool re const float zFar = ((mParam->par.continuousTracking ? (mMaxClusterZ * GL_SCALE_FACTOR) : 8.f) + 50.f) * 2.f; const float aspect = mBackend->mRenderHeight ? (float)mBackend->mRenderWidth / (float)mBackend->mRenderHeight : 1.f; // No render size with the None backend const hmm_mat4 proj = HMM_Perspective(mCfgR.fov, aspect, 0.1f, zFar); - if (!mBackend->prepareDraw(proj, nextViewMatrix, doScreenshot || mCfgC.mRequestScreenshot, renderToMixBuffer, mixSlaveImage)) { + if (!mBackend->prepareDraw(proj, nextViewMatrix, screenshotFrame, renderToMixBuffer, mixSlaveImage)) { return; // No image available (e.g. hidden window), retry with the next frame } mBackend->pointSizeFactor(1); @@ -726,15 +728,15 @@ void GPUDisplay::DrawGLScene_internal(bool suspended, float animateTime, bool re } } - mBackend->finishFrame(mCfgC.mRequestScreenshot, renderToMixBuffer, mixSlaveImage); + mBackend->finishFrame(doScreenshot, renderToMixBuffer, mixSlaveImage); if (doScreenshot) { mCfgC.mRequestScreenshot = false; std::vector pixels = mBackend->getPixels(); char tmpFileName[48]; - if (mCfgC.mAnimateScreenshot) { + if (animateScreenshot) { snprintf(tmpFileName, 48, "mAnimation%d_%05d.bmp", mAnimationExport, mAnimationFrame); } - DoScreenshot(mCfgC.mAnimateScreenshot ? tmpFileName : mScreenshotFile.c_str(), pixels); + DoScreenshot(animateScreenshot ? tmpFileName : mScreenshotFile.c_str(), pixels); } } From 7606ffcbe3c4665f6e4e6cba4ed5f1158fdd8429 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 05:23:53 +0100 Subject: [PATCH 23/27] GPU Display Frontend: auto-generate info message for key-press if none given --- .../display/frontend/GPUDisplayFrontend.h | 1 + .../frontend/GPUDisplayFrontendConfig.cxx | 76 +++++++++++-------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index a18920ae0acb9..222b69f315c8f 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -138,6 +138,7 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface void HandleExternalGUISignals(); // Callback to handle signals from external GUIs bool HasExternalGUISignals() const; // Returns whether there are currently pending signals from external GUIs to be handled bool processKeyLambda(uint32_t key); // Process key lambda callback + void showConfigInfo(uint32_t index); // Info for a changed option (index in the config RTTI): its info callback, or "struct.option changed to value" void ResizeScene(int32_t width, int32_t height); // Callback when GL window is resized int32_t InitDisplay(bool initFailure = false); // Callback to initialize the GL Display (to be called in StartDisplay) void ExitDisplay(); // Callback to clean up the GL Display diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index e068e5865714a..7e2299eac84d6 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -86,6 +86,27 @@ struct GPUDisplayFrontend::configRTTIInfo { std::apply([&](auto... s) { ((retVal = i++ == index ? reinterpret_cast(&(display.*s)) : retVal), ...); }, configStructs); return retVal; } + std::optional valueString(GPUDisplay& display, uint32_t index) const // Current value of an option, printed with print_type as in qconfig.cxx, nullopt for unsupported types + { + const auto& e = rttiEntries[index]; + const char* ptr = configStructPtr(display, e.configStructIndex) + e.offset; + if (e.isVector || e.arraySize) { + return std::nullopt; + } else if (e.type == "bool") { + return qConfig::print_type(*(const bool*)ptr); + } else if (e.type == "float") { + return qConfig::print_type(*(const float*)ptr); + } else if (e.type == "int8_t") { + return qConfig::print_type(*(const int8_t*)ptr); + } else if (e.type == "uint8_t") { + return qConfig::print_type(*(const uint8_t*)ptr); + } else if (e.type == "int32_t" || e.type == "int") { + return qConfig::print_type(*(const int32_t*)ptr); + } else if (e.type == "uint32_t") { + return qConfig::print_type(*(const uint32_t*)ptr); + } + return std::nullopt; + } template uint32_t getRTTIIndex(GPUDisplay& display, M T::* member) // RTTI index of a direct member of a config struct { @@ -306,41 +327,24 @@ void GPUDisplay::buildConfig() buildConfigKeys(); rtti->configInfos.resize(rtti->rttiEntries.size()); - auto messageInfo = [this](const std::string& message, const T* value) { // Info from the qConfig message, value printed with print_type as in qconfig.cxx - return configKeyCallback([this, message, value]() { - char buffer[256]; - const int32_t n = snprintf(buffer, sizeof(buffer), message.c_str(), qConfig::print_type(*value).c_str()); - if (n < 0 || n >= (int32_t)sizeof(buffer)) { - GPUError("Info message too long: %s", message.c_str()); - return; - } - SetInfo("%s", buffer); - }); - }; - for (uint32_t i = 0; i < rtti->rttiEntries.size(); i++) { + for (uint32_t i = 0; i < rtti->rttiEntries.size(); i++) { // Info from the qConfig message, with the value as %s as in qconfig.cxx const auto& e = rtti->rttiEntries[i]; - if (e.message.empty() || e.isVector || e.arraySize) { + if (e.message.empty()) { continue; } - const char* ptr = GPUDisplayFrontend::configRTTIInfo::configStructPtr(*this, e.configStructIndex) + e.offset; - configKeyCallback info; - if (e.type == "bool") { - info = messageInfo(e.message, (const bool*)ptr); - } else if (e.type == "float") { - info = messageInfo(e.message, (const float*)ptr); - } else if (e.type == "int8_t") { - info = messageInfo(e.message, (const int8_t*)ptr); - } else if (e.type == "uint8_t") { - info = messageInfo(e.message, (const uint8_t*)ptr); - } else if (e.type == "int32_t" || e.type == "int") { - info = messageInfo(e.message, (const int32_t*)ptr); - } else if (e.type == "uint32_t") { - info = messageInfo(e.message, (const uint32_t*)ptr); - } else { + if (!rtti->valueString(*this, i)) { GPUError("No info message support for type %s of %s", e.type.c_str(), e.name.c_str()); continue; } - rtti->configInfos[i] = std::move(info); + rtti->configInfos[i] = [this, i, message = e.message]() { + char buffer[256]; + const int32_t n = snprintf(buffer, sizeof(buffer), message.c_str(), mFrontend->mConfigRTTI->valueString(*this, i)->c_str()); + if (n < 0 || n >= (int32_t)sizeof(buffer)) { + GPUError("Info message too long: %s", message.c_str()); + return; + } + SetInfo("%s", buffer); + }; } buildConfigInfo(); rtti->buildHelpText(); @@ -353,14 +357,24 @@ bool GPUDisplayFrontend::processKeyLambda(uint32_t key) } if (key < mConfigRTTI->configKeys.size() && mConfigRTTI->configKeys[key].callback) { mConfigRTTI->configKeys[key].callback(); - if (const uint32_t index = mConfigRTTI->configKeys[key].configRTTIIndex; index < mConfigRTTI->configInfos.size() && mConfigRTTI->configInfos[index]) { - mConfigRTTI->configInfos[index](); + if (const uint32_t index = mConfigRTTI->configKeys[key].configRTTIIndex; index < mConfigRTTI->configInfos.size()) { + showConfigInfo(index); } return true; } return false; } +void GPUDisplayFrontend::showConfigInfo(uint32_t index) +{ + if (mConfigRTTI->configInfos[index]) { + mConfigRTTI->configInfos[index](); + } else if (const auto value = mConfigRTTI->valueString(*mDisplay, index)) { // Generic info for options without info callback + const auto& e = mConfigRTTI->rttiEntries[index]; + mDisplay->SetInfo("%s.%s changed to %s", mConfigRTTI->configStructNames[e.configStructIndex].c_str(), e.name.c_str(), value->c_str()); + } +} + void GPUDisplay::HandleKey(uint8_t key) { if (key == mFrontend->KEY_ALT) { From 5e63de4bbd506f8644c6eacbfd74b3d6eed0fc87 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 16:06:31 +0100 Subject: [PATCH 24/27] GPU Display: Order key bindings for better grouping in the help text --- .../frontend/GPUDisplayFrontendConfigKeys.inc | 318 +++++++++--------- 1 file changed, 158 insertions(+), 160 deletions(-) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc index 62dbe363d38d5..a981576edcc2b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc @@ -20,18 +20,22 @@ void GPUDisplay::buildConfigKeys() { + mFrontend->mConfigRTTI->helpTextEntries.emplace_back("[MOUSE 1] / [MOUSE 2] / [MOUSE 1+2]", "Look around / Strafe camera / Zoom / Rotate"); + mFrontend->mConfigRTTI->helpTextEntries.emplace_back("[SHIFT]", "Slow Zoom / Move / Rotate"); + mFrontend->add_key_binding( - KEY_LAMBDA('1', mCfgL, drawClusters, val ^= 1;), - KEY_LAMBDA('2', mCfgL, drawInitLinks, val ^= 1;), - KEY_LAMBDA('3', mCfgL, drawLinks, val ^= 1;), - KEY_LAMBDA('4', mCfgL, drawSeeds, val ^= 1;), - KEY_LAMBDA('5', mCfgL, drawTracklets, val ^= 1;), - KEY_LAMBDA('6', mCfgL, drawTracks, val ^= 1;), - KEY_LAMBDA('7', mCfgL, drawExtrapolatedTracks, val ^= 1;), - KEY_LAMBDA('8', mCfgL, drawFinal, val ^= 1;), - KEY_LAMBDA('N', mCfgL, colorClusters, val ^= 1;), // - "Enable display of clusters, preseeds, seeds, starthits, tracklets, tracks, extrapolated tracks, merged tracks / Show assigned clusters in colors", - "[1] ... [8] / [N]"); + KEY_DOWN_MAP('w', FORWARD), + KEY_DOWN_MAP('s', BACKWARD), + KEY_DOWN_MAP('a', STRAFE_LEFT), + KEY_DOWN_MAP('d', STRAFE_RIGHT), // + "Zoom / Strafe Left and Right"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_PAGEUP, STRAFE_UP), + KEY_DOWN_MAP(mFrontend->KEY_PAGEDOWN, STRAFE_DOWN), + KEY_DOWN_MAP('e', ROTATE_LEFT), + KEY_DOWN_MAP('f', ROTATE_RIGHT), // + "Strafe up / down / Rotate left / right"); mFrontend->add_key_binding( KEY_LAMBDA(mFrontend->KEY_ESCAPE, mCfgC, mDisplayControl, val.store(2); SetInfo("Exiting", 1);), @@ -48,6 +52,99 @@ void GPUDisplay::buildConfigKeys() }), // "Quit / Next event / Reset Display Settings / Start / Stop Qt GUI"); + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_LEFT, ROTATE_YAW_LEFT), + KEY_DOWN_MAP(mFrontend->KEY_RIGHT, ROTATE_YAW_RIGHT), + KEY_DOWN_MAP(mFrontend->KEY_UP, ROTATE_PITCH_UP), + KEY_DOWN_MAP(mFrontend->KEY_DOWN, ROTATE_PITCH_DOWN), // + "Turn camera left / right / up / down"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_ALT, FOCUS_ORIGIN), + KEY_DOWN_MAP(mFrontend->KEY_CTRL, FOCUS_ORIENT_UP), // + KEY_LAMBDA(mFrontend->KEY_ALT + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camLookOrigin, { + val ^= 1; + mCfgR.cameraMode = val + 2 * mCfgR.camYUp; + }), + KEY_LAMBDA(mFrontend->KEY_CTRL + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camYUp, { + val ^= 1; + mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * val; + }), + KEY_LAMBDA(mFrontend->KEY_ENTER, mCfgR, cameraMode, { + val++; + if (val == 4) { + val = 0; + } + mCfgR.camLookOrigin = val & 1; + mCfgR.camYUp = val & 2; + }), + "Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", // + "[ALT] / [CTRL] / [ENTER]"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP(mFrontend->KEY_RCTRL, ROTATE_MODEL), + KEY_DOWN_MAP(mFrontend->KEY_RALT, ROTATE_AROUND_Z), // + "Rotate model instead of camera / rotate TPC around beamline"); + + mFrontend->add_key_binding( + KEY_LAMBDA('F', mCfgR, fullScreen, val ^= 1;), // + KEY_LAMBDA('_', mCfgR, maximized, val ^= 1;), // + KEY_LAMBDA('b', mCfgR, fov, { + if ((val += 5) > 175) { + val = 5; + } + }), + KEY_LAMBDA('m', mCfgR, rendererCompatSmoothing, { + if (!displayOpenGLCore || backend()->backendType() != GPUDisplayBackend::TYPE_OPENGL) { + val ^= 1; + } + }), + KEY_LAMBDA('I', mCfgR, useGLIndirectDraw, val ^= true;), // + "Switch mFullScreen / Maximized window / Change FOV (field of view) / Switch between core / compat renderer path (OpenGL profile, Vulkan smooth lines) / Enable / disable GL indirect draw"); + + mFrontend->add_key_binding( + KEY_LAMBDA('S', mCfgL, smoothPoints, val ^= true;), // + KEY_LAMBDA('A', mCfgL, smoothLines, val ^= true;), // + KEY_LAMBDA('D', mCfgL, depthBuffer, { + val ^= true; + mBackend->setDepthBuffer(); + }), // + "Enable or disable smoothing of points / smoothing of lines / depth buffer"); + + mFrontend->add_key_binding( + KEY_LAMBDA('W', mCfgR, drawQualityMSAA, { + val *= 2; + if (val < 2) { + val = 2; + } + if (val > 16 || val > mBackend->getMaxMSAA()) { + val = 0; + } + }), + KEY_LAMBDA('U', mCfgR, drawQualityDownsampleFSAA, { + val++; + if (val == 1) { + val = 2; + } + if (val == 5) { + val = 0; + } + }), + KEY_LAMBDA('V', mCfgR, drawQualityVSync, val ^= true;), // + KEY_LAMBDA('R', mCfgR, fpsLimit, val = (val + 1) % 3;), // + "Toggle anti-aliasing (MSAA at raster level / change downsampling FSAA factor) / toggle VSync / FPS limit (none, refresh rate, fpsRate)"); + + mFrontend->add_key_binding( + KEY_LAMBDA('#', mCfgL, invertColors, val ^= 1;), + KEY_LAMBDA('g', mCfgL, drawGrid, val ^= 1;), + KEY_LAMBDA('i', mCfgH, projectXY, val ^= 1;), // + "Invert colors / Draw Grid / Project onto XY-plane"); + + mFrontend->add_key_binding( + KEY_DOWN_MAP('+', INCREASE_WIDTH), + KEY_DOWN_MAP('-', DECREASE_WIDTH), // + "Increase / decrease point size (Hold SHIFT for lines)"); + mFrontend->add_key_binding( KEY_LAMBDA('l', mCfgL, drawSector, { if (val >= (mCfgL.drawRelatedSectors ? (NSECTORS / 4 - 1) : (NSECTORS - 1))) { @@ -77,35 +174,6 @@ void GPUDisplay::buildConfigKeys() }), // "Draw single sector (next / previous sector), draw related sectors (same plane in phi) / Show splitting of TPC in sectors by extruding volume, [:] resets"); - mFrontend->add_key_binding( - KEY_LAMBDA('y', mCfgC, dummy, { - if (mAnimateVectors[0].size() > 1) { - if (mCfgC.mAnimateScreenshot) { - mAnimationExport++; - } - startAnimation(); - SetInfo("Starting Animation", 1); - } else { - SetInfo("Insufficient Animation points to start Animation", 1); - } - }), - KEY_LAMBDA('Y', mCfgC, dummy, setAnimationPoint(); SetInfo("Added Animation point (%d points, %6.2f seconds)", (int32_t)mAnimateVectors[0].size(), mAnimateVectors[0].back());), // - KEY_LAMBDA('\'', mCfgC, dummy, removeAnimationPoint(); SetInfo("Removed Animation point", 1);), // - KEY_LAMBDA('X', mCfgC, dummy, resetAnimation(); SetInfo("Reset Animation points", 1);), // - KEY_LAMBDA('M', mCfgL, animationMode, { - val++; - if (val == 7) { - val = 0; - } - }), // - "Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)"); - - mFrontend->add_key_binding( - KEY_LAMBDA('#', mCfgL, invertColors, val ^= 1;), - KEY_LAMBDA('g', mCfgL, drawGrid, val ^= 1;), - KEY_LAMBDA('i', mCfgH, projectXY, val ^= 1;), // - "Invert colors / Draw Grid / Project onto XY-plane"); - mFrontend->add_key_binding( KEY_LAMBDA('x', mCfgL, excludeClusters, val = (val + 1) % 3;), KEY_LAMBDA('.', mCfgH, hideRejectedTracks, val ^= 1;), // @@ -139,24 +207,6 @@ void GPUDisplay::buildConfigKeys() }), // "Mark flagged clusters (splitPad = 0x1, splitTime = 0x2, edge = 0x4, singlePad = 0x8, rejectDistance = 0x10, rejectErr = 0x20 / Mark fake attached clusters / Mark clusters attached as adjacent"); - mFrontend->add_key_binding( - KEY_LAMBDA('L', mCfgL, showCollision, { - if (val >= mNCollissions - 1) { - val = -1; - } else { - val++; - } - }), - KEY_LAMBDA('K', mCfgL, showCollision, { - if (val <= -1) { - val = mNCollissions - 1; - } else { - val--; - } - }), - KEY_LAMBDA('C', mCfgL, colorCollisions, val ^= 1;), // - "Draw single collisions (next / previous) / Colorcode clusters of different collisions"); - mFrontend->add_key_binding( KEY_LAMBDA('v', mCfgH, hideRejectedClusters, val ^= 1;), // KEY_LAMBDA('j', mCfgH, separateExtrapolatedTracks, { @@ -178,6 +228,24 @@ void GPUDisplay::buildConfigKeys() KEY_LAMBDA('G', mCfgH, propagateLoopers, val ^= 1;), // "Extrapolate tracks / loopers"); + mFrontend->add_key_binding( + KEY_LAMBDA('L', mCfgL, showCollision, { + if (val >= mNCollissions - 1) { + val = -1; + } else { + val++; + } + }), + KEY_LAMBDA('K', mCfgL, showCollision, { + if (val <= -1) { + val = mNCollissions - 1; + } else { + val--; + } + }), + KEY_LAMBDA('C', mCfgL, colorCollisions, val ^= 1;), // + "Draw single collisions (next / previous) / Colorcode clusters of different collisions"); + mFrontend->add_key_binding( KEY_LAMBDA('t', mCfgC, mRequestScreenshot, { static int32_t nScreenshot = 1; @@ -197,52 +265,32 @@ void GPUDisplay::buildConfigKeys() "Take Screenshot / Record Animation to pictures / Change screenshot resolution (scaling factor)"); mFrontend->add_key_binding( - KEY_LAMBDA('S', mCfgL, smoothPoints, val ^= true;), // - KEY_LAMBDA('A', mCfgL, smoothLines, val ^= true;), // - KEY_LAMBDA('D', mCfgL, depthBuffer, { - val ^= true; - mBackend->setDepthBuffer(); - }), // - "Enable or disable smoothing of points / smoothing of lines / depth buffer"); - - mFrontend->add_key_binding( - KEY_LAMBDA('W', mCfgR, drawQualityMSAA, { - val *= 2; - if (val < 2) { - val = 2; - } - if (val > 16 || val > mBackend->getMaxMSAA()) { - val = 0; + KEY_LAMBDA('y', mCfgC, dummy, { + if (mAnimateVectors[0].size() > 1) { + if (mCfgC.mAnimateScreenshot) { + mAnimationExport++; + } + startAnimation(); + SetInfo("Starting Animation", 1); + } else { + SetInfo("Insufficient Animation points to start Animation", 1); } }), - KEY_LAMBDA('U', mCfgR, drawQualityDownsampleFSAA, { + KEY_LAMBDA('Y', mCfgC, dummy, setAnimationPoint(); SetInfo("Added Animation point (%d points, %6.2f seconds)", (int32_t)mAnimateVectors[0].size(), mAnimateVectors[0].back());), // + KEY_LAMBDA('\'', mCfgC, dummy, removeAnimationPoint(); SetInfo("Removed Animation point", 1);), // + KEY_LAMBDA('X', mCfgC, dummy, resetAnimation(); SetInfo("Reset Animation points", 1);), // + KEY_LAMBDA('M', mCfgL, animationMode, { val++; - if (val == 1) { - val = 2; - } - if (val == 5) { + if (val == 7) { val = 0; } - }), - KEY_LAMBDA('V', mCfgR, drawQualityVSync, val ^= true;), // - KEY_LAMBDA('R', mCfgR, fpsLimit, val = (val + 1) % 3;), // - "Toggle anti-aliasing (MSAA at raster level / change downsampling FSAA factor) / toggle VSync / FPS limit (none, refresh rate, fpsRate)"); + }), // + "Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)"); mFrontend->add_key_binding( - KEY_LAMBDA('F', mCfgR, fullScreen, val ^= 1;), // - KEY_LAMBDA('_', mCfgR, maximized, val ^= 1;), // - KEY_LAMBDA('b', mCfgR, fov, { - if ((val += 5) > 175) { - val = 5; - } - }), - KEY_LAMBDA('m', mCfgR, rendererCompatSmoothing, { - if (!displayOpenGLCore || backend()->backendType() != GPUDisplayBackend::TYPE_OPENGL) { - val ^= 1; - } - }), - KEY_LAMBDA('I', mCfgR, useGLIndirectDraw, val ^= true;), // - "Switch mFullScreen / Maximized window / Change FOV (field of view) / Switch between core / compat renderer path (OpenGL profile, Vulkan smooth lines) / Enable / disable GL indirect draw"); + KEY_LAMBDA('>', mCfgC, mAnimationChangeConfig, val ^= 1; SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled");), + KEY_DOWN_MAP('<', CHANGE_ANIMATION_INTERVAL), // + "Toggle config interpolation during Animation / change Animation interval (via movement)"); mFrontend->add_key_binding( KEY_LAMBDA('o', mCfgC, dummy, { @@ -313,6 +361,19 @@ void GPUDisplay::buildConfigKeys() }), // "Save / restore current camera position / Animation path"); + mFrontend->add_key_binding( + KEY_LAMBDA('1', mCfgL, drawClusters, val ^= 1;), + KEY_LAMBDA('2', mCfgL, drawInitLinks, val ^= 1;), + KEY_LAMBDA('3', mCfgL, drawLinks, val ^= 1;), + KEY_LAMBDA('4', mCfgL, drawSeeds, val ^= 1;), + KEY_LAMBDA('5', mCfgL, drawTracklets, val ^= 1;), + KEY_LAMBDA('6', mCfgL, drawTracks, val ^= 1;), + KEY_LAMBDA('7', mCfgL, drawExtrapolatedTracks, val ^= 1;), + KEY_LAMBDA('8', mCfgL, drawFinal, val ^= 1;), + KEY_LAMBDA('N', mCfgL, colorClusters, val ^= 1;), // + "Enable display of clusters, preseeds, seeds, starthits, tracklets, tracks, extrapolated tracks, merged tracks / Show assigned clusters in colors", + "[1] ... [8] / [N]"); + mFrontend->add_key_binding( KEY_LAMBDA(mFrontend->KEY_F1, mCfgL, drawTPC, val ^= 1;), KEY_LAMBDA(mFrontend->KEY_F2, mCfgL, drawTRD, val ^= 1;), @@ -349,69 +410,6 @@ void GPUDisplay::buildConfigKeys() SetInfo("Info text display - console: %s, onscreen %s", (val & 2) ? "enabled" : "disabled", (val & 1) ? "enabled" : "disabled"); }), // "Print Help / Show info texts"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP('w', FORWARD), - KEY_DOWN_MAP('s', BACKWARD), - KEY_DOWN_MAP('a', STRAFE_LEFT), - KEY_DOWN_MAP('d', STRAFE_RIGHT), // - "Zoom / Strafe Left and Right"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP(mFrontend->KEY_PAGEUP, STRAFE_UP), - KEY_DOWN_MAP(mFrontend->KEY_PAGEDOWN, STRAFE_DOWN), // - "Strafe up / down"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP('e', ROTATE_LEFT), - KEY_DOWN_MAP('f', ROTATE_RIGHT), // - "Rotate left / right"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP(mFrontend->KEY_LEFT, ROTATE_YAW_LEFT), - KEY_DOWN_MAP(mFrontend->KEY_RIGHT, ROTATE_YAW_RIGHT), - KEY_DOWN_MAP(mFrontend->KEY_UP, ROTATE_PITCH_UP), - KEY_DOWN_MAP(mFrontend->KEY_DOWN, ROTATE_PITCH_DOWN), // - "Turn camera left / right / up / down"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP('+', INCREASE_WIDTH), - KEY_DOWN_MAP('-', DECREASE_WIDTH), // - "Increase / decrease point size (Hold SHIFT for lines)"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP(mFrontend->KEY_ALT, FOCUS_ORIGIN), - KEY_DOWN_MAP(mFrontend->KEY_CTRL, FOCUS_ORIENT_UP), // - KEY_LAMBDA(mFrontend->KEY_ALT + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camLookOrigin, { - val ^= 1; - mCfgR.cameraMode = val + 2 * mCfgR.camYUp; - }), - KEY_LAMBDA(mFrontend->KEY_CTRL + mFrontend->SPECIAL_KEYS_SHIFTED, mCfgR, camYUp, { - val ^= 1; - mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * val; - }), - KEY_LAMBDA(mFrontend->KEY_ENTER, mCfgR, cameraMode, { - val++; - if (val == 4) { - val = 0; - } - mCfgR.camLookOrigin = val & 1; - mCfgR.camYUp = val & 2; - }), - "Focus camera on origin / orient y-axis upwards (combine with [SHIFT] to lock) / Cycle through modes", // - "[ALT] / [CTRL] / [ENTER]"); - - mFrontend->add_key_binding( - KEY_DOWN_MAP(mFrontend->KEY_RCTRL, ROTATE_MODEL), - KEY_DOWN_MAP(mFrontend->KEY_RALT, ROTATE_AROUND_Z), // - "Rotate model instead of camera / rotate TPC around beamline"); - - mFrontend->add_key_binding( - KEY_LAMBDA('>', mCfgC, mAnimationChangeConfig, val ^= 1; SetInfo("Interpolating visualization settings during Animation %s", mCfgC.mAnimationChangeConfig ? "enabled" : "disabled");), - KEY_DOWN_MAP('<', CHANGE_ANIMATION_INTERVAL), // - "Toggle config interpolation during Animation / change Animation interval (via movement)"); - - mFrontend->mConfigRTTI->helpTextEntries.emplace_back("[MOUSE 1] / [MOUSE 2] / [MOUSE 1+2] / [SHIFT]", "Look around / Strafe camera / Zoom / Rotate / Slow Zoom / Move / Rotate"); } #endif // GPUDISPLAYFRONTENDCONFIGKEYS_INC From 406c208ef0a71b9ac5fd30d6e94e851dddd2c64e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 31 Oct 2026 05:45:09 +0100 Subject: [PATCH 25/27] GPU Display: Drop obsolete compat profile for OpenGL --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 - GPU/GPUTracking/display/CMakeLists.txt | 2 +- GPU/GPUTracking/display/GPUDisplay.cxx | 2 - GPU/GPUTracking/display/GPUDisplay.h | 2 - .../display/backend/GPUDisplayBackend.h | 3 - .../display/backend/GPUDisplayBackendNone.h | 1 - .../backend/GPUDisplayBackendOpenGL.cxx | 202 ++++-------------- .../display/backend/GPUDisplayBackendOpenGL.h | 8 +- .../backend/GPUDisplayBackendVulkan.cxx | 54 ++--- .../display/backend/GPUDisplayBackendVulkan.h | 6 - .../display/frontend/GPUDisplayFrontend.h | 2 - .../frontend/GPUDisplayFrontendConfig.cxx | 6 - .../frontend/GPUDisplayFrontendConfigInfo.inc | 9 +- .../frontend/GPUDisplayFrontendConfigKeys.inc | 7 +- .../frontend/GPUDisplayFrontendGlfw.cxx | 6 +- .../display/frontend/GPUDisplayFrontendGlfw.h | 1 - .../frontend/GPUDisplayFrontendGlut.cxx | 15 +- .../display/frontend/GPUDisplayFrontendGlut.h | 1 - .../display/frontend/GPUDisplayFrontendNone.h | 1 - .../frontend/GPUDisplayFrontendWayland.cxx | 4 - .../frontend/GPUDisplayFrontendWayland.h | 1 - .../frontend/GPUDisplayFrontendWindows.cxx | 1 - .../frontend/GPUDisplayFrontendWindows.h | 1 - .../frontend/GPUDisplayFrontendX11.cxx | 55 +---- .../display/frontend/GPUDisplayFrontendX11.h | 3 - .../display/helpers/GPUDisplayHelpers.cxx | 2 - 26 files changed, 63 insertions(+), 333 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 553f0304abe24..afb40211247d9 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -502,7 +502,6 @@ AddOption(camYUp, bool, false, "", 0, "Orient the camera such that the y-axis is AddOption(cameraMode, int32_t, 0, "", 0, "Camera mode") AddOption(fullScreen, bool, false, "", 0, "Full Screen") AddOption(maximized, bool, true, "", 0, "Maximized window") -AddOption(rendererCompatSmoothing, bool, false, "", 0, "Use compat smoothing (if available) instead of shader for smooth lines / points") AddOption(drawQualityMSAA, int32_t, 0, "", 0, "MultiSample Anti Aliasing") AddOption(drawQualityDownsampleFSAA, int32_t, 0, "", 0, "Downsampling Anti Aliasing") AddOption(drawQualityVSync, bool, true, "", 0, "Enable Vertical Sync") diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index 4d242b5a0930e..e5674b91d52b2 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -251,7 +251,7 @@ if (OPENGL_GLU_FOUND) endif() if(GPUCA_CONFIG_GL3W) - target_compile_definitions(${targetName} PRIVATE GPUCA_DISPLAY_GL3W GPUCA_DISPLAY_OPENGL_CORE) + target_compile_definitions(${targetName} PRIVATE GPUCA_DISPLAY_GL3W) endif() if(ALIGPU_BUILD_TYPE STREQUAL "O2") diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 3e57de651e1c5..8651c054bc034 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -75,7 +75,6 @@ GPUDisplay::GPUDisplay(GPUDisplayFrontend* frontend, GPUChainTracking* chain, GP mBackend->mDisplay = this; frontend->mDisplay = this; frontend->mBackend = mBackend.get(); - mCfgR.rendererCompatSmoothing &= !mBackend->CoreProfile(); } void GPUDisplay::calcXYZ(const float* matrix) @@ -189,7 +188,6 @@ int32_t GPUDisplay::InitDisplay_internal() return 1; } mYFactor = mBackend->getYFactor(); - mDrawTextInCompatMode = !mBackend->mFreetypeInitialized && mFrontend->mCanDrawText == 1; int32_t height = 0, width = 0; mFrontend->getSize(width, height); if (height == 0 || width == 0) { diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 905db94448773..977bbc0b46b0c 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -85,7 +85,6 @@ class GPUDisplay : public GPUDisplayInterface const GPUParam* param() { return mParam; } const GPUSettingsProcessing& GetProcessingSettings() const { return mProcessingSettings; } GPUDisplayFrontend* frontend() { return mFrontend; } - bool drawTextInCompatMode() const { return mDrawTextInCompatMode; } int32_t& drawTextFontSize() { return mDrawTextFontSize; } struct control { @@ -253,7 +252,6 @@ class GPUDisplay : public GPUDisplayInterface std::mutex mMutexLoadAndShowEvent; std::condition_variable mCVLoadAndShowEvent; - bool mDrawTextInCompatMode = false; int32_t mDrawTextFontSize = 0; int32_t mNDrawCalls = 0; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackend.h b/GPU/GPUTracking/display/backend/GPUDisplayBackend.h index 38114662714af..4245a16cee303 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackend.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackend.h @@ -39,8 +39,6 @@ class GPUDisplayBackend virtual ~GPUDisplayBackend(); virtual int32_t ExtInit() { return 0; }; - virtual bool CoreProfile() { return false; }; - virtual uint32_t DepthBits() = 0; typedef std::tuple vboList; @@ -122,7 +120,6 @@ class GPUDisplayBackend vecpod mCmdBuffer; bool mFreetypeInitialized = false; float mFontRasterScale = 1.f; // Display scale the font was rasterized for - bool mFrontendCompatTextDraw = false; std::vector mScreenshotPixels; static constexpr float SMOOTH_POINT_EXTRA_SIZE = 0.6f; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h index 6d4160c093206..8746d5214db82 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h @@ -26,7 +26,6 @@ class GPUDisplayBackendNone : public GPUDisplayBackend ~GPUDisplayBackendNone() override = default; protected: - uint32_t DepthBits() override { return 32; }; uint32_t drawVertices(const vboList& v, const drawType t) override { return 0; } void ActivateColor(std::array& color) override {} void setDepthBuffer() override {} diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx index 91b431a7d07c6..be9a805c27739 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx @@ -66,15 +66,6 @@ bool GPUDisplayBackendOpenGL::canLimitToVSync() const return mDisplay->frontend()->canSetVSync(); } -bool GPUDisplayBackendOpenGL::CoreProfile() -{ -#ifdef GPUCA_DISPLAY_OPENGL_CORE - return true; -#else - return false; -#endif -} - // #define CHKERR(cmd) {cmd;} #define CHKERR(cmd) \ do { \ @@ -86,15 +77,6 @@ bool GPUDisplayBackendOpenGL::CoreProfile() } \ } while (false) -uint32_t GPUDisplayBackendOpenGL::DepthBits() -{ - GLint depthBits = 0; -#ifndef GPUCA_DISPLAY_OPENGL_CORE - glGetIntegerv(GL_DEPTH_BITS, &depthBits); -#endif - return depthBits; -} - void GPUDisplayBackendOpenGL::createFB(GLfb& fb, bool tex, bool withDepth, bool msaa, uint32_t width, uint32_t height) { fb.tex = tex; @@ -185,22 +167,13 @@ uint32_t GPUDisplayBackendOpenGL::drawVertices(const vboList& v, const drawType } if (mDisplay->useMultiVBO()) { - if (!mDisplay->cfgR().rendererCompatSmoothing) { // TODO: Remove OpenGL compat profile completely - CHKERR(glBindVertexArray(mVertexArray)); - } + CHKERR(glBindVertexArray(mVertexArray)); CHKERR(glBindBuffer(GL_ARRAY_BUFFER, mVBOId[iSector])); -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - CHKERR(glVertexPointer(3, GL_FLOAT, 0, nullptr)); - } else -#endif - { - CHKERR(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); - glEnableVertexAttribArray(0); - } + CHKERR(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); + glEnableVertexAttribArray(0); } - const uint32_t smoothProgram = tt == POINTS ? (mUseSmoothPointShader ? mShaderProgramSmoothPoint : 0) : (mUseSmoothLineShader ? mShaderProgramSmoothLine : 0); + const uint32_t smoothProgram = tt == POINTS ? (mDisplay->cfgL().smoothPoints ? mShaderProgramSmoothPoint : 0) : (mDisplay->cfgL().smoothLines ? mShaderProgramSmoothLine : 0); if (smoothProgram) { CHKERR(glUseProgram(smoothProgram)); } @@ -222,19 +195,12 @@ uint32_t GPUDisplayBackendOpenGL::drawVertices(const vboList& v, const drawType void GPUDisplayBackendOpenGL::ActivateColor(std::array& color) { -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - glColor4f(color[0], color[1], color[2], color[3]); - } else -#endif - { - CHKERR(glUniform4fv(UNIFORM_LOCATION_COLOR, 1, &color[0])); - if (mUseSmoothPointShader) { - CHKERR(glProgramUniform4fv(mShaderProgramSmoothPoint, UNIFORM_LOCATION_COLOR, 1, &color[0])); - } - if (mUseSmoothLineShader) { - CHKERR(glProgramUniform4fv(mShaderProgramSmoothLine, UNIFORM_LOCATION_COLOR, 1, &color[0])); - } + CHKERR(glUniform4fv(UNIFORM_LOCATION_COLOR, 1, &color[0])); + if (mDisplay->cfgL().smoothPoints) { + CHKERR(glProgramUniform4fv(mShaderProgramSmoothPoint, UNIFORM_LOCATION_COLOR, 1, &color[0])); + } + if (mDisplay->cfgL().smoothLines) { + CHKERR(glProgramUniform4fv(mShaderProgramSmoothLine, UNIFORM_LOCATION_COLOR, 1, &color[0])); } } @@ -325,9 +291,6 @@ int32_t GPUDisplayBackendOpenGL::InitBackendA() CHKERR(glBindBuffer(GL_ARRAY_BUFFER, mVBOId[0])); CHKERR(glGenBuffers(1, &mIndirectId)); CHKERR(glBindBuffer(GL_DRAW_INDIRECT_BUFFER, mIndirectId)); -#ifndef GPUCA_DISPLAY_OPENGL_CORE - CHKERR(glShadeModel(GL_SMOOTH)); // Enable Smooth Shading -#endif setDepthBuffer(); setQuality(); CHKERR(mVertexShader = loadShader(GL_VERTEX_SHADER, _binary_opengl_vertex_vert_spv_start, _binary_opengl_vertex_vert_spv_len)); @@ -493,77 +456,34 @@ bool GPUDisplayBackendOpenGL::prepareDraw(const hmm_mat4& proj, const hmm_mat4& setFrameBuffer(0); } clearScreen(); - mUseSmoothPointShader = mDisplay->cfgL().smoothPoints && !mDisplay->cfgR().rendererCompatSmoothing; // The compat profile uses GL_POINT_SMOOTH / GL_LINE_SMOOTH - mUseSmoothLineShader = mDisplay->cfgL().smoothLines && !mDisplay->cfgR().rendererCompatSmoothing; -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mUseSmoothPointShader) { // Compatibility context: gl_PointCoord is only defined with point sprites - CHKERR(glEnable(GL_POINT_SPRITE)); - } else { - CHKERR(glDisable(GL_POINT_SPRITE)); - } - if (mDisplay->cfgL().smoothPoints && mDisplay->cfgR().rendererCompatSmoothing) { - CHKERR(glEnable(GL_POINT_SMOOTH)); - } else { - CHKERR(glDisable(GL_POINT_SMOOTH)); - } - if (mDisplay->cfgL().smoothLines && mDisplay->cfgR().rendererCompatSmoothing) { - CHKERR(glEnable(GL_LINE_SMOOTH)); - } else { - CHKERR(glDisable(GL_LINE_SMOOTH)); - } -#endif CHKERR(glEnable(GL_BLEND)); CHKERR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glViewport(0, 0, mRenderWidth, mRenderHeight); -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - CHKERR(glEnableClientState(GL_VERTEX_ARRAY)); - CHKERR(glVertexPointer(3, GL_FLOAT, 0, nullptr)); - } else -#endif - { - CHKERR(glBindVertexArray(mVertexArray)); - CHKERR(glUseProgram(mShaderProgram)); - CHKERR(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); - CHKERR(glEnableVertexAttribArray(0)); - } -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - CHKERR(glMatrixMode(GL_PROJECTION)); - CHKERR(glLoadMatrixf(&proj.Elements[0][0])); - CHKERR(glMatrixMode(GL_MODELVIEW)); - CHKERR(glLoadMatrixf(&view.Elements[0][0])); - } else -#endif - { - const hmm_mat4 modelViewProj = proj * view; - CHKERR(glUniformMatrix4fv(UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &modelViewProj.Elements[0][0])); - if (mUseSmoothPointShader) { - CHKERR(glProgramUniformMatrix4fv(mShaderProgramSmoothPoint, UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &modelViewProj.Elements[0][0])); - } - if (mUseSmoothLineShader) { - CHKERR(glProgramUniformMatrix4fv(mShaderProgramSmoothLine, UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &modelViewProj.Elements[0][0])); - CHKERR(glProgramUniform2f(mShaderProgramSmoothLine, UNIFORM_LOCATION_VIEWPORT, mRenderWidth, mRenderHeight)); - } - if (mMagneticFieldVisualization) { - CHKERR(glNamedBufferSubData(mFieldModelViewBuffer, 0, sizeof(modelViewProj), &modelViewProj)); - } + CHKERR(glBindVertexArray(mVertexArray)); + CHKERR(glUseProgram(mShaderProgram)); + CHKERR(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr)); + CHKERR(glEnableVertexAttribArray(0)); + + const hmm_mat4 modelViewProj = proj * view; + CHKERR(glUniformMatrix4fv(UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &modelViewProj.Elements[0][0])); + if (mDisplay->cfgL().smoothPoints) { + CHKERR(glProgramUniformMatrix4fv(mShaderProgramSmoothPoint, UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &modelViewProj.Elements[0][0])); + } + if (mDisplay->cfgL().smoothLines) { + CHKERR(glProgramUniformMatrix4fv(mShaderProgramSmoothLine, UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &modelViewProj.Elements[0][0])); + CHKERR(glProgramUniform2f(mShaderProgramSmoothLine, UNIFORM_LOCATION_VIEWPORT, mRenderWidth, mRenderHeight)); + } + if (mMagneticFieldVisualization) { + CHKERR(glNamedBufferSubData(mFieldModelViewBuffer, 0, sizeof(modelViewProj), &modelViewProj)); } return true; } void GPUDisplayBackendOpenGL::finishDraw(bool doScreenshot, bool toMixBuffer, float includeMixImage) { -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - CHKERR(glDisableClientState(GL_VERTEX_ARRAY)); - } else -#endif - { - CHKERR(glBindVertexArray(0)); - CHKERR(glUseProgram(0)); - } + CHKERR(glBindVertexArray(0)); + CHKERR(glUseProgram(0)); if (mOffscreenBufferMSAA.created) { uint32_t dstId = toMixBuffer ? mMixBuffer.fb_id : (mDownsampleFactor != 1 ? mOffscreenBuffer.fb_id : 0); @@ -616,15 +536,7 @@ void GPUDisplayBackendOpenGL::prepareText() setFrameBuffer(0); glViewport(0, 0, mScreenWidth, mScreenHeight); hmm_mat4 proj = HMM_Orthographic(0.f, mScreenWidth, 0.f, mScreenHeight, -1, 1); - if (mDisplay->drawTextInCompatMode()) { -#ifndef GPUCA_DISPLAY_OPENGL_CORE - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(&proj.Elements[0][0]); - glViewport(0, 0, mScreenWidth, mScreenHeight); -#endif - } else if (mFreetypeInitialized) { + if (mFreetypeInitialized) { CHKERR(glUseProgram(mShaderProgramText)); CHKERR(glActiveTexture(GL_TEXTURE0)); CHKERR(glBindVertexArray(VAO_text)); @@ -636,7 +548,7 @@ void GPUDisplayBackendOpenGL::prepareText() void GPUDisplayBackendOpenGL::finishText() { - if (!mDisplay->drawTextInCompatMode() && mFreetypeInitialized) { + if (mFreetypeInitialized) { CHKERR(glBindVertexArray(0)); CHKERR(glBindTexture(GL_TEXTURE_2D, 0)); CHKERR(glUseProgram(0)); @@ -666,55 +578,25 @@ void GPUDisplayBackendOpenGL::mixImages(float mixSlaveImage) { hmm_mat4 proj = HMM_Orthographic(0.f, mRenderWidth, 0.f, mRenderHeight, -1.f, 1.f); glDisable(GL_DEPTH_TEST); -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(&proj.Elements[0][0]); - CHKERR(glEnable(GL_TEXTURE_2D)); - } else -#endif - { - CHKERR(glUseProgram(mShaderProgramTexture)); - CHKERR(glActiveTexture(GL_TEXTURE0)); - CHKERR(glBindVertexArray(VAO_texture)); - CHKERR(glUniformMatrix4fv(UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &proj.Elements[0][0])); - CHKERR(glUniform1f(UNIFORM_LOCATION_COLOR, mixSlaveImage)); - } + CHKERR(glUseProgram(mShaderProgramTexture)); + CHKERR(glActiveTexture(GL_TEXTURE0)); + CHKERR(glBindVertexArray(VAO_texture)); + CHKERR(glUniformMatrix4fv(UNIFORM_LOCATION_MATRIX, 1, GL_FALSE, &proj.Elements[0][0])); + CHKERR(glUniform1f(UNIFORM_LOCATION_COLOR, mixSlaveImage)); CHKERR(glBindTexture(GL_TEXTURE_2D, mMixBuffer.fbCol_id)); -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (mDisplay->cfgR().rendererCompatSmoothing) { - glColor4f(1, 1, 1, mixSlaveImage); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex3f(0, 0, 0); - glTexCoord2f(0, 1); - glVertex3f(0, mRenderHeight, 0); - glTexCoord2f(1, 1); - glVertex3f(mRenderWidth, mRenderHeight, 0); - glTexCoord2f(1, 0); - glVertex3f(mRenderWidth, 0, 0); - glEnd(); - glColor4f(1, 1, 1, 0); - CHKERR(glDisable(GL_TEXTURE_2D)); - } else -#endif - { - CHKERR(glDrawArrays(GL_TRIANGLES, 0, 6)); - CHKERR(glBindVertexArray(0)); - CHKERR(glUseProgram(0)); - } + CHKERR(glDrawArrays(GL_TRIANGLES, 0, 6)); + CHKERR(glBindVertexArray(0)); + CHKERR(glUseProgram(0)); CHKERR(glBindTexture(GL_TEXTURE_2D, 0)); setDepthBuffer(); } void GPUDisplayBackendOpenGL::pointSizeFactor(float factor) { - const float size = mDisplay->cfgL().pointSize * mDownsampleFactor * factor + (mUseSmoothPointShader ? SMOOTH_POINT_EXTRA_SIZE : 0.f); - if (mUseSmoothPointShader) { + const float size = mDisplay->cfgL().pointSize * mDownsampleFactor * factor + (mDisplay->cfgL().smoothPoints ? SMOOTH_POINT_EXTRA_SIZE : 0.f); + if (mDisplay->cfgL().smoothPoints) { CHKERR(glProgramUniform1f(mShaderProgramSmoothPoint, UNIFORM_LOCATION_POINT_SIZE, size)); } CHKERR(glPointSize(size)); @@ -723,7 +605,7 @@ void GPUDisplayBackendOpenGL::pointSizeFactor(float factor) void GPUDisplayBackendOpenGL::lineWidthFactor(float factor) { float width = mDisplay->cfgL().lineWidth * mDownsampleFactor * factor; - if (mUseSmoothLineShader) { + if (mDisplay->cfgL().smoothLines) { width += SMOOTH_LINE_EXTRA_WIDTH; CHKERR(glProgramUniform1f(mShaderProgramSmoothLine, UNIFORM_LOCATION_LINE_WIDTH, width)); width *= 1.4143f; // Wide lines are parallelograms in OpenGL, widen so that the perpendicular width is covered @@ -788,7 +670,7 @@ void GPUDisplayBackendOpenGL::initializeTextDrawing() void GPUDisplayBackendOpenGL::OpenGLPrint(const char* s, float x, float y, float* color, float scale) { - if (!mFreetypeInitialized || mDisplay->drawTextInCompatMode()) { + if (!mFreetypeInitialized) { return; } CHKERR(glUniform4fv(UNIFORM_LOCATION_COLOR, 1, color)); diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h index f30a22846a1ac..768f046a89664 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h @@ -34,9 +34,7 @@ class GPUDisplayBackendOpenGL : public GPUDisplayBackend GPUDisplayBackendOpenGL(); ~GPUDisplayBackendOpenGL() override = default; int32_t ExtInit() override; - bool CoreProfile() override; bool canLimitToVSync() const override; // Buffer swap blocks with VSync, if the frontend can set the swap interval - uint32_t DepthBits() override; protected: void createFB(GLfb& fb, bool tex, bool withDepth, bool msaa, uint32_t width, uint32_t height); @@ -101,10 +99,8 @@ class GPUDisplayBackendOpenGL : public GPUDisplayBackend uint32_t mFragmentShaderDownsample; uint32_t mGeometryShader; uint32_t mShaderProgram; - uint32_t mShaderProgramSmoothPoint; // Points with fragmentSmoothPoint.frag, for smoothPoints with the shader path (not rendererCompatSmoothing / GL_POINT_SMOOTH) - bool mUseSmoothPointShader = false; - uint32_t mShaderProgramSmoothLine; // Lines with vertexSmoothLine.vert / fragmentSmoothLine.frag, for smoothLines with the shader path (not rendererCompatSmoothing / GL_LINE_SMOOTH) - bool mUseSmoothLineShader = false; + uint32_t mShaderProgramSmoothPoint; // Points with fragmentSmoothPoint.frag, for smoothPoints + uint32_t mShaderProgramSmoothLine; // Lines with vertexSmoothLine.vert / fragmentSmoothLine.frag, for smoothLines uint32_t mShaderProgramText; uint32_t mShaderProgramTexture; uint32_t mShaderProgramDownsample; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx index 7a9a95996b3ce..114966c964136 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx @@ -556,8 +556,6 @@ void GPUDisplayBackendVulkan::createDevice() if (checkDevice(mPhysicalDevice, reqDeviceExtensions, reqDeviceFeatures, true) <= 0) { throw std::runtime_error("Selected vulkan device is incompatible"); } - mSmoothLinesSupported = getFeatures(mPhysicalDevice, reqDeviceFeatures).get().smoothLines; - reqDeviceFeatures.get().smoothLines = mSmoothLinesSupported; std::vector deviceExtensions = reqDeviceExtensions; void* featureChain = &reqDeviceFeatures.get(); const std::vector availableDeviceExtensions = mPhysicalDevice.enumerateDeviceExtensionProperties(nullptr); @@ -578,7 +576,7 @@ void GPUDisplayBackendVulkan::createDevice() mStencilSupported = (bool)(depth64FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment); bool mailboxSupported = std::find(mSwapChainDetails.presentModes.begin(), mSwapChainDetails.presentModes.end(), vk::PresentModeKHR::eMailbox) != mSwapChainDetails.presentModes.end(); if (mDisplay->GetProcessingSettings().debugLevel >= 2) { - GPUInfo("Max MSAA: %d, 32 bit Z buffer %d, 32 bit Z buffer + stencil buffer %d, Mailbox present mode %d, FIFO latest ready present mode %d, smooth lines %d\n", (int32_t)mMaxMSAAsupported, (int32_t)mZSupported, (int32_t)mStencilSupported, (int32_t)mailboxSupported, (int32_t)mFifoLatestReadySupported, (int32_t)mSmoothLinesSupported); + GPUInfo("Max MSAA: %d, 32 bit Z buffer %d, 32 bit Z buffer + stencil buffer %d, Mailbox present mode %d, FIFO latest ready present mode %d\n", (int32_t)mMaxMSAAsupported, (int32_t)mZSupported, (int32_t)mStencilSupported, (int32_t)mailboxSupported, (int32_t)mFifoLatestReadySupported); } vk::DeviceQueueCreateInfo queueCreateInfo{}; @@ -877,24 +875,21 @@ void GPUDisplayBackendVulkan::recreateRendering(bool forScreenshot, bool forMixi needRecordCommandBuffers(); const bool needUpdateSwapChain = mMustUpdateSwapChain; const bool needUpdateOffscreenBuffers = needUpdateSwapChain || mDownsampleFactor != getDownsampleFactor(forScreenshot) || mMSAASampleCount != getMSAASamplesFlag(std::min(mMaxMSAAsupported, mDisplay->cfgR().drawQualityMSAA)) || mZActive != (mZSupported && mDisplay->cfgL().depthBuffer) || mMixingSupported != forMixing; - const bool smoothLinesChanged = mSmoothLines != useNativeSmoothLines(); - if (!needUpdateOffscreenBuffers && !smoothLinesChanged) { + if (!needUpdateOffscreenBuffers) { return; } mDevice.waitIdle(); const vk::Format oldFormat = mSurfaceFormat.format; const vk::SampleCountFlagBits oldMSAASampleCount = mMSAASampleCount; const bool oldZActive = mZActive; - if (needUpdateOffscreenBuffers) { - clearOffscreenBuffers(); - if (needUpdateSwapChain) { - clearSwapChain(true); - createSwapChain(mSwapChain); - } - createOffscreenBuffers(forScreenshot, forMixing); - mUpdateUBOtextMatrix = mUpdateUBOmixMatrix = -1; // Screen / render size may have changed + clearOffscreenBuffers(); + if (needUpdateSwapChain) { + clearSwapChain(true); + createSwapChain(mSwapChain); } - if (mSurfaceFormat.format != oldFormat || mMSAASampleCount != oldMSAASampleCount || mZActive != oldZActive || smoothLinesChanged) { // Viewport / scissor are dynamic + createOffscreenBuffers(forScreenshot, forMixing); + mUpdateUBOtextMatrix = mUpdateUBOmixMatrix = -1; // Screen / render size may have changed + if (mSurfaceFormat.format != oldFormat || mMSAASampleCount != oldMSAASampleCount || mZActive != oldZActive) { // Viewport / scissor are dynamic clearPipeline(); createPipeline(); } @@ -1112,15 +1107,10 @@ void GPUDisplayBackendVulkan::createPipeline() pipelineInfo.basePipelineHandle = VkPipeline(VK_NULL_HANDLE); // Optional pipelineInfo.basePipelineIndex = -1; // Optional - mSmoothLines = useNativeSmoothLines(); - vk::PipelineRasterizationLineStateCreateInfo smoothLineState{}; - smoothLineState.lineRasterizationMode = vk::LineRasterizationMode::eRectangularSmooth; // Coverage goes to alpha, blending is enabled - mPipelines.resize(9); // 0 points, 1 lines, 2 line strip (drawType index), 3 text, 4 texture, 5-7 smooth (shader-based) points, lines, line strip, 8 FSAA downsampling static constexpr vk::PrimitiveTopology types[3] = {vk::PrimitiveTopology::ePointList, vk::PrimitiveTopology::eLineList, vk::PrimitiveTopology::eLineStrip}; for (uint32_t i = 0; i < mPipelines.size(); i++) { if (i == 8) { // FSAA downsampling, full-screen triangle without vertex input (last pipeline, the changed states are not reset) - rasterizer.pNext = nullptr; rasterizer.cullMode = vk::CullModeFlagBits::eNone; vertexInputInfo.vertexBindingDescriptionCount = vertexInputInfo.vertexAttributeDescriptionCount = 0; inputAssembly.topology = vk::PrimitiveTopology::eTriangleList; @@ -1133,7 +1123,6 @@ void GPUDisplayBackendVulkan::createPipeline() colorBlendAttachment.blendEnable = false; multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1; } else if (i == 4) { // Texture rendering - rasterizer.pNext = nullptr; bindingDescription.stride = 4 * sizeof(float); attributeDescriptions.format = vk::Format::eR32G32B32A32Sfloat; inputAssembly.topology = vk::PrimitiveTopology::eTriangleList; @@ -1146,7 +1135,6 @@ void GPUDisplayBackendVulkan::createPipeline() colorBlendAttachment.blendEnable = true; multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1; } else if (i == 3) { // Text rendering - rasterizer.pNext = nullptr; bindingDescription.stride = 4 * sizeof(float); attributeDescriptions.format = vk::Format::eR32G32B32A32Sfloat; inputAssembly.topology = vk::PrimitiveTopology::eTriangleList; @@ -1163,7 +1151,6 @@ void GPUDisplayBackendVulkan::createPipeline() attributeDescriptions.format = vk::Format::eR32G32B32Sfloat; const uint32_t drawIdx = i >= 5 ? i - 5 : i; inputAssembly.topology = types[drawIdx]; - rasterizer.pNext = mSmoothLines && (i == 1 || i == 2) ? &smoothLineState : nullptr; vertShaderStageInfo.pNext = &mShaders.at(i < 5 ? (drawIdx == 0 ? "vertexPoint" : "vertex") : (drawIdx == 0 ? "vertexSmoothPoint" : "vertexSmoothLine")); fragShaderStageInfo.pNext = &mShaders.at(i < 5 ? "fragment" : (drawIdx == 0 ? "fragmentSmoothPoint" : "fragmentSmoothLine")); pipelineInfo.layout = mPipelineLayout; @@ -1471,7 +1458,7 @@ uint32_t GPUDisplayBackendVulkan::drawVertices(const vboList& v, const drawType return count; } - const int32_t pipeline = tt + ((tt == POINTS ? mDisplay->cfgL().smoothPoints : useSmoothLineShader()) ? 5 : 0); + const int32_t pipeline = tt + ((tt == POINTS ? mDisplay->cfgL().smoothPoints : mDisplay->cfgL().smoothLines) ? 5 : 0); if (mCurrentCommandBufferLastPipeline != pipeline) { mCurrentCommandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mPipelines[pipeline]); mCurrentCommandBufferLastPipeline = pipeline; @@ -1514,7 +1501,7 @@ bool GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4& if (mPresentModeForVSyncLimit != (fpsLimitMode() == FPS_LIMIT_VSYNC)) { // Present mode depends on the FPS limit mode mMustUpdateSwapChain = true; } - bool mustUpdateSwapChain = mMustUpdateSwapChain || mDisplay->updateRenderPipeline() || (toMixBuffer && !mMixingSupported) || mDownsampleFactor != getDownsampleFactor(requestScreenshot) || mSmoothLines != useNativeSmoothLines(); + bool mustUpdateSwapChain = mMustUpdateSwapChain || mDisplay->updateRenderPipeline() || (toMixBuffer && !mMixingSupported) || mDownsampleFactor != getDownsampleFactor(requestScreenshot); if (!mustUpdateSwapChain) { retVal = getImage(); if (retVal == vk::Result::eErrorOutOfDateKHR) { @@ -1799,23 +1786,13 @@ void GPUDisplayBackendVulkan::pointSizeFactor(float factor) mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eVertex, sizeof(std::array), sizeof(size), &size); } -bool GPUDisplayBackendVulkan::useNativeSmoothLines() const -{ - return mDisplay->cfgL().smoothLines && mSmoothLinesSupported && mDisplay->cfgR().rendererCompatSmoothing; -} - -bool GPUDisplayBackendVulkan::useSmoothLineShader() const -{ - return mDisplay->cfgL().smoothLines && !useNativeSmoothLines(); -} - void GPUDisplayBackendVulkan::lineWidthFactor(float factor) { if (mCommandBufferUpToDate[mCurrentImageIndex]) { return; } float width = mDisplay->cfgL().lineWidth * mDownsampleFactor * factor; - if (useSmoothLineShader()) { + if (mDisplay->cfgL().smoothLines) { const float expand = mStrictLines ? 1.f : 1.4143f; // Non-strict wide lines may be parallelograms, widen so that the perpendicular width is covered width = std::min(width + SMOOTH_LINE_EXTRA_WIDTH, mMaxLineWidth / expand); // The shader must see the width that is actually rasterized mCurrentCommandBuffer.pushConstants(mPipelineLayout, vk::ShaderStageFlagBits::eVertex, 5 * sizeof(float), sizeof(width), &width); @@ -1913,7 +1890,7 @@ void GPUDisplayBackendVulkan::clearFontSymbols() void GPUDisplayBackendVulkan::OpenGLPrint(const char* s, float x, float y, float* color, float scale) { - if (!mFreetypeInitialized || mDisplay->drawTextInCompatMode()) { + if (!mFreetypeInitialized) { return; } @@ -2042,11 +2019,6 @@ void GPUDisplayBackendVulkan::readImageToPixels(vk::Image image, vk::ImageView v } } -uint32_t GPUDisplayBackendVulkan::DepthBits() -{ - return 32; -} - bool GPUDisplayBackendVulkan::backendNeedRedraw() { return !mCommandBufferUpToDate[mCurrentImageIndex]; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h index 3e76e0a2c8360..2949e8cedfeb7 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h @@ -32,8 +32,6 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend GPUDisplayBackendVulkan(); ~GPUDisplayBackendVulkan() override; - uint32_t DepthBits() override; - protected: struct SwapChainSupportDetails { vk::SurfaceCapabilitiesKHR capabilities; @@ -81,8 +79,6 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend void cmdPushDescriptors(vk::CommandBuffer commandBuffer, vk::PipelineLayout layout, const VulkanBuffer& ubo, vk::ImageView textureView = {}); // Push descriptors: UBO at binding 0, optional texture at binding 2 void pointSizeFactor(float factor) override; void lineWidthFactor(float factor) override; - bool useNativeSmoothLines() const; - bool useSmoothLineShader() const; bool canLimitToVSync() const override { return true; } // Present wait void waitPresentFences(); void resizeScene(uint32_t width, uint32_t height) override; @@ -222,12 +218,10 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend bool mStencilSupported = false; float mMaxLineWidth = 1.f, mMaxPointSize = 1.f; bool mStrictLines = false; // Wide lines are rectangles, not parallelograms - bool mSmoothLinesSupported = false; // Optional Vulkan 1.4 feature smoothLines bool mFifoLatestReadySupported = false; // Optional present mode FIFO_LATEST_READY (VK_KHR / VK_EXT_present_mode_fifo_latest_ready) vk::SampleCountFlagBits mMSAASampleCount = vk::SampleCountFlagBits::e16; bool mZActive = false; - bool mSmoothLines = false; bool mPresentModeErrorShown = false; bool mPresentModeForVSyncLimit = false; // Swapchain created with FIFO for the refresh rate FPS limit std::vector mPresentFences; // Per swapchain image, signaled when its last present is done (swapchain_maintenance1) diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 222b69f315c8f..6953e8c4b20d9 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -61,7 +61,6 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface virtual void ToggleMaximized(bool set) = 0; // Maximize window virtual void SetVSync(bool enable) = 0; // Enable / disable vsync virtual bool canSetVSync() { return false; } // OpenGL: swap interval can be set - virtual void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) = 0; // Print text on the display (needs the backend to build the font) GPUDisplayBackend* backend(); static GPUDisplayFrontend* getFrontend(const char* type); virtual void getSize(int32_t& width, int32_t& height) { width = height = 0; } @@ -118,7 +117,6 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface bool mKeys[256] = {false}; // Array of mKeys currently pressed int32_t mDisplayHeight = INIT_HEIGHT; int32_t mDisplayWidth = INIT_WIDTH; - int32_t mCanDrawText = 0; // 1 = in compat mode, 2 = with shader std::chrono::steady_clock::time_point mNextFrameTime; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 7e2299eac84d6..8fc2f962df400 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -43,12 +43,6 @@ struct GPUDisplayFrontend::configKey { configKeyCallback callback; }; -#ifdef GPUCA_DISPLAY_OPENGL_CORE -static constexpr bool displayOpenGLCore = true; // OpenGL compat profile not available -#else -static constexpr bool displayOpenGLCore = false; -#endif - struct GPUDisplayFrontend::configRTTIInfo { static constexpr std::tuple configStructs{&GPUDisplay::mCfgL, &GPUDisplay::mCfgH, &GPUDisplay::mCfgR}; // Config structs with RTTI, as members of GPUDisplay std::vector rttiEntries; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc index 2644f4e87949b..0cbc8a4cdd571 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigInfo.inc @@ -73,7 +73,7 @@ void GPUDisplay::buildConfigInfo() addInfo(INFO_LAMBDA(mCfgR, screenshotScaleFactor, SetInfo("Screenshot scaling factor set to %d", val);)); addInfo(INFO_LAMBDA(mCfgL, smoothPoints, SetInfo("Smoothing of points %s", val ? "enabled" : "disabled");)); addInfo(INFO_LAMBDA(mCfgL, smoothLines, SetInfo("Smoothing of lines %s", val ? "enabled" : "disabled");)); - addInfo(INFO_LAMBDA(mCfgL, depthBuffer, SetInfo("Depth buffer (z-buffer, %u bits) %s", mBackend->DepthBits(), val ? "enabled" : "disabled");)); + addInfo(INFO_LAMBDA(mCfgL, depthBuffer, SetInfo("Depth buffer (z-buffer) %s", val ? "enabled" : "disabled");)); addInfo(INFO_LAMBDA(mCfgR, drawQualityMSAA, SetInfo("Multisampling anti-aliasing factor set to %d", val);)); addInfo(INFO_LAMBDA(mCfgR, drawQualityDownsampleFSAA, SetInfo("Downsampling anti-aliasing factor set to %d", val);)); addInfo(INFO_LAMBDA(mCfgR, drawQualityVSync, SetInfo("VSync: %s", val ? "enabled" : "disabled");)); @@ -100,13 +100,6 @@ void GPUDisplay::buildConfigInfo() addInfo(INFO_LAMBDA(mCfgR, fullScreen, SetInfo("Toggling full screen (%d)", (int32_t)val);)); addInfo(INFO_LAMBDA(mCfgR, maximized, SetInfo("Toggling Maximized window (%d)", (int32_t)val);)); addInfo(INFO_LAMBDA(mCfgR, fov, SetInfo("Set FOV to %f", val);)); - addInfo(INFO_LAMBDA(mCfgR, rendererCompatSmoothing, { - if (displayOpenGLCore && backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { - SetInfo("OpenGL compat profile not available, using core profile", 1); - } else { - SetInfo("Using renderer path for %s profile", val ? "compat" : "core (shader)"); - } - })); addInfo(INFO_LAMBDA(mCfgR, useGLIndirectDraw, SetInfo("OpenGL Indirect Draw %s", val ? "enabled" : "disabled");)); addInfo(INFO_LAMBDA(mCfgL, drawTPC, SetInfo("Showing TPC Clusters: %d", (int32_t)val);)); addInfo(INFO_LAMBDA(mCfgL, drawTRD, SetInfo("Showing TRD Tracklets: %d", (int32_t)val);)); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc index a981576edcc2b..8cb8fecec59f2 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc @@ -94,13 +94,8 @@ void GPUDisplay::buildConfigKeys() val = 5; } }), - KEY_LAMBDA('m', mCfgR, rendererCompatSmoothing, { - if (!displayOpenGLCore || backend()->backendType() != GPUDisplayBackend::TYPE_OPENGL) { - val ^= 1; - } - }), KEY_LAMBDA('I', mCfgR, useGLIndirectDraw, val ^= true;), // - "Switch mFullScreen / Maximized window / Change FOV (field of view) / Switch between core / compat renderer path (OpenGL profile, Vulkan smooth lines) / Enable / disable GL indirect draw"); + "Switch Full Screen / Maximized window / Change FOV (field of view) / Enable / disable GL indirect draw"); mFrontend->add_key_binding( KEY_LAMBDA('S', mCfgL, smoothPoints, val ^= true;), // diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx index 255ba11828be1..e9b861abd131c 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx @@ -271,7 +271,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain() glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_MIN_VERSION_MAJOR); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_MIN_VERSION_MINOR); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 0); - glfwWindowHint(GLFW_OPENGL_PROFILE, mBackend->CoreProfile() ? GLFW_OPENGL_CORE_PROFILE : GLFW_OPENGL_COMPAT_PROFILE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); } glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Initial size scaled by the content scale on X11 / Win32 mWindow = glfwCreateWindow(INIT_WIDTH, INIT_HEIGHT, DISPLAY_WINDOW_NAME, nullptr, nullptr); @@ -358,10 +358,6 @@ void GPUDisplayFrontendGlfw::StopDisplayA() glfwSetWindowShouldClose(mWindow, true); } -void GPUDisplayFrontendGlfw::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton) -{ -} - void GPUDisplayFrontendGlfw::SwitchFullscreen(bool set) { GPUInfo("Setting Full Screen %d", (int32_t)set); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h index 58de6b0990aa7..fafde0ca89cc2 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h @@ -31,7 +31,6 @@ class GPUDisplayFrontendGlfw : public GPUDisplayFrontend void SwitchFullscreen(bool set) override; void ToggleMaximized(bool set) override; void SetVSync(bool enable) override; - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) override; void getSize(int32_t& width, int32_t& height) override; float getDisplayScale() const override { return mScale; } float getRefreshRate() override; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx index 0d64a7f79d40a..5e79e85b6cf84 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx @@ -209,7 +209,6 @@ int32_t GPUDisplayFrontendGlut::FrontendMain() fprintf(stderr, "Only OpenGL backend supported\n"); return 1; } - mCanDrawText = 1; if (drawTextFontSize() == 0) { drawTextFontSize() = 12; } @@ -220,7 +219,7 @@ int32_t GPUDisplayFrontendGlut::FrontendMain() char* opts[] = {opt1, opt2}; glutInit(&nopts, opts); glutInitContextVersion(GL_MIN_VERSION_MAJOR, GL_MIN_VERSION_MINOR); - glutInitContextProfile(mBackend->CoreProfile() ? GLUT_CORE_PROFILE : GLUT_COMPATIBILITY_PROFILE); + glutInitContextProfile(GLUT_CORE_PROFILE); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(INIT_WIDTH, INIT_HEIGHT); glutCreateWindow(DISPLAY_WINDOW_NAME); @@ -271,18 +270,6 @@ void GPUDisplayFrontendGlut::StopDisplayA() glutLeaveMainLoop(); } -void GPUDisplayFrontendGlut::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton) -{ -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (!fromBotton) { - y = mDisplayHeight - y; - } - glColor4f(r, g, b, a); - glRasterPos2f(x, y); - glutBitmapString(GLUT_BITMAP_HELVETICA_12, (const uint8_t*)s); -#endif -} - void GPUDisplayFrontendGlut::SwitchFullscreen(bool set) { mFullScreen = set; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h index eb6d4a220d0ce..9d3efac3996d0 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h @@ -30,7 +30,6 @@ class GPUDisplayFrontendGlut : public GPUDisplayFrontend void ToggleMaximized(bool set) override; void SetVSync(bool enable) override; bool canSetVSync() override; - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) override; private: int32_t FrontendMain() override; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.h index c6eab47c60441..47080c821542b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendNone.h @@ -28,7 +28,6 @@ class GPUDisplayFrontendNone : public GPUDisplayFrontend void SwitchFullscreen(bool set) override {} void ToggleMaximized(bool set) override {} void SetVSync(bool enable) override {} - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) override {} private: int32_t FrontendMain() override; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx index 900af8061b587..68177449f06c8 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.cxx @@ -49,10 +49,6 @@ GPUDisplayFrontendWayland::GPUDisplayFrontendWayland() mFrontendName = "Wayland"; } -void GPUDisplayFrontendWayland::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton) -{ -} - int32_t GPUDisplayFrontendWayland::GetKey(uint32_t key, uint32_t state) { int32_t retVal = 0; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h index 068135eb2b725..b6083e66e0711 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWayland.h @@ -43,7 +43,6 @@ class GPUDisplayFrontendWayland : public GPUDisplayFrontend void SwitchFullscreen(bool set) override; void ToggleMaximized(bool set) override; void SetVSync(bool enable) override; - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) override; void getSize(int32_t& width, int32_t& height) override; float getDisplayScale() const override { return mScale; } float getRefreshRate() override; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx index bb72a330bba0b..d71afb3414668 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.cxx @@ -368,7 +368,6 @@ int32_t GPUDisplayFrontendWindows::FrontendMain() return (0); } -void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton) {} void SwitchFullscreen(bool set) {} void ToggleMaximized(bool set) {} void SetVSync(bool enable) {} diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.h index e28a9f4ced75b..ac38427367dc5 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendWindows.h @@ -28,7 +28,6 @@ class GPUDisplayFrontendWindows : public GPUDisplayFrontend void SwitchFullscreen(bool set) override; void ToggleMaximized(bool set) override; void SetVSync(bool enable) override; - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) override; private: int32_t FrontendMain() override; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx index e90a3062a54ce..6448c83303c0a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.cxx @@ -163,28 +163,6 @@ void GPUDisplayFrontendX11::GetKey(XEvent& event, int32_t& keyOut, int32_t& keyP } } -void GPUDisplayFrontendX11::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton) -{ -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { - if (!fromBotton) { - y = mDisplayHeight - y; - } - glColor4f(r, g, b, a); - glRasterPos2f(x, y); - if (!glIsList(mFontBase)) { - GPUError("print string: Bad display list."); - exit(1); - } else if (s && strlen(s)) { - glPushAttrib(GL_LIST_BIT); - glListBase(mFontBase); - glCallLists(strlen(s), GL_UNSIGNED_BYTE, (GLubyte*)s); - glPopAttrib(); - } - } -#endif -} - int32_t GPUDisplayFrontendX11::FrontendMain() { XSetWindowAttributes windowAttributes; @@ -261,7 +239,7 @@ int32_t GPUDisplayFrontendX11::FrontendMain() int32_t context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, GL_MIN_VERSION_MAJOR, GLX_CONTEXT_MINOR_VERSION_ARB, GL_MIN_VERSION_MINOR, - GLX_CONTEXT_PROFILE_MASK_ARB, mBackend->CoreProfile() ? GLX_CONTEXT_CORE_PROFILE_BIT_ARB : GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None}; glxContext = glXCreateContextAttribsARB(mXDisplay, fbconfig, nullptr, GL_TRUE, context_attribs); } else { @@ -300,31 +278,6 @@ int32_t GPUDisplayFrontendX11::FrontendMain() // Receive signal when window closed Atom WM_DELETE_WINDOW = XInternAtom(mXDisplay, "WM_DELETE_WINDOW", False); XSetWMProtocols(mXDisplay, mWindow, &WM_DELETE_WINDOW, 1); -#ifndef GPUCA_DISPLAY_OPENGL_CORE - XFontStruct* font_info = nullptr; - if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { - // Prepare fonts - mFontBase = glGenLists(256); - if (!glIsList(mFontBase)) { - GPUError("Out of display lists."); - return (-1); - } - const char* f = "fixed"; - font_info = XLoadQueryFont(mXDisplay, f); - if (!font_info) { - GPUError("XLoadQueryFont failed."); - return (-1); - } else { - int32_t first = font_info->min_char_or_byte2; - int32_t last = font_info->max_char_or_byte2; - glXUseXFont(font_info->fid, first, last - first + 1, mFontBase + first); - } - } - mCanDrawText = 1; - if (drawTextFontSize() == 0) { - drawTextFontSize() = 12; - } -#endif if (mBackend->ExtInit()) { fprintf(stderr, "Error initializing backend\n"); @@ -459,12 +412,6 @@ int32_t GPUDisplayFrontendX11::FrontendMain() waitForFPSLimit(); } -#ifndef GPUCA_DISPLAY_OPENGL_CORE - if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { - glDeleteLists(mFontBase, 256); - XUnloadFont(mXDisplay, font_info->fid); - } -#endif ExitDisplay(); if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) { glXDestroyContext(mXDisplay, glxContext); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.h index 41032debeb9f5..abceeaaa670f0 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendX11.h @@ -32,7 +32,6 @@ class GPUDisplayFrontendX11 : public GPUDisplayFrontend void SwitchFullscreen(bool set) override; void ToggleMaximized(bool set) override; void SetVSync(bool enable) override; - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true) override; void getSize(int32_t& width, int32_t& height) override; float getDisplayScale() const override { return mScale; } int32_t getVulkanSurface(void* instance, void* surface) override; @@ -43,8 +42,6 @@ class GPUDisplayFrontendX11 : public GPUDisplayFrontend int32_t GetKey(int32_t key); void GetKey(XEvent& event, int32_t& keyOut, int32_t& keyPressOut); - GLuint mFontBase; - Display* mXDisplay = nullptr; Window mWindow; void updateWindowState(); diff --git a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx index 99a14a8319c24..37d8e4b5cde56 100644 --- a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx +++ b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx @@ -131,7 +131,5 @@ void GPUDisplay::OpenGLPrint(const char* s, float x, float y, float r, float g, } float color[4] = {r, g, b, a}; mBackend->OpenGLPrint(s, x, y, color, scale / mBackend->mFontRasterScale); - } else if (mFrontend->mCanDrawText) { - mFrontend->OpenGLPrint(s, x, y, r, g, b, a, fromBotton); } } From 48acba77c3b8faa7c73582543b51146d51940f39 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 31 Oct 2026 06:01:23 +0100 Subject: [PATCH 26/27] GPU Display: Wrap lines of help text if they do not fit on screen --- GPU/GPUTracking/display/GPUDisplay.h | 2 +- .../display/backend/GPUDisplayBackend.cxx | 3 ++ .../display/backend/GPUDisplayBackend.h | 3 +- .../display/backend/GPUDisplayBackendNone.h | 2 +- .../backend/GPUDisplayBackendOpenGL.cxx | 4 +-- .../display/backend/GPUDisplayBackendOpenGL.h | 2 +- .../backend/GPUDisplayBackendVulkan.cxx | 4 +-- .../display/backend/GPUDisplayBackendVulkan.h | 2 +- .../display/frontend/GPUDisplayHelp.cxx | 28 +++++++++++++++++-- .../display/helpers/GPUDisplayHelpers.cxx | 8 +++--- 10 files changed, 43 insertions(+), 15 deletions(-) diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 977bbc0b46b0c..cfdfde0b9c0d9 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -233,7 +233,7 @@ class GPUDisplay : public GPUDisplayInterface void PrintHelp(); void createQuaternionFromMatrix(float* v, const float* mat); void drawVertices(const vboList& v, const GPUDisplayBackend::drawType t); - void OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton = true); + void GLPrintText(const char* s, float x, float y, float r, float g, float b, float a, uint32_t nCharacters = 0, bool fromBotton = true); // nCharacters > 0: print at most that many characters GPUDisplayFrontend* mFrontend = nullptr; std::unique_ptr mBackend; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx index bb8d1ac8176fc..2f353ea8b60c2 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackend.cxx @@ -140,6 +140,9 @@ void GPUDisplayBackend::loadFont() return; } const auto& glyph = face->glyph; + if (i == 'M') { + mFontCharWidth = (glyph->advance.x >> 6) * (smoothFont() ? 0.25f : 1.f) / mFontRasterScale; // As in GLPrintText of the backends + } addFontSymbol(i, glyph->bitmap.width, glyph->bitmap.rows, glyph->bitmap_left, glyph->bitmap_top, glyph->advance.x, glyph->bitmap.buffer); } initializeTextDrawing(); diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackend.h b/GPU/GPUTracking/display/backend/GPUDisplayBackend.h index 4245a16cee303..411af384221b5 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackend.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackend.h @@ -99,7 +99,7 @@ class GPUDisplayBackend const char* backendName() const { return mBackendName; } virtual void resizeScene(uint32_t width, uint32_t height) {} virtual size_t needMultiVBO() { return 0; } - virtual void OpenGLPrint(const char* s, float x, float y, float* color, float scale) = 0; + virtual void GLPrintText(const char* s, float x, float y, float* color, float scale, uint32_t nCharacters) = 0; // nCharacters > 0: at most that many characters, otherwise until the terminating 0 static GPUDisplayBackend* getBackend(const char* type); std::vector getPixels(); virtual float getYFactor() const { return 1.0f; } @@ -120,6 +120,7 @@ class GPUDisplayBackend vecpod mCmdBuffer; bool mFreetypeInitialized = false; float mFontRasterScale = 1.f; // Display scale the font was rasterized for + float mFontCharWidth = 0.f; // Advance of a character of the (monospace) font in logical pixels, 0 without Freetype font std::vector mScreenshotPixels; static constexpr float SMOOTH_POINT_EXTRA_SIZE = 0.6f; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h index 8746d5214db82..825fd9b353aa6 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendNone.h @@ -39,7 +39,7 @@ class GPUDisplayBackendNone : public GPUDisplayBackend void finishText() override {} void pointSizeFactor(float factor) override {} void lineWidthFactor(float factor) override {} - void OpenGLPrint(const char* s, float x, float y, float* color, float scale) override {} + void GLPrintText(const char* s, float x, float y, float* color, float scale, uint32_t nCharacters) override {} void addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void* data) override {} void initializeTextDrawing() override {} }; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx index be9a805c27739..f79675cbad8a6 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.cxx @@ -668,7 +668,7 @@ void GPUDisplayBackendOpenGL::initializeTextDrawing() CHKERR(glBindVertexArray(0)); } -void GPUDisplayBackendOpenGL::OpenGLPrint(const char* s, float x, float y, float* color, float scale) +void GPUDisplayBackendOpenGL::GLPrintText(const char* s, float x, float y, float* color, float scale, uint32_t nCharacters) { if (!mFreetypeInitialized) { return; @@ -678,7 +678,7 @@ void GPUDisplayBackendOpenGL::OpenGLPrint(const char* s, float x, float y, float if (smoothFont()) { scale *= 0.25f; // Font size is 48 to have nice bitmap, scale to size 12 } - for (const unsigned char* c = (const unsigned char*)s; *c; c++) { + for (const unsigned char* c = (const unsigned char*)s; *c && (nCharacters == 0 || c < (const unsigned char*)s + nCharacters); c++) { if ((int32_t)*c >= (int32_t)mFontSymbols.size()) { GPUError("Trying to draw unsupported symbol: %d > %d\n", (int32_t)*c, (int32_t)mFontSymbols.size()); continue; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h index 768f046a89664..f6e9d8801b6d0 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendOpenGL.h @@ -79,7 +79,7 @@ class GPUDisplayBackendOpenGL : public GPUDisplayBackend void addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void* data) override; void initializeTextDrawing() override; void clearFontSymbols() override; - void OpenGLPrint(const char* s, float x, float y, float* color, float scale) override; + void GLPrintText(const char* s, float x, float y, float* color, float scale, uint32_t nCharacters) override; struct FontSymbolOpenGL : public FontSymbol { uint32_t texId; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx index 114966c964136..b76fc71de66d1 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx @@ -1888,7 +1888,7 @@ void GPUDisplayBackendVulkan::clearFontSymbols() mFontSymbols.clear(); } -void GPUDisplayBackendVulkan::OpenGLPrint(const char* s, float x, float y, float* color, float scale) +void GPUDisplayBackendVulkan::GLPrintText(const char* s, float x, float y, float* color, float scale, uint32_t nCharacters) { if (!mFreetypeInitialized) { return; @@ -1899,7 +1899,7 @@ void GPUDisplayBackendVulkan::OpenGLPrint(const char* s, float x, float y, float scale *= 0.25f; // Font size is 48 to have nice bitmap, scale to size 12 } - for (const unsigned char* c = (const unsigned char*)s; *c; c++) { + for (const unsigned char* c = (const unsigned char*)s; *c && (nCharacters == 0 || c < (const unsigned char*)s + nCharacters); c++) { if ((int32_t)*c >= (int32_t)mFontSymbols.size()) { GPUError("Trying to draw unsupported symbol: %d > %d\n", (int32_t)*c, (int32_t)mFontSymbols.size()); continue; diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h index 2949e8cedfeb7..d76fa357bb216 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.h @@ -136,7 +136,7 @@ class GPUDisplayBackendVulkan : public GPUDisplayBackend void addFontSymbol(int32_t symbol, int32_t sizex, int32_t sizey, int32_t offsetx, int32_t offsety, int32_t advance, void* data) override; void initializeTextDrawing() override; void clearFontSymbols() override; - void OpenGLPrint(const char* s, float x, float y, float* color, float scale) override; + void GLPrintText(const char* s, float x, float y, float* color, float scale, uint32_t nCharacters) override; uint32_t mGraphicsFamily; SwapChainSupportDetails mSwapChainDetails; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx index 275ac348653d8..73b11a253c0d4 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayHelp.cxx @@ -15,6 +15,8 @@ #include "GPUDisplay.h" #include "GPULogging.h" +#include + using namespace o2::gpu; void GPUDisplay::PrintHelp() @@ -28,7 +30,29 @@ void GPUDisplay::PrintHelp() void GPUDisplay::PrintGLHelpText(float colorValue) { const auto& lines = mFrontend->getHelpText(); - for (uint32_t i = 0; i < lines.size(); i++) { - OpenGLPrint(lines[i].c_str(), 40.f, 35 + std::max(20, mDrawTextFontSize + 4) * (1 + i), colorValue, colorValue, colorValue, mInfoHelpTimer.GetCurrentElapsedTime() >= 5 ? (6 - mInfoHelpTimer.GetCurrentElapsedTime()) : 1, false); + const int32_t maxChars = mBackend->mFontCharWidth > 0.f ? (mBackend->mScreenWidth / mFrontend->getDisplayScale() - 80.f) / mBackend->mFontCharWidth : 0; // Characters fitting between the margins, 0 = unknown (no Freetype font), no wrapping + uint32_t row = 0; + auto print = [&](const char* s, uint32_t nCharacters, uint32_t indent) { GLPrintText(s, 40.f + indent * mBackend->mFontCharWidth, 35 + std::max(20, mDrawTextFontSize + 4) * (1 + row++), colorValue, colorValue, colorValue, mInfoHelpTimer.GetCurrentElapsedTime() >= 5 ? (6 - mInfoHelpTimer.GetCurrentElapsedTime()) : 1, nCharacters, false); }; + for (const auto& line : lines) { + if (maxChars <= 0 || line.size() <= (size_t)maxChars) { + print(line.c_str(), 0, 0); + continue; + } + const size_t textStart = line.find(": "); + const size_t indent = textStart != std::string::npos && textStart + 2 < (size_t)maxChars / 2 ? textStart + 2 : 0; // Continuation lines aligned with the text after the keys, if that leaves enough space + size_t pos = 0; // Start of the part not yet printed + for (bool first = true;; first = false) { // Wrap at the last space that fits, or hard if there is none (also if it is only in the keys) + const size_t lineIndent = first ? 0 : indent, available = maxChars - lineIndent; + if (line.size() - pos <= available) { + print(line.c_str() + pos, line.size() - pos, lineIndent); + break; + } + size_t cut = line.rfind(' ', pos + available); + if (cut == std::string::npos || cut <= pos + (first ? indent : 0)) { + cut = pos + available; + } + print(line.c_str() + pos, cut - pos, lineIndent); + pos = line[cut] == ' ' ? cut + 1 : cut; + } } } diff --git a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx index 37d8e4b5cde56..b08a55967eda4 100644 --- a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx +++ b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx @@ -102,12 +102,12 @@ void GPUDisplay::showInfo(const char* info) mBackend->updateFontScale(mFrontend->getDisplayScale()); mBackend->prepareText(); float colorValue = mCfgL.invertColors ? 0.f : 1.f; - OpenGLPrint(info, 40.f, 20.f + std::max(20, mDrawTextFontSize + 4), colorValue, colorValue, colorValue, 1); + GLPrintText(info, 40.f, 20.f + std::max(20, mDrawTextFontSize + 4), colorValue, colorValue, colorValue, 1); if (mInfoText2Timer.IsRunning()) { if (mInfoText2Timer.GetCurrentElapsedTime() >= 6) { mInfoText2Timer.Reset(); } else { - OpenGLPrint(mInfoText2, 40.f, 20.f, colorValue, colorValue, colorValue, 6 - mInfoText2Timer.GetCurrentElapsedTime()); + GLPrintText(mInfoText2, 40.f, 20.f, colorValue, colorValue, colorValue, 6 - mInfoText2Timer.GetCurrentElapsedTime()); } } if (mInfoHelpTimer.IsRunning() || mCfgC.mPrintInfoTextAlways) { @@ -120,7 +120,7 @@ void GPUDisplay::showInfo(const char* info) mBackend->finishText(); } -void GPUDisplay::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton) +void GPUDisplay::GLPrintText(const char* s, float x, float y, float r, float g, float b, float a, uint32_t nCharacters, bool fromBotton) { if (mBackend->mFreetypeInitialized) { const float scale = mFrontend->getDisplayScale(); @@ -130,6 +130,6 @@ void GPUDisplay::OpenGLPrint(const char* s, float x, float y, float r, float g, y = mBackend->mScreenHeight - y; } float color[4] = {r, g, b, a}; - mBackend->OpenGLPrint(s, x, y, color, scale / mBackend->mFontRasterScale); + mBackend->GLPrintText(s, x, y, color, scale / mBackend->mFontRasterScale, nCharacters); } } From 04a8217f30d9f06345dfb4b75a803053aaf02ef6 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 29 Oct 2026 06:20:23 +0100 Subject: [PATCH 27/27] GPU Display Qt GUI: Add proper passing of frames with config elements to the GUI and returning lambda functions with value updates --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 10 +- GPU/GPUTracking/display/GPUDisplay.cxx | 4 +- GPU/GPUTracking/display/GPUDisplay.h | 7 +- .../display/frontend/GPUDisplayFrontend.cxx | 9 +- .../display/frontend/GPUDisplayFrontend.h | 8 + .../frontend/GPUDisplayFrontendConfig.cxx | 171 +++++++- .../GPUDisplayFrontendConfigQtGUI.inc | 92 +++++ .../display/frontend/GPUDisplayGUI.cxx | 379 +++++++++++++++++- .../display/frontend/GPUDisplayGUI.h | 28 +- .../display/frontend/GPUDisplayGUI.ui | 34 -- .../display/frontend/GPUDisplayGUIChannel.h | 38 +- 11 files changed, 713 insertions(+), 67 deletions(-) create mode 100644 GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigQtGUI.inc diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index afb40211247d9..aca6b98f07dce 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -456,8 +456,8 @@ AddOption(propagateTracks, int32_t, 0, "", 0, "Propagate final tracks further (i AddOption(showCollision, int32_t, -1, "", 0, "Show only individual collision") AddOption(colorCollisions, int32_t, 0, "", 0, "Distinguish collisions in timeframe by color") AddOption(colorClusters, int32_t, 1, "", 0, "Color clusters belonging to track objects") -AddOption(pointSize, float, 2.0f, "", 0, "Set point size") -AddOption(lineWidth, float, 1.4f, "", 0, "Set line width") +AddOption(pointSize, float, 2.0f, "", 0, "Set point size", min(0.8f), max(20.f)) +AddOption(lineWidth, float, 1.4f, "", 0, "Set line width", min(0.4f), max(20.f)) AddOption(drawTPC, bool, true, "", 0, "Enable drawing TPC data") AddOption(drawTRD, bool, true, "", 0, "Enabale drawing TRD data") 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 AddOption(cameraMode, int32_t, 0, "", 0, "Camera mode") AddOption(fullScreen, bool, false, "", 0, "Full Screen") AddOption(maximized, bool, true, "", 0, "Maximized window") -AddOption(drawQualityMSAA, int32_t, 0, "", 0, "MultiSample Anti Aliasing") -AddOption(drawQualityDownsampleFSAA, int32_t, 0, "", 0, "Downsampling Anti Aliasing") +AddOption(drawQualityMSAA, int32_t, 0, "", 0, "MultiSample Anti Aliasing", min(0), max(8)) +AddOption(drawQualityDownsampleFSAA, int32_t, 0, "", 0, "Downsampling Anti Aliasing", min(0), max(4)) AddOption(drawQualityVSync, bool, true, "", 0, "Enable Vertical Sync") AddOption(fpsLimit, int32_t, 1, "", 0, "FPS limit: 0 = none (maximum rate), 1 = display refresh rate (no limit without VSync), 2 = fpsRate") -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)") +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)) AddOption(useGLIndirectDraw, bool, true, "", 0, "Use OpenGL indirect draws to reduce number of draw calls") AddOption(screenshotScaleFactor, int32_t, 1, "", 0, "Resolution scale factor when taking screenshots") AddOption(fov, int32_t, 45, "", 0, "Display FOV") diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 8651c054bc034..a3c117b23d805 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -244,8 +244,8 @@ void GPUDisplay::DrawGLScene_cameraAndAnimation(bool suspended, float animateTim mFrontend->mMouseWheel = 0; bool lookOrigin = mCfgR.camLookOrigin ^ mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIGIN]]; bool yUp = mCfgR.camYUp ^ mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_FOCUS_ORIENT_UP]] ^ lookOrigin; - bool rotateModel = mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_MODEL]] || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]]; - bool rotateModelTPC = mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]]; + bool rotateModelTPC = mCfgC.mRotateAroundZ || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_AROUND_Z]]; + bool rotateModel = rotateModelTPC || mCfgC.mRotateModel || mFrontend->mKeys[mFrontend->mKeyDownMap[mFrontend->KEY_MAP_ROTATE_MODEL]]; // Calculate rotation / translation scaling factors float scalefactor = mFrontend->mKeys[mFrontend->KEY_SHIFT] ? 0.2f : 1.0f; diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index cfdfde0b9c0d9..8d92c3d903e8b 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -95,13 +95,18 @@ class GPUDisplay : public GPUDisplayInterface bool mRequestScreenshot = false; int32_t mAnimateScreenshot = 0; bool mAnimationChangeConfig = true; - bool dummy = false; // dummy value to reference for keys that do not change variables + bool mGuiPrintInfo = false; // Show the info of options changed with Qt GUI elements (not for key presses in the GUI) + bool mRotateModel = false; // Rotate the model instead of the camera, like holding RCTRL (set in the Qt GUI) + bool mRotateAroundZ = false; // Rotate the TPC around the beamline, like holding RALT (set in the Qt GUI) + 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 + bool dummy = false; // dummy value to reference for keys that do not change variables } mCfgC; private: void buildConfig(); void buildConfigKeys(); void buildConfigInfo(); + void buildConfigQtGUI(); static constexpr int32_t NSECTORS = o2::tpc::constants::MAXSECTOR; static constexpr float GL_SCALE_FACTOR = (1.f / 100.f); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx index 0db41feae33e9..14abcd1193203 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx @@ -123,15 +123,19 @@ void GPUDisplayFrontend::HandleExternalGUISignals() for (int32_t key : update->keys) { mDisplay->HandleExternalGUIKey(key); } - if (update->lineWidth) { - mDisplay->mCfgL.lineWidth = *update->lineWidth; + applyGUIValues(update->values); + if (update->guiScale) { + mDisplay->mCfgC.mGuiScale = *update->guiScale; } } if (gui) { + auto values = getGUIValues(); { std::lock_guard lock(mGUIChannel->mutex); mGUIChannel->state.cfg = {mDisplay->mCfgH, mDisplay->mCfgL, mDisplay->mCfgR}; + mGUIChannel->state.values = std::move(values); + mGUIChannel->state.guiScale = mDisplay->mCfgC.mGuiScale; if (update) { mGUIChannel->state.appliedUpdate = update->id; } @@ -205,6 +209,7 @@ int32_t GPUDisplayFrontend::startGUI() mGUIChannel->replyTimeout = std::chrono::milliseconds(mDisplay->cfg().guiReplyTimeout); mGUIChannel->maxUpdatesAhead = std::max(mDisplay->cfg().guiMaxUpdatesAhead, 1); // 0 would block all updates mGUIChannel->windowName = DISPLAY_WINDOW_NAME; + setGUILayout(*mGUIChannel); } mGUI->start(); } else { diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h index 6953e8c4b20d9..3866f082bd89b 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h @@ -22,10 +22,13 @@ #include #include #include +#include +#include #include namespace o2::gpu { +using GPUDisplayGUIValue = std::variant; // Value of a Qt GUI element class GPUReconstruction; class GPUDisplay; class GPUDisplayBackend; @@ -158,6 +161,11 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface bool mWindowFullScreen = false, mWindowMaximized = false; // Window state last requested or reported by the window system template 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 + template + 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 + void setGUILayout(GPUDisplayGUIChannel& channel) const; // Copy the GUI elements of the add_gui_binding calls to the channel + std::vector getGUIValues(); // Current values of the options of the GUI elements, by element id + void applyGUIValues(const std::vector>& values); // Call the setter of the GUI elements changed in the GUI (element id, value), and show the info of their options void listConfig(); const std::vector& getHelpText() const; // Help text of all key bindings, one line per binding }; diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx index 8fc2f962df400..e85443f757a8a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx @@ -16,6 +16,7 @@ #include "GPUDisplay.h" #include "frontend/GPUDisplayInfo.inc" #include "GPULogging.h" +#include "GPUDisplayGUIChannel.h" #include "utils/qconfig_rtti.h" #include "utils/qconfig_internal_helpers.h" @@ -31,6 +32,7 @@ #include #include #include +#include using namespace o2::gpu; struct GPUDisplayFrontend::configRTTIExt : public qConfig::qConfigRTTIentry { @@ -57,6 +59,13 @@ struct GPUDisplayFrontend::configRTTIInfo { std::vector keyMapNames; // Names of the key down mapping ids (KEY_MAP_*) std::vector> helpTextEntries; // Keys and text of each add_key_binding call std::vector helpText; + struct guiElementCallbacks { + uint32_t rttiIndex; + std::function set; // GUI_LAMBDA setter, applies a value from the GUI + std::function get; // Current value of the option of the element + }; + std::vector guiElements; // By element id + std::vector guiLayout; // Frames of the add_gui_binding calls static constexpr std::string_view possibleKeys = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#.:;>'^_"; // Bindable keys >= 32 (space), special keys < 32 are those with a name in keyNames template @@ -232,6 +241,148 @@ void GPUDisplayFrontend::add_key_binding(Args&&... args) mConfigRTTI->helpTextEntries.emplace_back(std::move(keys), std::get(argTuple)); } +struct guiType { // Element type and choices, GUI_CHECKBOX, GUI_DROPDOWN(...), etc. in GPUDisplayFrontendConfigQtGUI.inc + guiType(GPUDisplayGUIChannel::ElementType t, std::vector> c = {}) : type(t), choices(std::move(c)) {} + GPUDisplayGUIChannel::ElementType type; + std::vector> choices; +}; + +static std::vector> guiSliderValues(std::initializer_list values) // Choices of GUI_SLIDER_VALUES, named by their value +{ + auto name = [](const auto& x) -> std::string { + if constexpr (std::is_same_v, std::string>) { + return x; + } else { + return qConfig::print_type(x); + } + }; + std::vector> retVal; + for (const auto& v : values) { + retVal.emplace_back(std::visit(name, v), v); + } + return retVal; +} + +struct guiElement { // GUI_LAMBDA(...) + guiElement(std::string n, uint32_t i, guiType t, std::function s, std::function g) : name(std::move(n)), rttiIndex(i), type(std::move(t)), set(std::move(s)), get(std::move(g)) {} + std::string name; + uint32_t rttiIndex; + guiType type; + std::function set; + std::function get; +}; + +template +static void guiAssign(T& dst, const GPUDisplayGUIValue& value) // Default GUI_LAMBDA setter: assign the GUI value, converted to the type of the option +{ + std::visit([&](const auto& v) { + using V = std::remove_cvref_t; + if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { + dst = v; + } else { + dst = std::to_string(v); + } + } else if constexpr (std::is_same_v) { + dst = static_cast(std::stod(v)); // Throws for invalid text + } else { + dst = static_cast(v); + } + }, + value); +} + +template +static GPUDisplayGUIValue toGUIValue(const T& v) +{ + if constexpr (requires { v.load(); }) { // std::atomic in GPUDisplay::control + return toGUIValue(v.load()); + } else if constexpr (std::is_same_v) { + return v; + } else if constexpr (std::is_integral_v) { + return (int64_t)v; + } else { + static_assert(std::is_floating_point_v, "Unsupported option type for the Qt GUI"); + return (double)v; + } +} + +template +void GPUDisplayFrontend::add_gui_binding(Args&&... args) +{ + constexpr size_t nElements = (0 + ... + std::is_same_v, guiElement>); + constexpr size_t nHorizontal = (0 + ... + std::is_same_v, bool>); + 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"); + GPUDisplayGUIChannel::Frame frame; + auto addElement = [&](auto& element) { + using Type = GPUDisplayGUIChannel::ElementType; + const uint32_t id = mConfigRTTI->guiElements.size(); + auto& el = frame.elements.emplace_back(GPUDisplayGUIChannel::Element{.name = element.name, .rttiIndex = element.rttiIndex, .type = element.type.type, .id = id, .isInteger = std::holds_alternative(element.get()), .choices = std::move(element.type.choices)}); + if (el.type == Type::SLIDER) { + if (element.rttiIndex >= mConfigRTTI->rttiEntries.size()) { + throw std::runtime_error("GUI slider " + element.name + " needs an option with RTTI for min and max values"); + } + const auto& e = mConfigRTTI->rttiEntries[element.rttiIndex]; + if (e.min.empty() || e.max.empty()) { + throw std::runtime_error("GUI slider " + element.name + ": option " + e.name + " needs min and max values"); + } + el.min = std::stod(e.min); + el.max = std::stod(e.max); + } + if (el.type == Type::TEXTBOX && element.rttiIndex < mConfigRTTI->rttiEntries.size() && !mConfigRTTI->rttiEntries[element.rttiIndex].min.empty() && !mConfigRTTI->rttiEntries[element.rttiIndex].max.empty()) { + el.min = std::stod(mConfigRTTI->rttiEntries[element.rttiIndex].min); + el.max = std::stod(mConfigRTTI->rttiEntries[element.rttiIndex].max); + el.hasRange = true; + } + if ((el.type == Type::RADIO_BUTTONS || el.type == Type::DROPDOWN) ? el.choices.empty() : (!el.choices.empty() && el.type != Type::SLIDER)) { + throw std::runtime_error("GUI element " + element.name + ": choices are needed for radio buttons and dropdown menus, and only allowed for them and sliders"); + } + mConfigRTTI->guiElements.emplace_back(configRTTIInfo::guiElementCallbacks{element.rttiIndex, std::move(element.set), std::move(element.get)}); + }; + auto addArg = [&](auto& arg) { + if constexpr (std::is_same_v, guiElement>) { + addElement(arg); + } else if constexpr (std::is_same_v, bool>) { + frame.horizontal = arg; + } else { + frame.description = arg; + } + }; + (addArg(args), ...); + mConfigRTTI->guiLayout.emplace_back(std::move(frame)); +} + +void GPUDisplayFrontend::setGUILayout(GPUDisplayGUIChannel& channel) const { channel.layout = mConfigRTTI->guiLayout; } + +std::vector GPUDisplayFrontend::getGUIValues() +{ + std::vector retVal; + retVal.reserve(mConfigRTTI->guiElements.size()); + for (const auto& e : mConfigRTTI->guiElements) { + retVal.emplace_back(e.get()); + } + return retVal; +} + +void GPUDisplayFrontend::applyGUIValues(const std::vector>& values) +{ + for (const auto& [id, value] : values) { + if (id >= mConfigRTTI->guiElements.size()) { + continue; + } + const auto& e = mConfigRTTI->guiElements[id]; + try { + e.set(value); + } catch (const std::exception& ex) { + GPUError("Invalid value from the GUI for element %u: %s", id, ex.what()); + continue; + } + if (mDisplay->mCfgC.mGuiPrintInfo && e.rttiIndex < mConfigRTTI->configInfos.size()) { + showConfigInfo(e.rttiIndex); + } + } +} + const std::vector& GPUDisplayFrontend::getHelpText() const { return mConfigRTTI->helpText; } void GPUDisplayFrontend::listConfig() @@ -256,8 +407,8 @@ void GPUDisplayFrontend::listConfig() std::sort(boundKeys.begin(), boundKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); }); std::sort(availableKeys.begin(), availableKeys.end(), [&](uint16_t a, uint16_t b) { return keyOrder(a) < keyOrder(b); }); - GPUInfo("Display config options with RTTI (struct, option, type, keys):"); - uint32_t nOptionsWithKey = 0; + GPUInfo("Display config options with RTTI (struct, option, type, in Qt GUI, keys):"); + uint32_t nOptionsWithKey = 0, nOptionsInGUI = 0; for (uint32_t i = 0; i < rtti.rttiEntries.size(); i++) { const auto& e = rtti.rttiEntries[i]; std::string keys; @@ -266,9 +417,12 @@ void GPUDisplayFrontend::listConfig() keys += (keys.empty() ? "[" : " [") + rtti.getKeyName(k) + "]"; } } + const bool inGUI = std::any_of(rtti.guiElements.begin(), rtti.guiElements.end(), [i](const auto& g) { return g.rttiIndex == i; }); nOptionsWithKey += !keys.empty(); - GPUInfo(" %-28s %-32s %-12s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), keys.c_str()); + nOptionsInGUI += inGUI; + GPUInfo(" %-28s %-32s %-12s %-3s %s", e.configStruct.c_str(), e.name.c_str(), e.type.c_str(), inGUI ? "GUI" : "---", keys.c_str()); } + GPUInfo(""); GPUInfo("Bound keys (key, struct, option):"); std::vector mappedKeys(KEY_MAP_MAX); int32_t keyWidth = 0; @@ -285,20 +439,28 @@ void GPUDisplayFrontend::listConfig() 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() : ""); } } + GPUInfo(""); GPUInfo("Key down mappings (id, name, key):"); for (uint32_t i = 0; i < KEY_MAP_MAX; i++) { GPUInfo(" %2u %-28s %s", i, rtti.keyMapNames[i].c_str(), mappedKeys[i].c_str()); } + GPUInfo(""); std::string available; for (uint16_t k : availableKeys) { available += (available.empty() ? "[" : " [") + rtti.getKeyName(k) + "]"; } GPUInfo("Available keys: %s", available.c_str()); - 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()); + GPUInfo("Help text:"); + for (const auto& line : rtti.helpText) { + GPUInfo(" %s", line.c_str()); + } + GPUInfo(""); + 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()); } #include "GPUDisplayFrontendConfigKeys.inc" #include "GPUDisplayFrontendConfigInfo.inc" +#include "GPUDisplayFrontendConfigQtGUI.inc" void GPUDisplay::buildConfig() { @@ -341,6 +503,7 @@ void GPUDisplay::buildConfig() }; } buildConfigInfo(); + buildConfigQtGUI(); rtti->buildHelpText(); } diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigQtGUI.inc b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigQtGUI.inc new file mode 100644 index 0000000000000..e9198c5621fbd --- /dev/null +++ b/GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigQtGUI.inc @@ -0,0 +1,92 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUDisplayFrontendConfigQtGUI.inc +/// \author David Rohr + +#ifndef GPUDISPLAYFRONTENDCONFIGQTGUI_INC +#define GPUDISPLAYFRONTENDCONFIGQTGUI_INC + +#define GUI_CHECKBOX guiType(GPUDisplayGUIChannel::ElementType::CHECKBOX) +#define GUI_TEXTBOX guiType(GPUDisplayGUIChannel::ElementType::TEXTBOX) +#define GUI_SLIDER guiType(GPUDisplayGUIChannel::ElementType::SLIDER) // Range from the min / max of the option +#define GUI_SLIDER_VALUES(...) guiType(GPUDisplayGUIChannel::ElementType::SLIDER, guiSliderValues({__VA_ARGS__})) // Slider with only these values +#define GUI_RADIO_BUTTONS(...) guiType(GPUDisplayGUIChannel::ElementType::RADIO_BUTTONS, {__VA_ARGS__}) // Choices {"name", value}, ... +#define GUI_DROPDOWN(...) guiType(GPUDisplayGUIChannel::ElementType::DROPDOWN, {__VA_ARGS__}) +#define GUI_BUTTON guiType(GPUDisplayGUIChannel::ElementType::BUTTON) // Body of GUI_LAMBDA runs when the button is clicked +// GUI element for an option, type one of the GUI_* above. The optional body sets the value from the GUI: val is a reference to obj.value, guiVal the GPUDisplayGUIValue from the GUI (int64_t, double, or std::string). Without body, guiVal is assigned to val. +#define GUI_LAMBDA(name, obj, value, type, ...) guiElement(std::string(name), mFrontend->mConfigRTTI->getRTTIIndex(*this, &std::remove_cvref_t::value), type, [this](const GPUDisplayGUIValue& guiVal) { [[maybe_unused]] auto& val = obj.value; __VA_OPT__(if constexpr (true) { __VA_ARGS__ } else) guiAssign(val, guiVal); }, [this]() { return toGUIValue(obj.value); }) + +void GPUDisplay::buildConfigQtGUI() +{ + mFrontend->add_gui_binding( + GUI_LAMBDA("Clusters", mCfgL, drawClusters, GUI_CHECKBOX), + GUI_LAMBDA("Initial links", mCfgL, drawInitLinks, GUI_CHECKBOX), + GUI_LAMBDA("Links", mCfgL, drawLinks, GUI_CHECKBOX), + GUI_LAMBDA("Seeds", mCfgL, drawSeeds, GUI_CHECKBOX), + GUI_LAMBDA("Tracklets", mCfgL, drawTracklets, GUI_CHECKBOX), + GUI_LAMBDA("Sector tracks", mCfgL, drawTracks, GUI_CHECKBOX), + GUI_LAMBDA("Extrapolated tracks", mCfgL, drawExtrapolatedTracks, GUI_CHECKBOX), + GUI_LAMBDA("Final tracks", mCfgL, drawFinal, GUI_CHECKBOX), + "Draw Types", true); + + mFrontend->add_gui_binding( + GUI_LAMBDA("TPC", mCfgL, drawTPC, GUI_CHECKBOX), + GUI_LAMBDA("TRD", mCfgL, drawTRD, GUI_CHECKBOX), + GUI_LAMBDA("TOF", mCfgL, drawTOF, GUI_CHECKBOX), + GUI_LAMBDA("ITS", mCfgL, drawITS, GUI_CHECKBOX), + "Detectors", true); + + mFrontend->add_gui_binding( + GUI_LAMBDA("Look at origin", mCfgR, camLookOrigin, GUI_CHECKBOX, guiAssign(val, guiVal); mCfgR.cameraMode = val + 2 * mCfgR.camYUp;), + GUI_LAMBDA("Upward Orientation", mCfgR, camYUp, GUI_CHECKBOX, guiAssign(val, guiVal); mCfgR.cameraMode = mCfgR.camLookOrigin + 2 * val;), + GUI_LAMBDA("Rotate Model", mCfgC, mRotateModel, GUI_CHECKBOX), + GUI_LAMBDA("Rotate around Beamline", mCfgC, mRotateAroundZ, GUI_CHECKBOX), + GUI_LAMBDA("Project onto XY", mCfgH, projectXY, GUI_CHECKBOX), + "Camera Options", true); + + mFrontend->add_gui_binding( + GUI_LAMBDA("Line width", mCfgL, lineWidth, GUI_SLIDER), + GUI_LAMBDA("Point size", mCfgL, pointSize, GUI_SLIDER)); + + mFrontend->add_gui_binding( + GUI_LAMBDA("MSAA", mCfgR, drawQualityMSAA, GUI_SLIDER_VALUES(0, 2, 4, 8)), + GUI_LAMBDA("FSAA", mCfgR, drawQualityDownsampleFSAA, GUI_SLIDER_VALUES(0, 2, 3, 4)), + "Anti-Aliasing"); + + mFrontend->add_gui_binding( + GUI_LAMBDA("Smooth points", mCfgL, smoothPoints, GUI_CHECKBOX), + GUI_LAMBDA("Smooth lines", mCfgL, smoothLines, GUI_CHECKBOX), + GUI_LAMBDA("Depth buffer", mCfgL, depthBuffer, GUI_CHECKBOX, guiAssign(val, guiVal); mBackend->setDepthBuffer();), + GUI_LAMBDA("VSync", mCfgR, drawQualityVSync, GUI_CHECKBOX), + GUI_LAMBDA("FPS limit", mCfgR, fpsLimit, GUI_DROPDOWN({"None", 0}, {"Refresh rate", 1}, {"fpsRate", 2})), + GUI_LAMBDA("fpsRate", mCfgR, fpsRate, GUI_TEXTBOX), + GUI_LAMBDA("GL Indirect Draw", mCfgR, useGLIndirectDraw, GUI_CHECKBOX), + "Draw Quality", true); + + mFrontend->add_gui_binding( + GUI_LAMBDA("Invert Colors", mCfgL, invertColors, GUI_CHECKBOX), + GUI_LAMBDA("Draw Grid", mCfgL, drawGrid, GUI_CHECKBOX), + "Colors / Grid", true); + + mFrontend->add_gui_binding( + GUI_LAMBDA("Next Event", mCfgC, mDisplayControl, GUI_BUTTON, val.store(1);), + GUI_LAMBDA("Exit", mCfgC, mDisplayControl, GUI_BUTTON, val.store(2);), + GUI_LAMBDA("Reset", mCfgC, mResetScene, GUI_BUTTON, val.store(true);), + GUI_LAMBDA("Reset GUI", mCfgC, mGuiScale, GUI_BUTTON, val = 1.f;), + GUI_LAMBDA("Close GUI", mCfgC, dummy, GUI_BUTTON, mFrontend->stopGUI();), + GUI_LAMBDA("InfoText", mCfgC, mGuiPrintInfo, GUI_CHECKBOX), + GUI_LAMBDA("HelpText", mCfgC, mPrintInfoTextAlways, GUI_CHECKBOX), + GUI_LAMBDA("StatusText", mCfgC, mPrintInfoText, GUI_CHECKBOX, val = std::get(guiVal) ? (val | 1) : (val & ~1);), // Bit 0: status line on screen (bit 1: in the console) + true); +} + +#endif // GPUDISPLAYFRONTENDCONFIGQTGUI_INC diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx index c2e98b4e6e754..876ddd2f8e4ef 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.cxx @@ -16,25 +16,160 @@ #include "GPUDisplayFrontend.h" #include "ui_GPUDisplayGUI.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include +#include +#include using namespace o2::gpu; +static void mergeValue(std::vector>& values, uint32_t id, GPUDisplayGUIValue value) // Replace an earlier value of the same element +{ + auto it = std::find_if(values.begin(), values.end(), [id](const auto& v) { return v.first == id; }); + if (it != values.end()) { + it->second = std::move(value); + } else { + values.emplace_back(id, std::move(value)); + } +} + +namespace +{ +class FlowLayout : public QLayout // Places the items in a row with their preferred width, and wraps into further rows at the right border (Qt has no such layout) +{ + public: + FlowLayout(QWidget* parent, int hSpacingFactor, bool equalRowHeight) : QLayout(parent), mHSpacingFactor(hSpacingFactor), mEqualRowHeight(equalRowHeight) {} + ~FlowLayout() override + { + while (QLayoutItem* item = takeAt(0)) { + delete item; + } + } + void addItem(QLayoutItem* item) override { mItems.append(item); } + int count() const override { return mItems.size(); } + QLayoutItem* itemAt(int index) const override { return mItems.value(index); } + QLayoutItem* takeAt(int index) override { return index >= 0 && index < mItems.size() ? mItems.takeAt(index) : nullptr; } + Qt::Orientations expandingDirections() const override { return {}; } + bool hasHeightForWidth() const override { return true; } + int heightForWidth(int width) const override { return doLayout(QRect(0, 0, width, 0), true); } + void setGeometry(const QRect& rect) override + { + QLayout::setGeometry(rect); + doLayout(rect, false); + } + QSize sizeHint() const override // All items in one row + { + QSize size(0, 0); + for (const QLayoutItem* item : mItems) { + size = QSize(size.width() + (size.width() ? hSpacing() : 0) + itemWidth(item), std::max(size.height(), item->sizeHint().height())); + } + const QMargins m = contentsMargins(); + return size + QSize(m.left() + m.right(), m.top() + m.bottom()); + } + QSize minimumSize() const override // Widest item, height for that width is given by heightForWidth + { + QSize size; + for (const QLayoutItem* item : mItems) { + size = size.expandedTo(item->minimumSize()); + } + const QMargins m = contentsMargins(); + return size + QSize(m.left() + m.right(), m.top() + m.bottom()); + } + void setPreferredWidth(QWidget* widget, int width) { mPreferredWidth[widget] = width; } // Instead of the size hint of the widget + + private: + int hSpacing() const { return mHSpacingFactor * spacing(); } + int itemWidth(const QLayoutItem* item) const { return mPreferredWidth.value(item->widget(), item->sizeHint().width()); } + int doLayout(const QRect& rect, bool testOnly) const // Returns the height needed + { + const QRect r = rect.marginsRemoved(contentsMargins()); + int x = r.x(), y = r.y(), lineHeight = 0; + std::vector> line; // Items of the current row, placed when the row is complete (height of the row known) + auto placeLine = [&]() { + if (!testOnly) { + for (auto& [item, geometry] : line) { + item->setGeometry(mEqualRowHeight ? QRect(geometry.x(), geometry.y(), geometry.width(), lineHeight) : geometry); + } + } + line.clear(); + }; + for (QLayoutItem* item : mItems) { + const int width = std::max(std::min(itemWidth(item), r.width()), item->minimumSize().width()); // Items wider than the layout get its width (at least their minimum), and wrap internally if they can + const int height = item->hasHeightForWidth() ? item->heightForWidth(width) : item->sizeHint().height(); + if (x > r.x() && x + width > r.right() + 1) { + placeLine(); + x = r.x(); + y += lineHeight + spacing(); + lineHeight = 0; + } + line.emplace_back(item, QRect(x, y, width, height)); + x += width + hSpacing(); + lineHeight = std::max(lineHeight, height); + } + placeLine(); + return y + lineHeight - rect.y() + contentsMargins().bottom(); + } + QList mItems; + QHash mPreferredWidth; + int mHSpacingFactor; // Horizontal spacing in units of spacing() + bool mEqualRowHeight; // All items of a row get the height of the highest one +}; +} // namespace + +static double toDouble(const GPUDisplayGUIValue& v) +{ + return std::visit([](const auto& x) -> double { + if constexpr (std::is_same_v, std::string>) { + return QString::fromStdString(x).toDouble(); + } else { + return x; + } + }, + v); +} + +static bool sameValue(const GPUDisplayGUIValue& a, const GPUDisplayGUIValue& b) // Numbers compared by value, independent of int64_t / double +{ + const bool aString = std::holds_alternative(a), bString = std::holds_alternative(b); + return aString || bString ? a == b : toDouble(a) == toDouble(b); +} + GPUDisplayGUI::GPUDisplayGUI(GPUDisplayGUIChannel& channel, QWidget* parent) : QMainWindow(parent), mUi(std::make_unique()), mChannel(channel) { mUi->setupUi(this); + std::vector layout; { std::lock_guard lock(mChannel.mutex); setWindowTitle(QString::fromStdString(mChannel.windowName + " GUI")); + layout = mChannel.layout; } - connect(mUi->lineWidthSlider, &QSlider::valueChanged, this, [this](int32_t value) { - mUpdate.lineWidth = value * 0.1f; - mUi->lineWidthValue->setNum(*mUpdate.lineWidth); - }); + mBaseFontSize = font().pointSizeF() > 0 ? font().pointSizeF() : 10.f; // Fonts with pixel size have no point size + buildElements(layout); + setScale(mScale); // Scale from the display with the first state + qApp->installEventFilter(this); // Sliders / dropdowns would consume Ctrl + mouse wheel themselves connect(&mTimer, &QTimer::timeout, this, &GPUDisplayGUI::timerTick); mTimer.start(std::chrono::milliseconds(10)); } @@ -70,8 +205,11 @@ void GPUDisplayGUI::timerTick() if (mChannel.update) { // Previous update not yet picked up by the display, merge auto& update = *mChannel.update; update.keys.insert(update.keys.end(), mUpdate.keys.begin(), mUpdate.keys.end()); - if (mUpdate.lineWidth) { - update.lineWidth = mUpdate.lineWidth; + for (const auto& [id, value] : mUpdate.values) { + mergeValue(update.values, id, value); + } + if (mUpdate.guiScale) { + update.guiScale = mUpdate.guiScale; } } else { mChannel.update = std::move(mUpdate); @@ -92,9 +230,232 @@ void GPUDisplayGUI::timerTick() void GPUDisplayGUI::applySettings() { - const QSignalBlocker blocker(mUi->lineWidthSlider); // Not a user change - mUi->lineWidthSlider->setValue(std::lround(mState.cfg.cfgL.lineWidth * 10.f)); - mUi->lineWidthValue->setNum(mState.cfg.cfgL.lineWidth); + if (mState.guiScale != mScale) { // Changed by the display (Reset GUI button) or from a previous GUI session + setScale(mState.guiScale); + } + for (uint32_t id = 0; id < mElementSetters.size() && id < mState.values.size(); id++) { + if (mElementSetters[id]) { + mElementSetters[id](mState.values[id]); + } + } +} + +void GPUDisplayGUI::setValue(uint32_t id, GPUDisplayGUIValue value) { mergeValue(mUpdate.values, id, std::move(value)); } + +void GPUDisplayGUI::buildElements(const std::vector& layout) +{ + using Type = GPUDisplayGUIChannel::ElementType; + static constexpr int32_t SLIDER_STEPS = 1000; // Resolution of sliders for floating point options + auto* frames = new QWidget; // Frames with their natural width, next to each other while they fit into the window + auto* framesLayout = new FlowLayout(frames, 1, true); + mFramesLayout = framesLayout; + framesLayout->setContentsMargins(0, 0, 0, 0); + mUi->mainLayout->insertWidget(mUi->mainLayout->count() - 1, frames); // Before the vertical spacer + for (const auto& frame : layout) { + auto* box = new QGroupBox(QString::fromStdString(frame.description)); + if (frame.description.empty()) { + box->setStyleSheet("QGroupBox { margin-top: 0; }"); // No space for a title + } + const bool horizontal = frame.horizontal; + QLayout* boxLayout = horizontal ? (QLayout*)new FlowLayout(box, 3, false) : (QLayout*)new QFormLayout(box); // More space between the elements than between the rows + for (const auto& el : frame.elements) { + const uint32_t id = el.id; + QWidget* widget = nullptr; + std::function setter; // Setting a widget programmatically is not a user change, signals emitted by it are blocked + if (el.type == Type::CHECKBOX) { + auto* w = new QCheckBox(QString::fromStdString(el.name)); // Own text instead of a label + connect(w, &QCheckBox::toggled, this, [this, id](bool v) { setValue(id, (int64_t)v); }); + setter = [w](const GPUDisplayGUIValue& v) { const QSignalBlocker blocker(w); w->setChecked(toDouble(v) != 0.); }; + widget = w; + } else if (el.type == Type::TEXTBOX) { + auto* w = new QLineEdit; + if (!el.hasRange) { // With range, any text is accepted and converted / limited + w->setValidator(el.isInteger ? (QValidator*)new QIntValidator(w) : (QValidator*)new QDoubleValidator(w)); + } + connect(w, &QLineEdit::editingFinished, this, [this, w, id, isInteger = el.isInteger, hasRange = el.hasRange, min = el.min, max = el.max]() { + bool ok = false; + GPUDisplayGUIValue v = isInteger ? GPUDisplayGUIValue((int64_t)w->text().toLongLong(&ok)) : GPUDisplayGUIValue(w->text().toDouble(&ok)); + if (hasRange) { + const double clamped = std::clamp(ok ? toDouble(v) : 0., min, max); + v = isInteger ? GPUDisplayGUIValue((int64_t)std::llround(clamped)) : GPUDisplayGUIValue(clamped); + w->setText(QString::number(clamped)); // Show the value actually set, the display state does not overwrite the focused field + } else if (!ok) { + return; + } + setValue(id, v); + }); + setter = [w](const GPUDisplayGUIValue& v) { + if (!w->hasFocus()) { // Do not overwrite while the user edits + w->setText(std::visit([](const auto& x) { + if constexpr (std::is_same_v, std::string>) { + return QString::fromStdString(x); + } else { + return QString::number(x); + } + }, + v)); + } + }; + widget = w; + } else if (el.type == Type::SLIDER) { + widget = new QWidget; + auto* row = new QHBoxLayout(widget); + row->setContentsMargins(0, 0, 0, 0); + auto* slider = new QSlider(Qt::Horizontal); + auto* label = new QLabel; + if (el.isInteger || el.choices.size()) { // Length by number of steps, about twice a checkbox indicator per step + const int steps = el.choices.size() ? el.choices.size() - 1 : std::lround(el.max - el.min); + mScaledWidths.push_back({slider, steps * 2 * style()->pixelMetric(QStyle::PM_IndicatorWidth), true}); + } else { + mScaledWidths.push_back({slider, 150, false}); + } + mScaledWidths.push_back({label, 50, false}); + row->addWidget(slider); + row->addWidget(label); + if (el.choices.size()) { // Positions are the indices of the choices + slider->setRange(0, el.choices.size() - 1); + connect(slider, &QSlider::valueChanged, this, [this, label, id, choices = el.choices](int32_t pos) { + label->setText(QString::fromStdString(choices[pos].first)); + setValue(id, choices[pos].second); + }); + setter = [slider, label, choices = el.choices](const GPUDisplayGUIValue& value) { + uint32_t best = 0; // Nearest choice for values not in the list + for (uint32_t i = 1; i < choices.size(); i++) { + if (std::abs(toDouble(choices[i].second) - toDouble(value)) < std::abs(toDouble(choices[best].second) - toDouble(value))) { + best = i; + } + } + const QSignalBlocker blocker(slider); + slider->setValue(best); + label->setText(sameValue(choices[best].second, value) ? QString::fromStdString(choices[best].first) : QString::number(toDouble(value))); + }; + } else { + const double min = el.min, step = el.isInteger ? 1. : (el.max - el.min) / SLIDER_STEPS; + slider->setRange(0, el.isInteger ? std::lround(el.max - el.min) : SLIDER_STEPS); + connect(slider, &QSlider::valueChanged, this, [this, label, id, min, step, isInteger = el.isInteger](int32_t pos) { + const double v = min + pos * step; + label->setNum(v); + setValue(id, isInteger ? GPUDisplayGUIValue((int64_t)std::llround(v)) : GPUDisplayGUIValue(v)); + }); + setter = [slider, label, min, step](const GPUDisplayGUIValue& value) { + const double v = toDouble(value); + const QSignalBlocker blocker(slider); + slider->setValue(std::lround((v - min) / step)); + label->setNum(v); + }; + } + } else if (el.type == Type::RADIO_BUTTONS) { + widget = new QWidget; + auto* row = new QHBoxLayout(widget); + row->setContentsMargins(0, 0, 0, 0); + auto* group = new QButtonGroup(widget); + for (uint32_t i = 0; i < el.choices.size(); i++) { + auto* button = new QRadioButton(QString::fromStdString(el.choices[i].first)); + group->addButton(button, i); + row->addWidget(button); + } + connect(group, &QButtonGroup::idClicked, this, [this, id, choices = el.choices](int32_t i) { setValue(id, choices[i].second); }); // idClicked only on user clicks + setter = [group, choices = el.choices](const GPUDisplayGUIValue& v) { + for (uint32_t i = 0; i < choices.size(); i++) { + if (sameValue(choices[i].second, v)) { + group->button(i)->setChecked(true); + } + } + }; + } else if (el.type == Type::BUTTON) { // No value from the display + auto* w = new QPushButton(QString::fromStdString(el.name)); + connect(w, &QPushButton::clicked, this, [this, id]() { setValue(id, (int64_t)1); }); + widget = w; + } else if (el.type == Type::DROPDOWN) { + auto* w = new QComboBox; + for (const auto& choice : el.choices) { + w->addItem(QString::fromStdString(choice.first)); + } + connect(w, &QComboBox::currentIndexChanged, this, [this, id, choices = el.choices](int32_t i) { + if (i >= 0) { + setValue(id, choices[i].second); + } + }); + setter = [w, choices = el.choices](const GPUDisplayGUIValue& v) { + const QSignalBlocker blocker(w); + for (uint32_t i = 0; i < choices.size(); i++) { + if (sameValue(choices[i].second, v)) { + w->setCurrentIndex(i); + } + } + }; + widget = w; + } + QLabel* name = el.type == Type::CHECKBOX || el.type == Type::BUTTON ? nullptr : new QLabel(QString::fromStdString(el.name)); // Checkboxes and buttons show their own text + if (!horizontal) { + name ? ((QFormLayout*)boxLayout)->addRow(name, widget) : ((QFormLayout*)boxLayout)->addRow(widget); + } else if (name) { // Label and element as one item of the flow layout + auto* item = new QWidget; + auto* row = new QHBoxLayout(item); + row->setContentsMargins(0, 0, 0, 0); + row->addWidget(name); + row->addWidget(widget); + boxLayout->addWidget(item); + } else { + boxLayout->addWidget(widget); + } + if (mElementSetters.size() <= id) { + mElementSetters.resize(id + 1); + } + mElementSetters[id] = std::move(setter); + } + framesLayout->addWidget(box); + if (std::any_of(frame.elements.begin(), frame.elements.end(), [](const auto& el) { return el.type == Type::SLIDER && !el.isInteger && el.choices.empty(); })) { + mSliderFrames.emplace_back(box); // Preferred width set in setScale(), sliders range to the frame border, frame limited to the window width by the layout + } + } +} + +void GPUDisplayGUI::setScale(float scale) +{ + mScale = std::clamp(scale, 0.25f, 4.f); + for (const auto& w : mScaledWidths) { + if (w.fixed) { + w.widget->setFixedWidth(std::lround(w.width * mScale)); + } else { + w.widget->setMinimumWidth(std::lround(w.width * mScale)); + } + } + for (auto* box : mSliderFrames) { + static_cast(mFramesLayout)->setPreferredWidth(box, std::lround(screen()->availableGeometry().width() / 3 * mScale)); + } + if (mFramesLayout) { + mFramesLayout->invalidate(); + } + const int indicator = std::lround(style()->pixelMetric(QStyle::PM_IndicatorWidth) * mScale); + setStyleSheet(QString("QGroupBox { border: 1px solid palette(mid); border-radius: 3px; margin-top: 0.6em; padding-top: 0.3em; }" + "QGroupBox::title { subcontrol-origin: margin; left: 8px; padding: 0 3px; }" // Title on the frame border instead of in an extra line above + "QCheckBox::indicator, QRadioButton::indicator { width: %1px; height: %1px; }" + "QWidget { font-size: %2pt; }") // Style sheet, since setFont() does not override the fonts the platform theme sets for some classes (QLabel, QPushButton), sizes derived from the text follow + .arg(indicator) + .arg(mBaseFontSize * mScale, 0, 'f', 1)); +} + +bool GPUDisplayGUI::eventFilter(QObject* watched, QEvent* event) +{ + const auto* w = qobject_cast(watched); + if (!w || w->window() != this) { + return false; + } + if (event->type() == QEvent::Wheel && (static_cast(event)->modifiers() & Qt::ControlModifier)) { + const int delta = static_cast(event)->angleDelta().y(); // 120 per notch + if (delta) { + setScale(mScale * std::pow(1.1f, delta / 120.f)); + mUpdate.guiScale = mScale; + } + return true; + } + if (event->type() == QEvent::KeyPress && static_cast(event)->key() == Qt::Key_0 && (static_cast(event)->modifiers() & Qt::ControlModifier)) { + setScale(1.f); + mUpdate.guiScale = mScale; + return true; + } + return false; } void GPUDisplayGUI::bringToFront() diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.h b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.h index e0da6423e94d2..b64671df7374a 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.h @@ -23,7 +23,10 @@ #include "GPUDisplayGUIChannel.h" #include #include +#include #include +#include +#include class QKeyEvent; @@ -47,19 +50,34 @@ class GPUDisplayGUI : public QMainWindow protected: void keyPressEvent(QKeyEvent* event) override; + bool eventFilter(QObject* watched, QEvent* event) override; // Ctrl + mouse wheel / Ctrl + 0 for all widgets of the window private: void timerTick(); // Exchange updates / settings with the display void applySettings(); - static int32_t translateKey(const QKeyEvent* event); // Qt key → display key code, -1 if not forwarded + void buildElements(const std::vector& layout); // One frame per add_gui_binding call, with a labeled element per option + void setValue(uint32_t id, GPUDisplayGUIValue value); // Element changed by the user, sent with the next update + void setScale(float scale); // Font size, fixed element sizes, and check / radio indicators (not sent to the display) + static int32_t translateKey(const QKeyEvent* event); // Qt key → display key code, -1 if not forwarded std::unique_ptr mUi; QTimer mTimer; GPUDisplayGUIChannel& mChannel; - GPUDisplayGUIChannel::Update mUpdate; // Changes collected since the last tick - GPUDisplayGUIChannel::State mState; // Last state received from the display - uint64_t mLastSentId = 0; // Id of the last update sent - uint64_t mLastAppliedId = 0; // Id of the last update for which the display state was received + GPUDisplayGUIChannel::Update mUpdate; // Changes collected since the last tick + GPUDisplayGUIChannel::State mState; // Last state received from the display + uint64_t mLastSentId = 0; // Id of the last update sent + uint64_t mLastAppliedId = 0; // Id of the last update for which the display state was received + std::vector> mElementSetters; // By element id, set the element to a value from the display + struct ScaledWidth { + QWidget* widget; + int width; // Unscaled pixels + bool fixed; // Fixed width, otherwise minimum width + }; + std::vector mScaledWidths; // Widget widths that scale with the GUI + std::vector mSliderFrames; // Frames with floating point sliders, preferred width 1/3 of the screen width times the scale + QLayout* mFramesLayout = nullptr; // FlowLayout of the frames + float mScale = 1.f; + float mBaseFontSize = 0.f; // Point size of the font at scale 1 }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.ui b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.ui index 77b2a7401be11..fce6ed6964ca1 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayGUI.ui +++ b/GPU/GPUTracking/display/frontend/GPUDisplayGUI.ui @@ -15,40 +15,6 @@ - - - - - - Line width - - - - - - - 1 - - - 50 - - - Qt::Orientation::Horizontal - - - - - - - - 40 - 0 - - - - - - diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h b/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h index 0ee36a4ec213e..93e82d8aa1c82 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h +++ b/GPU/GPUTracking/display/frontend/GPUDisplayGUIChannel.h @@ -16,6 +16,7 @@ #define GPUDISPLAYGUICHANNEL_H #include "GPUSettings.h" +#include "GPUDisplayFrontend.h" #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include namespace o2::gpu @@ -36,16 +38,41 @@ struct GPUDisplayGUIChannel { GPUSettingsDisplayLight cfgL; GPUSettingsDisplayRenderer cfgR; }; + enum class ElementType : uint8_t { CHECKBOX, + TEXTBOX, + SLIDER, + RADIO_BUTTONS, + DROPDOWN, + BUTTON }; + + struct Element { // GUI element for one option, from GUI_LAMBDA in GPUDisplayFrontendConfigQtGUI.inc + std::string name; // Label + uint32_t rttiIndex; // Option in the display config RTTI, invalid for options without RTTI (GPUDisplay::control) + ElementType type; // Widget type + uint32_t id; // Index of the element in Update::values / State::values + bool isInteger; // Integer / bool option, otherwise floating point + bool hasRange = false; // Text field: value converted (0 if invalid) and limited to min / max + double min = 0, max = 0; // Slider range / text field range (if hasRange), from the min / max of the option + std::vector> choices; // Radio buttons / dropdown / slider with only these values: name -> value + }; + struct Frame { // One add_gui_binding call + std::string description; + std::vector elements; + bool horizontal = false; // Elements in a row, wrapping into further rows at the window border, otherwise one element per row + }; struct Update { // GUI → display uint64_t id = 0; - std::vector keys; // Keys pressed in the GUI and not handled by it, display key codes - std::optional lineWidth; // For testing, to be replaced by generic settings updates - bool empty() const { return keys.empty() && !lineWidth; } + std::vector keys; // Keys pressed in the GUI and not handled by it, display key codes + std::vector> values; // Elements changed in the GUI: element id, new value + std::optional guiScale; // GUI scale changed with Ctrl + mouse wheel / Ctrl + 0 + bool empty() const { return keys.empty() && values.empty() && !guiScale; } }; struct State { // display → GUI Settings cfg; - uint64_t appliedUpdate = 0; // Id of the last GUI update included in cfg - uint64_t counter = 0; // Incremented with every published state + std::vector values; // Current values of the options of the GUI elements, by element id + float guiScale = 1.f; // GPUDisplay::control::mGuiScale + uint64_t appliedUpdate = 0; // Id of the last GUI update included in cfg + uint64_t counter = 0; // Incremented with every published state }; std::mutex mutex; // Protects all members below @@ -58,6 +85,7 @@ struct GPUDisplayGUIChannel { std::chrono::milliseconds replyTimeout; // Maximum time the GUI waits for a reply uint32_t maxUpdatesAhead = 0; // GUI waits for the reply if it is that many updates ahead of the last one it applied std::string windowName; // Name of the display window, set when starting the GUI + std::vector layout; // GUI elements, set when starting the GUI std::function updatePosted; // Set by the display, called by the GUI (without holding mutex) after posting an update, wakes up the display }; } // namespace o2::gpu