//// Application.cpp// interface/src//// Created by Andrzej Kapolka on 5/10/13.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "Application.h" #include <gl/Config.h>#include <glm/glm.hpp>#include <glm/gtx/component_wise.hpp>#include <glm/gtx/quaternion.hpp>#include <glm/gtx/vector_angle.hpp>#include <glm/gtc/type_ptr.hpp> #include <QtCore/QAbstractNativeEventFilter>#include <QtCore/QMimeData>#include <QtCore/QThreadPool> #include <QtGui/QScreen>#include <QtGui/QWindow>#include <QtGui/QDesktopServices> #include <QtNetwork/QLocalSocket>#include <QtNetwork/QLocalServer> #include <QtQml/QQmlContext>#include <QtQml/QQmlEngine>#include <QtQuick/QQuickWindow> #include <QtWidgets/QDesktopWidget>#include <QtWidgets/QMessageBox> #include <QtMultimedia/QMediaPlayer> #include <QProcessEnvironment> #include <gl/QOpenGLContextWrapper.h> #include <ResourceScriptingInterface.h>#include <AccountManager.h>#include <AddressManager.h>#include <AnimDebugDraw.h>#include <BuildInfo.h>#include <AssetClient.h>#include <AutoUpdater.h>#include <AudioInjectorManager.h>#include <CursorManager.h>#include <DebugDraw.h>#include <DeferredLightingEffect.h>#include <display-plugins/DisplayPlugin.h>#include <EntityScriptingInterface.h>#include <ErrorDialog.h>#include <Finally.h>#include <FramebufferCache.h>#include <gpu/Batch.h>#include <gpu/Context.h>#include <gpu/gl/GLBackend.h>#include <HFActionEvent.h>#include <HFBackEvent.h>#include <InfoView.h>#include <input-plugins/InputPlugin.h>#include <controllers/UserInputMapper.h>#include <controllers/StateController.h>#include <LogHandler.h>#include <MainWindow.h>#include <MessagesClient.h>#include <ModelEntityItem.h>#include <NetworkAccessManager.h>#include <NetworkingConstants.h>#include <ObjectMotionState.h>#include <OctalCode.h>#include <OctreeSceneStats.h>#include <OffscreenUi.h>#include <gl/OffscreenGLCanvas.h>#include <PathUtils.h>#include <PerfStat.h>#include <PhysicsEngine.h>#include <PhysicsHelpers.h>#include <plugins/PluginContainer.h>#include <plugins/PluginManager.h>#include <RenderableWebEntityItem.h>#include <RenderShadowTask.h>#include <RenderDeferredTask.h>#include <ResourceCache.h>#include <SceneScriptingInterface.h>#include <RecordingScriptingInterface.h>#include <ScriptCache.h>#include <SoundCache.h>#include <ScriptEngines.h>#include <Tooltip.h>#include <udt/PacketHeaders.h>#include <UserActivityLogger.h>#include <recording/Deck.h>#include <recording/Recorder.h>#include <shared/StringHelpers.h>#include <QmlWebWindowClass.h>#include <Preferences.h>#include <display-plugins/CompositorHelper.h> #include "AudioClient.h"#include "audio/AudioScope.h"#include "avatar/AvatarManager.h"#include "CrashHandler.h"#include "input-plugins/SpacemouseManager.h"#include "devices/DdeFaceTracker.h"#include "devices/EyeTracker.h"#include "devices/Faceshift.h"#include "devices/Leapmotion.h"#include "DiscoverabilityManager.h"#include "GLCanvas.h"#include "InterfaceActionFactory.h"#include "InterfaceLogging.h"#include "LODManager.h"#include "ModelPackager.h"#include "PluginContainerProxy.h"#include "scripting/AccountScriptingInterface.h"#include "scripting/AssetMappingsScriptingInterface.h"#include "scripting/AudioDeviceScriptingInterface.h"#include "scripting/ClipboardScriptingInterface.h"#include "scripting/DesktopScriptingInterface.h"#include "scripting/GlobalServicesScriptingInterface.h"#include "scripting/HMDScriptingInterface.h"#include "scripting/LocationScriptingInterface.h"#include "scripting/MenuScriptingInterface.h"#include "scripting/SettingsScriptingInterface.h"#include "scripting/WebWindowClass.h"#include "scripting/WindowScriptingInterface.h"#include "scripting/ControllerScriptingInterface.h"#include "scripting/RatesScriptingInterface.h"#if defined(Q_OS_MAC) || defined(Q_OS_WIN)#include "SpeechRecognizer.h"#endif#include "Stars.h"#include "ui/AddressBarDialog.h"#include "ui/AvatarInputs.h"#include "ui/DialogsManager.h"#include "ui/LoginDialog.h"#include "ui/overlays/Cube3DOverlay.h"#include "ui/Snapshot.h"#include "ui/StandAloneJSConsole.h"#include "ui/Stats.h"#include "ui/UpdateDialog.h"#include "Util.h"#include "InterfaceParentFinder.h" #include "FrameTimingsScriptingInterface.h" // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU// FIXME seems to be broken.#if defined(Q_OS_WIN)extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;}#endif using namespace std; static QTimer locationUpdateTimer;static QTimer identityPacketTimer;static QTimer pingTimer; static const QString SNAPSHOT_EXTENSION = ".jpg";static const QString SVO_EXTENSION = ".svo";static const QString SVO_JSON_EXTENSION = ".svo.json";static const QString JS_EXTENSION = ".js";static const QString FST_EXTENSION = ".fst";static const QString FBX_EXTENSION = ".fbx";static const QString OBJ_EXTENSION = ".obj";static const QString AVA_JSON_EXTENSION = ".ava.json"; static const int MSECS_PER_SEC = 1000;static const int MIRROR_VIEW_TOP_PADDING = 5;static const int MIRROR_VIEW_LEFT_PADDING = 10;static const int MIRROR_VIEW_WIDTH = 265;static const int MIRROR_VIEW_HEIGHT = 215;static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;static const float MIRROR_REARVIEW_DISTANCE = 0.722f;static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f;static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_HELP_PATH = "html/interface-welcome.html";static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; static const unsigned int THROTTLED_SIM_FRAMERATE = 15;static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; static const uint32_t INVALID_FRAME = UINT32_MAX; static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions { { SNAPSHOT_EXTENSION, &Application::acceptSnapshot }, { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }}; class DeadlockWatchdogThread : public QThread {public: static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND; static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples // Set the heartbeat on launch DeadlockWatchdogThread() { setObjectName("Deadlock Watchdog"); // Give the heartbeat an initial value _heartbeat = usecTimestampNow(); connect(qApp, &QCoreApplication::aboutToQuit, [this] { _quit = true; }); } static void updateHeartbeat() { auto now = usecTimestampNow(); auto elapsed = now - _heartbeat; _movingAverage.addSample(elapsed); _heartbeat = now; } static void deadlockDetectionCrash() { uint32_t* crashTrigger = nullptr; *crashTrigger = 0xDEAD10CC; } void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us uint64_t now = usecTimestampNow(); auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; auto elapsedMovingAverage = _movingAverage.getAverage(); if (elapsedMovingAverage > _maxElapsedAverage) { qDebug() << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage << "maxElapsed:" << _maxElapsed << "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage << "NEW maxElapsedAverage:" << elapsedMovingAverage << "** NEW MAX ELAPSED AVERAGE **" << "samples:" << _movingAverage.getSamples(); _maxElapsedAverage = elapsedMovingAverage; } if (lastHeartbeatAge > _maxElapsed) { qDebug() << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage << "PREVIOUS maxElapsed:" << _maxElapsed << "NEW maxElapsed:" << lastHeartbeatAge << "** NEW MAX ELAPSED **" << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); _maxElapsed = lastHeartbeatAge; } if (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT) { qDebug() << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage << "** OVER EXPECTED VALUE **" << "maxElapsed:" << _maxElapsed << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); } if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { qDebug() << "DEADLOCK DETECTED -- " << "lastHeartbeatAge:" << lastHeartbeatAge << "[ lastHeartbeat :" << lastHeartbeat << "now:" << now << " ]" << "elapsedMovingAverage:" << elapsedMovingAverage << "maxElapsed:" << _maxElapsed << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); // Don't actually crash in debug builds, in case this apparent deadlock is simply from // the developer actively debugging code #ifdef NDEBUG deadlockDetectionCrash(); #endif } } } static std::atomic<uint64_t> _heartbeat; static std::atomic<uint64_t> _maxElapsed; static std::atomic<int> _maxElapsedAverage; static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage; bool _quit { false };}; std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat;std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage; #ifdef Q_OS_WINclass MyNativeEventFilter : public QAbstractNativeEventFilter {public: static MyNativeEventFilter& getInstance() { static MyNativeEventFilter staticInstance; return staticInstance; } bool nativeEventFilter(const QByteArray &eventType, void* msg, long* result) Q_DECL_OVERRIDE { if (eventType == "windows_generic_MSG") { MSG* message = (MSG*)msg; if (message->message == UWM_IDENTIFY_INSTANCES) { *result = UWM_IDENTIFY_INSTANCES; return true; } if (message->message == UWM_SHOW_APPLICATION) { MainWindow* applicationWindow = qApp->getWindow(); if (applicationWindow->isMinimized()) { applicationWindow->showNormal(); // Restores to windowed or maximized state appropriately. } qApp->setActiveWindow(applicationWindow); // Flashes the taskbar icon if not focus. return true; } if (message->message == WM_COPYDATA) { COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam); QUrl url = QUrl((const char*)(pcds->lpData)); if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { DependencyManager::get<AddressManager>()->handleLookupString(url.toString()); return true; } } } return false; }};#endif class LambdaEvent : public QEvent { std::function<void()> _fun;public: LambdaEvent(const std::function<void()> & fun) : QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) { } LambdaEvent(std::function<void()> && fun) : QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) { } void call() const { _fun(); }}; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) {#ifdef Q_OS_WIN OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n");#endif qApp->getLogger()->addMessage(qPrintable(logMessage + "\n")); }} static const QString STATE_IN_HMD = "InHMD";static const QString STATE_SNAP_TURN = "SnapTurn";static const QString STATE_GROUNDED = "Grounded";static const QString STATE_NAV_FOCUSED = "NavigationFocused"; bool setupEssentials(int& argc, char** argv) { unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast<const char**>(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } // Set build version QCoreApplication::setApplicationVersion(BuildInfo::VERSION); Setting::preInit(); bool previousSessionCrashed = CrashHandler::checkForResetSettings(); CrashHandler::writeRunningMarkerFiler(); qAddPostRoutine(CrashHandler::deleteRunningMarkerFile); DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>(); DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>(); DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>(); Setting::init(); // Set dependencies DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp)); DependencyManager::set<ScriptEngines>(); DependencyManager::set<Preferences>(); DependencyManager::set<recording::Deck>(); DependencyManager::set<recording::Recorder>(); DependencyManager::set<AddressManager>(); DependencyManager::set<NodeList>(NodeType::Agent, listenPort); DependencyManager::set<GeometryCache>(); DependencyManager::set<ModelCache>(); DependencyManager::set<ScriptCache>(); DependencyManager::set<SoundCache>(); DependencyManager::set<Faceshift>(); DependencyManager::set<DdeFaceTracker>(); DependencyManager::set<EyeTracker>(); DependencyManager::set<AudioClient>(); DependencyManager::set<AudioScope>(); DependencyManager::set<DeferredLightingEffect>(); DependencyManager::set<TextureCache>(); DependencyManager::set<FramebufferCache>(); DependencyManager::set<AnimationCache>(); DependencyManager::set<ModelBlender>(); DependencyManager::set<AvatarManager>(); DependencyManager::set<LODManager>(); DependencyManager::set<StandAloneJSConsole>(); DependencyManager::set<DialogsManager>(); DependencyManager::set<BandwidthRecorder>(); DependencyManager::set<ResourceCacheSharedItems>(); DependencyManager::set<DesktopScriptingInterface>(); DependencyManager::set<EntityScriptingInterface>(true); DependencyManager::set<RecordingScriptingInterface>(); DependencyManager::set<WindowScriptingInterface>(); DependencyManager::set<HMDScriptingInterface>(); DependencyManager::set<ResourceScriptingInterface>(); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) DependencyManager::set<SpeechRecognizer>();#endif DependencyManager::set<DiscoverabilityManager>(); DependencyManager::set<SceneScriptingInterface>(); DependencyManager::set<OffscreenUi>(); DependencyManager::set<AutoUpdater>(); DependencyManager::set<PathUtils>(); DependencyManager::set<InterfaceActionFactory>(); DependencyManager::set<AudioInjectorManager>(); DependencyManager::set<MessagesClient>(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set<UserInputMapper>(); DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>(); DependencyManager::set<InterfaceParentFinder>(); DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp); DependencyManager::set<CompositorHelper>(); return previousSessionCrashed;} // FIXME move to header, or better yet, design some kind of UI manager// to take care of highlighting keyboard focused items, rather than// continuing to overburden Application.cppCube3DOverlay* _keyboardFocusHighlight{ nullptr };int _keyboardFocusHighlightID{ -1 };PluginContainer* _pluginContainer; // FIXME hack access to the internal share context for the Chromium helper// Normally we'd want to use QWebEngine::initialize(), but we can't because// our primary context is a QGLWidget, which can't easily be initialized to share// from a QOpenGLContext.//// So instead we create a new offscreen context to share with the QGLWidget,// and manually set THAT to be the shared context for the Chromium helperOffscreenGLCanvas* _chromiumShareContext { nullptr };Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 }; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv), _window(new MainWindow(desktop())), _sessionRunTimer(startupTimer), _previousSessionCrashed(setupEssentials(argc, argv)), _undoStackScriptingInterface(&_undoStack), _entitySimulation(new PhysicalEntitySimulation()), _physicsEngine(new PhysicsEngine(Vectors::ZERO)), _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), _enableProcessOctreeThread(true), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _aboutToQuit(false),Lines 1–500 of 5300. Load more
// // Application.cpp // interface/src // // Created by Andrzej Kapolka on 5/10/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Application.h" #include <gl/Config.h> #include <glm/glm.hpp> #include <glm/gtx/component_wise.hpp> #include <glm/gtx/quaternion.hpp> #include <glm/gtx/vector_angle.hpp> #include <glm/gtc/type_ptr.hpp> #include <QtCore/QAbstractNativeEventFilter> #include <QtCore/QMimeData> #include <QtCore/QThreadPool> #include <QtGui/QScreen> #include <QtGui/QWindow> #include <QtGui/QDesktopServices> #include <QtNetwork/QLocalSocket> #include <QtNetwork/QLocalServer> #include <QtQml/QQmlContext> #include <QtQml/QQmlEngine> #include <QtQuick/QQuickWindow> #include <QtWidgets/QDesktopWidget> #include <QtWidgets/QMessageBox> #include <QtMultimedia/QMediaPlayer> #include <QProcessEnvironment> #include <gl/QOpenGLContextWrapper.h> #include <ResourceScriptingInterface.h> #include <AccountManager.h> #include <AddressManager.h> #include <AnimDebugDraw.h> #include <BuildInfo.h> #include <AssetClient.h> #include <AutoUpdater.h> #include <AudioInjectorManager.h> #include <CursorManager.h> #include <DebugDraw.h> #include <DeferredLightingEffect.h> #include <display-plugins/DisplayPlugin.h> #include <EntityScriptingInterface.h> #include <ErrorDialog.h> #include <Finally.h> #include <FramebufferCache.h> #include <gpu/Batch.h> #include <gpu/Context.h> #include <gpu/gl/GLBackend.h> #include <HFActionEvent.h> #include <HFBackEvent.h> #include <InfoView.h> #include <input-plugins/InputPlugin.h> #include <controllers/UserInputMapper.h> #include <controllers/StateController.h> #include <LogHandler.h> #include <MainWindow.h> #include <MessagesClient.h> #include <ModelEntityItem.h> #include <NetworkAccessManager.h> #include <NetworkingConstants.h> #include <ObjectMotionState.h> #include <OctalCode.h> #include <OctreeSceneStats.h> #include <OffscreenUi.h> #include <gl/OffscreenGLCanvas.h> #include <PathUtils.h> #include <PerfStat.h> #include <PhysicsEngine.h> #include <PhysicsHelpers.h> #include <plugins/PluginContainer.h> #include <plugins/PluginManager.h> #include <RenderableWebEntityItem.h> #include <RenderShadowTask.h> #include <RenderDeferredTask.h> #include <ResourceCache.h> #include <SceneScriptingInterface.h> #include <RecordingScriptingInterface.h> #include <ScriptCache.h> #include <SoundCache.h> #include <ScriptEngines.h> #include <Tooltip.h> #include <udt/PacketHeaders.h> #include <UserActivityLogger.h> #include <recording/Deck.h> #include <recording/Recorder.h> #include <shared/StringHelpers.h> #include <QmlWebWindowClass.h> #include <Preferences.h> #include <display-plugins/CompositorHelper.h> #include "AudioClient.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "CrashHandler.h" #include "input-plugins/SpacemouseManager.h" #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" #include "devices/Faceshift.h" #include "devices/Leapmotion.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" #include "InterfaceActionFactory.h" #include "InterfaceLogging.h" #include "LODManager.h" #include "ModelPackager.h" #include "PluginContainerProxy.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" #include "scripting/RatesScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif #include "Stars.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" #include "ui/overlays/Cube3DOverlay.h" #include "ui/Snapshot.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" #include "ui/UpdateDialog.h" #include "Util.h" #include "InterfaceParentFinder.h" #include "FrameTimingsScriptingInterface.h" // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } #endif using namespace std; static QTimer locationUpdateTimer; static QTimer identityPacketTimer; static QTimer pingTimer; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SVO_EXTENSION = ".svo"; static const QString SVO_JSON_EXTENSION = ".svo.json"; static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const int MSECS_PER_SEC = 1000; static const int MIRROR_VIEW_TOP_PADDING = 5; static const int MIRROR_VIEW_LEFT_PADDING = 10; static const int MIRROR_VIEW_WIDTH = 265; static const int MIRROR_VIEW_HEIGHT = 215; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const float MIRROR_REARVIEW_DISTANCE = 0.722f; static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_HELP_PATH = "html/interface-welcome.html"; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; static const uint32_t INVALID_FRAME = UINT32_MAX; static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions { { SNAPSHOT_EXTENSION, &Application::acceptSnapshot }, { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl } }; class DeadlockWatchdogThread : public QThread { public: static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND; static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples // Set the heartbeat on launch DeadlockWatchdogThread() { setObjectName("Deadlock Watchdog"); // Give the heartbeat an initial value _heartbeat = usecTimestampNow(); connect(qApp, &QCoreApplication::aboutToQuit, [this] { _quit = true; }); } static void updateHeartbeat() { auto now = usecTimestampNow(); auto elapsed = now - _heartbeat; _movingAverage.addSample(elapsed); _heartbeat = now; } static void deadlockDetectionCrash() { uint32_t* crashTrigger = nullptr; *crashTrigger = 0xDEAD10CC; } void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us uint64_t now = usecTimestampNow(); auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; auto elapsedMovingAverage = _movingAverage.getAverage(); if (elapsedMovingAverage > _maxElapsedAverage) { qDebug() << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage << "maxElapsed:" << _maxElapsed << "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage << "NEW maxElapsedAverage:" << elapsedMovingAverage << "** NEW MAX ELAPSED AVERAGE **" << "samples:" << _movingAverage.getSamples(); _maxElapsedAverage = elapsedMovingAverage; } if (lastHeartbeatAge > _maxElapsed) { qDebug() << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage << "PREVIOUS maxElapsed:" << _maxElapsed << "NEW maxElapsed:" << lastHeartbeatAge << "** NEW MAX ELAPSED **" << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); _maxElapsed = lastHeartbeatAge; } if (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT) { qDebug() << "DEADLOCK WATCHDOG WARNING:" << "lastHeartbeatAge:" << lastHeartbeatAge << "elapsedMovingAverage:" << elapsedMovingAverage << "** OVER EXPECTED VALUE **" << "maxElapsed:" << _maxElapsed << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); } if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { qDebug() << "DEADLOCK DETECTED -- " << "lastHeartbeatAge:" << lastHeartbeatAge << "[ lastHeartbeat :" << lastHeartbeat << "now:" << now << " ]" << "elapsedMovingAverage:" << elapsedMovingAverage << "maxElapsed:" << _maxElapsed << "maxElapsedAverage:" << _maxElapsedAverage << "samples:" << _movingAverage.getSamples(); // Don't actually crash in debug builds, in case this apparent deadlock is simply from // the developer actively debugging code #ifdef NDEBUG deadlockDetectionCrash(); #endif } } } static std::atomic<uint64_t> _heartbeat; static std::atomic<uint64_t> _maxElapsed; static std::atomic<int> _maxElapsedAverage; static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage; bool _quit { false }; }; std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat; std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed; std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage; ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage; #ifdef Q_OS_WIN class MyNativeEventFilter : public QAbstractNativeEventFilter { public: static MyNativeEventFilter& getInstance() { static MyNativeEventFilter staticInstance; return staticInstance; } bool nativeEventFilter(const QByteArray &eventType, void* msg, long* result) Q_DECL_OVERRIDE { if (eventType == "windows_generic_MSG") { MSG* message = (MSG*)msg; if (message->message == UWM_IDENTIFY_INSTANCES) { *result = UWM_IDENTIFY_INSTANCES; return true; } if (message->message == UWM_SHOW_APPLICATION) { MainWindow* applicationWindow = qApp->getWindow(); if (applicationWindow->isMinimized()) { applicationWindow->showNormal(); // Restores to windowed or maximized state appropriately. } qApp->setActiveWindow(applicationWindow); // Flashes the taskbar icon if not focus. return true; } if (message->message == WM_COPYDATA) { COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam); QUrl url = QUrl((const char*)(pcds->lpData)); if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) { DependencyManager::get<AddressManager>()->handleLookupString(url.toString()); return true; } } } return false; } }; #endif class LambdaEvent : public QEvent { std::function<void()> _fun; public: LambdaEvent(const std::function<void()> & fun) : QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) { } LambdaEvent(std::function<void()> && fun) : QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) { } void call() const { _fun(); } }; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) { #ifdef Q_OS_WIN OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); #endif qApp->getLogger()->addMessage(qPrintable(logMessage + "\n")); } } static const QString STATE_IN_HMD = "InHMD"; static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; bool setupEssentials(int& argc, char** argv) { unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast<const char**>(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } // Set build version QCoreApplication::setApplicationVersion(BuildInfo::VERSION); Setting::preInit(); bool previousSessionCrashed = CrashHandler::checkForResetSettings(); CrashHandler::writeRunningMarkerFiler(); qAddPostRoutine(CrashHandler::deleteRunningMarkerFile); DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>(); DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>(); DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>(); Setting::init(); // Set dependencies DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp)); DependencyManager::set<ScriptEngines>(); DependencyManager::set<Preferences>(); DependencyManager::set<recording::Deck>(); DependencyManager::set<recording::Recorder>(); DependencyManager::set<AddressManager>(); DependencyManager::set<NodeList>(NodeType::Agent, listenPort); DependencyManager::set<GeometryCache>(); DependencyManager::set<ModelCache>(); DependencyManager::set<ScriptCache>(); DependencyManager::set<SoundCache>(); DependencyManager::set<Faceshift>(); DependencyManager::set<DdeFaceTracker>(); DependencyManager::set<EyeTracker>(); DependencyManager::set<AudioClient>(); DependencyManager::set<AudioScope>(); DependencyManager::set<DeferredLightingEffect>(); DependencyManager::set<TextureCache>(); DependencyManager::set<FramebufferCache>(); DependencyManager::set<AnimationCache>(); DependencyManager::set<ModelBlender>(); DependencyManager::set<AvatarManager>(); DependencyManager::set<LODManager>(); DependencyManager::set<StandAloneJSConsole>(); DependencyManager::set<DialogsManager>(); DependencyManager::set<BandwidthRecorder>(); DependencyManager::set<ResourceCacheSharedItems>(); DependencyManager::set<DesktopScriptingInterface>(); DependencyManager::set<EntityScriptingInterface>(true); DependencyManager::set<RecordingScriptingInterface>(); DependencyManager::set<WindowScriptingInterface>(); DependencyManager::set<HMDScriptingInterface>(); DependencyManager::set<ResourceScriptingInterface>(); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) DependencyManager::set<SpeechRecognizer>(); #endif DependencyManager::set<DiscoverabilityManager>(); DependencyManager::set<SceneScriptingInterface>(); DependencyManager::set<OffscreenUi>(); DependencyManager::set<AutoUpdater>(); DependencyManager::set<PathUtils>(); DependencyManager::set<InterfaceActionFactory>(); DependencyManager::set<AudioInjectorManager>(); DependencyManager::set<MessagesClient>(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set<UserInputMapper>(); DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>(); DependencyManager::set<InterfaceParentFinder>(); DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp); DependencyManager::set<CompositorHelper>(); return previousSessionCrashed; } // FIXME move to header, or better yet, design some kind of UI manager // to take care of highlighting keyboard focused items, rather than // continuing to overburden Application.cpp Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; // FIXME hack access to the internal share context for the Chromium helper // Normally we'd want to use QWebEngine::initialize(), but we can't because // our primary context is a QGLWidget, which can't easily be initialized to share // from a QOpenGLContext. // // So instead we create a new offscreen context to share with the QGLWidget, // and manually set THAT to be the shared context for the Chromium helper OffscreenGLCanvas* _chromiumShareContext { nullptr }; Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 }; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv), _window(new MainWindow(desktop())), _sessionRunTimer(startupTimer), _previousSessionCrashed(setupEssentials(argc, argv)), _undoStackScriptingInterface(&_undoStack), _entitySimulation(new PhysicalEntitySimulation()), _physicsEngine(new PhysicsEngine(Vectors::ZERO)), _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), _enableProcessOctreeThread(true), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _aboutToQuit(false),