From fee13d4375c53688f3b3beee2f09b641244e86d4 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 17 Nov 2015 20:06:25 -0800 Subject: [PATCH 01/15] 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(); From 97898a1ad1a4ce6972c52fe5dbf1736a5b34acc6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 17 Nov 2015 20:43:40 -0800 Subject: [PATCH 02/15] forgot new files --- libraries/shared/src/PIDController.cpp | 71 ++++++++++++++++++++ libraries/shared/src/PIDController.h | 91 ++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 libraries/shared/src/PIDController.cpp create mode 100644 libraries/shared/src/PIDController.h diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp new file mode 100644 index 0000000000..42447e9809 --- /dev/null +++ b/libraries/shared/src/PIDController.cpp @@ -0,0 +1,71 @@ +// +// PIDController.cpp +// libraries/shared/src +// +// Created by Howard Stearns 11/13/15. +// Copyright 2015 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 +#include +#include "SharedLogging.h" +#include "PIDController.h" + +float PIDController::update(float measuredValue, float dt, bool resetAccumulator, float FIXME1, float FIXME2) { + const float error = getMeasuredValueSetpoint() - measuredValue; // Sign is the direction we want measuredValue to go. Positive means go higher. + + const float p = getKP() * error; // term is Proportional to error + + const float accumulatedError = glm::clamp(error * dt + (resetAccumulator ? 0 : _lastAccumulation), // integrate error + getAccumulatedValueLowLimit(), // but clamp by anti-windup limits + getAccumulatedValueHighLimit()); + const float i = getKI() * accumulatedError; // term is Integral of error + + const float changeInError = (error - _lastError) / dt; // positive value denotes increasing deficit + const float d = getKD() * changeInError; // term is Derivative of Error + + const float computedValue = glm::clamp(p + i + d + getBias(), + getControlledValueLowLimit(), + getControlledValueHighLimit()); + + if (_history.capacity()) { // THIS SECTION ONLY FOR LOGGING + // Don't report each update(), as the I/O messes with the results a lot. + // Instead, add to history, and then dump out at once when full. + // Typically, the first few values reported in each batch should be ignored. + const int n = _history.size(); + _history.resize(n + 1); + Row& next = _history[n]; + next.measured = measuredValue; + next.FIXME1 = FIXME1; + next.FIXME2 = FIXME2; + next.dt = dt; + next.error = error; + next.accumulated = accumulatedError; + next.changed = changeInError; + next.p = p; + next.i = i; + next.d = d; + next.computed = computedValue; + if (_history.size() == _history.capacity()) { // report when buffer is full + qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; + for (int i = 0; i < _history.size(); i++) { + Row& row = _history[i]; + qCDebug(shared) << row.measured << row.dt << row.FIXME1 << row.FIXME2 << + "||" << row.error << row.accumulated << row.changed << + "||" << row.p << row.i << row.d << row.computed; + } + qCDebug(shared) << "Limits: accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << + "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << + "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); + _history.resize(0); + } + } + + // update state for next time + _lastError = error; + _lastAccumulation = accumulatedError; + return computedValue; +} \ No newline at end of file diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h new file mode 100644 index 0000000000..dd0c4a8528 --- /dev/null +++ b/libraries/shared/src/PIDController.h @@ -0,0 +1,91 @@ +// +// PIDController.h +// libraries/shared/src +// +// Given a measure of system performance (such as frame rate, where bigger denotes more system work), +// compute a value that the system can take as input to control the amount of work done (such as an 1/LOD-distance, +// where bigger tends to give a higher measured system performance value). The controller's job is to compute a +// controlled value such that the measured value stays near the specified setpoint, even as system load changes. +// See http://www.wetmachine.com/inventing-the-future/mostly-reliable-performance-of-software-processes-by-dynamic-control-of-quality-parameters/ +// +// Created by Howard Stearns 11/13/15. +// Copyright 2015 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 +// + +#ifndef hifi_PIDController_h +#define hifi_PIDController_h + +#include +#include + +// Although our coding standard shuns abbreviations, the control systems literature pretty uniformly uses p, i, d, and dt rather than +// proportionalTerm, integralTerm, derivativeTerm, and deltaTime. Here we will be consistent with the literature. +class PIDController { + +public: + // These three are the main interface: + void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } + float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue + void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging + + // There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other + // special-but-expected circumstances. Thus the instance vars are not const. + float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; } + // In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max. + // Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1] + float getControlledValueLowLimit() const { return _controlledValueLowLimit; } + float getControlledValueHighLimit() const { return _controlledValueHighLimit; } + float getAntiWindupFactor() const { return _antiWindupFactor; } // default 10 + float getKP() const { return _kp; } + float getKI() const { return _ki; } + float getKD() const { return _kd; } + float getBias() const { return _bias; } + float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); } + float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); } + + void setControlledValueLowLimit(float newValue) { _controlledValueLowLimit = newValue; } + void setControlledValueHighLimit(float newValue) { _controlledValueHighLimit = newValue; } + void setAntiWindupFactor(float newValue) { _antiWindupFactor = newValue; } + void setKP(float newValue) { _kp = newValue; } + void setKI(float newValue) { _ki = newValue; } + void setKD(float newValue) { _kd = newValue; } + void setBias(float newValue) { _bias = newValue; } + + class Row { // one row of accumulated history, used only for logging (if at all) + public: + float FIXME1; + float FIXME2; + float measured; + float dt; + float error; + float accumulated; + float changed; + float p; + float i; + float d; + float bias; + float computed; + }; +protected: + float _measuredValueSetpoint { 0.0f }; + float _controlledValueLowLimit { 0.0f }; + float _controlledValueHighLimit { std::numeric_limits::max() }; + float _antiWindupFactor { 10.0f }; + float _kp { 0.0f }; + float _ki { 0.0f }; + float _kd { 0.0f }; + float _bias { 0.0f }; + + // Controller state + float _lastError{ 0.0f }; + float _lastAccumulation{ 0.0f }; + // reporting + QVector _history{}; + QString _label{ "" }; + +}; + +#endif // hifi_PIDController_h From 42a1ee353e2acddf174ba057c65a9aa299e9646b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 18 Nov 2015 14:09:33 -0800 Subject: [PATCH 03/15] checkpoint --- interface/src/Application.cpp | 25 +++++---- interface/src/avatar/AvatarManager.cpp | 53 +++++++++---------- libraries/shared/src/PIDController.cpp | 72 ++++++++++++++------------ libraries/shared/src/PIDController.h | 2 + 4 files changed, 82 insertions(+), 70 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f33cd763ca..2eaf898e72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1095,6 +1095,21 @@ void Application::paintGL() { _inPaint = true; Finally clearFlagLambda([this] { _inPaint = false; }); + _lastInstantaneousFps = instantaneousFps; + // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. + // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. + // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. + const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize + const float targetPeriod = isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; + const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); + const float accuracyAllowance = 0.0005f; // sometimes paint goes over and it isn't reflected in actualPeriod + const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); + const float nSyncs = nSyncsByFrameRate + nSyncsByPaintWait; + const float modularPeriod = ((nSyncs - 1) * targetPeriod) + actualPeriod; + const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; + _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; + + auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); _offscreenContext->makeCurrent(); @@ -1342,18 +1357,8 @@ void Application::paintGL() { 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/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b2a2e27999..18e4dd40be 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -61,34 +61,12 @@ 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"); @@ -112,6 +90,29 @@ void AvatarManager::init() { _myAvatar->addToScene(_myAvatar, scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); + + const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; + _renderDistanceController.setMeasuredValueSetpoint(target_fps); + 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.0015f); + // 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.001f); + _renderDistanceController.setKD(0.0001f); // a touch of kd increases the speed by which we get there + } void AvatarManager::updateMyAvatar(float deltaTime) { @@ -142,14 +143,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { 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 distance = 1.0f / _renderDistanceController.update(deduced + (isAtSetpoint ? _renderFeedForward : 0.0f), deltaTime, isAtSetpoint, fps, paintWait); + const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, isAtSetpoint, fps, paintWait); const float RENDER_DISTANCE_DEADBAND = 0.0f; //FIXME 0.3f; // meters if (fabsf(distance - _renderDistance) > RENDER_DISTANCE_DEADBAND) { diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index 42447e9809..2756ab615f 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -31,41 +31,49 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator getControlledValueLowLimit(), getControlledValueHighLimit()); - if (_history.capacity()) { // THIS SECTION ONLY FOR LOGGING - // Don't report each update(), as the I/O messes with the results a lot. - // Instead, add to history, and then dump out at once when full. - // Typically, the first few values reported in each batch should be ignored. - const int n = _history.size(); - _history.resize(n + 1); - Row& next = _history[n]; - next.measured = measuredValue; - next.FIXME1 = FIXME1; - next.FIXME2 = FIXME2; - next.dt = dt; - next.error = error; - next.accumulated = accumulatedError; - next.changed = changeInError; - next.p = p; - next.i = i; - next.d = d; - next.computed = computedValue; - if (_history.size() == _history.capacity()) { // report when buffer is full - qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; - for (int i = 0; i < _history.size(); i++) { - Row& row = _history[i]; - qCDebug(shared) << row.measured << row.dt << row.FIXME1 << row.FIXME2 << - "||" << row.error << row.accumulated << row.changed << - "||" << row.p << row.i << row.d << row.computed; - } - qCDebug(shared) << "Limits: accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << - "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << - "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); - _history.resize(0); - } - } + if (_history.capacity()) { // if logging/reporting + updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); + } // update state for next time _lastError = error; _lastAccumulation = accumulatedError; return computedValue; +} + +// Just for logging/reporting. Used when picking/verifying the operational parameters. +void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue, float FIXME1, float FIXME2) { + // Don't report each update(), as the I/O messes with the results a lot. + // Instead, add to history, and then dump out at once when full. + // Typically, the first few values reported in each batch should be ignored. + const int n = _history.size(); + _history.resize(n + 1); + Row& next = _history[n]; + next.measured = measuredValue; + next.FIXME1 = FIXME1; + next.FIXME2 = FIXME2; + next.dt = dt; + next.error = error; + next.accumulated = accumulatedError; + next.changed = changeInError; + next.p = p; + next.i = i; + next.d = d; + next.computed = computedValue; + if (_history.size() == _history.capacity()) { // report when buffer is full + reportHistory(); + _history.resize(0); + } +} +void PIDController::reportHistory() { + qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; + for (int i = 0; i < _history.size(); i++) { + Row& row = _history[i]; + qCDebug(shared) << row.measured << (row.dt * 1000) << row.FIXME1 << (row.FIXME2 * 1000) << + "||" << row.error << row.accumulated << row.changed << + "||" << row.p << row.i << row.d << row.computed; + } + qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << + "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << + "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); } \ No newline at end of file diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index dd0c4a8528..a2adbf98ab 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -70,6 +70,8 @@ public: float computed; }; protected: + void reportHistory(); + void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue, float FIXME1, float FIXME2); float _measuredValueSetpoint { 0.0f }; float _controlledValueLowLimit { 0.0f }; float _controlledValueHighLimit { std::numeric_limits::max() }; From 7d30cd0159bb8692514cdfb235c0e5814451e9c0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 09:18:40 -0800 Subject: [PATCH 04/15] checkpoint: it works! --- interface/src/Application.cpp | 22 +++++++++++++--------- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 26 +++++++------------------- interface/src/avatar/AvatarManager.h | 3 +++ libraries/shared/src/PIDController.cpp | 2 +- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2eaf898e72..84b14497cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1095,20 +1095,24 @@ void Application::paintGL() { _inPaint = true; Finally clearFlagLambda([this] { _inPaint = false; }); - _lastInstantaneousFps = instantaneousFps; + // Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't + // include time waiting for vsync, and which can report a number above target if we've got the headroom. + // For example, if we're shooting for 75fps and paintWait is 3.3333ms (= 75% * 13.33ms), our deducedNonVSyncFps + // would be 100fps. In principle, a paintWait of zero would have deducedNonVSyncFps=75. + // Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod. + // // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. - // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize + // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. + float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait; // plus a some non-zero time for machinery we can't measure + // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion + // of paintWait up to the next vSync. const float targetPeriod = isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; - const float nSyncsByFrameRate = round(actualPeriod / targetPeriod); - const float accuracyAllowance = 0.0005f; // sometimes paint goes over and it isn't reflected in actualPeriod - const float nSyncsByPaintWait = floor((_lastPaintWait + accuracyAllowance) / targetPeriod); - const float nSyncs = nSyncsByFrameRate + nSyncsByPaintWait; - const float modularPeriod = ((nSyncs - 1) * targetPeriod) + actualPeriod; - const float deducedNonVSyncPeriod = modularPeriod - _lastPaintWait; + const float minumumMachinery = glm::max(0.0f, (floorf(_lastPaintWait / targetPeriod) * targetPeriod) - _lastPaintWait); + deducedNonVSyncPeriod += minumumMachinery; _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; - + _lastInstantaneousFps = instantaneousFps; auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 14824ad118..6a634bcc90 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -192,7 +192,7 @@ void Avatar::simulate(float deltaTime) { _shouldRenderBillboard = true; qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); } -#define PID_TUNING 1 +//#define PID_TUNING 1 #ifdef PID_TUNING const float SKIP_HYSTERESIS_PROPORTION = 0.0f; #else diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 18e4dd40be..132ddb9dfa 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -102,16 +102,10 @@ void AvatarManager::init() { // 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.0015f); - // 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.001f); - _renderDistanceController.setKD(0.0001f); // a touch of kd increases the speed by which we get there + //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // FIXME + _renderDistanceController.setKP(0.0003f); //Usually about 0.6 of largest that doesn't oscillate, with other constants 0. + _renderDistanceController.setKI(0.001f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKD(0.00001f); // a touch of kd increases the speed by which we get there } @@ -140,18 +134,12 @@ 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 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 distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, isAtSetpoint, fps, paintWait); - - const float RENDER_DISTANCE_DEADBAND = 0.0f; //FIXME 0.3f; // meters - if (fabsf(distance - _renderDistance) > RENDER_DISTANCE_DEADBAND) { - _renderDistance = distance; - } + const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); + _renderDistanceAverage.updateAverage(distance); + _renderDistance = _renderDistanceAverage.getAverage(); // simulate avatars AvatarHash::iterator avatarIterator = _avatarHash.begin(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 92f9958962..7a4dd2632e 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "Avatar.h" #include "AvatarMotionState.h" @@ -106,6 +107,8 @@ private: float _renderFeedForward { 5.0f }; int _renderedAvatarCount {0}; PIDController _renderDistanceController {}; + SimpleMovingAverage _renderDistanceAverage {10}; + SetOfAvatarMotionStates _avatarMotionStates; diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index 2756ab615f..d5697538d5 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -71,7 +71,7 @@ void PIDController::reportHistory() { Row& row = _history[i]; qCDebug(shared) << row.measured << (row.dt * 1000) << row.FIXME1 << (row.FIXME2 * 1000) << "||" << row.error << row.accumulated << row.changed << - "||" << row.p << row.i << row.d << row.computed; + "||" << row.p << row.i << row.d << row.computed << 1.0f/row.computed; } qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << From 0a8fd9cbad939cf82668cd6457cafd629e63ae22 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 09:22:41 -0800 Subject: [PATCH 05/15] Spread avatars around (not up to) a larger area. --- examples/acScripts/animatedAvatarAgent.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/acScripts/animatedAvatarAgent.js b/examples/acScripts/animatedAvatarAgent.js index 4e550e9789..3e5c90ed1a 100644 --- a/examples/acScripts/animatedAvatarAgent.js +++ b/examples/acScripts/animatedAvatarAgent.js @@ -14,16 +14,17 @@ // An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'. // In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'. -var origin = {x: 500, y: 502, z: 500}; -var spread = 10; // meters +var origin = {x: 500, y: 500, z: 500}; +var spread = 20; // meters var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35}; Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy Avatar.displayName = "'Bot"; var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start. Agent.isAvatar = true; +function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2. Script.setTimeout(function () { - Avatar.position = Vec3.sum(origin, {x: Math.random() * spread, y: 0, z: Math.random() * spread}); + Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()}); print("Starting at", JSON.stringify(Avatar.position)); Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame); }, millisecondsToWaitBeforeStarting); From f6286201f46daa296004c6528417241e557198ee Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 11:55:22 -0800 Subject: [PATCH 06/15] cleanup --- interface/resources/qml/Stats.qml | 8 +++++++- interface/src/avatar/Avatar.cpp | 24 ++++++++++-------------- interface/src/avatar/AvatarManager.cpp | 12 ++++++------ interface/src/avatar/AvatarManager.h | 22 ++++++++++------------ interface/src/ui/Stats.cpp | 2 +- interface/src/ui/Stats.h | 4 ++-- libraries/shared/src/PIDController.cpp | 6 +++--- libraries/shared/src/PIDController.h | 11 ++++------- 8 files changed, 43 insertions(+), 46 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 336b8ff577..84381cc754 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 + ", " + root.avatarRenderedCount + " w/in " + root.avatarRenderDistance + "m" + text: "Avatars: " + root.avatarCount } Text { color: root.fontColor; @@ -256,6 +256,12 @@ Item { visible: root.expanded text: "LOD: " + root.lodStatus; } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded + text: "Renderable avatars: " + root.avatarRenderableCount + " w/in " + root.avatarRenderDistance + "m"; + } } } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6a634bcc90..bfee330700 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -115,7 +115,7 @@ Avatar::~Avatar() { } } -/*const fixme*/ float BILLBOARD_LOD_DISTANCE = 40.0f; +const float BILLBOARD_LOD_DISTANCE = 40.0f; void Avatar::init() { getHead()->init(); @@ -182,7 +182,6 @@ 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; @@ -192,26 +191,23 @@ void Avatar::simulate(float deltaTime) { _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 + + const bool isControllerLogging = DependencyManager::get()->getRenderDistanceControllerIsLogging(); float renderDistance = DependencyManager::get()->getRenderDistance(); + const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION; 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 + if (!isControllerLogging) { // Test for isMyAvatar is prophylactic. Never occurs in current code. + qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance; + } } } else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) { _shouldSkipRender = true; -#ifndef PID_TUNING - qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance(); -#endif + if (!isControllerLogging) { + qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance; + } } // simple frustum check diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 132ddb9dfa..d2765e0122 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -91,18 +91,17 @@ void AvatarManager::init() { } scene->enqueuePendingChanges(pendingChanges); - const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; + const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; FIXME _renderDistanceController.setMeasuredValueSetpoint(target_fps); const float TREE_SCALE = 32768.0f; // Not in shared library, alas. - const float SMALLEST_REASONABLE_HORIZON = 0.5f; // FIXME 5 + const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters _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 + // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) + //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); + // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. _renderDistanceController.setKP(0.0003f); //Usually about 0.6 of largest that doesn't oscillate, with other constants 0. _renderDistanceController.setKI(0.001f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.00001f); // a touch of kd increases the speed by which we get there @@ -134,6 +133,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); + const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); const float deduced = qApp->getLastDeducedNonVSyncFps(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 7a4dd2632e..9f8b6dc646 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -67,17 +67,17 @@ public: void handleCollisionEvents(const CollisionEvents& collisionEvents); void updateAvatarPhysicsShape(const QUuid& id); - Q_INVOKABLE int getNumberRendered() { return _renderedAvatarCount; } + + // Expose results and parameter-tuning operations to other systems, such as stats and javascript. 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; } + Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; } + Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); } + Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); } + Q_INVOKABLE void setRenderDistanceKP(float f) { _renderDistanceController.setKP(f); } + Q_INVOKABLE void setRenderDistanceKI(float f) { _renderDistanceController.setKI(f); } + Q_INVOKABLE void setRenderDistanceKD(float f) { _renderDistanceController.setKD(f); } + Q_INVOKABLE void setRenderDistanceLowLimit(float f) { _renderDistanceController.setControlledValueLowLimit(f); } + Q_INVOKABLE void setRenderDistanceHighLimit(float f) { _renderDistanceController.setControlledValueHighLimit(f); } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -102,9 +102,7 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; - float _fixmeUpdate = 40.0f; float _renderDistance { 40.0f }; - float _renderFeedForward { 5.0f }; int _renderedAvatarCount {0}; PIDController _renderDistanceController {}; SimpleMovingAverage _renderDistanceAverage {10}; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 3f27052f60..663cf144b1 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -115,7 +115,7 @@ 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(avatarRenderableCount, avatarManager->getNumberInRenderRange()); STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(framerate, (int)qApp->getFps()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 12caf19d18..d1c0dd19d7 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -36,7 +36,7 @@ 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, avatarRenderableCount, 0) STATS_PROPERTY(int, avatarRenderDistance, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) @@ -119,7 +119,7 @@ signals: void simrateChanged(); void avatarSimrateChanged(); void avatarCountChanged(); - void avatarRenderedCountChanged(); + void avatarRenderableCountChanged(); void avatarRenderDistanceChanged(); void packetInCountChanged(); void packetOutCountChanged(); diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index d5697538d5..6080b878b8 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -27,11 +27,11 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator const float changeInError = (error - _lastError) / dt; // positive value denotes increasing deficit const float d = getKD() * changeInError; // term is Derivative of Error - const float computedValue = glm::clamp(p + i + d + getBias(), + const float computedValue = glm::clamp(p + i + d, getControlledValueLowLimit(), getControlledValueHighLimit()); - if (_history.capacity()) { // if logging/reporting + if (getIsLogging()) { // if logging/reporting updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); } @@ -75,5 +75,5 @@ void PIDController::reportHistory() { } qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() << "controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() << - "kp/ki/kd/bias" << getKP() << getKI() << getKD() << getBias(); + "kp/ki/kd" << getKP() << getKI() << getKD(); } \ No newline at end of file diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index a2adbf98ab..7c63206406 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -26,13 +26,12 @@ class PIDController { public: - // These three are the main interface: + // These four the main interface: void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging - // There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other - // special-but-expected circumstances. Thus the instance vars are not const. + bool getIsLogging() { return _history.capacity(); } float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; } // In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max. // Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1] @@ -42,17 +41,17 @@ public: float getKP() const { return _kp; } float getKI() const { return _ki; } float getKD() const { return _kd; } - float getBias() const { return _bias; } float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); } float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); } + // There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other + // special-but-expected circumstances. Thus the instance vars are not const. void setControlledValueLowLimit(float newValue) { _controlledValueLowLimit = newValue; } void setControlledValueHighLimit(float newValue) { _controlledValueHighLimit = newValue; } void setAntiWindupFactor(float newValue) { _antiWindupFactor = newValue; } void setKP(float newValue) { _kp = newValue; } void setKI(float newValue) { _ki = newValue; } void setKD(float newValue) { _kd = newValue; } - void setBias(float newValue) { _bias = newValue; } class Row { // one row of accumulated history, used only for logging (if at all) public: @@ -66,7 +65,6 @@ public: float p; float i; float d; - float bias; float computed; }; protected: @@ -79,7 +77,6 @@ protected: float _kp { 0.0f }; float _ki { 0.0f }; float _kd { 0.0f }; - float _bias { 0.0f }; // Controller state float _lastError{ 0.0f }; From 431a8c95848a2599743e70cf4034a0970549ae94 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 17:12:50 -0800 Subject: [PATCH 07/15] cleanup --- interface/src/Application.cpp | 11 +++++++---- interface/src/Application.h | 1 + interface/src/avatar/AvatarManager.cpp | 8 ++++---- libraries/shared/src/PIDController.cpp | 5 +++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 84b14497cd..1e970828ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1102,15 +1102,18 @@ void Application::paintGL() { // Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod. // // Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing. + // We're using this as a proxy for the time between vsync and displayEnd, below. (Not exact, but tends to be the same over time.) // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait; // plus a some non-zero time for machinery we can't measure // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion - // of paintWait up to the next vSync. - const float targetPeriod = isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; - const float minumumMachinery = glm::max(0.0f, (floorf(_lastPaintWait / targetPeriod) * targetPeriod) - _lastPaintWait); - deducedNonVSyncPeriod += minumumMachinery; + // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, + // the key part (and not an exagerated part) of _lastPaintWait is accounted for. + const float targetPeriod = getTargetFramePeriod(); + if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) { + deducedNonVSyncPeriod += remainder(actualPeriod, _lastPaintWait); + } _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; _lastInstantaneousFps = instantaneousFps; diff --git a/interface/src/Application.h b/interface/src/Application.h index 01d0c72645..0222f585cf 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -159,6 +159,7 @@ public: bool isForeground() const { return _isForeground; } float getFps() const { return _fps; } + float getTargetFramePeriod() { return isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; } float getLastInstanteousFps() const { return _lastInstantaneousFps; } float getLastPaintWait() const { return _lastPaintWait; }; float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d2765e0122..6c1097a42b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -91,7 +91,7 @@ void AvatarManager::init() { } scene->enqueuePendingChanges(pendingChanges); - const float target_fps = 75.0f; // qApp->isHMDMode() ? 75.0f : 60.0f; FIXME + const float target_fps = 1.0f / qApp->getTargetFramePeriod(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); const float TREE_SCALE = 32768.0f; // Not in shared library, alas. const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters @@ -102,9 +102,9 @@ void AvatarManager::init() { // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0003f); //Usually about 0.6 of largest that doesn't oscillate, with other constants 0. - _renderDistanceController.setKI(0.001f); // Big enough to bring us to target with the above KP. - _renderDistanceController.setKD(0.00001f); // a touch of kd increases the speed by which we get there + _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other constants 0. + _renderDistanceController.setKI(0.0004f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKD(0.00001f); // A touch of kd increases the speed by which we get there. } diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index 6080b878b8..fc75b91fb2 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -33,8 +33,9 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator if (getIsLogging()) { // if logging/reporting updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); - } - + } + Q_ASSERT(!isnan(computedValue)); + // update state for next time _lastError = error; _lastAccumulation = accumulatedError; From 0f6a37f0f7de9223419eb4d7954bb5b7b7367388 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 09:49:14 -0800 Subject: [PATCH 08/15] remainder->fmod! --- interface/src/Application.cpp | 3 ++- interface/src/avatar/AvatarManager.cpp | 11 +++++++---- interface/src/avatar/AvatarManager.h | 2 +- libraries/shared/src/PIDController.h | 7 ++++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e970828ec..5640827982 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1112,7 +1112,8 @@ void Application::paintGL() { // the key part (and not an exagerated part) of _lastPaintWait is accounted for. const float targetPeriod = getTargetFramePeriod(); if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) { - deducedNonVSyncPeriod += remainder(actualPeriod, _lastPaintWait); + // Don't use C++ remainder(). It's authors are mathematically insane. + deducedNonVSyncPeriod += fmod(actualPeriod, _lastPaintWait); } _lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod; _lastInstantaneousFps = instantaneousFps; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6c1097a42b..789865679d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -93,16 +93,15 @@ void AvatarManager::init() { const float target_fps = 1.0f / qApp->getTargetFramePeriod(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); - const float TREE_SCALE = 32768.0f; // Not in shared library, alas. const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters _renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON); - _renderDistanceController.setControlledValueLowLimit(1.0f / TREE_SCALE); + _renderDistanceController.setControlledValueLowLimit(1.0f / (float) TREE_SCALE); // Advice for tuning parameters: - // See PIDController.h. There's a sectionon tuning in the reference. + // See PIDController.h. There's a section on tuning in the reference. // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other constants 0. + _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. _renderDistanceController.setKI(0.0004f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.00001f); // A touch of kd increases the speed by which we get there. @@ -136,6 +135,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); + // The PID controller raises the controlled value when the measured value goes up. + // The measured value is frame rate. When the controlled value (1 / render cutoff distance) + // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate + // goes up. const float deduced = qApp->getLastDeducedNonVSyncFps(); const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); _renderDistanceAverage.updateAverage(distance); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 9f8b6dc646..f17a34da35 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -102,7 +102,7 @@ private: QVector _localLights; bool _shouldShowReceiveStats = false; - float _renderDistance { 40.0f }; + float _renderDistance { (float) TREE_SCALE }; int _renderedAvatarCount {0}; PIDController _renderDistanceController {}; SimpleMovingAverage _renderDistanceAverage {10}; diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index 7c63206406..55519063bc 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -21,12 +21,12 @@ #include #include -// Although our coding standard shuns abbreviations, the control systems literature pretty uniformly uses p, i, d, and dt rather than +// Although our coding standard shuns abbreviations, the control systems literature uniformly uses p, i, d, and dt rather than // proportionalTerm, integralTerm, derivativeTerm, and deltaTime. Here we will be consistent with the literature. class PIDController { public: - // These four the main interface: + // These are the main interfaces: void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging @@ -78,9 +78,10 @@ protected: float _ki { 0.0f }; float _kd { 0.0f }; - // Controller state + // Controller operating state float _lastError{ 0.0f }; float _lastAccumulation{ 0.0f }; + // reporting QVector _history{}; QString _label{ "" }; From b5e5adc41e9485e6e2a02c7634c2df0e095edf35 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 09:51:57 -0800 Subject: [PATCH 09/15] Update for changed target frame period. --- interface/src/avatar/AvatarManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 789865679d..5c9796b1e4 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -135,6 +135,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const float fps = qApp->getLastInstanteousFps(); const float paintWait = qApp->getLastPaintWait(); + _renderDistanceController.setMeasuredValueSetpoint(1.0f / qApp->getTargetFramePeriod()); // No problem updating in flight. // The PID controller raises the controlled value when the measured value goes up. // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate From a3665985986cb98103f40a8084fc8270ad0364c5 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 20 Nov 2015 11:48:44 -0800 Subject: [PATCH 10/15] snapshot. It works. --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 8 ++++---- interface/src/avatar/AvatarManager.h | 18 ++++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5640827982..9a3f3b071e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1106,7 +1106,7 @@ void Application::paintGL() { // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. - float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait; // plus a some non-zero time for machinery we can't measure + float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + 0.002f; // plus a some non-zero time for machinery we can't measure // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, // the key part (and not an exagerated part) of _lastPaintWait is accounted for. diff --git a/interface/src/Application.h b/interface/src/Application.h index 0222f585cf..7d9efb34af 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -163,6 +163,7 @@ public: float getLastInstanteousFps() const { return _lastInstantaneousFps; } float getLastPaintWait() const { return _lastPaintWait; }; float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } + void setMarginForDeducedFramePeriod(float newValue) { _marginForDeducedFramePeriod = newValue; } float getFieldOfView() { return _fieldOfView.get(); } void setFieldOfView(float fov); @@ -437,6 +438,7 @@ private: float _lastInstantaneousFps { 0.0f }; float _lastPaintWait { 0.0f }; float _lastDeducedNonVSyncFps { 0.0f }; + float _marginForDeducedFramePeriod { 1.0f }; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5c9796b1e4..80e35174bd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,9 +101,9 @@ void AvatarManager::init() { // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0005f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. - _renderDistanceController.setKI(0.0004f); // Big enough to bring us to target with the above KP. - _renderDistanceController.setKD(0.00001f); // A touch of kd increases the speed by which we get there. + _renderDistanceController.setKP(0.0006f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. + _renderDistanceController.setKI(0.0005f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there. } @@ -140,7 +140,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate // goes up. - const float deduced = qApp->getLastDeducedNonVSyncFps(); + const float deduced = qApp->getLastDeducedNonVSyncFps() - _renderPresentationAllowance; const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); _renderDistanceAverage.updateAverage(distance); _renderDistance = _renderDistanceAverage.getAverage(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index f17a34da35..8be7df43f9 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -73,11 +73,12 @@ public: Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; } Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); } Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); } - Q_INVOKABLE void setRenderDistanceKP(float f) { _renderDistanceController.setKP(f); } - Q_INVOKABLE void setRenderDistanceKI(float f) { _renderDistanceController.setKI(f); } - Q_INVOKABLE void setRenderDistanceKD(float f) { _renderDistanceController.setKD(f); } - Q_INVOKABLE void setRenderDistanceLowLimit(float f) { _renderDistanceController.setControlledValueLowLimit(f); } - Q_INVOKABLE void setRenderDistanceHighLimit(float f) { _renderDistanceController.setControlledValueHighLimit(f); } + Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); } + Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); } + Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } + Q_INVOKABLE void setRenderDistanceLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } + Q_INVOKABLE void setRenderDistanceHighLimit(float newValue) { _renderDistanceController.setControlledValueHighLimit(newValue); } + // Q_INVOKABLE void setRenderPresentationAllowance(float newValue) { qApp->setMarginForDeducedFramePeriod(newValue); } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -103,9 +104,10 @@ private: bool _shouldShowReceiveStats = false; float _renderDistance { (float) TREE_SCALE }; - int _renderedAvatarCount {0}; - PIDController _renderDistanceController {}; - SimpleMovingAverage _renderDistanceAverage {10}; + int _renderedAvatarCount { 0 }; + float _renderPresentationAllowance { 0.0f }; + PIDController _renderDistanceController { }; + SimpleMovingAverage _renderDistanceAverage { 10 }; From 6c784256cb9eae3269d943e8e3053d20f458219b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 20 Nov 2015 13:51:12 -0800 Subject: [PATCH 11/15] final(?) cleanup --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 +- interface/src/avatar/AvatarManager.cpp | 10 ++++------ interface/src/avatar/AvatarManager.h | 2 -- libraries/shared/src/PIDController.cpp | 10 ++++------ libraries/shared/src/PIDController.h | 6 ++---- 6 files changed, 12 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9a3f3b071e..9863248cf8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1106,7 +1106,7 @@ void Application::paintGL() { // This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second. const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize // Note that _lastPaintWait (stored at end of last call) is for the same paint cycle. - float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + 0.002f; // plus a some non-zero time for machinery we can't measure + float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + _marginForDeducedFramePeriod; // plus a some non-zero time for machinery we can't measure // We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion // of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles, // the key part (and not an exagerated part) of _lastPaintWait is accounted for. diff --git a/interface/src/Application.h b/interface/src/Application.h index 7d9efb34af..b90ad43d60 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -438,7 +438,7 @@ private: float _lastInstantaneousFps { 0.0f }; float _lastPaintWait { 0.0f }; float _lastDeducedNonVSyncFps { 0.0f }; - float _marginForDeducedFramePeriod { 1.0f }; + float _marginForDeducedFramePeriod{ 0.002f }; // 2ms, adjustable ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 80e35174bd..eea4edafc9 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,8 +101,8 @@ void AvatarManager::init() { // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. - _renderDistanceController.setKP(0.0006f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. - _renderDistanceController.setKI(0.0005f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. + _renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP. _renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there. } @@ -133,15 +133,13 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - const float fps = qApp->getLastInstanteousFps(); - const float paintWait = qApp->getLastPaintWait(); _renderDistanceController.setMeasuredValueSetpoint(1.0f / qApp->getTargetFramePeriod()); // No problem updating in flight. // The PID controller raises the controlled value when the measured value goes up. // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate // goes up. - const float deduced = qApp->getLastDeducedNonVSyncFps() - _renderPresentationAllowance; - const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime, false, fps, paintWait); + const float deduced = qApp->getLastDeducedNonVSyncFps(); + const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime); _renderDistanceAverage.updateAverage(distance); _renderDistance = _renderDistanceAverage.getAverage(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 8be7df43f9..52741970aa 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -78,7 +78,6 @@ public: Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } Q_INVOKABLE void setRenderDistanceLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } Q_INVOKABLE void setRenderDistanceHighLimit(float newValue) { _renderDistanceController.setControlledValueHighLimit(newValue); } - // Q_INVOKABLE void setRenderPresentationAllowance(float newValue) { qApp->setMarginForDeducedFramePeriod(newValue); } public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } @@ -105,7 +104,6 @@ private: bool _shouldShowReceiveStats = false; float _renderDistance { (float) TREE_SCALE }; int _renderedAvatarCount { 0 }; - float _renderPresentationAllowance { 0.0f }; PIDController _renderDistanceController { }; SimpleMovingAverage _renderDistanceAverage { 10 }; diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp index fc75b91fb2..a853553b89 100644 --- a/libraries/shared/src/PIDController.cpp +++ b/libraries/shared/src/PIDController.cpp @@ -14,7 +14,7 @@ #include "SharedLogging.h" #include "PIDController.h" -float PIDController::update(float measuredValue, float dt, bool resetAccumulator, float FIXME1, float FIXME2) { +float PIDController::update(float measuredValue, float dt, bool resetAccumulator) { const float error = getMeasuredValueSetpoint() - measuredValue; // Sign is the direction we want measuredValue to go. Positive means go higher. const float p = getKP() * error; // term is Proportional to error @@ -32,7 +32,7 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator getControlledValueHighLimit()); if (getIsLogging()) { // if logging/reporting - updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue, FIXME1, FIXME2); + updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue); } Q_ASSERT(!isnan(computedValue)); @@ -43,7 +43,7 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator } // Just for logging/reporting. Used when picking/verifying the operational parameters. -void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue, float FIXME1, float FIXME2) { +void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue) { // Don't report each update(), as the I/O messes with the results a lot. // Instead, add to history, and then dump out at once when full. // Typically, the first few values reported in each batch should be ignored. @@ -51,8 +51,6 @@ void PIDController::updateHistory(float measuredValue, float dt, float error, fl _history.resize(n + 1); Row& next = _history[n]; next.measured = measuredValue; - next.FIXME1 = FIXME1; - next.FIXME2 = FIXME2; next.dt = dt; next.error = error; next.accumulated = accumulatedError; @@ -70,7 +68,7 @@ void PIDController::reportHistory() { qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled"; for (int i = 0; i < _history.size(); i++) { Row& row = _history[i]; - qCDebug(shared) << row.measured << (row.dt * 1000) << row.FIXME1 << (row.FIXME2 * 1000) << + qCDebug(shared) << row.measured << row.dt << "||" << row.error << row.accumulated << row.changed << "||" << row.p << row.i << row.d << row.computed << 1.0f/row.computed; } diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index 55519063bc..e89ff2a693 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -28,7 +28,7 @@ class PIDController { public: // These are the main interfaces: void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; } - float update(float measuredValue, float dt, bool resetAccumulator = false, float FIXME1 = 0.0f, float FIXME2 = 0.0f); // returns the new computedValue + float update(float measuredValue, float dt, bool resetAccumulator = false); // returns the new computedValue void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging bool getIsLogging() { return _history.capacity(); } @@ -55,8 +55,6 @@ public: class Row { // one row of accumulated history, used only for logging (if at all) public: - float FIXME1; - float FIXME2; float measured; float dt; float error; @@ -69,7 +67,7 @@ public: }; protected: void reportHistory(); - void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue, float FIXME1, float FIXME2); + void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue); float _measuredValueSetpoint { 0.0f }; float _controlledValueLowLimit { 0.0f }; float _controlledValueHighLimit { std::numeric_limits::max() }; From 9673133bf4d76aade057a5a0d3d50e7cf2d33af7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 20 Nov 2015 14:26:37 -0800 Subject: [PATCH 12/15] whitespace (reduce diffs) --- interface/src/avatar/Avatar.cpp | 1 + interface/src/avatar/AvatarManager.h | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 26fb9c1b2e..d3f47703fc 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -606,6 +606,7 @@ 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(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 9918fc9374..96383b7e60 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -65,6 +65,7 @@ public: void getObjectsToChange(VectorOfMotionStates& motionStates); void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); + void updateAvatarPhysicsShape(Avatar* avatar); // Expose results and parameter-tuning operations to other systems, such as stats and javascript. @@ -108,8 +109,6 @@ private: PIDController _renderDistanceController { }; SimpleMovingAverage _renderDistanceAverage { 10 }; - - SetOfAvatarMotionStates _avatarMotionStates; SetOfMotionStates _motionStatesToAdd; VectorOfMotionStates _motionStatesToDelete; From f1e0742aeb6aa5e65de04c96b974bd197082e023 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:20:59 -0800 Subject: [PATCH 13/15] Add getTargetFrameRate(). --- interface/src/Application.h | 5 ++++- interface/src/avatar/AvatarManager.cpp | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 356fabce71..730158c689 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -159,7 +159,10 @@ public: bool isForeground() const { return _isForeground; } float getFps() const { return _fps; } - float getTargetFramePeriod() { return isHMDMode() ? 1.0f / 75.0f : 1.0f / 60.0f; } + float const HMD_TARGET_FRAME_RATE = 75.0f; + float const DESKTOP_TARGET_FRAME_RATE = 60.0f; + float getTargetFrameRate() { return isHMDMode() ? HMD_TARGET_FRAME_RATE : DESKTOP_TARGET_FRAME_RATE; } + float getTargetFramePeriod() { return isHMDMode() ? 1.0f / HMD_TARGET_FRAME_RATE : 1.0f / DESKTOP_TARGET_FRAME_RATE; } // same as 1/getTargetFrameRate, but w/compile-time division float getLastInstanteousFps() const { return _lastInstantaneousFps; } float getLastPaintWait() const { return _lastPaintWait; }; float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e7701dcb24..6d8182dfc0 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -91,7 +91,7 @@ void AvatarManager::init() { } scene->enqueuePendingChanges(pendingChanges); - const float target_fps = 1.0f / qApp->getTargetFramePeriod(); + const float target_fps = qApp->getTargetFrameRate(); _renderDistanceController.setMeasuredValueSetpoint(target_fps); const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters _renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON); @@ -139,7 +139,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - _renderDistanceController.setMeasuredValueSetpoint(1.0f / qApp->getTargetFramePeriod()); // No problem updating in flight. + _renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight. // The PID controller raises the controlled value when the measured value goes up. // The measured value is frame rate. When the controlled value (1 / render cutoff distance) // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate From 591280807795d9e6e05478475a1d58beb0c95b63 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:27:49 -0800 Subject: [PATCH 14/15] getShouldSkipRendering() => !getShouldRender(). --- interface/src/avatar/Avatar.h | 2 +- interface/src/avatar/AvatarManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index cb238dc82c..64ef321f1c 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -140,7 +140,7 @@ public: Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; } Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; } - Q_INVOKABLE bool getShouldSkipRendering() const { return _shouldSkipRender; } + Q_INVOKABLE bool getShouldRender() 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 diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6d8182dfc0..40f469961c 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -167,7 +167,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } else { avatar->startUpdate(); avatar->simulate(deltaTime); - if (!avatar->getShouldSkipRendering()) { + if (avatar->getShouldRender()) { renderableCount++; } avatar->endUpdate(); From c94a5389a0af9144182e90659c0807d61a91d22b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 23 Nov 2015 10:31:37 -0800 Subject: [PATCH 15/15] comments --- libraries/shared/src/PIDController.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/PIDController.h b/libraries/shared/src/PIDController.h index e89ff2a693..0b2411530a 100644 --- a/libraries/shared/src/PIDController.h +++ b/libraries/shared/src/PIDController.h @@ -38,9 +38,9 @@ public: float getControlledValueLowLimit() const { return _controlledValueLowLimit; } float getControlledValueHighLimit() const { return _controlledValueHighLimit; } float getAntiWindupFactor() const { return _antiWindupFactor; } // default 10 - float getKP() const { return _kp; } - float getKI() const { return _ki; } - float getKD() const { return _kd; } + float getKP() const { return _kp; } // proportional to error. See comment above class. + float getKI() const { return _ki; } // to time integral of error + float getKD() const { return _kd; } // to time derivative of error float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); } float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); }