overte-HifiExperiments/interface/src/LODManager.cpp
Anthony J. Thibault ba4c0f189e code review feedback
removed discontinuity in safeDeltaTime, which is used to prevent division by zero.
2017-11-10 10:25:29 -08:00

288 lines
11 KiB
C++

//
// LODManager.cpp
// interface/src/LODManager.h
//
// Created by Clement on 1/16/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 <SettingHandle.h>
#include <OctreeUtils.h>
#include <Util.h>
#include "Application.h"
#include "ui/DialogsManager.h"
#include "InterfaceLogging.h"
#include "LODManager.h"
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
LODManager::LODManager() {
}
float LODManager::getLODDecreaseFPS() {
if (qApp->isHMDMode()) {
return getHMDLODDecreaseFPS();
}
return getDesktopLODDecreaseFPS();
}
float LODManager::getLODIncreaseFPS() {
if (qApp->isHMDMode()) {
return getHMDLODIncreaseFPS();
}
return getDesktopLODIncreaseFPS();
}
void LODManager::autoAdjustLOD(float batchTime, 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
const float OVERLAY_AND_SWAP_TIME_BUDGET = 2.0f; // msec
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET;
float maxTime = glm::max(renderTime, engineRunTime);
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
// translate into fps for legacy implementation
float currentFPS = (float)MSECS_PER_SECOND / _avgRenderTime;
_fpsAverageStartWindow.updateAverage(currentFPS);
_fpsAverageDownWindow.updateAverage(currentFPS);
_fpsAverageUpWindow.updateAverage(currentFPS);
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) {
_octreeSizeScale *= ADJUST_LOD_DOWN_BY;
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
}
changed = true;
}
if (changed) {
if (_isDownshifting) {
// subsequent downshift
qCDebug(interfaceapp) << "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
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 (changed) {
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
if (lodToolsDialog) {
lodToolsDialog->reloadSliders();
}
}
}
}
void LODManager::resetLODAdjust() {
_fpsAverageStartWindow.reset();
_fpsAverageDownWindow.reset();
_fpsAverageUpWindow.reset();
_lastUpShift = _lastDownShift = usecTimestampNow();
_isDownshifting = false;
}
const float MIN_DECREASE_FPS = 0.5f;
void LODManager::setDesktopLODDecreaseFPS(float fps) {
if (fps < MIN_DECREASE_FPS) {
// avoid divide by zero
fps = MIN_DECREASE_FPS;
}
_desktopMaxRenderTime = (float)MSECS_PER_SECOND / fps;
}
float LODManager::getDesktopLODDecreaseFPS() const {
return (float)MSECS_PER_SECOND / _desktopMaxRenderTime;
}
float LODManager::getDesktopLODIncreaseFPS() const {
return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP, MAX_LIKELY_DESKTOP_FPS);
}
void LODManager::setHMDLODDecreaseFPS(float fps) {
if (fps < MIN_DECREASE_FPS) {
// avoid divide by zero
fps = MIN_DECREASE_FPS;
}
_hmdMaxRenderTime = (float)MSECS_PER_SECOND / fps;
}
float LODManager::getHMDLODDecreaseFPS() const {
return (float)MSECS_PER_SECOND / _hmdMaxRenderTime;
}
float LODManager::getHMDLODIncreaseFPS() const {
return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP, MAX_LIKELY_HMD_FPS);
}
QString LODManager::getLODFeedbackText() {
// determine granularity feedback
int boundaryLevelAdjust = getBoundaryLevelAdjust();
QString granularityFeedback;
switch (boundaryLevelAdjust) {
case 0: {
granularityFeedback = QString(".");
} break;
case 1: {
granularityFeedback = QString(" at half of standard granularity.");
} break;
case 2: {
granularityFeedback = QString(" at a third of standard granularity.");
} break;
default: {
granularityFeedback = QString(" at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1);
} break;
}
// distance feedback
float octreeSizeScale = getOctreeSizeScale();
float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
int relativeToTwentyTwenty = 20 / relativeToDefault;
QString result;
if (relativeToDefault > 1.01f) {
result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback);
} else if (relativeToDefault > 0.99f) {
result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback);
} else if (relativeToDefault > 0.01f) {
result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
} else {
result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
}
return result;
}
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
// FIXME - eventually we want to use the render accuracy as an indicator for the level of detail
// to use in rendering.
float renderAccuracy = calculateRenderAccuracy(args->getViewFrustum().getPosition(), bounds, args->_sizeScale, args->_boundaryLevelAdjust);
return (renderAccuracy > 0.0f);
};
void LODManager::setOctreeSizeScale(float sizeScale) {
_octreeSizeScale = sizeScale;
}
void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
_boundaryLevelAdjust = boundaryLevelAdjust;
}
void LODManager::loadSettings() {
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
}
void LODManager::saveSettings() {
desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS());
hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS());
}