diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 8b942dcfe1..cb4913df72 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -40,26 +40,55 @@ float LODManager::getLODIncreaseFPS() { void LODManager::autoAdjustLOD(float currentFPS) { + // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't // really want to count them in our average, so we will ignore the real frame rates and stuff // our moving average with simulated good data const int IGNORE_THESE_SAMPLES = 100; - const float ASSUMED_FPS = 60.0f; - if (_fpsAverage.getSampleCount() < IGNORE_THESE_SAMPLES) { + if (_fpsAverageUpWindow.getSampleCount() < IGNORE_THESE_SAMPLES) { currentFPS = ASSUMED_FPS; + _lastStable = _lastUpShift = _lastDownShift = usecTimestampNow(); } - _fpsAverage.updateAverage(currentFPS); - _fastFPSAverage.updateAverage(currentFPS); + + _fpsAverageStartWindow.updateAverage(currentFPS); + _fpsAverageDownWindow.updateAverage(currentFPS); + _fpsAverageUpWindow.updateAverage(currentFPS); quint64 now = usecTimestampNow(); bool changed = false; - bool octreeChanged = false; - quint64 elapsed = now - _lastAdjust; + quint64 elapsedSinceDownShift = now - _lastDownShift; + quint64 elapsedSinceUpShift = now - _lastUpShift; + + quint64 lastStableOrUpshift = glm::max(_lastUpShift, _lastStable); + quint64 elapsedSinceStableOrUpShift = now - lastStableOrUpshift; if (_automaticLODAdjust) { - // LOD Downward adjustment - if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < getLODDecreaseFPS()) { + + // LOD Downward adjustment + // If we've been downshifting, we watch a shorter downshift window so that we will quickly move toward our + // target frame rate. But if we haven't just done a downshift (either because our last shift was an upshift, + // or because we've just started out) then we look at a much longer window to consider whether or not to start + // downshifting. + bool doDownShift = false; + + if (_isDownshifting) { + // only consider things if our DOWN_SHIFT time has elapsed... + if (elapsedSinceDownShift > DOWN_SHIFT_ELPASED) { + doDownShift = _fpsAverageDownWindow.getAverage() < getLODDecreaseFPS(); + + if (!doDownShift) { + qDebug() << "---- WE APPEAR TO BE DONE DOWN SHIFTING -----"; + _isDownshifting = false; + _lastStable = now; + } + } + } else { + doDownShift = (elapsedSinceStableOrUpShift > START_SHIFT_ELPASED + && _fpsAverageStartWindow.getAverage() < getLODDecreaseFPS()); + } + + if (doDownShift) { // Octree items... stepwise adjustment if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { @@ -67,40 +96,66 @@ void LODManager::autoAdjustLOD(float currentFPS) { if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; } - octreeChanged = changed = true; + changed = true; } - if (changed) { - _lastAdjust = now; - qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() - << "_octreeSizeScale=" << _octreeSizeScale; - + if (changed) { + if (_isDownshifting) { + // subsequent downshift + qDebug() << "adjusting LOD DOWN..." + << "average fps for last "<< DOWN_SHIFT_WINDOW_IN_SECS <<"seconds was " + << _fpsAverageDownWindow.getAverage() + << "minimum is:" << getLODDecreaseFPS() + << "elapsedSinceDownShift:" << elapsedSinceDownShift + << " NEW _octreeSizeScale=" << _octreeSizeScale; + } else { + // first downshift + qDebug() << "adjusting LOD DOWN after initial delay..." + << "average fps for last "<< START_DELAY_WINDOW_IN_SECS <<"seconds was " + << _fpsAverageStartWindow.getAverage() + << "minimum is:" << getLODDecreaseFPS() + << "elapsedSinceUpShift:" << elapsedSinceUpShift + << " NEW _octreeSizeScale=" << _octreeSizeScale; + } + + _lastDownShift = now; + _isDownshifting = true; + emit LODDecreased(); } - } + } else { - // LOD Upward adjustment - if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > getLODIncreaseFPS()) { + // LOD Upward adjustment + if (elapsedSinceUpShift > UP_SHIFT_ELPASED) { + + if (_fpsAverageUpWindow.getAverage() > getLODIncreaseFPS()) { - // Octee items... stepwise adjustment - if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { - if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { - _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; - } else { - _octreeSizeScale *= ADJUST_LOD_UP_BY; + // Octee items... stepwise adjustment + if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { + if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { + _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; + } else { + _octreeSizeScale *= ADJUST_LOD_UP_BY; + } + if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) { + _octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE; + } + changed = true; + } } - if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) { - _octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE; - } - octreeChanged = changed = true; - } - if (changed) { - _lastAdjust = now; - qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() - << "_octreeSizeScale=" << _octreeSizeScale; + if (changed) { + qDebug() << "adjusting LOD UP... average fps for last "<< UP_SHIFT_WINDOW_IN_SECS <<"seconds was " + << _fpsAverageUpWindow.getAverage() + << "upshift point is:" << getLODIncreaseFPS() + << "elapsedSinceUpShift:" << elapsedSinceUpShift + << " NEW _octreeSizeScale=" << _octreeSizeScale; - emit LODIncreased(); + _lastUpShift = now; + _isDownshifting = false; + + emit LODIncreased(); + } } } @@ -116,9 +171,11 @@ void LODManager::autoAdjustLOD(float currentFPS) { } void LODManager::resetLODAdjust() { - _fpsAverage.reset(); - _fastFPSAverage.reset(); - _lastAdjust = usecTimestampNow(); + _fpsAverageStartWindow.reset(); + _fpsAverageDownWindow.reset(); + _fpsAverageUpWindow.reset(); + _lastUpShift = _lastDownShift = usecTimestampNow(); + _isDownshifting = false; } QString LODManager::getLODFeedbackText() { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index c14f17ca61..77f156531a 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -19,10 +19,22 @@ const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0; const float DEFAULT_HMD_LOD_DOWN_FPS = 60.0; -const float INCREASE_LOD_GAP = 5.0f; +const float MAX_LIKELY_DESKTOP_FPS = 59.0; // this is essentially, V-synch - 1 fps +const float MAX_LIKELY_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps +const float INCREASE_LOD_GAP = 15.0f; -const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 0.5; // Consider adjusting LOD down after half a second -const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2; +const float START_DELAY_WINDOW_IN_SECS = 3.0f; // wait at least this long after steady state/last upshift to consider downshifts +const float DOWN_SHIFT_WINDOW_IN_SECS = 0.5f; +const float UP_SHIFT_WINDOW_IN_SECS = 2.5f; + +const int ASSUMED_FPS = 60; +const quint64 START_SHIFT_ELPASED = USECS_PER_SECOND * START_DELAY_WINDOW_IN_SECS; +const quint64 DOWN_SHIFT_ELPASED = USECS_PER_SECOND * DOWN_SHIFT_WINDOW_IN_SECS; // Consider adjusting LOD down after half a second +const quint64 UP_SHIFT_ELPASED = USECS_PER_SECOND * UP_SHIFT_WINDOW_IN_SECS; + +const int START_DELAY_SAMPLES_OF_FRAMES = ASSUMED_FPS * START_DELAY_WINDOW_IN_SECS; +const int DOWN_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * DOWN_SHIFT_WINDOW_IN_SECS; +const int UP_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * UP_SHIFT_WINDOW_IN_SECS; const float ADJUST_LOD_DOWN_BY = 0.9f; const float ADJUST_LOD_UP_BY = 1.1f; @@ -37,9 +49,6 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; // do. But both are still culled using the same angular size logic. const float AVATAR_TO_ENTITY_RATIO = 2.0f; -const int ONE_SECOND_OF_FRAMES = 60; -const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES; - class LODManager : public QObject, public Dependency { Q_OBJECT @@ -51,11 +60,11 @@ public: Q_INVOKABLE void setDesktopLODDecreaseFPS(float value) { _desktopLODDecreaseFPS = value; } Q_INVOKABLE float getDesktopLODDecreaseFPS() const { return _desktopLODDecreaseFPS; } - Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return _desktopLODDecreaseFPS + INCREASE_LOD_GAP; } + Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return glm::min(_desktopLODDecreaseFPS + INCREASE_LOD_GAP, MAX_LIKELY_DESKTOP_FPS); } Q_INVOKABLE void setHMDLODDecreaseFPS(float value) { _hmdLODDecreaseFPS = value; } Q_INVOKABLE float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; } - Q_INVOKABLE float getHMDLODIncreaseFPS() const { return _hmdLODDecreaseFPS + INCREASE_LOD_GAP; } + Q_INVOKABLE float getHMDLODIncreaseFPS() const { return glm::min(_hmdLODDecreaseFPS + INCREASE_LOD_GAP, MAX_LIKELY_HMD_FPS); } Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } @@ -67,10 +76,6 @@ public: Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } - Q_INVOKABLE void resetLODAdjust(); - Q_INVOKABLE float getFPSAverage() const { return _fpsAverage.getAverage(); } - Q_INVOKABLE float getFastFPSAverage() const { return _fastFPSAverage.getAverage(); } - Q_INVOKABLE float getLODDecreaseFPS(); Q_INVOKABLE float getLODIncreaseFPS(); @@ -79,6 +84,7 @@ public: void loadSettings(); void saveSettings(); + void resetLODAdjust(); signals: void LODIncreased(); @@ -96,9 +102,14 @@ private: float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE; int _boundaryLevelAdjust = 0; - quint64 _lastAdjust = 0; - SimpleMovingAverage _fpsAverage = FIVE_SECONDS_OF_FRAMES; - SimpleMovingAverage _fastFPSAverage = ONE_SECOND_OF_FRAMES; + quint64 _lastDownShift = 0; + quint64 _lastUpShift = 0; + quint64 _lastStable = 0; + bool _isDownshifting = false; // start out as if we're not downshifting + + SimpleMovingAverage _fpsAverageStartWindow = START_DELAY_SAMPLES_OF_FRAMES; + SimpleMovingAverage _fpsAverageDownWindow = DOWN_SHIFT_SAMPLES_OF_FRAMES; + SimpleMovingAverage _fpsAverageUpWindow = UP_SHIFT_SAMPLES_OF_FRAMES; bool _shouldRenderTableNeedsRebuilding = true; QMap _shouldRenderTable;