diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a9e4bedbbd..1e6e945f5f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -724,8 +724,8 @@ extern DisplayPluginList getDisplayPlugins(); extern InputPluginList getInputPlugins(); extern void saveInputPluginSettings(const InputPluginList& plugins); -bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, bool runningMarkerExisted) { - qInstallMessageHandler(messageHandler); +bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted) { + const int listenPort = parser.isSet("listenPort") ? parser.value("listenPort").toInt() : INVALID_PORT; @@ -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); } @@ -763,13 +764,12 @@ bool setupEssentials(int& argc, char** argv, const QCommandLineParser& parser, b } } - // Tell the plugin manager about our statically linked plugins + + DependencyManager::set<ScriptInitializers>(); - DependencyManager::set<PluginManager>(); + + // 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(); } @@ -777,6 +777,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 +994,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,10 +1004,7 @@ Application::Application( #ifndef Q_OS_ANDROID _logger(new FileLogger(this)), #endif - _previousSessionCrashed(setupEssentials(argc, argv, parser, runningMarkerExisted)), - _entitySimulation(std::make_shared<PhysicalEntitySimulation>()), - _physicsEngine(std::make_shared<PhysicsEngine>(Vectors::ZERO)), - _entityClipboard(std::make_shared<EntityTree>()), + _previousSessionCrashed(false), //setupEssentials(parser, false)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), @@ -1032,12 +1029,72 @@ 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(); + qInstallMessageHandler(messageHandler); + + DependencyManager::set<PathUtils>(); +} + +void Application::initializePluginManager(const QCommandLineParser& parser) { + DependencyManager::set<PluginManager>(); + auto pluginManager = PluginManager::getInstance(); + + // 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); }); + + + // 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("disableDisplayPlugins")) { + auto disabledDisplays = parser.value("disableDisplayPlugins").split(',', Qt::SkipEmptyParts); + qInfo() << "Disabling following display plugins:" << disabledDisplays; + PluginManager::getInstance()->disableDisplays(disabledDisplays); + } + + if (parser.isSet("disableInputPlugins")) { + auto disabledInputs = parser.value("disableInputPlugins").split(',', Qt::SkipEmptyParts); + qInfo() << "Disabling following input plugins:" << disabledInputs; + PluginManager::getInstance()->disableInputs(disabledInputs); + } + +} + +void Application::initialize(const QCommandLineParser &parser) { + + //qCDebug(interfaceapp) << "Setting up essentials"; + setupEssentials(parser, _previousSessionCrashed); + qCDebug(interfaceapp) << "Initializing application"; + + _entitySimulation = std::make_shared<PhysicalEntitySimulation>(); + _physicsEngine = std::make_shared<PhysicsEngine>(Vectors::ZERO); + _entityClipboard = std::make_shared<EntityTree>(); + _octreeProcessor = std::make_shared<OctreePacketProcessor>(); + _entityEditSender = std::make_shared<EntityEditPacketSender>(); + _graphicsEngine = std::make_shared<GraphicsEngine>(); + _applicationOverlay = std::make_shared<ApplicationOverlay>(); + + + + auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); + setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); + { if (parser.isSet("testScript")) { @@ -1405,7 +1462,7 @@ Application::Application( connect(myAvatar.get(), &MyAvatar::positionGoneTo, this, [this] { if (!_physicsEnabled) { // when we arrive somewhere without physics enabled --> startSafeLanding - _octreeProcessor.startSafeLanding(); + _octreeProcessor->startSafeLanding(); } }, Qt::QueuedConnection); @@ -1578,9 +1635,9 @@ Application::Application( 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(); @@ -1698,7 +1755,7 @@ Application::Application( 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); @@ -1707,7 +1764,7 @@ Application::Application( // 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(); @@ -2375,7 +2432,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<NodeList>()->soloNodeOfType(NodeType::EntityServer); @@ -2572,7 +2629,7 @@ Application::Application( } _pendingIdleEvent = false; - _graphicsEngine.startup(); + _graphicsEngine->startup(); qCDebug(interfaceapp) << "Directory Service session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); @@ -2879,43 +2936,59 @@ void Application::cleanupBeforeQuit() { Application::~Application() { // remove avatars from physics engine - auto avatarManager = DependencyManager::get<AvatarManager>(); - avatarManager->clearOtherAvatars(); - auto myCharacterController = getMyAvatar()->getCharacterController(); - myCharacterController->clearDetailedMotionStates(); + if (auto avatarManager = DependencyManager::get<AvatarManager>()) { + // 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<Preferences>(); 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<PluginManager>(); @@ -2943,7 +3016,9 @@ Application::~Application() { DependencyManager::destroy<GeometryCache>(); DependencyManager::destroy<ScreenshareScriptingInterface>(); - DependencyManager::get<ResourceManager>()->cleanup(); + if (auto resourceManager = DependencyManager::get<ResourceManager>()) { + resourceManager->cleanup(); + } // remove the NodeList from the DependencyManager DependencyManager::destroy<NodeList>(); @@ -2957,13 +3032,14 @@ Application::~Application() { _window->deleteLater(); // make sure that the quit event has finished sending before we take the application down - auto closeEventSender = DependencyManager::get<CloseEventSender>(); - 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<CloseEventSender>()) { + 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); @@ -3103,7 +3179,7 @@ void Application::initializeGL() { glClear(GL_COLOR_BUFFER_BIT); _glWidget->swapBuffers(); - _graphicsEngine.initializeGPU(_glWidget); + _graphicsEngine->initializeGPU(_glWidget); } void Application::initializeDisplayPlugins() { @@ -3115,7 +3191,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; } @@ -3167,7 +3243,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<Keyboard>()->registerKeyboardHighlighting(); }); } @@ -3424,7 +3500,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data()); surfaceContext->setContextProperty("Preferences", DependencyManager::get<Preferences>().data()); surfaceContext->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data()); - surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine._frameTimingsScriptingInterface); + surfaceContext->setContextProperty("FrameTimings", &_graphicsEngine->_frameTimingsScriptingInterface); surfaceContext->setContextProperty("Rates", new RatesScriptingInterface(this)); surfaceContext->setContextProperty("TREE_SCALE", TREE_SCALE); @@ -4060,7 +4136,7 @@ std::map<QString, QString> 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<QString, QString> namedPaths = tmpTree->getNamedPaths(); @@ -4130,8 +4206,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)); } } @@ -4201,7 +4277,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); } @@ -5246,8 +5324,8 @@ void Application::idle() { PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", uint32_t, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->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__); @@ -5614,7 +5692,7 @@ bool Application::importEntities(const QString& urlOrFilename, const bool isObse } QVector<EntityItemID> 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() { @@ -5664,7 +5742,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<EntityScriptingInterface>(); @@ -5688,7 +5766,7 @@ void Application::init() { } }, Qt::QueuedConnection); - _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine.getRenderScene(), _entitySimulation); + _gameWorkload.startup(getEntities()->getWorkloadSpace(), _graphicsEngine->getRenderScene(), _entitySimulation); _entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace()); } @@ -5860,7 +5938,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<LODManager>(); @@ -5896,8 +5974,8 @@ void Application::updateThreads(float deltaTime) { // parse voxel packets if (!_enableProcessOctreeThread) { - _octreeProcessor.threadRoutine(); - _entityEditSender.threadRoutine(); + _octreeProcessor->threadRoutine(); + _entityEditSender->threadRoutine(); } } @@ -6020,7 +6098,7 @@ void Application::resetPhysicsReadyInformation() { _gpuTextureMemSizeStabilityCount = 0; _gpuTextureMemSizeAtLastCheck = 0; _physicsEnabled = false; - _octreeProcessor.stopSafeLanding(); + _octreeProcessor->stopSafeLanding(); } void Application::reloadResourceCaches() { @@ -6165,7 +6243,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<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera")); @@ -6283,7 +6361,7 @@ void Application::tryToEnablePhysics() { auto myAvatar = getMyAvatar(); if (myAvatar->isReadyForPhysics()) { myAvatar->getCharacterController()->setPhysicsEngine(_physicsEngine); - _octreeProcessor.resetSafeLanding(); + _octreeProcessor->resetSafeLanding(); _physicsEnabled = true; setIsInterstitialMode(false); myAvatar->updateMotionBehaviorFromMenu(); @@ -6292,7 +6370,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; @@ -6309,12 +6387,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(); } } @@ -6801,7 +6879,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(); @@ -6830,7 +6908,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); @@ -6969,7 +7047,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; } @@ -7011,7 +7089,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; } @@ -7280,12 +7358,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(); } @@ -7357,9 +7435,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<AudioClient>().data(), "audioMixerKilled"); @@ -7448,7 +7526,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>(); - entityScriptingInterface->setPacketSender(&_entityEditSender); + entityScriptingInterface->setPacketSender(_entityEditSender.get()); entityScriptingInterface->setEntityTree(getEntities()->getTree()); if (property(hifi::properties::TEST).isValid()) { @@ -8762,26 +8840,6 @@ void Application::sendLambdaEvent(const std::function<void()>& f) { } } -void Application::initPlugins(const QCommandLineParser& parser) { - 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 82b39e868b..63a035dd45 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -123,6 +123,31 @@ class Application : public QApplication, friend class OctreePacketProcessor; public: + + /** + * @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 + * + * 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); } @@ -135,15 +160,12 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; - // FIXME? Empty methods, do we still need them? - static void initPlugins(const QCommandLineParser& parser); static void shutdownPlugins(); Application( int& argc, char** argv, const QCommandLineParser& parser, - QElapsedTimer& startup_time, - bool runningMarkerExisted + QElapsedTimer& startup_time ); ~Application(); @@ -197,16 +219,16 @@ public: const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; } - const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } + const OctreePacketProcessor& getOctreePacketProcessor() const { return *_octreeProcessor; } QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); } MainWindow* getWindow() const { return _window; } EntityTreePointer getEntityClipboard() const { return _entityClipboard; } - EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; } + std::shared_ptr<EntityEditPacketSender> getEntityEditPacketSender() { return _entityEditSender; } 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; } @@ -214,8 +236,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 @@ -293,9 +315,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; } @@ -709,8 +731,8 @@ private: bool _enableProcessOctreeThread; bool _interstitialMode { false }; - OctreePacketProcessor _octreeProcessor; - EntityEditPacketSender _entityEditSender; + std::shared_ptr<OctreePacketProcessor> _octreeProcessor; + std::shared_ptr<EntityEditPacketSender> _entityEditSender; StDev _idleLoopStdev; float _idleLoopMeasuredJitter; @@ -757,13 +779,13 @@ private: GameWorkload _gameWorkload; - GraphicsEngine _graphicsEngine; + std::shared_ptr<GraphicsEngine> _graphicsEngine; void updateRenderArgs(float deltaTime); bool _disableLoginScreen { true }; Overlays _overlays; - ApplicationOverlay _applicationOverlay; + std::shared_ptr<ApplicationOverlay> _applicationOverlay; OverlayConductor _overlayConductor; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); @@ -860,5 +882,7 @@ private: bool _crashOnShutdown { false }; DiscordPresence* _discordPresence{ nullptr }; + + bool _profilingInitialized { false }; }; #endif // hifi_Application_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 827388aa1c..62100f9cae 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<bool, bool> zoneInteractionProperties; entityTree->withWriteLock([&] { zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties(); - EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - entityTree->updateEntityQueryAACube(shared_from_this(), packetSender, false, true); + std::shared_ptr<EntityEditPacketSender> 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); }); } diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 835e4060a7..7e8c1afff3 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -24,6 +24,7 @@ #include <SharedUtil.h> #include <NetworkAccessManager.h> #include <gl/GLHelpers.h> +#include <iostream> #include "AddressManager.h" #include "Application.h" @@ -33,6 +34,9 @@ #include "MainWindow.h" #include "Profile.h" #include "LogHandler.h" +#include <plugins/PluginManager.h> +#include <plugins/DisplayPlugin.h> +#include <plugins/CodecPlugin.h> #ifdef Q_OS_WIN #include <Windows.h> @@ -63,11 +67,24 @@ 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. LogHandler::getInstance().setShouldUseJournald(false); + + // Extend argv to enable WebGL rendering + std::vector<const char*> 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(); @@ -125,12 +142,12 @@ int main(int argc, const char* argv[]) { "displays" ); QCommandLineOption disableDisplaysOption( - "disable-displays", + "disableDisplayPlugins", "Displays to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); QCommandLineOption disableInputsOption( - "disable-inputs", + "disableInputPlugins", "Inputs to disable. Valid options include \"OpenVR (Vive)\" and \"Oculus Rift\"", "string" ); @@ -246,6 +263,19 @@ 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." + ); + 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 +318,10 @@ int main(int argc, const char* argv[]) { parser.addOption(quitWhenFinishedOption); parser.addOption(fastHeartbeatOption); parser.addOption(logOption); + parser.addOption(abortAfterStartupOption); + parser.addOption(abortAfterInitOption); + parser.addOption(getPluginsOption); + QString applicationPath; // A temporary application instance is needed to get the location of the running executable @@ -310,6 +344,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<char**>(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(); @@ -321,6 +365,75 @@ int main(int argc, const char* argv[]) { } } + app.initializePluginManager(parser); + + 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; + data["subdeviceNames"] = QJsonArray::fromStringList(plugin->getSubdeviceNames()); + data["deviceName"] = plugin->getDeviceName(); + data["configurable"] = plugin->configurable(); + data["isHandController"] = plugin->isHandController(); + data["isHeadController"] = plugin->isHeadController(); + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + 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(); + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + displayJson[plugin->getName()] = data; + } + + QJsonObject codecsJson; + for (const auto &plugin : pluginManager->getCodecPlugins()) { + QJsonObject data; + data["isActive"] = plugin->isActive(); + data["isSupported"] = plugin->isSupported(); + + codecsJson[plugin->getName()] = data; + } + + QJsonObject platformsJson; + platformsJson["steamAvailable"] = (pluginManager->getSteamClientPlugin() != nullptr); + platformsJson["oculusAvailable"] = (pluginManager->getOculusPlatformPlugin() != nullptr); + + QJsonObject root; + root["plugins"] = pluginsJson; + root["inputs"] = inputJson; + root["displays"] = displayJson; + root["codecs"] = codecsJson; + root["platforms"] = platformsJson; + + std::cout << QJsonDocument(root).toJson().toStdString() << "\n"; + + return 0; + } + + // Act on arguments for early termination. if (parser.isSet(versionOption)) { parser.showVersion(); @@ -407,10 +520,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(); @@ -549,7 +661,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 @@ -587,17 +699,10 @@ int main(int argc, const char* argv[]) { SandboxUtils::runLocalSandbox(serverContentPath, true, noUpdater); } - // Extend argv to enable WebGL rendering - std::vector<const char*> 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<char**>(argvExtended.data()), parser, startupTime, runningMarkerExisted); + app.setPreviousSessionCrashed(runningMarkerExisted); + app.initialize(parser); PROFILE_SYNC_END(startup, "app full ctor", ""); #if defined(Q_OS_LINUX) @@ -665,6 +770,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(); diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index c1ca853563..0281a012d2 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -72,12 +72,13 @@ int getPluginInterfaceVersionFromMetaData(const QJsonObject& object) { QStringList preferredDisplayPlugins; QStringList disabledDisplays; QStringList disabledInputs; +std::vector<PluginManager::PluginInfo> pluginInfo; bool isDisabled(QJsonObject metaData) { auto name = getPluginNameFromMetaData(metaData); auto iid = getPluginIIDFromMetaData(metaData); - if (iid == DisplayProvider_iid) { + if (iid == DisplayProvider_iid || iid == SteamClientProvider_iid || iid == OculusPlatformProvider_iid) { return disabledDisplays.contains(name); } else if (iid == InputProvider_iid) { return disabledInputs.contains(name); @@ -126,18 +127,28 @@ int PluginManager::instantiate() { qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin); auto loader = QSharedPointer<QPluginLoader>::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; } @@ -145,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; @@ -163,6 +180,11 @@ int PluginManager::instantiate() { return loadedPlugins; } +std::vector<PluginManager::PluginInfo> PluginManager::getPluginInfo() const { + getLoadedPlugins(); // This builds the pluginInfo list + return pluginInfo; +} + const CodecPluginList& PluginManager::getCodecPlugins() { static CodecPluginList codecPlugins; static std::once_flag once; @@ -272,14 +294,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 26c98ce5db..b529472e1f 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -12,54 +12,268 @@ #include <DependencyManager.h> #include <SettingHandle.h> +#include <QJsonDocument> +#include <QJsonObject> #include "Forward.h" class QPluginLoader; using PluginManagerPointer = QSharedPointer<PluginManager>; +/** + * @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 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(); + /** + * @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); - 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<bool(const QJsonObject&)>; + + /** + * @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(); } void setEnableOculusPluginSetting(bool value); + /** + * @brief Returns information about known plugins + * + * This is a function for informative/debugging purposes. + * + * @return std::vector<PluginInfo> + */ + std::vector<PluginInfo> getPluginInfo() const; + signals: void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices); - + private: PluginManager() = default;