mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-15 12:38:46 +02:00
Merge pull request #12022 from AndrewMeadows/lod-auto-adjust-002
more responsive LOD auto-adjust
This commit is contained in:
commit
15fcf66d0e
5 changed files with 94 additions and 167 deletions
|
@ -4323,9 +4323,11 @@ void Application::updateLOD(float deltaTime) const {
|
|||
PerformanceTimer perfTimer("LOD");
|
||||
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
|
||||
if (!isThrottleRendering()) {
|
||||
float batchTime = (float)_gpuContext->getFrameTimerBatchAverage();
|
||||
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
||||
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
||||
DependencyManager::get<LODManager>()->autoAdjustLOD(batchTime, engineRunTime, deltaTime);
|
||||
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
||||
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
|
||||
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->resetLODAdjust();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "LODManager.h"
|
||||
|
||||
|
||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
||||
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
|
||||
|
||||
|
@ -39,156 +40,95 @@ float LODManager::getLODIncreaseFPS() {
|
|||
return getDesktopLODIncreaseFPS();
|
||||
}
|
||||
|
||||
void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float deltaTimeSec) {
|
||||
// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds
|
||||
// to determine if we should adjust the level of detail (LOD).
|
||||
//
|
||||
// A time-weighted running average has a timescale which determines how fast the average tracks the measured
|
||||
// value in real-time. Given a step-function in the mesured value, and assuming measurements happen
|
||||
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
|
||||
// reduced by 1/e every timescale of real-time that passes.
|
||||
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec
|
||||
//
|
||||
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
|
||||
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage settle
|
||||
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
|
||||
// multiples of the running average timescale:
|
||||
const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
|
||||
|
||||
// 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();
|
||||
}
|
||||
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
|
||||
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
|
||||
|
||||
void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
|
||||
// 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;
|
||||
// Note: we MUST clamp the blend to 1.0 for stability
|
||||
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * renderTime; // msec
|
||||
if (!_automaticLODAdjust) {
|
||||
// early exit
|
||||
return;
|
||||
}
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxTime; // msec
|
||||
|
||||
// translate into fps for legacy implementation
|
||||
float oldOctreeSizeScale = _octreeSizeScale;
|
||||
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
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (currentFPS < getLODDecreaseFPS()) {
|
||||
if (now > _decreaseFPSExpiry) {
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
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) {
|
||||
_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;
|
||||
|
||||
qCDebug(interfaceapp) << "adjusting LOD DOWN"
|
||||
<< "fps =" << currentFPS
|
||||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
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();
|
||||
}
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
|
||||
if (lodToolsDialog) {
|
||||
lodToolsDialog->reloadSliders();
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
} else if (currentFPS > getLODIncreaseFPS()) {
|
||||
if (now > _increaseFPSExpiry) {
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
} else {
|
||||
_octreeSizeScale *= LOD_AUTO_ADJUST_INCREMENT_FACTOR;
|
||||
}
|
||||
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
||||
}
|
||||
qCDebug(interfaceapp) << "adjusting LOD UP"
|
||||
<< "fps =" << currentFPS
|
||||
<< "targetFPS =" << getLODDecreaseFPS()
|
||||
<< "octreeSizeScale =" << _octreeSizeScale;
|
||||
emit LODIncreased();
|
||||
}
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
} else {
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
_decreaseFPSExpiry = _increaseFPSExpiry;
|
||||
}
|
||||
|
||||
if (oldOctreeSizeScale != _octreeSizeScale) {
|
||||
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
|
||||
if (lodToolsDialog) {
|
||||
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() {
|
||||
_fpsAverageStartWindow.reset();
|
||||
_fpsAverageDownWindow.reset();
|
||||
_fpsAverageUpWindow.reset();
|
||||
_lastUpShift = _lastDownShift = usecTimestampNow();
|
||||
_isDownshifting = false;
|
||||
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
||||
const float MIN_DECREASE_FPS = 0.5f;
|
||||
|
@ -206,7 +146,7 @@ float LODManager::getDesktopLODDecreaseFPS() 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) {
|
||||
|
@ -222,7 +162,7 @@ float LODManager::getHMDLODDecreaseFPS() 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() {
|
||||
|
|
|
@ -19,29 +19,13 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
#include <render/Args.h>
|
||||
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0;
|
||||
const float DEFAULT_HMD_LOD_DOWN_FPS = 20.0;
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0f;
|
||||
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_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_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps
|
||||
const float INCREASE_LOD_GAP = 15.0f;
|
||||
|
||||
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;
|
||||
const float MAX_LIKELY_DESKTOP_FPS = 59.0f; // 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_FPS = 15.0f; // fps
|
||||
|
||||
// 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;
|
||||
|
@ -78,7 +62,7 @@ public:
|
|||
Q_INVOKABLE float getLODIncreaseFPS();
|
||||
|
||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||
void autoAdjustLOD(float batchTime, float engineRunTime, float deltaTimeSec);
|
||||
void autoAdjustLOD(float renderTime, float realTimeDelta);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
@ -92,21 +76,15 @@ private:
|
|||
LODManager();
|
||||
|
||||
bool _automaticLODAdjust = true;
|
||||
float _avgRenderTime { 0.0 };
|
||||
float _avgRenderTime { 0.0f };
|
||||
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
|
||||
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
||||
|
||||
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
|
||||
int _boundaryLevelAdjust = 0;
|
||||
|
||||
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;
|
||||
|
||||
uint64_t _decreaseFPSExpiry { 0 };
|
||||
uint64_t _increaseFPSExpiry { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_LODManager_h
|
||||
|
|
|
@ -679,6 +679,7 @@ void OpenGLDisplayPlugin::internalPresent() {
|
|||
void OpenGLDisplayPlugin::present() {
|
||||
auto frameId = (uint64_t)presentCount();
|
||||
PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, frameId)
|
||||
uint64_t startPresent = usecTimestampNow();
|
||||
{
|
||||
PROFILE_RANGE_EX(render, "updateFrameData", 0xff00ff00, frameId)
|
||||
updateFrameData();
|
||||
|
@ -713,6 +714,7 @@ void OpenGLDisplayPlugin::present() {
|
|||
|
||||
gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory());
|
||||
}
|
||||
_movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent));
|
||||
}
|
||||
|
||||
float OpenGLDisplayPlugin::newFramePresentRate() const {
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
#include <QtCore/QWaitCondition>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <shared/Bilateral.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include "Plugin.h"
|
||||
|
||||
|
@ -203,6 +205,7 @@ public:
|
|||
virtual void cycleDebugOutput() {}
|
||||
|
||||
void waitForPresent();
|
||||
float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec
|
||||
|
||||
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator();
|
||||
|
||||
|
@ -220,6 +223,8 @@ protected:
|
|||
|
||||
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator { std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)>() };
|
||||
|
||||
MovingAverage<float, 10> _movingAveragePresent;
|
||||
|
||||
private:
|
||||
QMutex _presentMutex;
|
||||
QWaitCondition _presentCondition;
|
||||
|
|
Loading…
Reference in a new issue