Showing the changes to interface/src/Application.cpp (1 of 65 files). Show all
Unified · Split
| @@ -210,6 +210,10 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; · 209 unmodified lines | ||
| 210 | 210 | |
| 211 | 211 | static const unsigned int THROTTLED_SIM_FRAMERATE = 15; |
| 212 | 212 | static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; |
| 213 | +static const unsigned int CAPPED_SIM_FRAMERATE = 60; | |
| 214 | +static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE; | |
| 215 | + | |
| 216 | +static const uint32_t INVALID_FRAME = UINT32_MAX; | |
| 213 | 217 | |
| 214 | 218 | static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation |
| 215 | 219 | |
| @@ -273,7 +277,8 @@ public: · 57 unmodified lines | ||
| 273 | 277 | #endif |
| 274 | 278 | |
| 275 | 279 | enum CustomEventTypes { |
| 276 | − Lambda = QEvent::User + 1 | |
| 280 | + Lambda = QEvent::User + 1, | |
| 281 | + Paint = Lambda + 1, | |
| 277 | 282 | }; |
| 278 | 283 | |
| 279 | 284 | class LambdaEvent : public QEvent { |
| @@ -984,6 +989,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 704 unmodified lines | ||
| 984 | 989 | |
| 985 | 990 | connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); |
| 986 | 991 | qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); |
| 992 | + _idleTimer = new QTimer(this); | |
| 993 | + connect(_idleTimer, &QTimer::timeout, [=] { | |
| 994 | + idle(usecTimestampNow()); | |
| 995 | + }); | |
| 996 | + connect(this, &Application::beforeAboutToQuit, [=] { | |
| 997 | + disconnect(_idleTimer); | |
| 998 | + }); | |
| 999 | + // Setting the interval to zero forces this to get called whenever there are no messages | |
| 1000 | + // in the queue, which can be pretty damn frequent. Hence the idle function has a bunch | |
| 1001 | + // of logic to abort early if it's being called too often. | |
| 1002 | + _idleTimer->start(0); | |
| 987 | 1003 | } |
| 988 | 1004 | |
| 989 | 1005 | void Application::aboutToQuit() { |
| @@ -1297,9 +1313,6 @@ void Application::initializeUi() { · 307 unmodified lines | ||
| 1297 | 1313 | } |
| 1298 | 1314 | |
| 1299 | 1315 | void Application::paintGL() { |
| 1300 | − | |
| 1301 | − | |
| 1302 | − | |
| 1303 | 1316 | // paintGL uses a queued connection, so we can get messages from the queue even after we've quit |
| 1304 | 1317 | // and the plugins have shutdown |
| 1305 | 1318 | if (_aboutToQuit) { |
| @@ -1629,6 +1642,7 @@ void Application::paintGL() { · 323 unmodified lines | ||
| 1629 | 1642 | // Store both values now for use by next cycle. |
| 1630 | 1643 | _lastInstantaneousFps = instantaneousFps; |
| 1631 | 1644 | _lastUnsynchronizedFps = 1.0f / (((usecTimestampNow() - now) / (float)USECS_PER_SECOND) + paintWaitAndQTTimerAllowance); |
| 1645 | + _pendingPaint = false; | |
| 1632 | 1646 | } |
| 1633 | 1647 | |
| 1634 | 1648 | void Application::runTests() { |
| @@ -1717,6 +1731,10 @@ bool Application::event(QEvent* event) { · 82 unmodified lines | ||
| 1717 | 1731 | return true; |
| 1718 | 1732 | } |
| 1719 | 1733 | |
| 1734 | + if ((int)event->type() == (int)Paint) { | |
| 1735 | + paintGL(); | |
| 1736 | + } | |
| 1737 | + | |
| 1720 | 1738 | if (!_keyboardFocusedItem.isInvalidID()) { |
| 1721 | 1739 | switch (event->type()) { |
| 1722 | 1740 | case QEvent::KeyPress: |
| @@ -2401,26 +2419,62 @@ bool Application::acceptSnapshot(const QString& urlString) { · 678 unmodified lines | ||
| 2401 | 2419 | return true; |
| 2402 | 2420 | } |
| 2403 | 2421 | |
| 2422 | +static uint32_t _renderedFrameIndex { INVALID_FRAME }; | |
| 2423 | + | |
| 2404 | 2424 | void Application::idle(uint64_t now) { |
| 2405 | 2425 | if (_aboutToQuit) { |
| 2406 | 2426 | return; // bail early, nothing to do here. |
| 2407 | 2427 | } |
| 2408 | − | |
| 2428 | + | |
| 2429 | + auto displayPlugin = getActiveDisplayPlugin(); | |
| 2409 | 2430 | // depending on whether we're throttling or not. |
| 2410 | 2431 | // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in |
| 2411 | 2432 | // perpetuity and not expect events to get backed up. |
| 2412 | − bool isThrottled = getActiveDisplayPlugin()->isThrottled(); | |
| 2433 | + bool isThrottled = displayPlugin->isThrottled(); | |
| 2413 | 2434 | // Only run simulation code if more than the targetFramePeriod have passed since last time we ran |
| 2414 | 2435 | // This attempts to lock the simulation at 60 updates per second, regardless of framerate |
| 2415 | 2436 | float timeSinceLastUpdateUs = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC; |
| 2416 | 2437 | float secondsSinceLastUpdate = timeSinceLastUpdateUs / USECS_PER_SECOND; |
| 2417 | 2438 | |
| 2418 | 2439 | if (isThrottled && (timeSinceLastUpdateUs / USECS_PER_MSEC) < THROTTLED_SIM_FRAME_PERIOD_MS) { |
| 2440 | + // Throttling both rendering and idle | |
| 2419 | 2441 | return; // bail early, we're throttled and not enough time has elapsed |
| 2420 | 2442 | } |
| 2421 | 2443 | |
| 2422 | − _lastTimeUpdated.start(); | |
| 2444 | + auto presentCount = displayPlugin->presentCount(); | |
| 2445 | + if (presentCount < _renderedFrameIndex) { | |
| 2446 | + _renderedFrameIndex = INVALID_FRAME; | |
| 2447 | + } | |
| 2448 | + | |
| 2449 | + // Nested ifs are for clarity in the logic. Don't collapse them into a giant single if. | |
| 2450 | + // Don't saturate the main thread with rendering, no paint calls until the last one is complete | |
| 2451 | + if (!_pendingPaint) { | |
| 2452 | + // Also no paint calls until the display plugin has increased by at least one frame | |
| 2453 | + // (don't render at 90fps if the display plugin only goes at 60) | |
| 2454 | + if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) { | |
| 2455 | + // Record what present frame we're on | |
| 2456 | + _renderedFrameIndex = presentCount; | |
| 2457 | + // Don't allow paint requests to stack up in the event queue | |
| 2458 | + _pendingPaint = true; | |
| 2459 | + // But when we DO request a paint, get to it as soon as possible: high priority | |
| 2460 | + postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority); | |
| 2461 | + } | |
| 2462 | + } | |
| 2463 | + | |
| 2464 | + // For the rest of idle, we want to cap at the max sim rate, so we might not call | |
| 2465 | + // the remaining idle work every paint frame, or vice versa | |
| 2466 | + // In theory this means we could call idle processing more often than painting, | |
| 2467 | + // but in practice, when the paintGL calls aren't keeping up, there's no room left | |
| 2468 | + // in the main thread to call idle more often than paint. | |
| 2469 | + // This check is mostly to keep idle from burning up CPU cycles by running at | |
| 2470 | + // hundreds of idles per second when the rendering is that fast | |
| 2471 | + if ((timeSinceLastUpdateUs / USECS_PER_MSEC) < CAPPED_SIM_FRAME_PERIOD_MS) { | |
| 2472 | + // No paint this round, but might be time for a new idle, otherwise return | |
| 2473 | + return; | |
| 2474 | + } | |
| 2423 | 2475 | |
| 2476 | + // We're going to execute idle processing, so restart the last idle timer | |
| 2477 | + _lastTimeUpdated.start(); | |
| 2424 | 2478 | |
| 2425 | 2479 | { |
| 2426 | 2480 | PROFILE_RANGE(__FUNCTION__); |
| @@ -3071,13 +3125,18 @@ void Application::update(float deltaTime) { · 644 unmodified lines | ||
| 3071 | 3125 | |
| 3072 | 3126 | auto myAvatar = getMyAvatar(); |
| 3073 | 3127 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 3074 | − userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); | |
| 3075 | 3128 | userInputMapper->update(deltaTime); |
| 3076 | 3129 | |
| 3130 | + controller::InputCalibrationData calibrationData = { | |
| 3131 | + myAvatar->getSensorToWorldMatrix(), | |
| 3132 | + createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()), | |
| 3133 | + myAvatar->getHMDSensorMatrix() | |
| 3134 | + }; | |
| 3135 | + | |
| 3077 | 3136 | bool jointsCaptured = false; |
| 3078 | 3137 | for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { |
| 3079 | 3138 | if (inputPlugin->isActive()) { |
| 3080 | − inputPlugin->pluginUpdate(deltaTime, jointsCaptured); | |
| 3139 | + inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured); | |
| 3081 | 3140 | if (inputPlugin->isJointController()) { |
| 3082 | 3141 | jointsCaptured = true; |
| 3083 | 3142 | } |
| @@ -4777,17 +4836,9 @@ void Application::updateDisplayMode() { · 1693 unmodified lines | ||
| 4777 | 4836 | |
| 4778 | 4837 | foreach(auto displayPlugin, standard) { |
| 4779 | 4838 | addDisplayPluginToMenu(displayPlugin, first); |
| 4780 | − // This must be a queued connection to avoid a deadlock | |
| 4781 | − QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, [=] { | |
| 4782 | − postEvent(this, new LambdaEvent([=] { | |
| 4783 | − paintGL(); | |
| 4784 | − }), Qt::HighEventPriority); | |
| 4785 | − }); | |
| 4786 | − | |
| 4787 | 4839 | QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { |
| 4788 | 4840 | resizeGL(); |
| 4789 | 4841 | }); |
| 4790 | − | |
| 4791 | 4842 | first = false; |
| 4792 | 4843 | } |
| 4793 | 4844 | |
| @@ -4979,17 +5030,11 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de · 185 unmodified lines | ||
| 4979 | 5030 | // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about |
| 4980 | 5031 | // controller::Pose. More work is needed to clean this up. |
| 4981 | 5032 | hand->modifyPalm(whichHand, [&](PalmData& palm) { |
| 4982 | − auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | |
| 4983 | 5033 | palm.setActive(pose.isValid()); |
| 4984 | 5034 | |
| 4985 | − // transform from sensor space, to world space, to avatar model space. | |
| 4986 | − glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation() * myAvatar->getScale()); | |
| 4987 | − glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix(); | |
| 4988 | − glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); | |
| 4989 | − glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; | |
| 4990 | − | |
| 4991 | − glm::vec3 position = extractTranslation(objectPose); | |
| 4992 | − glm::quat rotation = glm::quat_cast(objectPose); | |
| 5035 | + // controller pose is in Avatar frame. | |
| 5036 | + glm::vec3 position = pose.getTranslation(); | |
| 5037 | + glm::quat rotation = pose.getRotation(); | |
| 4993 | 5038 | |
| 4994 | 5039 | // Compute current velocity from position change |
| 4995 | 5040 | glm::vec3 rawVelocity; |