use full 'present' time for FPS calculation

also, refactor LOD adjust logic
This commit is contained in:
Andrew Meadows 2017-12-19 13:13:13 -08:00
parent 5c84c5f845
commit 8e0d367a2d
3 changed files with 68 additions and 167 deletions

View file

@ -4323,9 +4323,9 @@ void Application::updateLOD(float deltaTime) const {
PerformanceTimer perfTimer("LOD"); PerformanceTimer perfTimer("LOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!isThrottleRendering()) { if (!isThrottleRendering()) {
float batchTime = (float)_gpuContext->getFrameTimerBatchAverage(); float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime()); float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
DependencyManager::get<LODManager>()->autoAdjustLOD(batchTime, engineRunTime, deltaTime); DependencyManager::get<LODManager>()->autoAdjustLOD(presentTime, engineRunTime, deltaTime);
} else { } else {
DependencyManager::get<LODManager>()->resetLODAdjust(); DependencyManager::get<LODManager>()->resetLODAdjust();
} }

View file

@ -19,6 +19,11 @@
#include "LODManager.h" #include "LODManager.h"
const uint64_t LOD_AUTO_ADJUST_PERIOD = 500 * USECS_PER_MSEC;
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
@ -39,156 +44,74 @@ float LODManager::getLODIncreaseFPS() {
return getDesktopLODIncreaseFPS(); return getDesktopLODIncreaseFPS();
} }
void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float deltaTimeSec) { void LODManager::autoAdjustLOD(float presentTime, float engineRunTime, float deltaTimeSec) {
// 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;
if (_fpsAverageUpWindow.getSampleCount() < IGNORE_THESE_SAMPLES) {
_lastStable = _lastUpShift = _lastDownShift = usecTimestampNow();
}
// compute time-weighted running average renderTime // compute time-weighted running average renderTime
const float OVERLAY_AND_SWAP_TIME_BUDGET = 2.0f; // msec float maxTime = glm::max(presentTime, engineRunTime);
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET; const float LOD_ADJUST_TIMESCALE = 0.1f; // sec
float maxTime = glm::max(renderTime, engineRunTime); float blend = (deltaTimeSec < LOD_ADJUST_TIMESCALE) ? deltaTimeSec / LOD_ADJUST_TIMESCALE : 1.0f;
const float BLEND_TIMESCALE = 0.3f; // sec
const float MIN_DELTA_TIME = 0.001f;
const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME);
float blend = BLEND_TIMESCALE / safeDeltaTime;
if (blend > 1.0f) {
blend = 1.0f;
}
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxTime; // msec _avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxTime; // msec
if (!_automaticLODAdjust) {
// early exit
return;
}
// translate into fps for legacy implementation float oldOctreeSizeScale = _octreeSizeScale;
float currentFPS = (float)MSECS_PER_SECOND / _avgRenderTime; float currentFPS = (float)MSECS_PER_SECOND / _avgRenderTime;
uint64_t now = usecTimestampNow();
_fpsAverageStartWindow.updateAverage(currentFPS); if (currentFPS < getLODDecreaseFPS()) {
_fpsAverageDownWindow.updateAverage(currentFPS); if (now > _decreaseFPSExpiry) {
_fpsAverageUpWindow.updateAverage(currentFPS); _decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
quint64 now = usecTimestampNow();
quint64 elapsedSinceDownShift = now - _lastDownShift;
quint64 elapsedSinceUpShift = now - _lastUpShift;
quint64 lastStableOrUpshift = glm::max(_lastUpShift, _lastStable);
quint64 elapsedSinceStableOrUpShift = now - lastStableOrUpshift;
if (_automaticLODAdjust) {
bool changed = false;
// 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) {
qCDebug(interfaceapp) << "---- 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) { if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale *= ADJUST_LOD_DOWN_BY; _octreeSizeScale *= LOD_AUTO_ADJUST_DECREMENT_FACTOR;
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
} }
changed = true; qCDebug(interfaceapp) << "adjusting LOD UP"
<< "fps =" << currentFPS
<< "targetFPS =" << getLODDecreaseFPS();
} }
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
if (changed) { }
if (_isDownshifting) { _increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
// subsequent downshift } else if (currentFPS > getLODIncreaseFPS()) {
qCDebug(interfaceapp) << "adjusting LOD DOWN..." if (now > _increaseFPSExpiry) {
<< "average fps for last "<< DOWN_SHIFT_WINDOW_IN_SECS <<"seconds was " _increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
<< _fpsAverageDownWindow.getAverage() if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
<< "minimum is:" << getLODDecreaseFPS() if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
<< "elapsedSinceDownShift:" << elapsedSinceDownShift _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
} else { } else {
// first downshift _octreeSizeScale *= LOD_AUTO_ADJUST_INCREMENT_FACTOR;
qCDebug(interfaceapp) << "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 (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;
}
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
}
changed = true;
}
}
if (changed) {
qCDebug(interfaceapp) << "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;
_lastUpShift = now;
_isDownshifting = false;
emit LODIncreased();
} }
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
}
qCDebug(interfaceapp) << "adjusting LOD DOWN"
<< "fps =" << currentFPS
<< "targetFPS =" << getLODDecreaseFPS();
} }
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
} }
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
} else {
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
_decreaseFPSExpiry = _increaseFPSExpiry;
}
if (changed) { if (oldOctreeSizeScale != _octreeSizeScale) {
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog(); auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
if (lodToolsDialog) { if (lodToolsDialog) {
lodToolsDialog->reloadSliders(); lodToolsDialog->reloadSliders();
}
} }
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to be at middle of target zone. It will drift close to its true value within
// about three few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
float expectedFPS = 0.5f * (getLODIncreaseFPS() + getLODDecreaseFPS());
_avgRenderTime = MSECS_PER_SECOND / expectedFPS;
} }
} }
void LODManager::resetLODAdjust() { void LODManager::resetLODAdjust() {
_fpsAverageStartWindow.reset(); _decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
_fpsAverageDownWindow.reset();
_fpsAverageUpWindow.reset();
_lastUpShift = _lastDownShift = usecTimestampNow();
_isDownshifting = false;
} }
const float MIN_DECREASE_FPS = 0.5f; const float MIN_DECREASE_FPS = 0.5f;
@ -206,7 +129,7 @@ float LODManager::getDesktopLODDecreaseFPS() const {
} }
float LODManager::getDesktopLODIncreaseFPS() const { float LODManager::getDesktopLODIncreaseFPS() const {
return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP, MAX_LIKELY_DESKTOP_FPS); return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS);
} }
void LODManager::setHMDLODDecreaseFPS(float fps) { void LODManager::setHMDLODDecreaseFPS(float fps) {
@ -222,7 +145,7 @@ float LODManager::getHMDLODDecreaseFPS() const {
} }
float LODManager::getHMDLODIncreaseFPS() const { float LODManager::getHMDLODIncreaseFPS() const {
return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP, MAX_LIKELY_HMD_FPS); return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS);
} }
QString LODManager::getLODFeedbackText() { QString LODManager::getLODFeedbackText() {

View file

@ -19,29 +19,13 @@
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <render/Args.h> #include <render/Args.h>
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0; const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0f;
const float DEFAULT_HMD_LOD_DOWN_FPS = 20.0; const float DEFAULT_HMD_LOD_DOWN_FPS = 45.0f;
const float DEFAULT_DESKTOP_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_DESKTOP_LOD_DOWN_FPS; // msec const float DEFAULT_DESKTOP_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_DESKTOP_LOD_DOWN_FPS; // msec
const float DEFAULT_HMD_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_HMD_LOD_DOWN_FPS; // msec const float DEFAULT_HMD_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_HMD_LOD_DOWN_FPS; // msec
const float MAX_LIKELY_DESKTOP_FPS = 59.0; // this is essentially, V-synch - 1 fps const float MAX_LIKELY_DESKTOP_FPS = 59.0f; // this is essentially, V-synch - 1 fps
const float MAX_LIKELY_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps const float MAX_LIKELY_HMD_FPS = 74.0f; // this is essentially, V-synch - 1 fps
const float INCREASE_LOD_GAP = 15.0f; const float INCREASE_LOD_GAP_FPS = 15.0f; // fps
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;
// The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision). // The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
@ -78,7 +62,7 @@ public:
Q_INVOKABLE float getLODIncreaseFPS(); Q_INVOKABLE float getLODIncreaseFPS();
static bool shouldRender(const RenderArgs* args, const AABox& bounds); static bool shouldRender(const RenderArgs* args, const AABox& bounds);
void autoAdjustLOD(float batchTime, float engineRunTime, float deltaTimeSec); void autoAdjustLOD(float presentTime, float engineRunTime, float deltaTimeSec);
void loadSettings(); void loadSettings();
void saveSettings(); void saveSettings();
@ -92,21 +76,15 @@ private:
LODManager(); LODManager();
bool _automaticLODAdjust = true; bool _automaticLODAdjust = true;
float _avgRenderTime { 0.0 }; float _avgRenderTime { 0.0f };
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME }; float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME }; float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE; float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
int _boundaryLevelAdjust = 0; int _boundaryLevelAdjust = 0;
quint64 _lastDownShift = 0; uint64_t _decreaseFPSExpiry { 0 };
quint64 _lastUpShift = 0; uint64_t _increaseFPSExpiry { 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;
}; };
#endif // hifi_LODManager_h #endif // hifi_LODManager_h