History

Merge branch 'master' of github.com:highfidelity/hifi into avatar-entities-3

Seth Alves committed 10 years ago

Commit 3380b2a2 parents 880b52a 8e3b31a24 files · +472 −395Browse files

Showing the changes to interface/src/Application.cpp (1 of 24 files). Show all

Unified · Split

interface/src/Application.cpp+67−79
@@ -349,19 +349,14 @@ public: · 348 unmodified lines
349};349};
350#endif350#endif
351 351
352enum CustomEventTypes {
353 Lambda = QEvent::User + 1,
354 Paint = Lambda + 1,
355};
356
357class LambdaEvent : public QEvent {352class LambdaEvent : public QEvent {
358 std::function<void()> _fun;353 std::function<void()> _fun;
359public:354public:
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 scripts1145 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 scripts1151 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 invokeMethod1157 // first stop all timers directly or by invokeMethod
1169 // depending on what thread they run in1158 // 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
1444void Application::paintGL() {1432void Application::paintGL() {
1445 updateHeartbeat();1433 // Some plugins process message events, allowing paintGL to be called reentrantly.
1446 // Some plugins process message events, potentially leading to1434 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
2599static uint32_t _renderedFrameIndex { INVALID_FRAME };2597static uint32_t _renderedFrameIndex { INVALID_FRAME };
2600 2598
2601void Application::idle(uint64_t now) {2599void Application::idle() {
2602 // NOTICE NOTICE NOTICE NOTICE2600 // 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 idle2608 static uint64_t paintDelaySamples{ 0 };
2627 return; // bail early, we're throttled and not enough time has elapsed2609 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 priority2623 // 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 NOTICE2628 // 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 of2639 // 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 through2640 // 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