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();