Refresh Rate Controller

This commit is contained in:
Dante Ruiz 2019-04-15 17:01:18 -07:00 committed by danteruiz
parent 1b67042091
commit 0bdc37859d
14 changed files with 458 additions and 2 deletions

View file

@ -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);

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
@ -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<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);
@ -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<QKeyEvent*>(event));
return true;
case QEvent::FocusIn:
focusInEvent(static_cast<QFocusEvent*>(event));
return true;
case QEvent::FocusOut:
focusOutEvent(static_cast<QFocusEvent*>(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<QString>& urls) {
@ -7345,6 +7374,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());
@ -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();

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(); }
@ -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

View file

@ -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 <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 {
return (RefreshRateManager::RefreshRateProfile) _refreshRateModeLock.resultWithReadLock<int>([&] {
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<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 { return _refreshRateRegime; }
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

@ -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()));

View file

@ -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

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
@ -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<OpenGLDisplayPlugin*> _newPluginQueue;
gl::Context* _context { nullptr };
std::shared_ptr<RefreshRateController> _refreshRateController { nullptr };
};
bool OpenGLDisplayPlugin::activate() {
@ -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->setRefreshRateLimit(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;

View file

@ -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 <QtCore/QThread>
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<std::chrono::milliseconds>(startTimeFromEpoch).count();
auto endMs = std::chrono::duration_cast<std::chrono::milliseconds>(endTimeFromEpoch).count();
auto duration = endMs - startMs;
if (duration < _refreshRateLimit) {
thread->msleep(_refreshRateLimit - duration);
}
}

View file

@ -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 <DependencyManager.h>
#include <chrono>
#include <atomic>
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<std::chrono::system_clock> _startTime { std::chrono::system_clock::now() };
std::chrono::time_point<std::chrono::system_clock> _endTime { std::chrono::system_clock::now() };
std::atomic_int _refreshRateLimit { 50 }; // milliseconds
};
#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; }