Showing the changes to interface/src/Application.cpp (1 of 24 files). Show all
Unified · Split
| @@ -349,19 +349,14 @@ public: · 348 unmodified lines | |||
| 349 | }; | 349 | }; |
| 350 | #endif | 350 | #endif |
| 351 | 351 | ||
| 352 | enum CustomEventTypes { | ||
| 353 | Lambda = QEvent::User + 1, | ||
| 354 | Paint = Lambda + 1, | ||
| 355 | }; | ||
| 356 | |||
| 357 | class LambdaEvent : public QEvent { | 352 | class LambdaEvent : public QEvent { |
| 358 | std::function<void()> _fun; | 353 | std::function<void()> _fun; |
| 359 | public: | 354 | public: |
| 360 | LambdaEvent(const std::function<void()> & fun) : | 355 | LambdaEvent(const std::function<void()> & fun) : |
| 361 | QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) { | 356 | QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) { |
| 362 | } | 357 | } |
| 363 | LambdaEvent(std::function<void()> && fun) : | 358 | LambdaEvent(std::function<void()> && fun) : |
| 364 | QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) { | 359 | QEvent(static_cast<QEvent::Type>(Application::Lambda)), _fun(fun) { |
| 365 | } | 360 | } |
| 366 | void call() const { _fun(); } | 361 | void call() const { _fun(); } |
| 367 | }; | 362 | }; |
| @@ -1062,18 +1057,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 694 unmodified lines | |||
| 1062 | connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); | 1057 | connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); |
| 1063 | qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); | 1058 | qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); |
| 1064 | 1059 | ||
| 1065 | _idleTimer = new QTimer(this); | ||
| 1066 | connect(_idleTimer, &QTimer::timeout, [=] { | ||
| 1067 | idle(usecTimestampNow()); | ||
| 1068 | }); | ||
| 1069 | connect(this, &Application::beforeAboutToQuit, [=] { | ||
| 1070 | disconnect(_idleTimer); | ||
| 1071 | }); | ||
| 1072 | // Setting the interval to zero forces this to get called whenever there are no messages | ||
| 1073 | // in the queue, which can be pretty damn frequent. Hence the idle function has a bunch | ||
| 1074 | // of logic to abort early if it's being called too often. | ||
| 1075 | _idleTimer->start(0); | ||
| 1076 | |||
| 1077 | // After all of the constructor is completed, then set firstRun to false. | 1060 | // After all of the constructor is completed, then set firstRun to false. |
| 1078 | Setting::Handle<bool> firstRun{ Settings::firstRun, true }; | 1061 | Setting::Handle<bool> firstRun{ Settings::firstRun, true }; |
| 1079 | firstRun.set(false); | 1062 | firstRun.set(false); |
| @@ -1161,10 +1144,16 @@ void Application::cleanupBeforeQuit() { · 81 unmodified lines | |||
| 1161 | 1144 | ||
| 1162 | getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts | 1145 | getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts |
| 1163 | 1146 | ||
| 1147 | // Clear any queued processing (I/O, FBX/OBJ/Texture parsing) | ||
| 1148 | QThreadPool::globalInstance()->clear(); | ||
| 1149 | |||
| 1164 | DependencyManager::get<ScriptEngines>()->saveScripts(); | 1150 | DependencyManager::get<ScriptEngines>()->saveScripts(); |
| 1165 | DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts | 1151 | DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts |
| 1166 | DependencyManager::destroy<ScriptEngines>(); | 1152 | DependencyManager::destroy<ScriptEngines>(); |
| 1167 | 1153 | ||
| 1154 | // Cleanup all overlays after the scripts, as scripts might add more | ||
| 1155 | _overlays.cleanupAllOverlays(); | ||
| 1156 | |||
| 1168 | // first stop all timers directly or by invokeMethod | 1157 | // first stop all timers directly or by invokeMethod |
| 1169 | // depending on what thread they run in | 1158 | // depending on what thread they run in |
| 1170 | locationUpdateTimer.stop(); | 1159 | locationUpdateTimer.stop(); |
| @@ -1440,23 +1429,15 @@ void Application::initializeUi() { · 269 unmodified lines | |||
| 1440 | }); | 1429 | }); |
| 1441 | } | 1430 | } |
| 1442 | 1431 | ||
| 1443 | |||
| 1444 | void Application::paintGL() { | 1432 | void Application::paintGL() { |
| 1445 | updateHeartbeat(); | 1433 | // Some plugins process message events, allowing paintGL to be called reentrantly. |
| 1446 | // Some plugins process message events, potentially leading to | 1434 | if (_inPaint || _aboutToQuit) { |
| 1447 | // re-entering a paint event. don't allow further processing if this | ||
| 1448 | // happens | ||
| 1449 | if (_inPaint) { | ||
| 1450 | return; | 1435 | return; |
| 1451 | } | 1436 | } |
| 1437 | |||
| 1452 | _inPaint = true; | 1438 | _inPaint = true; |
| 1453 | Finally clearFlagLambda([this] { _inPaint = false; }); | 1439 | Finally clearFlag([this] { _inPaint = false; }); |
| 1454 | 1440 | ||
| 1455 | // paintGL uses a queued connection, so we can get messages from the queue even after we've quit | ||
| 1456 | // and the plugins have shutdown | ||
| 1457 | if (_aboutToQuit) { | ||
| 1458 | return; | ||
| 1459 | } | ||
| 1460 | _frameCount++; | 1441 | _frameCount++; |
| 1461 | _frameCounter.increment(); | 1442 | _frameCounter.increment(); |
| 1462 | 1443 | ||
| @@ -1814,13 +1795,30 @@ bool Application::event(QEvent* event) { · 351 unmodified lines | |||
| 1814 | return false; | 1795 | return false; |
| 1815 | } | 1796 | } |
| 1816 | 1797 | ||
| 1817 | if ((int)event->type() == (int)Lambda) { | 1798 | static bool justPresented = false; |
| 1818 | static_cast<LambdaEvent*>(event)->call(); | 1799 | if ((int)event->type() == (int)Present) { |
| 1800 | if (justPresented) { | ||
| 1801 | justPresented = false; | ||
| 1802 | |||
| 1803 | // If presentation is hogging the main thread, repost as low priority to avoid hanging the GUI. | ||
| 1804 | // This has the effect of allowing presentation to exceed the paint budget by X times and | ||
| 1805 | // only dropping every (1/X) frames, instead of every ceil(X) frames. | ||
| 1806 | // (e.g. at a 60FPS target, painting for 17us would fall to 58.82FPS instead of 30FPS). | ||
| 1807 | removePostedEvents(this, Present); | ||
| 1808 | postEvent(this, new QEvent(static_cast<QEvent::Type>(Present)), Qt::LowEventPriority); | ||
| 1809 | return true; | ||
| 1810 | } | ||
| 1811 | |||
| 1812 | idle(); | ||
| 1813 | return true; | ||
| 1814 | } else if ((int)event->type() == (int)Paint) { | ||
| 1815 | justPresented = true; | ||
| 1816 | paintGL(); | ||
| 1819 | return true; | 1817 | return true; |
| 1820 | } | 1818 | } |
| 1821 | 1819 | ||
| 1822 | if ((int)event->type() == (int)Paint) { | 1820 | if ((int)event->type() == (int)Lambda) { |
| 1823 | paintGL(); | 1821 | static_cast<LambdaEvent*>(event)->call(); |
| 1824 | return true; | 1822 | return true; |
| 1825 | } | 1823 | } |
| 1826 | 1824 | ||
| @@ -2598,72 +2596,63 @@ bool Application::acceptSnapshot(const QString& urlString) { · 771 unmodified lines | |||
| 2598 | 2596 | ||
| 2599 | static uint32_t _renderedFrameIndex { INVALID_FRAME }; | 2597 | static uint32_t _renderedFrameIndex { INVALID_FRAME }; |
| 2600 | 2598 | ||
| 2601 | void Application::idle(uint64_t now) { | 2599 | void Application::idle() { |
| 2602 | // NOTICE NOTICE NOTICE NOTICE | 2600 | // idle is called on a queued connection, so make sure we should be here. |
| 2603 | // Do not insert new code between here and the PROFILE_RANGE declaration | 2601 | if (_inPaint || _aboutToQuit) { |
| 2604 | // unless you know exactly what you're doing. This idle function can be | 2602 | return; |
| 2605 | // called thousands per second or more, so any additional work that's done | ||
| 2606 | // here will have a serious impact on CPU usage. Only add code after all | ||
| 2607 | // the thottling logic, i.e. after PROFILE_RANGE | ||
| 2608 | // NOTICE NOTICE NOTICE NOTICE | ||
| 2609 | updateHeartbeat(); | ||
| 2610 | |||
| 2611 | if (_aboutToQuit || _inPaint) { | ||
| 2612 | return; // bail early, nothing to do here. | ||
| 2613 | } | 2603 | } |
| 2614 | 2604 | ||
| 2615 | auto displayPlugin = getActiveDisplayPlugin(); | 2605 | auto displayPlugin = getActiveDisplayPlugin(); |
| 2616 | // depending on whether we're throttling or not. | ||
| 2617 | // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in | ||
| 2618 | // perpetuity and not expect events to get backed up. | ||
| 2619 | bool isThrottled = displayPlugin->isThrottled(); | ||
| 2620 | // Only run simulation code if more than the targetFramePeriod have passed since last time we ran | ||
| 2621 | // This attempts to lock the simulation at 60 updates per second, regardless of framerate | ||
| 2622 | float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC; | ||
| 2623 | float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND; | ||
| 2624 | 2606 | ||
| 2625 | if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) { | 2607 | #ifdef DEBUG_PAINT_DELAY |
| 2626 | // Throttling both rendering and idle | 2608 | static uint64_t paintDelaySamples{ 0 }; |
| 2627 | return; // bail early, we're throttled and not enough time has elapsed | 2609 | static uint64_t paintDelayUsecs{ 0 }; |
| 2628 | } | 2610 | |
| 2611 | paintDelayUsecs += displayPlugin->getPaintDelayUsecs(); | ||
| 2629 | 2612 | ||
| 2630 | auto presentCount = displayPlugin->presentCount(); | 2613 | static const int PAINT_DELAY_THROTTLE = 1000; |
| 2631 | if (presentCount < _renderedFrameIndex) { | 2614 | if (++paintDelaySamples % PAINT_DELAY_THROTTLE == 0) { |
| 2632 | _renderedFrameIndex = INVALID_FRAME; | 2615 | qCDebug(interfaceapp).nospace() << |
| 2616 | "Paint delay (" << paintDelaySamples << " samples): " << | ||
| 2617 | (float)paintDelaySamples / paintDelayUsecs << "us"; | ||
| 2633 | } | 2618 | } |
| 2619 | #endif | ||
| 2634 | 2620 | ||
| 2635 | // Don't saturate the main thread with rendering and simulation, | 2621 | float msecondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC / USECS_PER_MSEC; |
| 2636 | // unless display plugin has increased by at least one frame | ||
| 2637 | if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) { | ||
| 2638 | // Record what present frame we're on | ||
| 2639 | _renderedFrameIndex = presentCount; | ||
| 2640 | 2622 | ||
| 2641 | // request a paint, get to it as soon as possible: high priority | 2623 | // Throttle if requested |
| 2642 | postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority); | 2624 | if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) { |
| 2643 | } else { | ||
| 2644 | // there's no use in simulating or rendering faster then the present rate. | ||
| 2645 | return; | 2625 | return; |
| 2646 | } | 2626 | } |
| 2647 | 2627 | ||
| 2648 | // NOTICE NOTICE NOTICE NOTICE | 2628 | // Sync up the _renderedFrameIndex |
| 2649 | // do NOT add new code above this line unless you want it to be executed potentially | 2629 | _renderedFrameIndex = displayPlugin->presentCount(); |
| 2650 | // thousands of times per second | ||
| 2651 | // NOTICE NOTICE NOTICE NOTICE | ||
| 2652 | 2630 | ||
| 2653 | PROFILE_RANGE(__FUNCTION__); | 2631 | // Request a paint ASAP |
| 2632 | postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority + 1); | ||
| 2633 | |||
| 2634 | // Update the deadlock watchdog | ||
| 2635 | updateHeartbeat(); | ||
| 2636 | |||
| 2637 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | ||
| 2654 | 2638 | ||
| 2655 | // These tasks need to be done on our first idle, because we don't want the showing of | 2639 | // These tasks need to be done on our first idle, because we don't want the showing of |
| 2656 | // overlay subwindows to do a showDesktop() until after the first time through | 2640 | // overlay subwindows to do a showDesktop() until after the first time through |
| 2657 | static bool firstIdle = true; | 2641 | static bool firstIdle = true; |
| 2658 | if (firstIdle) { | 2642 | if (firstIdle) { |
| 2659 | firstIdle = false; | 2643 | firstIdle = false; |
| 2660 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | ||
| 2661 | connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); | 2644 | connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); |
| 2662 | _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays)); | 2645 | _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays)); |
| 2646 | } else { | ||
| 2647 | // FIXME: AvatarInputs are positioned incorrectly if instantiated before the first paint | ||
| 2648 | AvatarInputs::getInstance()->update(); | ||
| 2663 | } | 2649 | } |
| 2664 | 2650 | ||
| 2651 | PROFILE_RANGE(__FUNCTION__); | ||
| 2652 | |||
| 2653 | float secondsSinceLastUpdate = msecondsSinceLastUpdate / MSECS_PER_SECOND; | ||
| 2654 | |||
| 2665 | // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. | 2655 | // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. |
| 2666 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | ||
| 2667 | if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { | 2656 | if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { |
| 2668 | _keyboardMouseDevice->pluginFocusOutEvent(); | 2657 | _keyboardMouseDevice->pluginFocusOutEvent(); |
| 2669 | _keyboardDeviceHasFocus = false; | 2658 | _keyboardDeviceHasFocus = false; |
| @@ -2679,7 +2668,6 @@ void Application::idle(uint64_t now) { · 9 unmodified lines | |||
| 2679 | checkChangeCursor(); | 2668 | checkChangeCursor(); |
| 2680 | 2669 | ||
| 2681 | Stats::getInstance()->updateStats(); | 2670 | Stats::getInstance()->updateStats(); |
| 2682 | AvatarInputs::getInstance()->update(); | ||
| 2683 | 2671 | ||
| 2684 | _simCounter.increment(); | 2672 | _simCounter.increment(); |
| 2685 | 2673 | ||