7461 lines · 314.0 KB · C++
RawDownload
////  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 <chrono>#include <thread> #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/QCommandLineParser>#include <QtCore/QMimeData>#include <QtCore/QThreadPool>#include <QtConcurrent/QtConcurrentRun> #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 <QFontDatabase>#include <QProcessEnvironment>#include <QTemporaryDir> #include <gl/QOpenGLContextWrapper.h> #include <shared/QtHelpers.h>#include <shared/GlobalAppProperties.h>#include <StatTracker.h>#include <Trace.h>#include <ResourceScriptingInterface.h>#include <AccountManager.h>#include <AddressManager.h>#include <AnimDebugDraw.h>#include <BuildInfo.h>#include <AssetClient.h>#include <AssetUpload.h>#include <AutoUpdater.h>#include <Midi.h>#include <AudioInjectorManager.h>#include <AvatarBookmarks.h>#include <CursorManager.h>#include <DebugDraw.h>#include <DeferredLightingEffect.h>#include <EntityScriptClient.h>#include <EntityScriptServerLogClient.h>#include <EntityScriptingInterface.h>#include "ui/overlays/ContextOverlayInterface.h"#include <ErrorDialog.h>#include <FileScriptingInterface.h>#include <Finally.h>#include <FingerprintUtils.h>#include <FramebufferCache.h>#include <gpu/Batch.h>#include <gpu/Context.h>#include <gpu/gl/GLBackend.h>#include <InfoView.h>#include <input-plugins/InputPlugin.h>#include <controllers/UserInputMapper.h>#include <controllers/InputRecorder.h>#include <controllers/ScriptingInterface.h>#include <controllers/StateController.h>#include <UserActivityLoggerScriptingInterface.h>#include <LogHandler.h>#include "LocationBookmarks.h"#include <LocationScriptingInterface.h>#include <MainWindow.h>#include <MappingRequest.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 <ui/OffscreenQmlSurfaceCache.h>#include <PathUtils.h>#include <PerfStat.h>#include <PhysicsEngine.h>#include <PhysicsHelpers.h>#include <plugins/CodecPlugin.h>#include <plugins/PluginManager.h>#include <plugins/PluginUtils.h>#include <plugins/SteamClientPlugin.h>#include <plugins/InputConfiguration.h>#include <RecordingScriptingInterface.h>#include <UpdateSceneTask.h>#include <RenderViewTask.h>#include <SecondaryCamera.h>#include <ResourceCache.h>#include <ResourceRequest.h>#include <SandboxUtils.h>#include <SceneScriptingInterface.h>#include <ScriptEngines.h>#include <ScriptCache.h>#include <SoundCache.h>#include <ui/TabletScriptingInterface.h>#include <ui/ToolbarScriptingInterface.h>#include <Tooltip.h>#include <udt/PacketHeaders.h>#include <UserActivityLogger.h>#include <UsersScriptingInterface.h>#include <recording/ClipCache.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 <trackers/EyeTracker.h>#include <avatars-renderer/ScriptAvatar.h>#include <RenderableEntityItem.h> #include "AudioClient.h"#include "audio/AudioScope.h"#include "avatar/AvatarManager.h"#include "avatar/MyHead.h"#include "CrashHandler.h"#include "devices/DdeFaceTracker.h"#include "DiscoverabilityManager.h"#include "GLCanvas.h"#include "InterfaceDynamicFactory.h"#include "InterfaceLogging.h"#include "LODManager.h"#include "ModelPackager.h"#include "scripting/Audio.h"#include "networking/CloseEventSender.h"#include "scripting/TestScriptingInterface.h"#include "scripting/AccountScriptingInterface.h"#include "scripting/AssetMappingsScriptingInterface.h"#include "scripting/ClipboardScriptingInterface.h"#include "scripting/DesktopScriptingInterface.h"#include "scripting/GlobalServicesScriptingInterface.h"#include "scripting/HMDScriptingInterface.h"#include "scripting/MenuScriptingInterface.h"#include "scripting/SettingsScriptingInterface.h"#include "scripting/WindowScriptingInterface.h"#include "scripting/ControllerScriptingInterface.h"#include "scripting/RatesScriptingInterface.h"#include "scripting/SelectionScriptingInterface.h"#include "scripting/WalletScriptingInterface.h"#if defined(Q_OS_MAC) || defined(Q_OS_WIN)#include "SpeechRecognizer.h"#endif#include "ui/ResourceImageItem.h"#include "ui/AddressBarDialog.h"#include "ui/AvatarInputs.h"#include "ui/DialogsManager.h"#include "ui/LoginDialog.h"#include "ui/overlays/Cube3DOverlay.h"#include "ui/overlays/Web3DOverlay.h"#include "ui/Snapshot.h"#include "ui/SnapshotAnimated.h"#include "ui/StandAloneJSConsole.h"#include "ui/Stats.h"#include "ui/UpdateDialog.h"#include "ui/overlays/Overlays.h"#include "ui/DomainConnectionModel.h"#include "Util.h"#include "InterfaceParentFinder.h"#include "ui/OctreeStatsProvider.h" #include <GPUIdent.h>#include <gl/GLHelpers.h>#include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>#include <EntityScriptClient.h>#include <ModelScriptingInterface.h> #include <raypick/RayPickScriptingInterface.h>#include <raypick/LaserPointerScriptingInterface.h> #include <FadeEffect.h> #include "commerce/Ledger.h"#include "commerce/Wallet.h"#include "commerce/QmlCommerce.h" // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU// FIXME seems to be broken.#if defined(Q_OS_WIN)#include <VersionHelpers.h> extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;}#endif enum ApplicationEvent {    // Execute a lambda function    Lambda = QEvent::User + 1,    // Trigger the next render    Render,    // Trigger the next idle    Idle,};  class RenderEventHandler : public QObject {    using Parent = QObject;    Q_OBJECTpublic:    RenderEventHandler(QOpenGLContext* context) {        _renderContext = new OffscreenGLCanvas();        _renderContext->setObjectName("RenderContext");        _renderContext->create(context);        if (!_renderContext->makeCurrent()) {            qFatal("Unable to make rendering context current");        }        _renderContext->doneCurrent();         // Deleting the object with automatically shutdown the thread        connect(qApp, &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);         // Transfer to a new thread        moveToNewNamedThread(this, "RenderThread", [this](QThread* renderThread) {            hifi::qt::addBlockingForbiddenThread("Render", renderThread);            _renderContext->moveToThreadWithContext(renderThread);            qApp->_lastTimeRendered.start();        }, std::bind(&RenderEventHandler::initialize, this), QThread::HighestPriority);    } private:    void initialize() {        PROFILE_SET_THREAD_NAME("Render");        if (!_renderContext->makeCurrent()) {            qFatal("Unable to make rendering context current on render thread");        }    }     void render() {        if (qApp->shouldPaint()) {            qApp->paintGL();        }    }     bool event(QEvent* event) override {        switch ((int)event->type()) {            case ApplicationEvent::Render:                render();                // Ensure we never back up the render events.  Each render should be triggered only in response                // to the NEXT render event after the last render occured                QCoreApplication::removePostedEvents(this, ApplicationEvent::Render);                return true;             default:                break;        }        return Parent::event(event);    }     OffscreenGLCanvas* _renderContext { nullptr };};  Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse") using namespace std; static QTimer locationUpdateTimer;static QTimer identityPacketTimer;static QTimer pingTimer; static const QString DISABLE_WATCHDOG_FLAG("HIFI_DISABLE_WATCHDOG");static bool DISABLE_WATCHDOG = QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG);  static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; // For processing on QThreadPool, we target a number of threads after reserving some// based on how many are being consumed by the application and the display plugin.  However,// we will never drop below the 'min' valuestatic const int MIN_PROCESSING_THREAD_POOL_SIZE = 1; static const QString SNAPSHOT_EXTENSION  = ".jpg";static const QString SVO_EXTENSION  = ".svo";static const QString SVO_JSON_EXTENSION = ".svo.json";static const QString JSON_GZ_EXTENSION = ".json.gz";static const QString JSON_EXTENSION = ".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 QString WEB_VIEW_TAG = "noDownload=true";static const QString ZIP_EXTENSION = ".zip"; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";static const QString INFO_HELP_PATH = "../../../html/tabletHelp.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); static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // millisecondsstatic const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0"; const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {    { SVO_EXTENSION, &Application::importSVOFromURL },    { SVO_JSON_EXTENSION, &Application::importSVOFromURL },    { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl },    { JSON_EXTENSION, &Application::importJSONFromURL },    { JS_EXTENSION, &Application::askToLoadScript },    { FST_EXTENSION, &Application::askToSetAvatarUrl },    { JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent },    { ZIP_EXTENSION, &Application::importFromZIP }}; 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);            // Don't do heartbeat detection under nsight            if (nsightActive()) {                continue;            }            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) {                qCDebug(interfaceapp_deadlock) << "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) {                qCDebug(interfaceapp_deadlock) << "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) {                qCDebug(interfaceapp_deadlock) << "DEADLOCK WATCHDOG WARNING:"                    << "lastHeartbeatAge:" << lastHeartbeatAge                    << "elapsedMovingAverage:" << elapsedMovingAverage << "** OVER EXPECTED VALUE **"                    << "maxElapsed:" << _maxElapsed                    << "maxElapsedAverage:" << _maxElapsedAverage                    << "samples:" << _movingAverage.getSamples();            }             if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) {                qCDebug(interfaceapp_deadlock) << "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;                }            }             if (message->message == WM_DEVICECHANGE) {                Midi::USBchanged();                // re-scan the MIDI bus            }        }        return false;    }};#endif class LambdaEvent : public QEvent {    std::function<void()> _fun;public:    LambdaEvent(const std::function<void()> & fun) :

Lines 1–500 of 7461.