From 059600e8d4e23f2ea17d2f178c46e08410281ca2 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 12:32:17 +0100 Subject: [PATCH 01/18] Initial effort to reorganize the startup code. This is intended to make things like plugin initialization more sane, and make Qt happier. Qt wants QApplication to start as soon as possible, but our code's attempt to load the entire world in the Application constructor doesn't quite mesh with this. This is an effort to first create Application much earlier, and do as little as possible in the constructor, moving almost all initialization to a later init function. A later stage would be to split the giant mess of init code into more digestible sections that run in some sort of logical order. --- interface/src/Application.cpp | 37 +++++++++++++++++------ interface/src/Application.h | 20 +++++++++++-- interface/src/main.cpp | 55 ++++++++++++++++++++++++++--------- 3 files changed, 87 insertions(+), 25 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 37204b0f9b..042a065a79 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -724,7 +724,7 @@ extern DisplayPluginList getDisplayPlugins(); extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); -bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, bool runningMarkerExisted) { +bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) { qInstallMessageHandler(messageHandler); @@ -743,6 +743,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b bool previousSessionCrashed { false }; if (!inTestMode) { + // TODO: FIX previousSessionCrashed = CrashRecoveryHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt); } @@ -764,6 +765,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b } // Tell the plugin manager about our statically linked plugins + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); auto pluginManager = PluginManager::getInstance(); @@ -777,6 +779,7 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b oculusPlatform->init(); } + PROFILE_SET_THREAD_NAME("Main Thread"); #if defined(Q_OS_WIN) @@ -993,8 +996,7 @@ bool Application::initMenu() { Application::Application( int& argc, char** argv, const QCommandLineParser& parser, - QElapsedTimer& startupTimer, - bool runningMarkerExisted + QElapsedTimer& startupTimer ) : QApplication(argc, argv), _window(new MainWindow(desktop())), @@ -1004,7 +1006,7 @@ Application::Application( #ifndef Q_OS_ANDROID _logger(new FileLogger(this)), #endif - _previousSessionCrashed(setupEssentials(argc, argv, parser, runningMarkerExisted)), + _previousSessionCrashed(setupEssentials(parser, false)), _entitySimulation(std::make_shared()), _physicsEngine(std::make_shared(Vectors::ZERO)), _entityClipboard(std::make_shared()), @@ -1032,12 +1034,22 @@ Application::Application( _snapshotSound(nullptr), _sampleSound(nullptr) { - auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); - setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); LogHandler::getInstance().moveToThread(thread()); LogHandler::getInstance().setupRepeatedMessageFlusher(); +} + +void Application::initialize(const QCommandLineParser &parser) { + + //qCDebug(interfaceapp) << "Setting up essentials"; + //setupEssentials(parser, _previousSessionCrashed); + qCDebug(interfaceapp) << "Initializing application"; + + + auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); + setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); + { if (parser.isSet("testScript")) { @@ -2375,7 +2387,7 @@ Application::Application( connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool))); - qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); + qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)_sessionRunTimer.elapsed() / 1000.0); EntityTreeRenderer::setEntitiesShouldFadeFunction([this]() { SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); @@ -4201,7 +4213,9 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) { } bool Application::notify(QObject * object, QEvent * event) { - if (thread() == QThread::currentThread()) { + if (thread() == QThread::currentThread() && _profilingInitialized ) { + // _profilingInitialized gets set once we're reading for profiling. + // this prevents a deadlock due to profiling not working yet PROFILE_RANGE_IF_LONGER(app, "notify", 2) return QApplication::notify(object, event); } @@ -8762,7 +8776,11 @@ void Application::sendLambdaEvent(const std::function& f) { } } -void Application::initPlugins(const QCommandLineParser& parser) { +void Application::configurePlugins(const QCommandLineParser& parser) { + // This must be a member function -- PluginManager must exist, and for that + // QApplication must exist, or it can't find the plugin path, as QCoreApplication:applicationDirPath + // won't work yet. + if (parser.isSet("display")) { auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts); qInfo() << "Setting prefered display plugins:" << preferredDisplays; @@ -8780,6 +8798,7 @@ void Application::initPlugins(const QCommandLineParser& parser) { qInfo() << "Disabling following input plugins:" << disabledInputs; PluginManager::getInstance()->disableInputs(disabledInputs); } + } void Application::shutdownPlugins() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 82b39e868b..9984855740 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -123,6 +123,19 @@ class Application : public QApplication, friend class OctreePacketProcessor; public: + /** + * @brief Initialize everything + * + * This is a QApplication, and for Qt reasons it's desirable to create this object + * as early as possible. Without that some Qt functions don't work, like QCoreApplication::applicationDirPath() + * + * So we keep the constructor as minimal as possible, and do the rest of the work in + * this function. + */ + void initialize(const QCommandLineParser &parser); + + void setPreviousSessionCrashed(bool value) { _previousSessionCrashed = value; } + // virtual functions required for PluginContainer virtual ui::Menu* getPrimaryMenu() override; virtual void requestReset() override { resetSensors(false); } @@ -136,14 +149,13 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; // FIXME? Empty methods, do we still need them? - static void initPlugins(const QCommandLineParser& parser); + void configurePlugins(const QCommandLineParser& parser); static void shutdownPlugins(); Application( int& argc, char** argv, const QCommandLineParser& parser, - QElapsedTimer& startup_time, - bool runningMarkerExisted + QElapsedTimer& startup_time ); ~Application(); @@ -860,5 +872,7 @@ private: bool _crashOnShutdown { false }; DiscordPresence* _discordPresence{ nullptr }; + + bool _profilingInitialized { false }; }; #endif // hifi_Application_h diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 18da2c2e00..b9501419f1 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -68,6 +68,18 @@ int main(int argc, const char* argv[]) { // Journald by default in user applications is probably a bit too modern still. LogHandler::getInstance().setShouldUseJournald(false); + + // Extend argv to enable WebGL rendering + std::vector argvExtended(&argv[0], &argv[argc]); + argvExtended.push_back("--ignore-gpu-blocklist"); +#ifdef Q_OS_ANDROID + argvExtended.push_back("--suppress-settings-reset"); +#endif + int argcExtended = (int)argvExtended.size(); + + QElapsedTimer startupTime; + startupTime.start(); + QCommandLineParser parser; parser.setApplicationDescription("Overte -- A free/libre and open-source virtual worlds client"); QCommandLineOption helpOption = parser.addHelpOption(); @@ -246,6 +258,15 @@ int main(int argc, const char* argv[]) { "Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald", "options" ); + QCommandLineOption abortAfterStartupOption( + "abortAfterStartup", + "Debug option. Aborts right after startup." + ); + QCommandLineOption abortAfterInitOption( + "abortAfterInit", + "Debug option. Aborts after initialization, right before the program starts running the event loop." + ); + // "--qmljsdebugger", which appears in output from "--help-all". // Those below don't seem to be optional. // --ignore-gpu-blacklist @@ -288,6 +309,9 @@ int main(int argc, const char* argv[]) { parser.addOption(quitWhenFinishedOption); parser.addOption(fastHeartbeatOption); parser.addOption(logOption); + parser.addOption(abortAfterStartupOption); + parser.addOption(abortAfterInitOption); + QString applicationPath; // A temporary application instance is needed to get the location of the running executable @@ -310,6 +334,16 @@ int main(int argc, const char* argv[]) { #endif } + // TODO: We need settings for Application, but Settings needs an Application + // to handle events. Needs splitting into two parts: enough initialization + // for Application to work, and then thread start afterwards. + Setting::init(); + Application app(argcExtended, const_cast(argvExtended.data()), parser, startupTime); + + if (parser.isSet("abortAfterStartup")) { + return 99; + } + // We want to configure the logging system as early as possible auto& logHandler = LogHandler::getInstance(); @@ -407,10 +441,9 @@ int main(int argc, const char* argv[]) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); #endif - QElapsedTimer startupTime; - startupTime.start(); - Setting::init(); + + // Instance UserActivityLogger now that the settings are loaded auto& ual = UserActivityLogger::getInstance(); @@ -548,7 +581,7 @@ int main(int argc, const char* argv[]) { // Oculus initialization MUST PRECEDE OpenGL context creation. // The nature of the Application constructor means this has to be either here, // or in the main window ctor, before GL startup. - Application::initPlugins(parser); + app.configurePlugins(parser); #ifdef Q_OS_WIN // If we're running in steam mode, we need to do an explicit check to ensure we're up to the required min spec @@ -586,17 +619,10 @@ int main(int argc, const char* argv[]) { SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater); } - // Extend argv to enable WebGL rendering - std::vector argvExtended(&argv[0], &argv[argc]); - argvExtended.push_back("--ignore-gpu-blocklist"); -#ifdef Q_OS_ANDROID - argvExtended.push_back("--suppress-settings-reset"); -#endif - int argcExtended = (int)argvExtended.size(); - PROFILE_SYNC_END(startup, "main startup", ""); PROFILE_SYNC_BEGIN(startup, "app full ctor", ""); - Application app(argcExtended, const_cast(argvExtended.data()), parser, startupTime, runningMarkerExisted); + app.setPreviousSessionCrashed(runningMarkerExisted); + app.initialize(parser); PROFILE_SYNC_END(startup, "app full ctor", ""); #if defined(Q_OS_LINUX) @@ -664,6 +690,9 @@ int main(int argc, const char* argv[]) { translator.load("i18n/interface_en"); app.installTranslator(&translator); qCDebug(interfaceapp, "Created QT Application."); + if (parser.isSet("abortAfterInit")) { + return 99; + } exitCode = app.exec(); server.close(); From 35b4c44e9a03f23b500fe39cf6c5d378a196ae6b Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 16:45:28 +0100 Subject: [PATCH 02/18] Delay initializing shared pointers --- interface/src/Application.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 042a065a79..f03ce82b1a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1007,9 +1007,6 @@ Application::Application( _logger(new FileLogger(this)), #endif _previousSessionCrashed(setupEssentials(parser, false)), - _entitySimulation(std::make_shared()), - _physicsEngine(std::make_shared(Vectors::ZERO)), - _entityClipboard(std::make_shared()), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), @@ -1046,6 +1043,13 @@ void Application::initialize(const QCommandLineParser &parser) { //setupEssentials(parser, _previousSessionCrashed); qCDebug(interfaceapp) << "Initializing application"; + _entitySimulation = std::make_shared(); + _physicsEngine = std::make_shared(Vectors::ZERO); + _entityClipboard = std::make_shared(); + + + + auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); From 3dabf23ff49cedb05d410181b2ac4ecb50a996ad Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 17:32:12 +0100 Subject: [PATCH 03/18] Change _octreeProcessor and _entityEditSender to shared_ptr --- interface/src/Application.cpp | 57 ++++++++++++++++--------------- interface/src/Application.h | 8 ++--- interface/src/avatar/MyAvatar.cpp | 10 +++--- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f03ce82b1a..9ce97dd5cb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1006,7 +1006,7 @@ Application::Application( #ifndef Q_OS_ANDROID _logger(new FileLogger(this)), #endif - _previousSessionCrashed(setupEssentials(parser, false)), + _previousSessionCrashed(false), //setupEssentials(parser, false)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), @@ -1046,7 +1046,8 @@ void Application::initialize(const QCommandLineParser &parser) { _entitySimulation = std::make_shared(); _physicsEngine = std::make_shared(Vectors::ZERO); _entityClipboard = std::make_shared(); - + _octreeProcessor = std::make_shared(); + _entityEditSender = std::make_shared(); @@ -1421,7 +1422,7 @@ void Application::initialize(const QCommandLineParser &parser) { connect(myAvatar.get(), &MyAvatar::positionGoneTo, this, [this] { if (!_physicsEnabled) { // when we arrive somewhere without physics enabled --> startSafeLanding - _octreeProcessor.startSafeLanding(); + _octreeProcessor->startSafeLanding(); } }, Qt::QueuedConnection); @@ -1594,9 +1595,9 @@ void Application::initialize(const QCommandLineParser &parser) { qCDebug(interfaceapp, "init() complete."); // create thread for parsing of octree data independent of the main network and rendering threads - _octreeProcessor.initialize(_enableProcessOctreeThread); - connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); - _entityEditSender.initialize(_enableProcessOctreeThread); + _octreeProcessor->initialize(_enableProcessOctreeThread); + connect(_octreeProcessor.get(), &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); + _entityEditSender->initialize(_enableProcessOctreeThread); _idleLoopStdev.reset(); @@ -1714,7 +1715,7 @@ void Application::initialize(const QCommandLineParser &parser) { userActivityLogger.logAction("launch", properties); } - _entityEditSender.setMyAvatar(myAvatar.get()); + _entityEditSender->setMyAvatar(myAvatar.get()); // The entity octree will have to know about MyAvatar for the parentJointName import getEntities()->getTree()->setMyAvatar(myAvatar); @@ -1723,7 +1724,7 @@ void Application::initialize(const QCommandLineParser &parser) { // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to // allow you to move an entity around in your hand - _entityEditSender.setPacketsPerSecond(3000); // super high!! + _entityEditSender->setPacketsPerSecond(3000); // super high!! // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -2923,8 +2924,8 @@ Application::~Application() { _entityClipboard->eraseAllOctreeElements(); _entityClipboard.reset(); - _octreeProcessor.terminate(); - _entityEditSender.terminate(); + _octreeProcessor->terminate(); + _entityEditSender->terminate(); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { steamClient->shutdown(); @@ -4076,7 +4077,7 @@ std::map Application::prepareServerlessDomainContents(QUrl dom bool success = tmpTree->readFromByteArray(domainURL.toString(), data); if (success) { tmpTree->reaverageOctreeElements(); - tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), "domain", 0, 0, 0); + tmpTree->sendEntities(_entityEditSender.get(), getEntities()->getTree(), "domain", 0, 0, 0); } std::map namedPaths = tmpTree->getNamedPaths(); @@ -5632,7 +5633,7 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse } QVector Application::pasteEntities(const QString& entityHostType, float x, float y, float z) { - return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), entityHostType, x, y, z); + return _entityClipboard->sendEntities(_entityEditSender.get(), getEntities()->getTree(), entityHostType, x, y, z); } void Application::init() { @@ -5682,7 +5683,7 @@ void Application::init() { _physicsEngine->init(); EntityTreePointer tree = getEntities()->getTree(); - _entitySimulation->init(tree, _physicsEngine, &_entityEditSender); + _entitySimulation->init(tree, _physicsEngine, _entityEditSender.get()); tree->setSimulation(_entitySimulation); auto entityScriptingInterface = DependencyManager::get(); @@ -5914,8 +5915,8 @@ void Application::updateThreads(float deltaTime) { // parse voxel packets if (!_enableProcessOctreeThread) { - _octreeProcessor.threadRoutine(); - _entityEditSender.threadRoutine(); + _octreeProcessor->threadRoutine(); + _entityEditSender->threadRoutine(); } } @@ -6038,7 +6039,7 @@ void Application::resetPhysicsReadyInformation() { _gpuTextureMemSizeStabilityCount = 0; _gpuTextureMemSizeAtLastCheck = 0; _physicsEnabled = false; - _octreeProcessor.stopSafeLanding(); + _octreeProcessor->stopSafeLanding(); } void Application::reloadResourceCaches() { @@ -6301,7 +6302,7 @@ void Application::tryToEnablePhysics() { auto myAvatar = getMyAvatar(); if (myAvatar->isReadyForPhysics()) { myAvatar->getCharacterController()->setPhysicsEngine(_physicsEngine); - _octreeProcessor.resetSafeLanding(); + _octreeProcessor->resetSafeLanding(); _physicsEnabled = true; setIsInterstitialMode(false); myAvatar->updateMotionBehaviorFromMenu(); @@ -6327,12 +6328,12 @@ void Application::update(float deltaTime) { if (isServerlessMode()) { tryToEnablePhysics(); } else if (_failedToConnectToEntityServer) { - if (_octreeProcessor.safeLandingIsActive()) { - _octreeProcessor.stopSafeLanding(); + if (_octreeProcessor->safeLandingIsActive()) { + _octreeProcessor->stopSafeLanding(); } } else { - _octreeProcessor.updateSafeLanding(); - if (_octreeProcessor.safeLandingIsComplete()) { + _octreeProcessor->updateSafeLanding(); + if (_octreeProcessor->safeLandingIsComplete()) { tryToEnablePhysics(); } } @@ -6987,7 +6988,7 @@ int Application::sendNackPackets() { // if there are octree packets from this node that are waiting to be processed, // don't send a NACK since the missing packets may be among those waiting packets. - if (_octreeProcessor.hasPacketsToProcessFrom(nodeUUID)) { + if (_octreeProcessor->hasPacketsToProcessFrom(nodeUUID)) { return; } @@ -7029,7 +7030,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { const bool isModifiedQuery = !_physicsEnabled; if (isModifiedQuery) { - if (!_octreeProcessor.safeLandingIsActive()) { + if (!_octreeProcessor->safeLandingIsActive()) { // don't send the octreeQuery until SafeLanding knows it has started return; } @@ -7298,12 +7299,12 @@ void Application::resettingDomain() { void Application::nodeAdded(SharedNodePointer node) { if (node->getType() == NodeType::EntityServer) { if (_failedToConnectToEntityServer && !_entityServerConnectionTimer.isActive()) { - _octreeProcessor.stopSafeLanding(); + _octreeProcessor->stopSafeLanding(); _failedToConnectToEntityServer = false; } else if (_entityServerConnectionTimer.isActive()) { _entityServerConnectionTimer.stop(); } - _octreeProcessor.startSafeLanding(); + _octreeProcessor->startSafeLanding(); _entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT); _entityServerConnectionTimer.start(); } @@ -7375,9 +7376,9 @@ void Application::nodeKilled(SharedNodePointer node) { // OctreePacketProcessor::nodeKilled is not being called when NodeList::nodeKilled is emitted. // This may have to do with GenericThread::threadRoutine() blocking the QThread event loop - _octreeProcessor.nodeKilled(node); + _octreeProcessor->nodeKilled(node); - _entityEditSender.nodeKilled(node); + _entityEditSender->nodeKilled(node); if (node->getType() == NodeType::AudioMixer) { QMetaObject::invokeMethod(DependencyManager::get().data(), "audioMixerKilled"); @@ -7466,7 +7467,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptManagerPoint // setup the packet sender of the script engine's scripting interfaces so // we can use the same ones from the application. auto entityScriptingInterface = DependencyManager::get(); - entityScriptingInterface->setPacketSender(&_entityEditSender); + entityScriptingInterface->setPacketSender(_entityEditSender.get()); entityScriptingInterface->setEntityTree(getEntities()->getTree()); if (property(hifi::properties::TEST).isValid()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 9984855740..8c9937401f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -209,11 +209,11 @@ public: const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; } - const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } + const OctreePacketProcessor& getOctreePacketProcessor() const { return *_octreeProcessor; } QSharedPointer getEntities() const { return DependencyManager::get(); } MainWindow* getWindow() const { return _window; } EntityTreePointer getEntityClipboard() const { return _entityClipboard; } - EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; } + std::shared_ptr getEntityEditPacketSender() { return _entityEditSender; } ivec2 getMouse() const; @@ -721,8 +721,8 @@ private: bool _enableProcessOctreeThread; bool _interstitialMode { false }; - OctreePacketProcessor _octreeProcessor; - EntityEditPacketSender _entityEditSender; + std::shared_ptr _octreeProcessor; + std::shared_ptr _entityEditSender; StDev _idleLoopStdev; float _idleLoopMeasuredJitter; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 58c86e5e8a..921355a9e4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1035,8 +1035,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) { std::pair zoneInteractionProperties; entityTree->withWriteLock([&] { zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties(); - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - entityTree->updateEntityQueryAACube(shared_from_this(), packetSender, false, true); + std::shared_ptr packetSender = qApp->getEntityEditPacketSender(); + entityTree->updateEntityQueryAACube(shared_from_this(), packetSender.get(), false, true); }); bool isPhysicsEnabled = qApp->isPhysicsEnabled(); bool zoneAllowsFlying = zoneInteractionProperties.first; @@ -1729,7 +1729,7 @@ void MyAvatar::handleChangedAvatarEntityData() { entityTree->deleteEntitiesByID(entitiesToDelete); // ADD real entities - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + auto packetSender = qApp->getEntityEditPacketSender(); for (const auto& id : entitiesToAdd) { bool blobFailed = false; EntityItemProperties properties; @@ -4231,7 +4231,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { _avatarEntitiesLock.withReadLock([&] { avatarEntityIDs = _packedAvatarEntityData.keys(); }); - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + auto packetSender = qApp->getEntityEditPacketSender(); entityTree->withWriteLock([&] { for (const auto& entityID : avatarEntityIDs) { auto entity = entityTree->findEntityByID(entityID); @@ -6888,7 +6888,7 @@ void MyAvatar::sendPacket(const QUuid& entityID) const { if (entityTree) { entityTree->withWriteLock([&] { // force an update packet - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); + auto packetSender = qApp->getEntityEditPacketSender(); packetSender->queueEditAvatarEntityMessage(entityTree, entityID); }); } From a22b06c3e7d34865afc1982af3ffa9881a318bb9 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 17:41:22 +0100 Subject: [PATCH 04/18] Change _graphicsEngine to a shared_ptr --- interface/src/Application.cpp | 34 +++++++++++++++++----------------- interface/src/Application.h | 12 ++++++------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ce97dd5cb..22594d635f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1048,7 +1048,7 @@ void Application::initialize(const QCommandLineParser &parser) { _entityClipboard = std::make_shared(); _octreeProcessor = std::make_shared(); _entityEditSender = std::make_shared(); - + _graphicsEngine = std::make_shared(); @@ -2589,7 +2589,7 @@ void Application::initialize(const QCommandLineParser &parser) { } _pendingIdleEvent = false; - _graphicsEngine.startup(); + _graphicsEngine->startup(); qCDebug(interfaceapp) << "Directory Service session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); @@ -2914,7 +2914,7 @@ Application::~Application() { assert(_shapeManager.getNumShapes() == 0); // shutdown graphics engine - _graphicsEngine.shutdown(); + _graphicsEngine->shutdown(); _gameWorkload.shutdown(); @@ -3120,7 +3120,7 @@ void Application::initializeGL() { glClear(GL_COLOR_BUFFER_BIT); _glWidget->swapBuffers(); - _graphicsEngine.initializeGPU(_glWidget); + _graphicsEngine->initializeGPU(_glWidget); } void Application::initializeDisplayPlugins() { @@ -3132,7 +3132,7 @@ void Application::initializeDisplayPlugins() { // Once time initialization code DisplayPluginPointer targetDisplayPlugin; for(const auto& displayPlugin : displayPlugins) { - displayPlugin->setContext(_graphicsEngine.getGPUContext()); + displayPlugin->setContext(_graphicsEngine->getGPUContext()); if (displayPlugin->getName() == lastActiveDisplayPluginName) { targetDisplayPlugin = displayPlugin; } @@ -3184,7 +3184,7 @@ void Application::initializeDisplayPlugins() { void Application::initializeRenderEngine() { // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. DeadlockWatchdogThread::withPause([&] { - _graphicsEngine.initializeRender(); + _graphicsEngine->initializeRender(); DependencyManager::get()->registerKeyboardHighlighting(); }); } @@ -3441,7 +3441,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Recording", DependencyManager::get().data()); surfaceContext->setContextProperty("Preferences", DependencyManager::get().data()); surfaceContext->setContextProperty("AddressManager", DependencyManager::get().data()); - surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine._frameTimingsScriptingInterface); + surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine->_frameTimingsScriptingInterface); surfaceContext->setContextProperty("Rates", new RatesScriptingInterface(this)); surfaceContext->setContextProperty("TREE_SCALE", TREE_SCALE); @@ -4147,8 +4147,8 @@ void Application::onPresent(quint32 frameCount) { postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); } expected = false; - if (_graphicsEngine.checkPendingRenderEvent() && !isAboutToQuit()) { - postEvent(_graphicsEngine._renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); + if (_graphicsEngine->checkPendingRenderEvent() && !isAboutToQuit()) { + postEvent(_graphicsEngine->_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } } @@ -5265,8 +5265,8 @@ void Application::idle() { PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", uint32_t, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); - auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); - PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine.getGPUContext()->getFrameTimerGPUAverage()); + auto renderConfig = _graphicsEngine->getRenderEngine()->getConfiguration(); + PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_graphicsEngine->getGPUContext()->getFrameTimerGPUAverage()); PROFILE_RANGE(app, __FUNCTION__); @@ -5707,7 +5707,7 @@ void Application::init() { } }, Qt::QueuedConnection); - _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine.getRenderScene(), _entitySimulation); + _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine->getRenderScene(), _entitySimulation); _entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace()); } @@ -5879,7 +5879,7 @@ void Application::updateLOD(float deltaTime) const { // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!isThrottleRendering()) { float presentTime = getActiveDisplayPlugin()->getAveragePresentTime(); - float engineRunTime = (float)(_graphicsEngine.getRenderEngine()->getConfiguration().get()->getCPURunTime()); + float engineRunTime = (float)(_graphicsEngine->getRenderEngine()->getConfiguration().get()->getCPURunTime()); float gpuTime = getGPUContext()->getFrameTimerGPUAverage(); float batchTime = getGPUContext()->getFrameTimerBatchAverage(); auto lodManager = DependencyManager::get(); @@ -6184,7 +6184,7 @@ void Application::updateSecondaryCameraViewFrustum() { // camera should be. // Code based on SecondaryCameraJob - auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration(); + auto renderConfig = _graphicsEngine->getRenderEngine()->getConfiguration(); assert(renderConfig); auto camera = dynamic_cast(renderConfig->getConfig("SecondaryCamera")); @@ -6311,7 +6311,7 @@ void Application::tryToEnablePhysics() { } void Application::update(float deltaTime) { - PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine._renderFrameCount + 1); + PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_graphicsEngine->_renderFrameCount + 1); if (_aboutToQuit) { return; @@ -6820,7 +6820,7 @@ void Application::update(float deltaTime) { } void Application::updateRenderArgs(float deltaTime) { - _graphicsEngine.editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { + _graphicsEngine->editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { PerformanceTimer perfTimer("editRenderArgs"); appRenderArgs._headPose = getHMDSensorPose(); @@ -6849,7 +6849,7 @@ void Application::updateRenderArgs(float deltaTime) { _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } - appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getVisibilityDistance(), + appRenderArgs._renderArgs = RenderArgs(_graphicsEngine->getGPUContext(), lodManager->getVisibilityDistance(), lodManager->getBoundaryLevelAdjust(), lodManager->getLODFarHalfAngleTan(), lodManager->getLODNearHalfAngleTan(), lodManager->getLODFarDistance(), lodManager->getLODNearDistance(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE); diff --git a/interface/src/Application.h b/interface/src/Application.h index 8c9937401f..e848e5e100 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -226,8 +226,8 @@ public: PerformanceManager& getPerformanceManager() { return _performanceManager; } RefreshRateManager& getRefreshRateManager() { return _refreshRateManager; } - size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); } - float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); } + size_t getRenderFrameCount() const { return _graphicsEngine->getRenderFrameCount(); } + float getRenderLoopRate() const { return _graphicsEngine->getRenderLoopRate(); } float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second @@ -305,9 +305,9 @@ public: void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond() const; - render::ScenePointer getMain3DScene() override { return _graphicsEngine.getRenderScene(); } - render::EnginePointer getRenderEngine() override { return _graphicsEngine.getRenderEngine(); } - gpu::ContextPointer getGPUContext() const { return _graphicsEngine.getGPUContext(); } + render::ScenePointer getMain3DScene() override { return _graphicsEngine->getRenderScene(); } + render::EnginePointer getRenderEngine() override { return _graphicsEngine->getRenderEngine(); } + gpu::ContextPointer getGPUContext() const { return _graphicsEngine->getGPUContext(); } const GameWorkload& getGameWorkload() const { return _gameWorkload; } @@ -769,7 +769,7 @@ private: GameWorkload _gameWorkload; - GraphicsEngine _graphicsEngine; + std::shared_ptr _graphicsEngine; void updateRenderArgs(float deltaTime); bool _disableLoginScreen { true }; From 812d0435873482c22d3748fe67bbbbcfac98cf46 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 17:59:18 +0100 Subject: [PATCH 05/18] Call setupEssentials() in initialize() --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 22594d635f..9a39811c28 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1040,7 +1040,7 @@ Application::Application( void Application::initialize(const QCommandLineParser &parser) { //qCDebug(interfaceapp) << "Setting up essentials"; - //setupEssentials(parser, _previousSessionCrashed); + setupEssentials(parser, _previousSessionCrashed); qCDebug(interfaceapp) << "Initializing application"; _entitySimulation = std::make_shared(); From c748064d0aaeff210211b55cd8dcb9682ce2247f Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 17:59:31 +0100 Subject: [PATCH 06/18] Make _applicationOverlay a shared_ptr --- interface/src/Application.cpp | 1 + interface/src/Application.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9a39811c28..482539b8e8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1049,6 +1049,7 @@ void Application::initialize(const QCommandLineParser &parser) { _octreeProcessor = std::make_shared(); _entityEditSender = std::make_shared(); _graphicsEngine = std::make_shared(); + _applicationOverlay = std::make_shared(); diff --git a/interface/src/Application.h b/interface/src/Application.h index e848e5e100..69d167d27b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -217,8 +217,8 @@ public: ivec2 getMouse() const; - ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } - const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } + ApplicationOverlay& getApplicationOverlay() { return *_applicationOverlay; } + const ApplicationOverlay& getApplicationOverlay() const { return *_applicationOverlay; } CompositorHelper& getApplicationCompositor() const; Overlays& getOverlays() { return _overlays; } @@ -775,7 +775,7 @@ private: bool _disableLoginScreen { true }; Overlays _overlays; - ApplicationOverlay _applicationOverlay; + std::shared_ptr _applicationOverlay; OverlayConductor _overlayConductor; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); From a4e79ff806810db4506b967c1a0977060feed0ce Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 19:23:57 +0100 Subject: [PATCH 07/18] Make Application destructor work when terminating early --- interface/src/Application.cpp | 77 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 482539b8e8..f3923a5ba5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2897,43 +2897,59 @@ void Application::cleanupBeforeQuit() { Application::~Application() { // remove avatars from physics engine - auto avatarManager = DependencyManager::get(); - avatarManager->clearOtherAvatars(); - auto myCharacterController = getMyAvatar()->getCharacterController(); - myCharacterController->clearDetailedMotionStates(); + if (auto avatarManager = DependencyManager::get()) { + // AvatarManager may not yet exist in case of an early exit - PhysicsEngine::Transaction transaction; - avatarManager->buildPhysicsTransaction(transaction); - _physicsEngine->processTransaction(transaction); - avatarManager->handleProcessedPhysicsTransaction(transaction); - avatarManager->deleteAllAvatars(); + avatarManager->clearOtherAvatars(); + auto myCharacterController = getMyAvatar()->getCharacterController(); + myCharacterController->clearDetailedMotionStates(); - _physicsEngine->setCharacterController(nullptr); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + avatarManager->deleteAllAvatars(); + } + + if (_physicsEngine) { + _physicsEngine->setCharacterController(nullptr); + } // the _shapeManager should have zero references _shapeManager.collectGarbage(); assert(_shapeManager.getNumShapes() == 0); - // shutdown graphics engine - _graphicsEngine->shutdown(); + if (_graphicsEngine) { + // shutdown graphics engine + _graphicsEngine->shutdown(); + } _gameWorkload.shutdown(); DependencyManager::destroy(); PlatformHelper::shutdown(); - _entityClipboard->eraseAllOctreeElements(); - _entityClipboard.reset(); - - _octreeProcessor->terminate(); - _entityEditSender->terminate(); - - if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { - steamClient->shutdown(); + if (_entityClipboard) { + _entityClipboard->eraseAllOctreeElements(); + _entityClipboard.reset(); } - if (auto oculusPlatform = PluginManager::getInstance()->getOculusPlatformPlugin()) { - oculusPlatform->shutdown(); + if (_octreeProcessor) { + _octreeProcessor->terminate(); + } + + if (_entityEditSender) { + _entityEditSender->terminate(); + } + + if (auto pluginManager = PluginManager::getInstance()) { + if (auto steamClient = pluginManager->getSteamClientPlugin()) { + steamClient->shutdown(); + } + + if (auto oculusPlatform = pluginManager->getOculusPlatformPlugin()) { + oculusPlatform->shutdown(); + } } DependencyManager::destroy(); @@ -2961,7 +2977,9 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); - DependencyManager::get()->cleanup(); + if (auto resourceManager = DependencyManager::get()) { + resourceManager->cleanup(); + } // remove the NodeList from the DependencyManager DependencyManager::destroy(); @@ -2975,13 +2993,14 @@ Application::~Application() { _window->deleteLater(); // make sure that the quit event has finished sending before we take the application down - auto closeEventSender = DependencyManager::get(); - while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) { - // sleep a little so we're not spinning at 100% - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + if (auto closeEventSender = DependencyManager::get()) { + while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) { + // sleep a little so we're not spinning at 100% + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + // quit the thread used by the closure event sender + closeEventSender->thread()->quit(); } - // quit the thread used by the closure event sender - closeEventSender->thread()->quit(); // Can't log to file past this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); From 57e1c99fff217961cbbb313e33240c6b9a0e564c Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 19:34:52 +0100 Subject: [PATCH 08/18] Initialize PathUtils and logging in constructor --- interface/src/Application.cpp | 11 +++++++++-- interface/src/Application.h | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f3923a5ba5..b05fb876a7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -725,7 +725,7 @@ extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) { - qInstallMessageHandler(messageHandler); + const int listenPort = parser.isSet("listenPort") ? parser.value("listenPort").toInt() : INVALID_PORT; @@ -765,7 +765,7 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted } // Tell the plugin manager about our statically linked plugins - DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); auto pluginManager = PluginManager::getInstance(); @@ -1035,6 +1035,13 @@ Application::Application( LogHandler::getInstance().moveToThread(thread()); LogHandler::getInstance().setupRepeatedMessageFlusher(); + qInstallMessageHandler(messageHandler); + + DependencyManager::set(); +} + +void Application::initializePlugins() { + } void Application::initialize(const QCommandLineParser &parser) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 69d167d27b..992b55a3b8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -123,6 +123,9 @@ class Application : public QApplication, friend class OctreePacketProcessor; public: + + void initializePlugins(); + /** * @brief Initialize everything * From b9662e5af85fa29392d8dcce8fd30720886f75e4 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 11 Nov 2023 21:03:16 +0100 Subject: [PATCH 09/18] Separate plugin initialization, allow dumping plugin info --- interface/src/Application.cpp | 16 +++++----- interface/src/Application.h | 2 +- interface/src/main.cpp | 59 +++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b05fb876a7..5a8eab34e8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -764,14 +764,12 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted } } - // Tell the plugin manager about our statically linked plugins + DependencyManager::set(); - DependencyManager::set(); + + // Tell the plugin manager about our statically linked plugins auto pluginManager = PluginManager::getInstance(); - pluginManager->setInputPluginProvider([] { return getInputPlugins(); }); - pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); }); - pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); if (auto steamClient = pluginManager->getSteamClientPlugin()) { steamClient->init(); } @@ -1040,8 +1038,12 @@ Application::Application( DependencyManager::set(); } -void Application::initializePlugins() { - +void Application::initializePluginManager() { + DependencyManager::set(); + auto pluginManager = PluginManager::getInstance(); + pluginManager->setInputPluginProvider([] { return getInputPlugins(); }); + pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); }); + pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); } void Application::initialize(const QCommandLineParser &parser) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 992b55a3b8..9663966df4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -124,7 +124,7 @@ class Application : public QApplication, public: - void initializePlugins(); + void initializePluginManager(); /** * @brief Initialize everything diff --git a/interface/src/main.cpp b/interface/src/main.cpp index b9501419f1..d8e2457b40 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "AddressManager.h" #include "Application.h" @@ -33,6 +34,9 @@ #include "MainWindow.h" #include "Profile.h" #include "LogHandler.h" +#include +#include +#include #ifdef Q_OS_WIN #include @@ -63,6 +67,7 @@ int main(int argc, const char* argv[]) { } #endif + // Setup QCoreApplication settings, install log message handler setupHifiApplication(BuildInfo::INTERFACE_NAME); // Journald by default in user applications is probably a bit too modern still. @@ -258,6 +263,10 @@ int main(int argc, const char* argv[]) { "Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald", "options" ); + QCommandLineOption getPluginsOption( + "getPlugins", + "Print out a list of plugins in JSON" + ); QCommandLineOption abortAfterStartupOption( "abortAfterStartup", "Debug option. Aborts right after startup." @@ -311,6 +320,7 @@ int main(int argc, const char* argv[]) { parser.addOption(logOption); parser.addOption(abortAfterStartupOption); parser.addOption(abortAfterInitOption); + parser.addOption(getPluginsOption); QString applicationPath; @@ -355,6 +365,55 @@ int main(int argc, const char* argv[]) { } } + app.initializePluginManager(); + + if (parser.isSet(getPluginsOption)) { + auto pluginManager = PluginManager::getInstance(); + + QJsonObject inputJson; + for (const auto &plugin : pluginManager->getInputPlugins()) { + QJsonObject data; + data["subdeviceNames"] = QJsonArray::fromStringList(plugin->getSubdeviceNames()); + data["deviceName"] = plugin->getDeviceName(); + data["configurable"] = plugin->configurable(); + data["isHandController"] = plugin->isHandController(); + data["isHeadController"] = plugin->isHeadController(); + + inputJson[plugin->getName()] = data; + } + + QJsonObject displayJson; + for (const auto &plugin : pluginManager->getDisplayPlugins()) { + QJsonObject data; + data["isHmd"] = plugin->isHmd(); + data["isStereo"] = plugin->isStereo(); + data["targetFramerate"] = plugin->getTargetFrameRate(); + data["hasAsyncReprojection"] = plugin->hasAsyncReprojection(); + + displayJson[plugin->getName()] = data; + } + + QJsonArray codecsArray; + for (const auto &plugin : pluginManager->getCodecPlugins()) { + codecsArray.append(plugin->getName()); + } + + QJsonObject staticJson; + staticJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() == nullptr); + staticJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() == nullptr); + + QJsonObject root; + root["input"] = inputJson; + root["display"] = displayJson; + root["codec"] = codecsArray; + root["staticPlugins"] = staticJson; + + std::cout << QJsonDocument(root).toJson().toStdString() << "\n"; + + return 0; + } + + // Act on arguments for early termination. if (parser.isSet(versionOption)) { parser.showVersion(); From aba921502dba0a109a57ff1aca115bf0d222f1ea Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 11:41:44 +0100 Subject: [PATCH 10/18] Improve codec JSON info --- interface/src/main.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index d8e2457b40..b0a83f2719 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -378,6 +378,8 @@ int main(int argc, const char* argv[]) { data["configurable"] = plugin->configurable(); data["isHandController"] = plugin->isHandController(); data["isHeadController"] = plugin->isHeadController(); + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); inputJson[plugin->getName()] = data; } @@ -389,13 +391,19 @@ int main(int argc, const char* argv[]) { data["isStereo"] = plugin->isStereo(); data["targetFramerate"] = plugin->getTargetFrameRate(); data["hasAsyncReprojection"] = plugin->hasAsyncReprojection(); + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); displayJson[plugin->getName()] = data; } - QJsonArray codecsArray; + QJsonObject codecsJson; for (const auto &plugin : pluginManager->getCodecPlugins()) { - codecsArray.append(plugin->getName()); + QJsonObject data; + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + codecsJson[plugin->getName()] = data; } QJsonObject staticJson; @@ -405,7 +413,7 @@ int main(int argc, const char* argv[]) { QJsonObject root; root["input"] = inputJson; root["display"] = displayJson; - root["codec"] = codecsArray; + root["codec"] = codecsJson; root["staticPlugins"] = staticJson; std::cout << QJsonDocument(root).toJson().toStdString() << "\n"; From e03768ddc5cf0975e3c363a7e2d937ba11fb962f Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 13:39:37 +0100 Subject: [PATCH 11/18] Document PluginManager --- libraries/plugins/src/plugins/PluginManager.h | 173 +++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 26c98ce5db..c5ff80774c 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -18,6 +18,22 @@ class QPluginLoader; using PluginManagerPointer = QSharedPointer; +/** + * @brief Manages loadable plugins + * + * The current implementation does initialization only once, as soon as it's needed. + * Once things are initialized the configuration is made permanent. + * + * Both loadable and statically modules are supported. Static modules have to be provided + * with setDisplayPluginProvider, setInputPluginProvider and setCodecPluginProvider. + * + * @warning Users of the PluginManager must take care to do any configuration very early + * on, because changes become impossible once initialization is done. Plugins can't be + * added or removed once that happens. + * + * Initialization is performed in the getDisplayPlugins, getInputPlugins and getCodecPlugins + * functions. + */ class PluginManager : public QObject, public Dependency { SINGLETON_DEPENDENCY Q_OBJECT @@ -25,33 +41,186 @@ class PluginManager : public QObject, public Dependency { public: static PluginManagerPointer getInstance(); + /** + * @brief Get the list of display plugins + * + * @note Calling this function will perform initialization and + * connects events to all the known the plugins on the first call. + * + * @return const DisplayPluginList& + */ const DisplayPluginList& getDisplayPlugins(); + + /** + * @brief Get the list of input plugins + * + * @note Calling this function will perform initialization and + * connects events to all the known the plugins on the first call. + * + * @return const InputPluginList& + */ const InputPluginList& getInputPlugins(); + + /** + * @brief Get the list of audio codec plugins + * + * @note Calling this function will perform initialization and + * connects events to all the known the plugins on the first call. + * + * @return const CodecPluginList& + */ const CodecPluginList& getCodecPlugins(); + + /** + * @brief Get the pointer to the Steam client plugin + * + * This may return a null pointer if Steam support isn't built in. + * + * @return const SteamClientPluginPointer + */ const SteamClientPluginPointer getSteamClientPlugin(); + + /** + * @brief Get the pointer to the Oculus Platform Plugin + * + * This may return a null pointer if Oculus support isn't built in. + * + * @return const OculusPlatformPluginPointer + */ const OculusPlatformPluginPointer getOculusPlatformPlugin(); + /** + * @brief Returns the list of preferred display plugins + * + * The preferred display plugins are set by setPreferredDisplayPlugins. + * + * @return DisplayPluginList + */ DisplayPluginList getPreferredDisplayPlugins(); + + /** + * @brief Sets the list of preferred display plugins + * + * @note This must be called early, before any call to getPreferredDisplayPlugins. + * + * @param displays + */ void setPreferredDisplayPlugins(const QStringList& displays); + /** + * @brief Disable a display plugin. + * + * This removes the plugin from the plugins list. + * The plugin is not deinitialized, and events are not disconnected. + * @param name + */ void disableDisplayPlugin(const QString& name); + + /** + * @brief Disable a list of displays + * + * This adds the display to a list of displays not to be used. + * + * @param displays + */ void disableDisplays(const QStringList& displays); + + /** + * @brief Disable a list of inputs + * + * This adds the input to a list of inputs not to be used. + * @param inputs + */ void disableInputs(const QStringList& inputs); + + /** + * @brief Save the settings + * + */ void saveSettings(); + + /** + * @brief Set the container for plugins + * + * This will be passed to all active plugins on initialization. + * + * @param container + */ void setContainer(PluginContainer* container) { _container = container; } int instantiate(); void shutdown(); - // Application that have statically linked plugins can expose them to the plugin manager with these function + + /** + * @brief Provide a list of statically linked plugins. + * + * This is used to provide a list of statically linked plugins to the plugin manager. + * + * @note This must be called very early on, and only works once. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * @param provider A std::function that returns a list of display plugins + */ void setDisplayPluginProvider(const DisplayPluginProvider& provider); + + /** + * @brief Provide a list of statically linked plugins. + * + * This is used to provide a list of statically linked plugins to the plugin manager. + * + * @note This must be called very early on, and only works once. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * @param provider A std::function that returns a list of input plugins + */ void setInputPluginProvider(const InputPluginProvider& provider); + + /** + * @brief Provide a list of statically linked plugins. + * + * This is used to provide a list of statically linked plugins to the plugin manager. + * + * @note This must be called very early on, and only works once. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * @param provider A std::function that returns a list of codec plugins + */ void setCodecPluginProvider(const CodecPluginProvider& provider); + + /** + * @brief Set the input plugin persister + * + * @param persister A std::function that saves input plugin settings + */ void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister); + + /** + * @brief Get the list of running input devices + * + * @return QStringList List of input devices in running state + */ QStringList getRunningInputDeviceNames() const; using PluginFilter = std::function; + + /** + * @brief Set the plugin filter that determines whether a plugin will be used or not + * + * @note This must be called very early on. Once the plugin manager + * builds its internal list of plugins, the final list becomes set in stone. + * + * As of writing, this is used in the audio mixer. + * + * @param pluginFilter + */ void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; } + + /** + * @brief Get a list of all the display plugins + * + * @return DisplayPluginList List of display plugins + */ Q_INVOKABLE DisplayPluginList getAllDisplayPlugins(); bool getEnableOculusPluginSetting() { return _enableOculusPluginSetting.get(); } @@ -59,7 +228,7 @@ public: signals: void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices); - + private: PluginManager() = default; From a7132c4ff615e31650539fbd19855bd3605a32b8 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 13:41:17 +0100 Subject: [PATCH 12/18] Remove PluginManager::disableDisplayPlugin This isn't called anywhere, and I believe the implementation is flawed anyway. While it tries to remove the plugin from the list, the plugin isn't shut down, or disconnected from events. This is likely to cause problems if it were to be used. --- libraries/plugins/src/plugins/PluginManager.cpp | 8 -------- libraries/plugins/src/plugins/PluginManager.h | 9 --------- 2 files changed, 17 deletions(-) diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index c1ca853563..e5cbbf1bb2 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -272,14 +272,6 @@ DisplayPluginList PluginManager::getAllDisplayPlugins() { return _displayPlugins; } -void PluginManager::disableDisplayPlugin(const QString& name) { - auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){ - return plugin->getName() == name; - }); - _displayPlugins.erase(it, _displayPlugins.end()); -} - - const InputPluginList& PluginManager::getInputPlugins() { static std::once_flag once; static auto deviceAddedCallback = [&](QString deviceName) { diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index c5ff80774c..c35db5a91e 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -107,15 +107,6 @@ public: */ void setPreferredDisplayPlugins(const QStringList& displays); - /** - * @brief Disable a display plugin. - * - * This removes the plugin from the plugins list. - * The plugin is not deinitialized, and events are not disconnected. - * @param name - */ - void disableDisplayPlugin(const QString& name); - /** * @brief Disable a list of displays * From 59f96cada117bacf65cbea9b6edbe75111dae3d5 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 18:12:05 +0100 Subject: [PATCH 13/18] Add explanatory comments --- interface/src/Application.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5a8eab34e8..ae0640f66b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1041,6 +1041,14 @@ Application::Application( void Application::initializePluginManager() { DependencyManager::set(); auto pluginManager = PluginManager::getInstance(); + + qWarning() << "Input plugins: " << getInputPlugins().size(); + + // To avoid any confusion: the getInputPlugins and getDisplayPlugins are not the ones + // from PluginManager, but functions exported by input-plugins/InputPlugin.cpp and + // display-plugins/DisplayPlugin.cpp. + // + // These functions provide the plugin manager with static default plugins. pluginManager->setInputPluginProvider([] { return getInputPlugins(); }); pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); }); pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); From 0bd72f3d54dee9d1e4332d97be63f88135e02a8b Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 18:43:36 +0100 Subject: [PATCH 14/18] Fix bug in reporting steam/oculus availability --- interface/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index b0a83f2719..59b6633f79 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -407,8 +407,8 @@ int main(int argc, const char* argv[]) { } QJsonObject staticJson; - staticJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() == nullptr); - staticJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() == nullptr); + staticJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() != nullptr); + staticJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() != nullptr); QJsonObject root; root["input"] = inputJson; From ec29cfcd5129f9dccc5c43f032e8a16021041773 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 18:44:00 +0100 Subject: [PATCH 15/18] Treat Oculus and Steam plugins as display plugins, for disabling purposes --- libraries/plugins/src/plugins/PluginManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index e5cbbf1bb2..759686300a 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -77,7 +77,8 @@ bool isDisabled(QJsonObject metaData) { auto name = getPluginNameFromMetaData(metaData); auto iid = getPluginIIDFromMetaData(metaData); - if (iid == DisplayProvider_iid) { + qDebug() << "Name = " << name << "; iid =" << iid; + if (iid == DisplayProvider_iid || iid == SteamClientProvider_iid || iid == OculusPlatformProvider_iid) { return disabledDisplays.contains(name); } else if (iid == InputProvider_iid) { return disabledInputs.contains(name); From b05dd3443ddcdd9e55fe214d4fa0f3f8cb7fa764 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 18:44:31 +0100 Subject: [PATCH 16/18] Refactor display/input disabling --- interface/src/Application.cpp | 53 +++++++++++++++++------------------ interface/src/Application.h | 13 +++++++-- interface/src/main.cpp | 8 +++--- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ae0640f66b..f85b8c0dc6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1038,12 +1038,10 @@ Application::Application( DependencyManager::set(); } -void Application::initializePluginManager() { +void Application::initializePluginManager(const QCommandLineParser& parser) { DependencyManager::set(); auto pluginManager = PluginManager::getInstance(); - qWarning() << "Input plugins: " << getInputPlugins().size(); - // To avoid any confusion: the getInputPlugins and getDisplayPlugins are not the ones // from PluginManager, but functions exported by input-plugins/InputPlugin.cpp and // display-plugins/DisplayPlugin.cpp. @@ -1052,6 +1050,30 @@ void Application::initializePluginManager() { pluginManager->setInputPluginProvider([] { return getInputPlugins(); }); pluginManager->setDisplayPluginProvider([] { return getDisplayPlugins(); }); pluginManager->setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); }); + + + // This must be a member function -- PluginManager must exist, and for that + // QApplication must exist, or it can't find the plugin path, as QCoreApplication:applicationDirPath + // won't work yet. + + if (parser.isSet("display")) { + auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts); + qInfo() << "Setting prefered display plugins:" << preferredDisplays; + PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); + } + + if (parser.isSet("disableDisplays")) { + auto disabledDisplays = parser.value("disableDisplays").split(',', Qt::SkipEmptyParts); + qInfo() << "Disabling following display plugins:" << disabledDisplays; + PluginManager::getInstance()->disableDisplays(disabledDisplays); + } + + if (parser.isSet("disableInputs")) { + auto disabledInputs = parser.value("disableInputs").split(',', Qt::SkipEmptyParts); + qInfo() << "Disabling following input plugins:" << disabledInputs; + PluginManager::getInstance()->disableInputs(disabledInputs); + } + } void Application::initialize(const QCommandLineParser &parser) { @@ -8818,31 +8840,6 @@ void Application::sendLambdaEvent(const std::function& f) { } } -void Application::configurePlugins(const QCommandLineParser& parser) { - // This must be a member function -- PluginManager must exist, and for that - // QApplication must exist, or it can't find the plugin path, as QCoreApplication:applicationDirPath - // won't work yet. - - if (parser.isSet("display")) { - auto preferredDisplays = parser.value("display").split(',', Qt::SkipEmptyParts); - qInfo() << "Setting prefered display plugins:" << preferredDisplays; - PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); - } - - if (parser.isSet("disable-displays")) { - auto disabledDisplays = parser.value("disable-displays").split(',', Qt::SkipEmptyParts); - qInfo() << "Disabling following display plugins:" << disabledDisplays; - PluginManager::getInstance()->disableDisplays(disabledDisplays); - } - - if (parser.isSet("disable-inputs")) { - auto disabledInputs = parser.value("disable-inputs").split(',', Qt::SkipEmptyParts); - qInfo() << "Disabling following input plugins:" << disabledInputs; - PluginManager::getInstance()->disableInputs(disabledInputs); - } - -} - void Application::shutdownPlugins() { } diff --git a/interface/src/Application.h b/interface/src/Application.h index 9663966df4..63a035dd45 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -124,7 +124,16 @@ class Application : public QApplication, public: - void initializePluginManager(); + /** + * @brief Initialize the plugin manager + * + * This both does the initial startup and parses arguments. This + * is necessary because the plugin manager's options must be set + * before any usage of it is made, or they won't apply. + * + * @param parser + */ + void initializePluginManager(const QCommandLineParser& parser); /** * @brief Initialize everything @@ -151,8 +160,6 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; - // FIXME? Empty methods, do we still need them? - void configurePlugins(const QCommandLineParser& parser); static void shutdownPlugins(); Application( diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 59b6633f79..acec355f7a 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -142,12 +142,12 @@ int main(int argc, const char* argv[]) { "displays" ); QCommandLineOption disableDisplaysOption( - "disable-displays", + "disableDisplays", "Displays to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); QCommandLineOption disableInputsOption( - "disable-inputs", + "disableInputs", "Inputs to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); @@ -365,7 +365,7 @@ int main(int argc, const char* argv[]) { } } - app.initializePluginManager(); + app.initializePluginManager(parser); if (parser.isSet(getPluginsOption)) { auto pluginManager = PluginManager::getInstance(); @@ -648,7 +648,7 @@ int main(int argc, const char* argv[]) { // Oculus initialization MUST PRECEDE OpenGL context creation. // The nature of the Application constructor means this has to be either here, // or in the main window ctor, before GL startup. - app.configurePlugins(parser); + //app.configurePlugins(parser); #ifdef Q_OS_WIN // If we're running in steam mode, we need to do an explicit check to ensure we're up to the required min spec From 1de6e6d4181a82d4c31cc9fe8375de1b8924f72c Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 20:08:29 +0100 Subject: [PATCH 17/18] Rename arguments for more clarity --- interface/src/Application.cpp | 8 ++++---- interface/src/main.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f85b8c0dc6..b1b4faf72c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1062,14 +1062,14 @@ void Application::initializePluginManager(const QCommandLineParser& parser) { PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); } - if (parser.isSet("disableDisplays")) { - auto disabledDisplays = parser.value("disableDisplays").split(',', Qt::SkipEmptyParts); + if (parser.isSet("disableDisplayPlugins")) { + auto disabledDisplays = parser.value("disableDisplayPlugins").split(',', Qt::SkipEmptyParts); qInfo() << "Disabling following display plugins:" << disabledDisplays; PluginManager::getInstance()->disableDisplays(disabledDisplays); } - if (parser.isSet("disableInputs")) { - auto disabledInputs = parser.value("disableInputs").split(',', Qt::SkipEmptyParts); + if (parser.isSet("disableInputPlugins")) { + auto disabledInputs = parser.value("disableInputPlugins").split(',', Qt::SkipEmptyParts); qInfo() << "Disabling following input plugins:" << disabledInputs; PluginManager::getInstance()->disableInputs(disabledInputs); } diff --git a/interface/src/main.cpp b/interface/src/main.cpp index acec355f7a..cfde65a845 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -142,12 +142,12 @@ int main(int argc, const char* argv[]) { "displays" ); QCommandLineOption disableDisplaysOption( - "disableDisplays", + "disableDisplayPlugins", "Displays to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); QCommandLineOption disableInputsOption( - "disableInputs", + "disableInputPlugins", "Inputs to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); From 111cea477d927e5b35d476845fa8cefb84f6d3a5 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sun, 12 Nov 2023 20:08:47 +0100 Subject: [PATCH 18/18] Provide plugin load state --- interface/src/main.cpp | 26 ++++++--- .../plugins/src/plugins/PluginManager.cpp | 23 +++++++- libraries/plugins/src/plugins/PluginManager.h | 54 +++++++++++++++++++ 3 files changed, 95 insertions(+), 8 deletions(-) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index cfde65a845..a5aa8004da 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -370,6 +370,17 @@ int main(int argc, const char* argv[]) { if (parser.isSet(getPluginsOption)) { auto pluginManager = PluginManager::getInstance(); + QJsonObject pluginsJson; + for (const auto &plugin : pluginManager->getPluginInfo()) { + QJsonObject data; + data["data"] = plugin.metaData; + data["loaded"] = plugin.loaded; + data["disabled"] = plugin.disabled; + data["filteredOut"] = plugin.filteredOut; + data["wrongVersion"] = plugin.wrongVersion; + pluginsJson[plugin.name] = data; + } + QJsonObject inputJson; for (const auto &plugin : pluginManager->getInputPlugins()) { QJsonObject data; @@ -406,15 +417,16 @@ int main(int argc, const char* argv[]) { codecsJson[plugin->getName()] = data; } - QJsonObject staticJson; - staticJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() != nullptr); - staticJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() != nullptr); + QJsonObject platformsJson; + platformsJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() != nullptr); + platformsJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() != nullptr); QJsonObject root; - root["input"] = inputJson; - root["display"] = displayJson; - root["codec"] = codecsJson; - root["staticPlugins"] = staticJson; + root["plugins"] = pluginsJson; + root["inputs"] = inputJson; + root["displays"] = displayJson; + root["codecs"] = codecsJson; + root["platforms"] = platformsJson; std::cout << QJsonDocument(root).toJson().toStdString() << "\n"; diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 759686300a..0281a012d2 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -72,12 +72,12 @@ int getPluginInterfaceVersionFromMetaData(const QJsonObject& object) { QStringList preferredDisplayPlugins; QStringList disabledDisplays; QStringList disabledInputs; +std::vector pluginInfo; bool isDisabled(QJsonObject metaData) { auto name = getPluginNameFromMetaData(metaData); auto iid = getPluginIIDFromMetaData(metaData); - qDebug() << "Name = " << name << "; iid =" << iid; if (iid == DisplayProvider_iid || iid == SteamClientProvider_iid || iid == OculusPlatformProvider_iid) { return disabledDisplays.contains(name); } else if (iid == InputProvider_iid) { @@ -127,18 +127,28 @@ int PluginManager::instantiate() { qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin); auto loader = QSharedPointer::create(pluginPath + plugin); const QJsonObject pluginMetaData = loader->metaData(); + + PluginInfo info; + info.name = plugin; + info.metaData = pluginMetaData; + #if defined(HIFI_PLUGINMANAGER_DEBUG) QJsonDocument metaDataDoc(pluginMetaData); qCInfo(plugins) << "Metadata for " << qPrintable(plugin) << ": " << QString(metaDataDoc.toJson()); #endif if (isDisabled(pluginMetaData)) { qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "is disabled"; + info.disabled = true; + pluginInfo.push_back(info); + // Skip this one, it's disabled continue; } if (!_pluginFilter(pluginMetaData)) { qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "doesn't pass provided filter"; + info.filteredOut = true; + pluginInfo.push_back(info); continue; } @@ -146,16 +156,22 @@ int PluginManager::instantiate() { qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "interface version doesn't match, not loading:" << getPluginInterfaceVersionFromMetaData(pluginMetaData) << "doesn't match" << HIFI_PLUGIN_INTERFACE_VERSION; + + info.wrongVersion = true; + pluginInfo.push_back(info); continue; } if (loader->load()) { qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "loaded successfully"; + info.loaded = true; loadedPlugins.push_back(loader); } else { qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "failed to load:"; qCDebug(plugins) << " " << qPrintable(loader->errorString()); } + + pluginInfo.push_back(info); } } else { qWarning() << "pluginPath does not exit..." << pluginDir; @@ -164,6 +180,11 @@ int PluginManager::instantiate() { return loadedPlugins; } +std::vector PluginManager::getPluginInfo() const { + getLoadedPlugins(); // This builds the pluginInfo list + return pluginInfo; +} + const CodecPluginList& PluginManager::getCodecPlugins() { static CodecPluginList codecPlugins; static std::once_flag once; diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index c35db5a91e..b529472e1f 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -12,6 +12,8 @@ #include #include +#include +#include #include "Forward.h" @@ -39,6 +41,49 @@ class PluginManager : public QObject, public Dependency { Q_OBJECT public: + + /** + * @brief Information about known plugins + * + */ + struct PluginInfo { + /** + * @brief Plugin metadata + */ + QJsonObject metaData; + + /** + * @brief Filename + * + */ + QString name; + + /** + * @brief Whether the plugin has been disabled + * + */ + bool disabled = false; + + /** + * @brief Whether the plugin has been filtered out by a filter + * + */ + bool filteredOut = false; + + /** + * @brief Whether the plugin has been not loaded because it's the wrong version + * + */ + bool wrongVersion = false; + + /** + * @brief Whether the plugin has been loaded successfully + * + */ + bool loaded = false; + }; + + static PluginManagerPointer getInstance(); /** @@ -217,6 +262,15 @@ public: bool getEnableOculusPluginSetting() { return _enableOculusPluginSetting.get(); } void setEnableOculusPluginSetting(bool value); + /** + * @brief Returns information about known plugins + * + * This is a function for informative/debugging purposes. + * + * @return std::vector + */ + std::vector getPluginInfo() const; + signals: void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices);