diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index e10f86a947..05886f59ce 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -77,6 +77,12 @@ Item { visible: root.expanded text: " Present New Rate: " + root.presentnewrate.toFixed(2); } + StatText { + visible: root.expanded + text: " RefreshRateController:\n " + + "refreshRateTarget:\t " + root.refreshRateTarget + "\n " + + "refreshRateMode:\t " + root.refreshRateMode ; + } StatText { visible: root.expanded text: " Present Drop Rate: " + root.presentdroprate.toFixed(2); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f8c115f555..b6024b00e2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -150,6 +150,7 @@ #include #include #include +#include #include #include #include @@ -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(); DependencyManager::registerInheritance(); @@ -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 @@ -2613,6 +2617,8 @@ void Application::onAboutToQuit() { _aboutToQuit = true; cleanupBeforeQuit(); + + getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::SHUTDOWN); } void Application::cleanupBeforeQuit() { @@ -3222,6 +3228,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) { surfaceContext->setContextProperty("Controller", DependencyManager::get().data()); surfaceContext->setContextProperty("Entities", DependencyManager::get().data()); + surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface()); _fileDownload = new FileScriptingInterface(engine); surfaceContext->setContextProperty("File", _fileDownload); connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip); @@ -3370,6 +3377,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 @@ -4041,6 +4049,9 @@ bool Application::event(QEvent* event) { case QEvent::KeyRelease: keyReleaseEvent(static_cast(event)); return true; + case QEvent::FocusIn: + focusInEvent(static_cast(event)); + return true; case QEvent::FocusOut: focusOutEvent(static_cast(event)); return true; @@ -4102,6 +4113,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; } @@ -4388,6 +4405,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) { @@ -4396,6 +4420,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(); @@ -5563,6 +5590,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& urls) { @@ -7345,6 +7374,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("RefreshRate", new RefreshRateScriptingInterface); scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); @@ -8758,6 +8788,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) { @@ -8847,6 +8878,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(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 12523f3108..3cb6d4ddbc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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(); } @@ -721,6 +723,7 @@ private: QUuid _loginDialogID; QUuid _avatarInputsBarID; LoginStateManager _loginStateManager; + RefreshRateManager _refreshRateManager; quint64 _lastFaceTrackerUpdate; @@ -818,5 +821,6 @@ private: bool _resumeAfterLoginDialogActionTaken_WasPostponed { false }; bool _resumeAfterLoginDialogActionTaken_SafeToRun { false }; + bool _startUpFinished { false }; }; #endif // hifi_Application_h diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp new file mode 100644 index 0000000000..f67c6cf34f --- /dev/null +++ b/interface/src/RefreshRateManager.cpp @@ -0,0 +1,138 @@ +// +// 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 +#include + + +#include + +#include + +static const int HMD_TARGET_RATE = 90; + +static const std::array REFRESH_RATE_PROFILE_TO_STRING = + { { "Eco", "Interactive", "Realtime" } }; + +static const std::array REFRESH_RATE_REGIME_TO_STRING = + { { "Running", "Unfocus", "Minimized", "StartUp", "ShutDown" } }; + +static const std::array UX_MODE_TO_STRING = + { { "Desktop", "HMD" } }; + +static const std::map REFRESH_RATE_PROFILE_FROM_STRING = + { { "Eco", RefreshRateManager::RefreshRateProfile::ECO }, + { "Interactive", RefreshRateManager::RefreshRateProfile::INTERACTIVE }, + { "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME } }; + +static const std::array RUNNING_REGIME_PROFILES = + { { 5, 20, 60 } }; + +static const std::array UNFOCUS_REGIME_PROFILES = + { { 5, 5, 10 } }; + +static const std::array MINIMIZED_REGIME_PROFILE = + { { 2, 2, 2 } }; + +static const std::array START_AND_SHUTDOWN_REGIME_PROFILES = + { { 30, 30, 30 } }; + +static const std::array, 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 { + return (RefreshRateManager::RefreshRateProfile) _refreshRateModeLock.resultWithReadLock([&] { + return _refreshRateMode.get(); + }); +} + +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([&] { + return _interactiveRefreshRate.get(); + }); +} diff --git a/interface/src/RefreshRateManager.h b/interface/src/RefreshRateManager.h new file mode 100644 index 0000000000..884725928c --- /dev/null +++ b/interface/src/RefreshRateManager.h @@ -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 +#include + +#include +#include + +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 { return _refreshRateRegime; } + + void setUXMode(UXMode uxMode); + UXMode getUXMode() const { return _uxMode; } + + void setRefreshRateOperator(std::function 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 _interactiveRefreshRate { "interactiveRefreshRate", 20}; + Setting::Handle _refreshRateMode { "refreshRateProfile", INTERACTIVE }; + + std::function _refreshRateOperator { nullptr }; +}; + +#endif diff --git a/interface/src/scripting/RefreshRateScriptingInterface.h b/interface/src/scripting/RefreshRateScriptingInterface.h new file mode 100644 index 0000000000..697141583f --- /dev/null +++ b/interface/src/scripting/RefreshRateScriptingInterface.h @@ -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 + +#include + +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 diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 75279ef889..bb84b88a1e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -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" }; { diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 022b57c0d9..c254c0f1b7 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -222,6 +222,10 @@ void Stats::updateStats(bool force) { STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getWorldVelocity()), 0.01f); STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); if (_expanded || force) { + RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager(); + std::string refreshRateMode = RefreshRateManager::refreshRateProfileToString(refreshRateManager.getRefreshRateProfile()); + STAT_UPDATE(refreshRateMode, QString::fromStdString(refreshRateMode)); + STAT_UPDATE(refreshRateTarget, refreshRateManager.getActiveRefreshRate()); SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { STAT_UPDATE(avatarMixerInKbps, (int)roundf(avatarMixer->getInboundKbps())); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 3134b223d6..5c39001fda 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -206,6 +206,8 @@ 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(int, heroAvatarCount, 0) STATS_PROPERTY(int, physicsObjectCount, 0) STATS_PROPERTY(int, updatedAvatarCount, 0) @@ -1067,6 +1069,11 @@ signals: */ void decimatedTextureCountChanged(); + + void refreshRateTargetChanged(); + + void refreshRateModeChanged(); + // QQuickItem signals. /**jsdoc diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 4c3de004bd..3b0f13f946 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -46,7 +46,7 @@ #include #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(); } ~PresentThread() { shutdown(); } + auto getRefreshRateController() { return _refreshRateController; } + void shutdown() { if (isRunning()) { // First ensure that we turn off any current display plugin @@ -179,6 +183,7 @@ public: } // Execute the frame and present it to the display device. + _refreshRateController->clockStartTime(); { PROFILE_RANGE(render, "PluginPresent") gl::globalLock(); @@ -186,6 +191,10 @@ public: gl::globalRelease(false); CHECK_GL_ERROR(); } + // stop time + _refreshRateController->clockEndTime(); + _refreshRateController->sleepThreadIfNeeded(this); + // sleep if needed } _context->doneCurrent(); @@ -234,6 +243,7 @@ private: bool _finishedOtherThreadOperation { false }; std::queue _newPluginQueue; gl::Context* _context { nullptr }; + std::shared_ptr _refreshRateController { nullptr }; }; bool OpenGLDisplayPlugin::activate() { @@ -759,6 +769,13 @@ float OpenGLDisplayPlugin::presentRate() const { return _presentRate.rate(); } +std::function OpenGLDisplayPlugin::getRefreshRateOperator() { + return [](int targetRefreshRate) { + auto refreshRateController = DependencyManager::get()->getRefreshRateController(); + refreshRateController->setRefreshRateLimit(targetRefreshRate); + }; +} + void OpenGLDisplayPlugin::resetPresentRate() { // FIXME // _presentRate = RateCounter<100>(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index e56804c151..8e6caae892 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -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 getRefreshRateOperator(); + bool activate() override final; void deactivate() override final; bool startStandBySession() override final; diff --git a/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp b/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp new file mode 100644 index 0000000000..2e583d5c9b --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/RefreshRateController.cpp @@ -0,0 +1,42 @@ +// +// 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 + +int refreshRateConverter(int refreshRate) { + const float ONE_SECOND_IN_MILLISECONDS = 1000.0f; + const float ONE_TIME_UNIT = 1.0f; + float frameInOneTimeUnit = ONE_TIME_UNIT / (float) refreshRate; + float convertedRefreshRate = frameInOneTimeUnit * ONE_SECOND_IN_MILLISECONDS; + return (int) convertedRefreshRate; +} + +void RefreshRateController::setRefreshRateLimit(int refreshRateLimit) { + _refreshRateLimit = refreshRateConverter(refreshRateLimit); +} + +int RefreshRateController::getRefreshRateLimit() const { + return refreshRateConverter(_refreshRateLimit); +} + +void RefreshRateController::sleepThreadIfNeeded(QThread* thread) { + auto startTimeFromEpoch = _startTime.time_since_epoch(); + auto endTimeFromEpoch = _endTime.time_since_epoch(); + + auto startMs = std::chrono::duration_cast(startTimeFromEpoch).count(); + auto endMs = std::chrono::duration_cast(endTimeFromEpoch).count(); + auto duration = endMs - startMs; + if (duration < _refreshRateLimit) { + thread->msleep(_refreshRateLimit - duration); + } +} diff --git a/libraries/display-plugins/src/display-plugins/RefreshRateController.h b/libraries/display-plugins/src/display-plugins/RefreshRateController.h new file mode 100644 index 0000000000..dc46b7c01d --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/RefreshRateController.h @@ -0,0 +1,41 @@ +// +// 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 + +#include +#include + +class QThread; + +class RefreshRateController { +public: + RefreshRateController() = default; + ~RefreshRateController() = default; + + void setRefreshRateLimit(int refreshRateLimiti); + int getRefreshRateLimit() const; + + void clockStartTime() { _startTime = std::chrono::system_clock::now(); } + void clockEndTime() { _endTime = std::chrono::system_clock::now(); } + void sleepThreadIfNeeded(QThread* thread); +private: + + std::chrono::time_point _startTime { std::chrono::system_clock::now() }; + std::chrono::time_point _endTime { std::chrono::system_clock::now() }; + std::atomic_int _refreshRateLimit { 50 }; // milliseconds + +}; + +#endif diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index c88d76e661..a07e6cea04 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -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; }