diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4494203a49..97bc3f3689 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2801,7 +2801,11 @@ void Application::update(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); - updateLOD(); + if (DependencyManager::get()->getUseAcuity()) { + updateLOD(); + } else { + DependencyManager::get()->updatePIDRenderDistance(getTargetFrameRate(), getLastInstanteousFps(), deltaTime, isThrottleRendering()); + } { PerformanceTimer perfTimer("devices"); diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 368143e36e..dfe03137d5 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -20,9 +20,25 @@ Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); +// There are two different systems in use, based on lodPreference: +// pid: renderDistance is automatically adjusted such that frame rate targets are met. +// acuity: a pseudo-acuity target is held, or adjusted to match minimum frame rates. +// If unspecified, acuity is used only if user has specified non-default minumum frame rates. +Setting::Handle lodPreference("lodPreference", (int)LODManager::LODPreference::unspecified); LODManager::LODManager() { calculateAvatarLODDistanceMultiplier(); + + _renderDistanceController.setControlledValueHighLimit(20.0f); + _renderDistanceController.setControlledValueLowLimit(1.0f / (float)TREE_SCALE); + // Advice for tuning parameters: + // See PIDController.h. There's a section on tuning in the reference. + // Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300)) + //_renderDistanceController.setHistorySize("avatar render", target_fps * 4); + // Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on. + _renderDistanceController.setKP(0.000012f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0. + _renderDistanceController.setKI(0.00002f); // Big enough to bring us to target with the above KP. + _renderDistanceController.setHistorySize("FIXME", 240); } float LODManager::getLODDecreaseFPS() { @@ -39,7 +55,6 @@ float LODManager::getLODIncreaseFPS() { return getDesktopLODIncreaseFPS(); } - void LODManager::autoAdjustLOD(float currentFPS) { // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't @@ -217,15 +232,53 @@ QString LODManager::getLODFeedbackText() { return result; } +static float renderDistance = (float)TREE_SCALE; +static int renderedCount = 0; +static int lastRenderedCount = 0; +bool LODManager::getUseAcuity() { return lodPreference.get() == (int)LODManager::LODPreference::acuity; } +void LODManager::setUseAcuity(bool newValue) { lodPreference.set(newValue ? (int)LODManager::LODPreference::acuity : (int)LODManager::LODPreference::pid); } +float LODManager::getRenderDistance() { + return renderDistance; +} +int LODManager::getRenderedCount() { + return lastRenderedCount; +} +// compare audoAdjustLOD() +void LODManager::updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled) { + float distance; + if (!isThrottled) { + _renderDistanceController.setMeasuredValueSetpoint(targetFps); // No problem updating in flight. + // The PID controller raises the controlled value when the measured value goes up. + // The measured value is frame rate. When the controlled value (1 / render cutoff distance) + // goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate + // goes up. + distance = 1.0f / _renderDistanceController.update(measuredFps, deltaTime); + } + else { + // Here we choose to just use the maximum render cutoff distance if throttled. + distance = 1.0f / _renderDistanceController.getControlledValueLowLimit(); + } + _renderDistanceAverage.updateAverage(distance); + renderDistance = _renderDistanceAverage.getAverage(); // average only once per cycle + lastRenderedCount = renderedCount; + renderedCount = 0; +} + bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { + float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition()); + float largestDimension = bounds.getLargestDimension(); + if (!getUseAcuity()) { + bool isRendered = fabsf(distanceToCamera - largestDimension) < renderDistance; + renderedCount += isRendered ? 1 : 0; + return isRendered; + } + const float maxScale = (float)TREE_SCALE; const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it. float octreeSizeScale = args->_sizeScale; int boundaryLevelAdjust = args->_boundaryLevelAdjust; float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio; - float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition()); - float largestDimension = bounds.getLargestDimension(); - + static bool shouldRenderTableNeedsBuilding = true; static QMap shouldRenderTable; if (shouldRenderTableNeedsBuilding) { @@ -315,6 +368,11 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { void LODManager::loadSettings() { setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get()); setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get()); + + if (lodPreference.get() == (int)LODManager::LODPreference::unspecified) { + setUseAcuity((getDesktopLODDecreaseFPS() != DEFAULT_DESKTOP_LOD_DOWN_FPS) || (getHMDLODDecreaseFPS() != DEFAULT_HMD_LOD_DOWN_FPS)); + } + Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(getUseAcuity()); } void LODManager::saveSettings() { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index b0185b528f..21a30bceaf 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -15,6 +15,7 @@ #include #include #include +#include #include const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 15.0; @@ -81,6 +82,24 @@ public: Q_INVOKABLE float getLODDecreaseFPS(); Q_INVOKABLE float getLODIncreaseFPS(); + enum class LODPreference { + pid = 0, + acuity, + unspecified + }; + static bool getUseAcuity(); + static void setUseAcuity(bool newValue); + Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); } + Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); } + Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); } + Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); } + Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); } + Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); } + Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue) { _renderDistanceController.setControlledValueHighLimit(newValue); } + void updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled); + float getRenderDistance(); + int getRenderedCount(); + static bool shouldRender(const RenderArgs* args, const AABox& bounds); bool shouldRenderMesh(float largestDimension, float distanceToCamera); void autoAdjustLOD(float currentFPS); @@ -116,6 +135,9 @@ private: bool _shouldRenderTableNeedsRebuilding = true; QMap _shouldRenderTable; + + PIDController _renderDistanceController{}; + SimpleMovingAverage _renderDistanceAverage{ 10 }; }; #endif // hifi_LODManager_h diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9a995263cd..bc08391bbe 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -48,6 +48,7 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser); connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL); + connect(ui.useAcuityCheckBox, &QCheckBox::clicked, this, &PreferencesDialog::changeUseAcuity); connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged); @@ -58,6 +59,15 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : UIUtil::scaleWidgetFontSizes(this); } +void PreferencesDialog::changeUseAcuity() { + bool useAcuity = ui.useAcuityCheckBox->isChecked(); + ui.label_desktopMinimumFPSSpin->setEnabled(useAcuity); + ui.desktopMinimumFPSSpin->setEnabled(useAcuity); + ui.label_hmdMinimumFPSSpin->setEnabled(useAcuity); + ui.hmdMinimumFPSSpin->setEnabled(useAcuity); + ui.label_smallestReasonableRenderHorizon->setText(useAcuity ? "Minimum Avatar Display Distance (@half speed)" : "Minimum Display Distance (@half speed)"); + Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(useAcuity); +} void PreferencesDialog::changeFullAvatarURL() { DependencyManager::get()->getMyAvatar()->useFullAvatarURL(ui.appearanceDescription->text(), ""); this->fullAvatarURLChanged(ui.appearanceDescription->text(), ""); @@ -212,9 +222,11 @@ void PreferencesDialog::loadPreferences() { // LOD items auto lodManager = DependencyManager::get(); + ui.useAcuityCheckBox->setChecked(lodManager->getUseAcuity()); ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS()); ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS()); - ui.avatarRenderSmallestReasonableHorizon->setValue(1.0f / DependencyManager::get()->getRenderDistanceInverseHighLimit()); + ui.smallestReasonableRenderHorizon->setValue(1.0f / DependencyManager::get()->getRenderDistanceInverseHighLimit()); + changeUseAcuity(); } void PreferencesDialog::savePreferences() { @@ -303,7 +315,8 @@ void PreferencesDialog::savePreferences() { // LOD items auto lodManager = DependencyManager::get(); + lodManager->setUseAcuity(ui.useAcuityCheckBox->isChecked()); lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value()); lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value()); - DependencyManager::get()->setRenderDistanceInverseHighLimit(1.0f / ui.avatarRenderSmallestReasonableHorizon->value()); + DependencyManager::get()->setRenderDistanceInverseHighLimit(1.0f / ui.smallestReasonableRenderHorizon->value()); } diff --git a/interface/src/ui/PreferencesDialog.h b/interface/src/ui/PreferencesDialog.h index 1536eca3ee..a6c27dee08 100644 --- a/interface/src/ui/PreferencesDialog.h +++ b/interface/src/ui/PreferencesDialog.h @@ -51,6 +51,7 @@ private slots: void openScriptsLocationBrowser(); void changeFullAvatarURL(); void fullAvatarURLChanged(const QString& newValue, const QString& modelName); + void changeUseAcuity(); }; #endif // hifi_PreferencesDialog_h diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 6acacee41d..e391d5c40a 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -116,8 +116,8 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); - STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange()); - STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating + STAT_UPDATE(avatarRenderableCount, DependencyManager::get()->getRenderedCount()); //FIXME avatarManager->getNumberInRenderRange()); + STAT_UPDATE(avatarRenderDistance, (int)round(DependencyManager::get()->getRenderDistance())); // FIXME avatarManager->getRenderDistance())); // deliberately truncating STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE(renderrate, (int)qApp->getFps()); if (qApp->getActiveDisplayPlugin()) { diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index e6a5e2228d..a66c4bf7ee 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -742,6 +742,43 @@ + + + + + 0 + 0 + + + + + 32 + 28 + + + + + 0 + 0 + + + + + Arial + + + + Render based on visual acuity + + + + 0 + 0 + + + + + @@ -757,7 +794,7 @@ 7 - + Arial @@ -842,7 +879,7 @@ 7 - + Arial @@ -927,7 +964,7 @@ 7 - + Arial @@ -963,7 +1000,7 @@ - + 100