From 7d30cd0159bb8692514cdfb235c0e5814451e9c0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 19 Nov 2015 09:18:40 -0800 Subject: [PATCH] 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() <<