From 82cab5e4a1552c6a987fba8d4ae3c9cfa8aef65a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 11 Jan 2018 17:53:34 -0800 Subject: [PATCH] add scripted/qml debug UI for render LOD --- interface/src/Application.cpp | 5 +- interface/src/LODManager.cpp | 44 ++++++++--- interface/src/LODManager.h | 49 +++++++++--- scripts/developer/utilities/render/lod.js | 77 ++++++++++++++++++ scripts/developer/utilities/render/lod.qml | 92 ++++++++++++++++++++++ scripts/system/assets/images/lod-a.svg | 46 +++++++++++ scripts/system/assets/images/lod-i.svg | 46 +++++++++++ 7 files changed, 333 insertions(+), 26 deletions(-) create mode 100644 scripts/developer/utilities/render/lod.js create mode 100644 scripts/developer/utilities/render/lod.qml create mode 100644 scripts/system/assets/images/lod-a.svg create mode 100644 scripts/system/assets/images/lod-i.svg diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6849bd07b5..cccb61c1f2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4351,8 +4351,9 @@ void Application::updateLOD(float deltaTime) const { float presentTime = getActiveDisplayPlugin()->getAveragePresentTime(); float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime()); float gpuTime = getGPUContext()->getFrameTimerGPUAverage(); - float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime)); - DependencyManager::get()->autoAdjustLOD(maxRenderTime, deltaTime); + auto lodManager = DependencyManager::get(); + lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime); + lodManager->autoAdjustLOD(deltaTime); } else { DependencyManager::get()->resetLODAdjust(); } diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 087a93cffe..d2d502dc47 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -26,21 +26,21 @@ Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO LODManager::LODManager() { } -float LODManager::getLODDecreaseFPS() { +float LODManager::getLODDecreaseFPS() const { if (qApp->isHMDMode()) { return getHMDLODDecreaseFPS(); } return getDesktopLODDecreaseFPS(); } -float LODManager::getLODIncreaseFPS() { +float LODManager::getLODIncreaseFPS() const { if (qApp->isHMDMode()) { return getHMDLODIncreaseFPS(); } return getDesktopLODIncreaseFPS(); } -// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds +// We use a "time-weighted running average" of the maxRenderTime 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 @@ -58,11 +58,18 @@ const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TI 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 +void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) { + _presentTime = presentTime; + _engineRunTime = engineRunTime; + _gpuTime = gpuTime; +} + +void LODManager::autoAdjustLOD(float realTimeDelta) { + float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime); + // compute time-weighted running average maxRenderTime // 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 + _avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec if (!_automaticLODAdjust) { // early exit return; @@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { << "targetFPS =" << getLODDecreaseFPS() << "octreeSizeScale =" << _octreeSizeScale; emit LODDecreased(); + // Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime + // to provide an FPS just above the decrease threshold. It will drift close to its + // true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary. + _avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f); } _decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD; } @@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { << "targetFPS =" << getLODDecreaseFPS() << "octreeSizeScale =" << _octreeSizeScale; emit LODIncreased(); + // Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime + // to provide an FPS just below the increase threshold. It will drift close to its + // true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary. + _avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f); } _increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD; } @@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { 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; } } @@ -131,6 +141,18 @@ void LODManager::resetLODAdjust() { _decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD; } +float LODManager::getLODLevel() const { + // simpleLOD is a linearized and normalized number that represents how much LOD is being applied. + // It ranges from: + // 1.0 = normal (max) level of detail + // 0.0 = min level of detail + // In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0. + const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE); + float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE); + float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO; + return simpleLOD; +} + const float MIN_DECREASE_FPS = 0.5f; void LODManager::setDesktopLODDecreaseFPS(float fps) { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index cf38342db0..344ee49850 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -37,7 +37,7 @@ class AABox; class LODManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - + public: Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; } Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; } @@ -49,34 +49,57 @@ public: Q_INVOKABLE void setHMDLODDecreaseFPS(float value); Q_INVOKABLE float getHMDLODDecreaseFPS() const; Q_INVOKABLE float getHMDLODIncreaseFPS() const; - + // User Tweakable LOD Items Q_INVOKABLE QString getLODFeedbackText(); Q_INVOKABLE void setOctreeSizeScale(float sizeScale); Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; } - + Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } - - Q_INVOKABLE float getLODDecreaseFPS(); - Q_INVOKABLE float getLODIncreaseFPS(); - + + Q_INVOKABLE float getLODDecreaseFPS() const; + Q_INVOKABLE float getLODIncreaseFPS() const; + Q_INVOKABLE float getFoo() const { return getMeasuredFPS(); } + + Q_PROPERTY(float presentTime READ getPresentTime) + Q_PROPERTY(float engineRunTime READ getEngineRunTime) + Q_PROPERTY(float gpuTime READ getGPUTime) + Q_PROPERTY(float avgRenderTime READ getAverageRenderTime) + Q_PROPERTY(float fps READ getMeasuredFPS) + Q_PROPERTY(float lodLevel READ getLODLevel) + + Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS) + Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS) + + float getPresentTime() const { return _presentTime; } + float getEngineRunTime() const { return _engineRunTime; } + float getGPUTime() const { return _gpuTime; } + static bool shouldRender(const RenderArgs* args, const AABox& bounds); - void autoAdjustLOD(float renderTime, float realTimeDelta); - + void setRenderTimes(float presentTime, float engineRunTime, float gpuTime); + void autoAdjustLOD(float realTimeDelta); + void loadSettings(); void saveSettings(); void resetLODAdjust(); - + + float getAverageRenderTime() const { return _avgRenderTime; }; + float getMeasuredFPS() const { return (float)MSEC_PER_SECOND / _avgRenderTime; }; + float getLODLevel() const; + signals: void LODIncreased(); void LODDecreased(); - + private: LODManager(); - + bool _automaticLODAdjust = true; - float _avgRenderTime { 0.0f }; + float _presentTime { 0.0f }; // msec + float _engineRunTime { 0.0f }; // msec + float _gpuTime { 0.0f }; // msec + float _avgRenderTime { 0.0f }; // msec float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME }; float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME }; diff --git a/scripts/developer/utilities/render/lod.js b/scripts/developer/utilities/render/lod.js new file mode 100644 index 0000000000..dc0b99edc2 --- /dev/null +++ b/scripts/developer/utilities/render/lod.js @@ -0,0 +1,77 @@ +"use strict"; + +// +// lodi.js +// tablet-engine app +// +// Copyright 2018 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 +// + +(function() { + var TABLET_BUTTON_NAME = "LOD"; + var QMLAPP_URL = Script.resolvePath("./lod.qml"); + var ICON_URL = Script.resolvePath("../../../system/assets/images/lod-i.svg"); + var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/lod-a.svg"); + + var onScreen = false; + + function onClicked() { + if (onScreen) { + tablet.gotoHomeScreen(); + } else { + tablet.loadQMLSource(QMLAPP_URL); + } + } + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: ICON_URL, + activeIcon: ACTIVE_ICON_URL, + sortOrder: 1 + }); + + var hasEventBridge = false; + + function wireEventBridge(on) { + if (!tablet) { + print("Warning in wireEventBridge(): 'tablet' undefined!"); + return; + } + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQml); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQml); + hasEventBridge = false; + } + } + } + + function onScreenChanged(type, url) { + onScreen = (url === QMLAPP_URL); + button.editProperties({isActive: onScreen}); + wireEventBridge(onScreen); + } + + function fromQml(message) { + } + + button.clicked.connect(onClicked); + tablet.screenChanged.connect(onScreenChanged); + + Script.scriptEnding.connect(function () { + if (onScreen) { + tablet.gotoHomeScreen(); + } + button.clicked.disconnect(onClicked); + tablet.screenChanged.disconnect(onScreenChanged); + tablet.removeButton(button); + }); +}()); diff --git a/scripts/developer/utilities/render/lod.qml b/scripts/developer/utilities/render/lod.qml new file mode 100644 index 0000000000..d7b9f1cd57 --- /dev/null +++ b/scripts/developer/utilities/render/lod.qml @@ -0,0 +1,92 @@ +// +// lod.qml +// scripts/developer/utilities/render +// +// Created by Andrew Meadows on 2018.01.10 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../lib/plotperf" + +Item { + id: lodIU + anchors.fill:parent + + Column { + id: stats + spacing: 8 + anchors.fill:parent + + function evalEvenHeight() { + // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ? + return (height - spacing * (children.length - 1)) / children.length + } + + PlotPerf { + title: "Load Indicators" + height: parent.evalEvenHeight() + object: LODManager + valueScale: 1 + valueUnit: "ms" + plots: [ + { + prop: "presentTime", + label: "present", + color: "#FFFF00" + }, + { + prop: "engineRunTime", + label: "engineRun", + color: "#FF00FF" + }, + { + prop: "gpuTime", + label: "gpu", + color: "#00FFFF" + } + ] + } + PlotPerf { + title: "FPS" + height: parent.evalEvenHeight() + object: LODManager + valueScale: 1 + valueUnit: "Hz" + plots: [ + { + prop: "lodIncreaseFPS", + label: "LOD++", + color: "#66FF66" + }, + { + prop: "fps", + label: "FPS", + color: "#FFFFFF" + }, + { + prop: "lodDecreaseFPS", + label: "LOD--", + color: "#FF6666" + } + ] + } + PlotPerf { + title: "LOD" + height: parent.evalEvenHeight() + object: LODManager + valueScale: 0.1 + valueUnit: "" + plots: [ + { + prop: "lodLevel", + label: "LOD", + color: "#9999FF" + } + ] + } + } +} diff --git a/scripts/system/assets/images/lod-a.svg b/scripts/system/assets/images/lod-a.svg new file mode 100644 index 0000000000..6845e0ff78 --- /dev/null +++ b/scripts/system/assets/images/lod-a.svg @@ -0,0 +1,46 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/scripts/system/assets/images/lod-i.svg b/scripts/system/assets/images/lod-i.svg new file mode 100644 index 0000000000..f909f3b495 --- /dev/null +++ b/scripts/system/assets/images/lod-i.svg @@ -0,0 +1,46 @@ + + + +image/svg+xml \ No newline at end of file