Merge pull request #15400 from danteruiz/throttle-refresh-rate

Case 22253: Refresh Rate Controller
This commit is contained in:
Sam Gateau 2019-04-26 12:04:39 -07:00 committed by GitHub
commit c647bbcb16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 485 additions and 6 deletions

View file

@ -57,6 +57,14 @@ Item {
StatText {
text: "Avatars: " + root.avatarCount
}
StatText {
visible: true
text: "Refresh: " + root.refreshRateRegime + " - " + root.refreshRateTarget
}
StatText {
visible: root.expanded
text:" " + root.refreshRateMode + " - " + root.uxMode;
}
StatText {
text: "Game Rate: " + root.gameLoopRate
}

View file

@ -150,6 +150,7 @@
#include <Preferences.h>
#include <display-plugins/CompositorHelper.h>
#include <display-plugins/hmd/HmdDisplayPlugin.h>
#include <display-plugins/RefreshRateController.h>
#include <trackers/EyeTracker.h>
#include <avatars-renderer/ScriptAvatar.h>
#include <RenderableEntityItem.h>
@ -192,6 +193,7 @@
#include "scripting/WalletScriptingInterface.h"
#include "scripting/TTSScriptingInterface.h"
#include "scripting/KeyboardScriptingInterface.h"
#include "scripting/RefreshRateScriptingInterface.h"
@ -820,7 +822,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
audioDLLPath += "/audioWin7";
}
QCoreApplication::addLibraryPath(audioDLLPath);
#endif
#endif
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
@ -1813,6 +1815,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::STARTUP);
// Setup the _keyboardMouseDevice, _touchscreenDevice, _touchscreenVirtualPadDevice and the user input mapper with the default bindings
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
// if the _touchscreenDevice is not supported it will not be registered
@ -2621,6 +2625,8 @@ void Application::onAboutToQuit() {
_aboutToQuit = true;
cleanupBeforeQuit();
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::SHUTDOWN);
}
void Application::cleanupBeforeQuit() {
@ -3230,6 +3236,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface());
_fileDownload = new FileScriptingInterface(engine);
surfaceContext->setContextProperty("File", _fileDownload);
connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip);
@ -3378,6 +3385,7 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface());
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
@ -4049,6 +4057,9 @@ bool Application::event(QEvent* event) {
case QEvent::KeyRelease:
keyReleaseEvent(static_cast<QKeyEvent*>(event));
return true;
case QEvent::FocusIn:
focusInEvent(static_cast<QFocusEvent*>(event));
return true;
case QEvent::FocusOut:
focusOutEvent(static_cast<QFocusEvent*>(event));
return true;
@ -4110,6 +4121,12 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
}
}
if (event->type() == QEvent::WindowStateChange) {
if (getWindow()->windowState() == Qt::WindowMinimized) {
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::MINIMIZED);
}
}
return false;
}
@ -4396,6 +4413,13 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
}
void Application::focusInEvent(QFocusEvent* event) {
if (!_aboutToQuit && _startUpFinished) {
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::RUNNING);
}
}
void Application::focusOutEvent(QFocusEvent* event) {
auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
foreach(auto inputPlugin, inputPlugins) {
@ -4404,6 +4428,9 @@ void Application::focusOutEvent(QFocusEvent* event) {
}
}
if (!_aboutToQuit && _startUpFinished) {
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::UNFOCUS);
}
// FIXME spacemouse code still needs cleanup
#if 0
//SpacemouseDevice::getInstance().focusOutEvent();
@ -5571,6 +5598,8 @@ void Application::resumeAfterLoginDialogActionTaken() {
menu->getMenu("Developer")->setVisible(_developerMenuVisible);
_myCamera.setMode(_previousCameraMode);
cameraModeChanged();
_startUpFinished = true;
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::RUNNING);
}
void Application::loadAvatarScripts(const QVector<QString>& urls) {
@ -7353,6 +7382,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get<KeyboardScriptingInterface>().data());
scriptEngine->registerGlobalObject("RefreshRate", new RefreshRateScriptingInterface);
scriptEngine->registerGlobalObject("Paths", DependencyManager::get<PathUtils>().data());
@ -8753,6 +8783,7 @@ void Application::updateDisplayMode() {
auto displayPlugins = getDisplayPlugins();
// Default to the first item on the list, in case none of the menu items match
DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0);
auto menu = getPrimaryMenu();
if (menu) {
@ -8842,6 +8873,14 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
if (desktop) {
desktop->setProperty("repositionLocked", wasRepositionLocked);
}
RefreshRateManager& refreshRateManager = getRefreshRateManager();
refreshRateManager.setRefreshRateOperator(OpenGLDisplayPlugin::getRefreshRateOperator());
bool isHmd = newDisplayPlugin->isHmd();
RefreshRateManager::UXMode uxMode = isHmd ? RefreshRateManager::UXMode::HMD :
RefreshRateManager::UXMode::DESKTOP;
refreshRateManager.setUXMode(uxMode);
}
bool isHmd = _displayPlugin->isHmd();

View file

@ -58,6 +58,7 @@
#include "gpu/Context.h"
#include "LoginStateManager.h"
#include "Menu.h"
#include "RefreshRateManager.h"
#include "octree/OctreePacketProcessor.h"
#include "render/Engine.h"
#include "scripting/ControllerScriptingInterface.h"
@ -203,6 +204,7 @@ public:
CompositorHelper& getApplicationCompositor() const;
Overlays& getOverlays() { return _overlays; }
RefreshRateManager& getRefreshRateManager() { return _refreshRateManager; }
size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); }
float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); }
@ -723,6 +725,7 @@ private:
QUuid _loginDialogID;
QUuid _avatarInputsBarID;
LoginStateManager _loginStateManager;
RefreshRateManager _refreshRateManager;
quint64 _lastFaceTrackerUpdate;
@ -820,5 +823,6 @@ private:
bool _resumeAfterLoginDialogActionTaken_WasPostponed { false };
bool _resumeAfterLoginDialogActionTaken_SafeToRun { false };
bool _startUpFinished { false };
};
#endif // hifi_Application_h

View file

@ -0,0 +1,149 @@
//
// RefreshRateManager.cpp
// interface/src/
//
// Created by Dante Ruiz on 2019-04-15.
// Copyright 2019 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 "RefreshRateManager.h"
#include <array>
#include <map>
#include <Application.h>
#include <display-plugins/hmd/HmdDisplayPlugin.h>
static const int HMD_TARGET_RATE = 90;
static const std::array<std::string, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILE_TO_STRING =
{ { "Eco", "Interactive", "Realtime" } };
static const std::array<std::string, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIME_TO_STRING =
{ { "Running", "Unfocus", "Minimized", "StartUp", "ShutDown" } };
static const std::array<std::string, RefreshRateManager::UXMode::UX_NUM> UX_MODE_TO_STRING =
{ { "Desktop", "HMD" } };
static const std::map<std::string, RefreshRateManager::RefreshRateProfile> REFRESH_RATE_PROFILE_FROM_STRING =
{ { "Eco", RefreshRateManager::RefreshRateProfile::ECO },
{ "Interactive", RefreshRateManager::RefreshRateProfile::INTERACTIVE },
{ "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME } };
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> RUNNING_REGIME_PROFILES =
{ { 5, 20, 60 } };
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> UNFOCUS_REGIME_PROFILES =
{ { 5, 5, 10 } };
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> MINIMIZED_REGIME_PROFILE =
{ { 2, 2, 2 } };
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> START_AND_SHUTDOWN_REGIME_PROFILES =
{ { 30, 30, 30 } };
static const std::array<std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM>, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIMES =
{ { RUNNING_REGIME_PROFILES, UNFOCUS_REGIME_PROFILES, MINIMIZED_REGIME_PROFILE,
START_AND_SHUTDOWN_REGIME_PROFILES, START_AND_SHUTDOWN_REGIME_PROFILES } };
std::string RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
return REFRESH_RATE_PROFILE_TO_STRING.at(refreshRateProfile);
}
RefreshRateManager::RefreshRateProfile RefreshRateManager::refreshRateProfileFromString(std::string refreshRateProfile) {
return REFRESH_RATE_PROFILE_FROM_STRING.at(refreshRateProfile);
}
std::string RefreshRateManager::refreshRateRegimeToString(RefreshRateManager::RefreshRateRegime refreshRateRegime) {
return REFRESH_RATE_REGIME_TO_STRING.at(refreshRateRegime);
}
std::string RefreshRateManager::uxModeToString(RefreshRateManager::RefreshRateManager::UXMode uxMode) {
return UX_MODE_TO_STRING.at(uxMode);
}
RefreshRateManager::RefreshRateManager() {
_refreshRateProfile = (RefreshRateManager::RefreshRateProfile) _refreshRateMode.get();
}
void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
if (_refreshRateProfile != refreshRateProfile) {
_refreshRateModeLock.withWriteLock([&] {
_refreshRateProfile = refreshRateProfile;
_refreshRateMode.set((int) refreshRateProfile);
});
updateRefreshRateController();
}
}
RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile() const {
RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME;
if (getUXMode() != RefreshRateManager::UXMode::HMD) {
profile =(RefreshRateManager::RefreshRateProfile) _refreshRateModeLock.resultWithReadLock<int>([&] {
return _refreshRateMode.get();
});
}
return profile;
}
RefreshRateManager::RefreshRateRegime RefreshRateManager::getRefreshRateRegime() const {
return getUXMode() == RefreshRateManager::UXMode::HMD ? RefreshRateManager::RefreshRateRegime::RUNNING :
_refreshRateRegime;
}
void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateRegime refreshRateRegime) {
if (_refreshRateRegime != refreshRateRegime) {
_refreshRateRegime = refreshRateRegime;
updateRefreshRateController();
}
}
void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) {
if (_uxMode != uxMode) {
_uxMode = uxMode;
updateRefreshRateController();
}
}
void RefreshRateManager::updateRefreshRateController() const {
if (_refreshRateOperator) {
int targetRefreshRate;
if (_uxMode == RefreshRateManager::UXMode::DESKTOP) {
if (_refreshRateRegime == RefreshRateManager::RefreshRateRegime::RUNNING &&
_refreshRateProfile == RefreshRateManager::RefreshRateProfile::INTERACTIVE) {
targetRefreshRate = getInteractiveRefreshRate();
} else {
targetRefreshRate = REFRESH_RATE_REGIMES[_refreshRateRegime][_refreshRateProfile];
}
} else {
targetRefreshRate = HMD_TARGET_RATE;
}
_refreshRateOperator(targetRefreshRate);
_activeRefreshRate = targetRefreshRate;
}
}
void RefreshRateManager::setInteractiveRefreshRate(int refreshRate) {
_refreshRateLock.withWriteLock([&] {
_interactiveRefreshRate.set(refreshRate);
});
updateRefreshRateController();
}
int RefreshRateManager::getInteractiveRefreshRate() const {
return _refreshRateLock.resultWithReadLock<int>([&] {
return _interactiveRefreshRate.get();
});
}

View file

@ -0,0 +1,83 @@
//
// RefreshRateManager.h
// interface/src/
//
// Created by Dante Ruiz on 2019-04-15.
// Copyright 2019 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
//
#ifndef hifi_RefreshRateManager_h
#define hifi_RefreshRateManager_h
#include <map>
#include <string>
#include <SettingHandle.h>
#include <shared/ReadWriteLockable.h>
class RefreshRateManager {
public:
enum RefreshRateProfile {
ECO = 0,
INTERACTIVE,
REALTIME,
PROFILE_NUM
};
enum RefreshRateRegime {
RUNNING = 0,
UNFOCUS,
MINIMIZED,
STARTUP,
SHUTDOWN,
REGIME_NUM
};
enum UXMode {
DESKTOP = 0,
HMD,
UX_NUM
};
RefreshRateManager();
~RefreshRateManager() = default;
void setRefreshRateProfile(RefreshRateProfile refreshRateProfile);
RefreshRateProfile getRefreshRateProfile() const;
void setRefreshRateRegime(RefreshRateRegime refreshRateRegime);
RefreshRateRegime getRefreshRateRegime() const;
void setUXMode(UXMode uxMode);
UXMode getUXMode() const { return _uxMode; }
void setRefreshRateOperator(std::function<void(int)> refreshRateOperator) { _refreshRateOperator = refreshRateOperator; }
int getActiveRefreshRate() const { return _activeRefreshRate; }
void updateRefreshRateController() const;
void setInteractiveRefreshRate(int refreshRate);
int getInteractiveRefreshRate() const;
static std::string refreshRateProfileToString(RefreshRateProfile refreshRateProfile);
static RefreshRateProfile refreshRateProfileFromString(std::string refreshRateProfile);
static std::string uxModeToString(UXMode uxMode);
static std::string refreshRateRegimeToString(RefreshRateRegime refreshRateRegime);
private:
mutable ReadWriteLockable _refreshRateLock;
mutable ReadWriteLockable _refreshRateModeLock;
mutable int _activeRefreshRate { 20 };
RefreshRateProfile _refreshRateProfile { RefreshRateProfile::INTERACTIVE};
RefreshRateRegime _refreshRateRegime { RefreshRateRegime::STARTUP };
UXMode _uxMode;
Setting::Handle<int> _interactiveRefreshRate { "interactiveRefreshRate", 20};
Setting::Handle<int> _refreshRateMode { "refreshRateProfile", INTERACTIVE };
std::function<void(int)> _refreshRateOperator { nullptr };
};
#endif

View file

@ -0,0 +1,46 @@
//
// RefreshRateScriptingInterface.h
// interface/src/scrfipting
//
// Created by Dante Ruiz on 2019-04-15.
// Copyright 2019 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
//
#ifndef hifi_RefreshRateScriptingInterface_h
#define hifi_RefreshRateScriptingInterface_h
#include <QtCore/QObject>
#include <Application.h>
class RefreshRateScriptingInterface : public QObject {
Q_OBJECT
public:
RefreshRateScriptingInterface() = default;
~RefreshRateScriptingInterface() = default;
public:
Q_INVOKABLE QString getRefreshRateProfile() {
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
return QString::fromStdString(RefreshRateManager::refreshRateProfileToString(refreshRateManager.getRefreshRateProfile()));
}
Q_INVOKABLE QString getRefreshRateRegime() {
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
return QString::fromStdString(RefreshRateManager::refreshRateRegimeToString(refreshRateManager.getRefreshRateRegime()));
}
Q_INVOKABLE QString getUXMode() {
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
return QString::fromStdString(RefreshRateManager::uxModeToString(refreshRateManager.getUXMode()));
}
Q_INVOKABLE int getActiveRefreshRate() {
return qApp->getRefreshRateManager().getActiveRefreshRate();
}
};
#endif

View file

@ -82,6 +82,28 @@ void setupPreferences() {
preferences->addPreference(new CheckPreference(GRAPHICS_QUALITY, "Show Shadows", getterShadow, setterShadow));
}
{
auto getter = []()->QString {
RefreshRateManager::RefreshRateProfile refreshRateProfile = qApp->getRefreshRateManager().getRefreshRateProfile();
return QString::fromStdString(RefreshRateManager::refreshRateProfileToString(refreshRateProfile));
};
auto setter = [](QString value) {
std::string profileName = value.toStdString();
RefreshRateManager::RefreshRateProfile refreshRateProfile = RefreshRateManager::refreshRateProfileFromString(profileName);
qApp->getRefreshRateManager().setRefreshRateProfile(refreshRateProfile);
};
auto preference = new ComboBoxPreference(GRAPHICS_QUALITY, "Refresh Rate", getter, setter);
QStringList refreshRateProfiles
{ QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::ECO)),
QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::INTERACTIVE)),
QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::REALTIME)) };
preference->setItems(refreshRateProfiles);
preferences->addPreference(preference);
}
// UI
static const QString UI_CATEGORY { "User Interface" };
{

View file

@ -132,6 +132,14 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated());
STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE_FLOAT(renderrate, qApp->getRenderLoopRate(), 0.1f);
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
std::string refreshRateMode = RefreshRateManager::refreshRateProfileToString(refreshRateManager.getRefreshRateProfile());
std::string refreshRateRegime = RefreshRateManager::refreshRateRegimeToString(refreshRateManager.getRefreshRateRegime());
std::string uxMode = RefreshRateManager::uxModeToString(refreshRateManager.getUXMode());
STAT_UPDATE(refreshRateMode, QString::fromStdString(refreshRateMode));
STAT_UPDATE(refreshRateRegime, QString::fromStdString(refreshRateRegime));
STAT_UPDATE(uxMode, QString::fromStdString(uxMode));
STAT_UPDATE(refreshRateTarget, refreshRateManager.getActiveRefreshRate());
if (qApp->getActiveDisplayPlugin()) {
auto displayPlugin = qApp->getActiveDisplayPlugin();
auto stats = displayPlugin->getHardwareStats();

View file

@ -206,6 +206,10 @@ class Stats : public QQuickItem {
STATS_PROPERTY(float, presentdroprate, 0)
STATS_PROPERTY(int, gameLoopRate, 0)
STATS_PROPERTY(int, avatarCount, 0)
STATS_PROPERTY(int, refreshRateTarget, 0)
STATS_PROPERTY(QString, refreshRateMode, QString())
STATS_PROPERTY(QString, refreshRateRegime, QString())
STATS_PROPERTY(QString, uxMode, QString())
STATS_PROPERTY(int, heroAvatarCount, 0)
STATS_PROPERTY(int, physicsObjectCount, 0)
STATS_PROPERTY(int, updatedAvatarCount, 0)
@ -1067,6 +1071,15 @@ signals:
*/
void decimatedTextureCountChanged();
void refreshRateTargetChanged();
void refreshRateModeChanged();
void refreshRateRegimeChanged();
void uxModeChanged();
// QQuickItem signals.
/**jsdoc

View file

@ -46,7 +46,7 @@
#include <TextureCache.h>
#include "CompositorHelper.h"
#include "Logging.h"
#include "RefreshRateController.h"
extern QThread* RENDER_THREAD;
class PresentThread : public QThread, public Dependency {
@ -60,12 +60,16 @@ public:
shutdown();
});
setObjectName("Present");
_refreshRateController = std::make_shared<RefreshRateController>();
}
~PresentThread() {
shutdown();
}
auto getRefreshRateController() { return _refreshRateController; }
void shutdown() {
if (isRunning()) {
// First ensure that we turn off any current display plugin
@ -182,10 +186,11 @@ public:
{
PROFILE_RANGE(render, "PluginPresent")
gl::globalLock();
currentPlugin->present();
currentPlugin->present(_refreshRateController);
gl::globalRelease(false);
CHECK_GL_ERROR();
}
_refreshRateController->sleepThreadIfNeeded(this, currentPlugin->isHmd());
}
_context->doneCurrent();
@ -234,6 +239,7 @@ private:
bool _finishedOtherThreadOperation { false };
std::queue<OpenGLDisplayPlugin*> _newPluginQueue;
gl::Context* _context { nullptr };
std::shared_ptr<RefreshRateController> _refreshRateController { nullptr };
};
bool OpenGLDisplayPlugin::activate() {
@ -687,11 +693,11 @@ void OpenGLDisplayPlugin::internalPresent() {
_presentRate.increment();
}
void OpenGLDisplayPlugin::present() {
void OpenGLDisplayPlugin::present(const std::shared_ptr<RefreshRateController>& refreshRateController) {
auto frameId = (uint64_t)presentCount();
PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, frameId)
uint64_t startPresent = usecTimestampNow();
refreshRateController->clockStartTime();
{
PROFILE_RANGE_EX(render, "updateFrameData", 0xff00ff00, frameId)
updateFrameData();
@ -735,6 +741,7 @@ void OpenGLDisplayPlugin::present() {
}
// Take the composite framebuffer and send it to the output device
refreshRateController->clockEndTime();
{
PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
internalPresent();
@ -742,7 +749,10 @@ void OpenGLDisplayPlugin::present() {
gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory());
} else if (alwaysPresent()) {
refreshRateController->clockEndTime();
internalPresent();
} else {
refreshRateController->clockEndTime();
}
_movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent));
}
@ -759,6 +769,13 @@ float OpenGLDisplayPlugin::presentRate() const {
return _presentRate.rate();
}
std::function<void(int)> OpenGLDisplayPlugin::getRefreshRateOperator() {
return [](int targetRefreshRate) {
auto refreshRateController = DependencyManager::get<PresentThread>()->getRefreshRateController();
refreshRateController->setRefreshRateLimitPeriod(targetRefreshRate);
};
}
void OpenGLDisplayPlugin::resetPresentRate() {
// FIXME
// _presentRate = RateCounter<100>();

View file

@ -29,6 +29,8 @@ namespace gpu {
}
}
class RefreshRateController;
class OpenGLDisplayPlugin : public DisplayPlugin {
Q_OBJECT
Q_PROPERTY(float hudAlpha MEMBER _hudAlpha)
@ -41,6 +43,9 @@ public:
~OpenGLDisplayPlugin();
// These must be final to ensure proper ordering of operations
// between the main thread and the presentation thread
static std::function<void(int)> getRefreshRateOperator();
bool activate() override final;
void deactivate() override final;
bool startStandBySession() override final;
@ -123,7 +128,7 @@ protected:
void withOtherThreadContext(std::function<void()> f) const;
void present();
void present(const std::shared_ptr<RefreshRateController>& refreshRateController);
virtual void swapBuffers();
ivec4 eyeViewport(Eye eye) const;

View file

@ -0,0 +1,43 @@
//
// RefreshRateController.cpp
// libraries/display-pluging/src/display-plugin
//
// Created by Dante Ruiz on 2019-04-15.
// Copyright 2019 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 "RefreshRateController.h"
#include <QtCore/QThread>
#include <NumericalConstants.h>
long int hzToDurationNanoseconds(int refreshRate) {
return (int64_t) (NSECS_PER_SECOND / (quint64) refreshRate);
}
int durationNanosecondsToHz(int64_t refreshRateLimitPeriod) {
return (int) (NSECS_PER_SECOND / (quint64) refreshRateLimitPeriod);
}
void RefreshRateController::setRefreshRateLimitPeriod(int refreshRateLimit) {
_refreshRateLimitPeriod = hzToDurationNanoseconds(refreshRateLimit);
}
int RefreshRateController::getRefreshRateLimitPeriod() const {
return durationNanosecondsToHz(_refreshRateLimitPeriod);
}
void RefreshRateController::sleepThreadIfNeeded(QThread* thread, bool isHmd) {
if (!isHmd) {
static const std::chrono::nanoseconds EPSILON = std::chrono::milliseconds(1);
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(_endTime - _startTime);
auto refreshRateLimitPeriod = std::chrono::nanoseconds(_refreshRateLimitPeriod);
auto sleepDuration = refreshRateLimitPeriod - (duration + EPSILON);
if (sleepDuration.count() > 0) {
thread->msleep(std::chrono::duration_cast<std::chrono::milliseconds>(sleepDuration).count());
}
}
}

View file

@ -0,0 +1,40 @@
//
// RefreshRateController.h
// libraries/display-pluging/src/display-plugin
//
// Created by Dante Ruiz on 2019-04-15.
// Copyright 2019 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
//
#ifndef hifi_RefreshRateController_h
#define hifi_RefreshRateController_h
#include <DependencyManager.h>
#include <chrono>
#include <atomic>
class QThread;
class RefreshRateController {
public:
RefreshRateController() = default;
~RefreshRateController() = default;
void setRefreshRateLimitPeriod(int refreshRateLimit);
int getRefreshRateLimitPeriod() const;
void clockStartTime() { _startTime = std::chrono::high_resolution_clock::now(); }
void clockEndTime() { _endTime = std::chrono::high_resolution_clock::now(); }
void sleepThreadIfNeeded(QThread* thread, bool isHmd);
private:
std::chrono::time_point<std::chrono::high_resolution_clock> _startTime { std::chrono::high_resolution_clock::now() };
std::chrono::time_point<std::chrono::high_resolution_clock> _endTime { std::chrono::high_resolution_clock::now() };
std::atomic<int64_t> _refreshRateLimitPeriod { 50 };
};
#endif

View file

@ -90,6 +90,7 @@ public:
// HMD display functionality
// TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when
// displayPlugin->isHmd returns true
class RefreshRateController;
class HmdDisplay : public StereoDisplay {
public:
// HMD specific methods
@ -128,6 +129,7 @@ public:
/// By default, all HMDs are stereo
virtual bool isStereo() const { return isHmd(); }
virtual bool isThrottled() const { return false; }
virtual float getTargetFrameRate() const { return 1.0f; }
virtual bool hasAsyncReprojection() const { return false; }