From fee13d4375c53688f3b3beee2f09b641244e86d4 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 17 Nov 2015 20:06:25 -0800 Subject: [PATCH] Rebuild on a new branch after original stopped building. --- interface/resources/qml/Stats.qml | 2 +- interface/src/Application.cpp | 19 +++++++++++- interface/src/Application.h | 6 ++++ interface/src/avatar/Avatar.cpp | 34 ++++++++++++++++++-- interface/src/avatar/Avatar.h | 3 ++ interface/src/avatar/AvatarManager.cpp | 43 ++++++++++++++++++++++++++ interface/src/avatar/AvatarManager.h | 19 ++++++++++++ interface/src/ui/Stats.cpp | 2 ++ interface/src/ui/Stats.h | 4 +++ 9 files changed, 127 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index c98d4741b0..336b8ff577 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -40,7 +40,7 @@ Item { Text { color: root.fontColor; font.pixelSize: root.fontSize - text: "Avatars: " + root.avatarCount + text: "Avatars: " + root.avatarCount + ", " + root.avatarRenderedCount + " w/in " + root.avatarRenderDistance + "m" } Text { color: root.fontColor; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96b8ab74a8..f33cd763ca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1061,8 +1061,10 @@ void Application::paintGL() { uint64_t now = usecTimestampNow(); static uint64_t lastPaintBegin{ now }; uint64_t diff = now - lastPaintBegin; + float instantaneousFps = 0.0f; if (diff != 0) { - _framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff); + instantaneousFps = (float)USECS_PER_SECOND / (float)diff; + _framesPerSecond.updateAverage(_lastInstantaneousFps); } lastPaintBegin = now; @@ -1326,6 +1328,7 @@ void Application::paintGL() { // Ensure all operations from the previous context are complete before we try to read the fbo glWaitSync(sync, 0, GL_TIMEOUT_IGNORED); glDeleteSync(sync); + uint64_t displayStart = usecTimestampNow(); { PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); @@ -1338,6 +1341,20 @@ void Application::paintGL() { PerformanceTimer perfTimer("bufferSwap"); displayPlugin->finishFrame(); } + uint64_t displayEnd = usecTimestampNow(); + // Store together, without Application::idle happening in between setting fps and period. + _lastInstantaneousFps = instantaneousFps; + const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs + _lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND; + const float targetPeriod = isHMDMode() ? 1.0f/75.0f : 1.0f/60.0f; + const float actualPeriod = 1.0f / instantaneousFps; + const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); + const float accuracyAllowance = 0.0005; + const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); // sometimes paint goes over and it isn't reflected in actualPeriod + const float modularPeriod = (nSyncsByFrameRate + nSyncsByPaintWait) * targetPeriod; + const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; + _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; + } { diff --git a/interface/src/Application.h b/interface/src/Application.h index 39e3879707..01d0c72645 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -159,6 +159,9 @@ public: bool isForeground() const { return _isForeground; } float getFps() const { return _fps; } + float getLastInstanteousFps() const { return _lastInstantaneousFps; } + float getLastPaintWait() const { return _lastPaintWait; }; + float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -430,6 +433,9 @@ private: float _fps; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; + float _lastInstantaneousFps { 0.0f }; + float _lastPaintWait { 0.0f }; + float _lastDeducedNonVSyncFps { 0.0f }; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b979334383..14824ad118 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -97,6 +97,7 @@ Avatar::Avatar(RigPointer rig) : _moving(false), _initialized(false), _shouldRenderBillboard(true), + _shouldSkipRender(true), _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread @@ -114,7 +115,7 @@ Avatar::~Avatar() { } } -const float BILLBOARD_LOD_DISTANCE = 40.0f; +/*const fixme*/ float BILLBOARD_LOD_DISTANCE = 40.0f; void Avatar::init() { getHead()->init(); @@ -181,12 +182,36 @@ void Avatar::simulate(float deltaTime) { // update the billboard render flag const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f; + BILLBOARD_LOD_DISTANCE = DependencyManager::get()->getFIXMEupdate(); if (_shouldRenderBillboard) { if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) { _shouldRenderBillboard = false; + qCDebug(interfaceapp) << "Unbillboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); } } else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) { _shouldRenderBillboard = true; + qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); + } +#define PID_TUNING 1 +#ifdef PID_TUNING + const float SKIP_HYSTERESIS_PROPORTION = 0.0f; +#else + const float SKIP_HYSTERESIS_PROPORTION = BILLBOARD_HYSTERESIS_PROPORTION; +#endif + float renderDistance = DependencyManager::get()->getRenderDistance(); + float distance = glm::distance(qApp->getCamera()->getPosition(), _position); + if (_shouldSkipRender) { + if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) { + _shouldSkipRender = false; +#ifndef PID_TUNING + qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); +#endif + } + } else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) { + _shouldSkipRender = true; +#ifndef PID_TUNING + qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); +#endif } // simple frustum check @@ -199,7 +224,7 @@ void Avatar::simulate(float deltaTime) { getHand()->simulate(deltaTime, false); } - if (!_shouldRenderBillboard && inViewFrustum) { + if (!_shouldRenderBillboard && !_shouldSkipRender && inViewFrustum) { { PerformanceTimer perfTimer("skeleton"); for (int i = 0; i < _jointData.size(); i++) { @@ -585,10 +610,13 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::fixupModelsInScene() { - // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene render::ScenePointer scene = qApp->getMain3DScene(); + _skeletonModel.setVisibleInScene(!_shouldSkipRender, scene); + if (_shouldSkipRender) { + return; + } render::PendingChanges pendingChanges; if (_skeletonModel.isRenderable() && _skeletonModel.needsFixupInScene()) { _skeletonModel.removeFromScene(scene, pendingChanges); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 44b5d91015..cb238dc82c 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -140,6 +140,8 @@ public: Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; } Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; } + Q_INVOKABLE bool getShouldSkipRendering() const { return _shouldSkipRender; } + /// Scales a world space position vector relative to the avatar position and scale /// \param vector position to be scaled. Will store the result void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; @@ -226,6 +228,7 @@ private: bool _initialized; NetworkTexturePointer _billboardTexture; bool _shouldRenderBillboard; + bool _shouldSkipRender { false }; bool _isLookAtTarget; void renderBillboard(RenderArgs* renderArgs); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4b9bfd21a3..b2a2e27999 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -61,12 +61,34 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) { qScriptRegisterSequenceMetaType >(engine); } +#define TARGET_FPS 60.0f +#define TARGET_PERIOD_MS (1000.0f / TARGET_FPS) AvatarManager::AvatarManager(QObject* parent) : _avatarFades() { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); _myAvatar = std::make_shared(std::make_shared()); + _renderDistanceController.setMeasuredValueSetpoint(TARGET_FPS); //FIXME + const float TREE_SCALE = 32768.0f; // Not in shared library, alas. + const float SMALLEST_REASONABLE_HORIZON = 0.5f; // FIXME 5 + _renderDistanceController.setControlledValueHighLimit(1.0f/SMALLEST_REASONABLE_HORIZON); + _renderDistanceController.setControlledValueLowLimit(1.0f/TREE_SCALE); + + // Advice for tuning parameters: + // See PIDController.h. There's a sectionon tuning in the reference. + // Turn off HYSTERESIS_PROPORTION and extra logging by defining PID_TUNING in Avatar.cpp. + // Turn on logging with the following: + _renderDistanceController.setHistorySize("avatar render", TARGET_FPS * 4); // FIXME + // KP is usually tuned by setting the other constants to zero, finding the maximum value that doesn't oscillate, + // and taking about 0.6 of that. A typical osciallation would be with error=37fps with avatars 10m away, so + // KP*37=1/10 => KP(oscillating)=0.1/37 = 0.0027 + _renderDistanceController.setKP(0.0012f); + // alt: + // Our anti-windup limits accumulated error to 10*targetFrameRate, so the sanity check on KI is + // KI*750=controlledValueHighLimit=1 => KI=1/750. + _renderDistanceController.setKI(0.001); + _renderDistanceController.setKD(0.0001); // a touch of kd increases the speed by which we get there auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -117,9 +139,26 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); + const float FEED_FORWARD_RANGE = 2; + const float fps = qApp->getLastInstanteousFps(); + const float paintWait = qApp->getLastPaintWait(); + //const float modularizedPeriod = floor((1000.0f / std::min(fps, TARGET_FPS)) / TARGET_PERIOD_MS) * TARGET_PERIOD_MS; + // measured value: 1) bigger => more desirable plant activity (i.e., more rendering), 2) setpoint=TARGET_PERIOD_MS=13.333 + // single vsync: no load=>1or2. high load=>12or13 + // over vsync: just over: 13. way over: 14...15...16 + //const float effective = ((1000.0f / fps) < TARGET_PERIOD_MS) ? (TARGET_PERIOD_MS - paintWait) : ((2.0f * TARGET_PERIOD_MS) - paintWait); + const float deduced = qApp->getLastDeducedNonVSyncFps(); + const bool isAtSetpoint = false; //FIXME fabsf(effectiveFps - _renderDistanceController.getMeasuredValueSetpoint()) < FEED_FORWARD_RANGE; + const float distance = 1.0f / _renderDistanceController.update(deduced + (isAtSetpoint ? _renderFeedForward : 0.0f), deltaTime, isAtSetpoint, fps, paintWait); + + const float RENDER_DISTANCE_DEADBAND = 0.0f; //FIXME 0.3f; // meters + if (fabsf(distance - _renderDistance) > RENDER_DISTANCE_DEADBAND) { + _renderDistance = distance; + } // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); + int renderableCount = 0; while (avatarIterator != _avatarHash.end()) { auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); @@ -135,10 +174,14 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } else { avatar->startUpdate(); avatar->simulate(deltaTime); + if (!avatar->getShouldSkipRendering()) { + renderableCount++; + } avatar->endUpdate(); ++avatarIterator; } } + _renderedAvatarCount = renderableCount; // simulate avatar fades simulateAvatarFades(deltaTime); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index fa0593368b..92f9958962 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -18,6 +18,7 @@ #include #include +#include #include "Avatar.h" #include "AvatarMotionState.h" @@ -43,6 +44,7 @@ public: void clearOtherAvatars(); bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; } + PIDController& getRenderDistanceController() { return _renderDistanceController; } class LocalLight { public: @@ -64,6 +66,17 @@ public: void handleCollisionEvents(const CollisionEvents& collisionEvents); void updateAvatarPhysicsShape(const QUuid& id); + Q_INVOKABLE int getNumberRendered() { return _renderedAvatarCount; } + Q_INVOKABLE float getRenderDistance() { return _renderDistance; } + Q_INVOKABLE float getFIXMEupdate() { return _fixmeUpdate; } + Q_INVOKABLE void setFIXMEupdate(float f) { _fixmeUpdate = f; } + Q_INVOKABLE void setFIXMEkp(float f) { _renderDistanceController.setKP(f); } + Q_INVOKABLE void setFIXMEki(float f) { _renderDistanceController.setKI(f); } + Q_INVOKABLE void setFIXMEkd(float f) { _renderDistanceController.setKD(f); } + Q_INVOKABLE void setFIXMEbias(float f) { _renderDistanceController.setBias(f); } + Q_INVOKABLE void setFIXMElow(float f) { _renderDistanceController.setControlledValueLowLimit(f); } + Q_INVOKABLE void setFIXMEhigh(float f) { _renderDistanceController.setControlledValueHighLimit(f); } + Q_INVOKABLE void setFIXMEfeedForward(float f) { _renderFeedForward = f; } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -88,6 +101,12 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; + float _fixmeUpdate = 40.0f; + float _renderDistance { 40.0f }; + float _renderFeedForward { 5.0f }; + int _renderedAvatarCount {0}; + PIDController _renderDistanceController {}; + SetOfAvatarMotionStates _avatarMotionStates; SetOfMotionStates _motionStatesToAdd; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1c0c03e16c..3f27052f60 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -115,6 +115,8 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(avatarRenderedCount, avatarManager->getNumberRendered()); + STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(framerate, (int)qApp->getFps()); STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 39e3c6b24a..12caf19d18 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -36,6 +36,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, avatarRenderedCount, 0) + STATS_PROPERTY(int, avatarRenderDistance, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) STATS_PROPERTY(float, mbpsIn, 0) @@ -117,6 +119,8 @@ signals: void simrateChanged(); void avatarSimrateChanged(); void avatarCountChanged(); + void avatarRenderedCountChanged(); + void avatarRenderDistanceChanged(); void packetInCountChanged(); void packetOutCountChanged(); void mbpsInChanged();