diff --git a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml index 06894d9576..5bac374fb5 100644 --- a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml +++ b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml @@ -347,6 +347,10 @@ Flickable { text: "Real-Time" refreshRatePreset: 2 // RefreshRateProfile::REALTIME } + ListElement { + text: "Custom" + refreshRatePreset: 3 // RefreshRateProfile::CUSTOM + } } HifiControlsUit.ComboBox { @@ -362,13 +366,7 @@ Flickable { currentIndex: -1 function refreshRefreshRateDropdownDisplay() { - if (Performance.getRefreshRateProfile() === 0) { - refreshRateDropdown.currentIndex = 0; - } else if (Performance.getRefreshRateProfile() === 1) { - refreshRateDropdown.currentIndex = 1; - } else { - refreshRateDropdown.currentIndex = 2; - } + refreshRateDropdown.currentIndex = Performance.getRefreshRateProfile(); } Component.onCompleted: { @@ -382,6 +380,180 @@ Flickable { } } + ColumnLayout { + width: parent.width + Layout.topMargin: 32 + visible: refreshRateDropdown.currentIndex == 3 + + RowLayout { + Layout.margins: 8 + + HifiControlsUit.SpinBox { + id: refreshRateCustomFocusActive + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Focus Active" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(0) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(0, realValue) + } + } + } + + HifiControlsUit.SpinBox { + id: refreshRateCustomFocusInactive + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Focus Inactive" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(1) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(1, realValue) + } + } + } + } + + RowLayout { + Layout.margins: 8 + + HifiControlsUit.SpinBox { + id: refreshRateCustomUnfocus + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Unfocus" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(2) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(2, realValue); + } + } + } + + HifiControlsUit.SpinBox { + id: refreshRateCustomMinimized + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Minimized" + realFrom: 1 + realTo: 1000 + realStepSize: 1 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(3) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(3, realValue) + } + } + } + } + + RowLayout { + Layout.margins: 8 + + HifiControlsUit.SpinBox { + id: refreshRateCustomStartup + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Startup" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(4) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(4, realValue) + } + } + } + + HifiControlsUit.SpinBox { + id: refreshRateCustomShutdown + decimals: 0 + width: 160 + height: 32 + suffix: " FPS" + label: "Shutdown" + realFrom: 1 + realTo: 1000 + realStepSize: 15 + realValue: 60 + colorScheme: hifi.colorSchemes.dark + property var loaded: false + + Component.onCompleted: { + realValue = Performance.getCustomRefreshRate(5) + loaded = true + } + + onRealValueChanged: { + if (loaded) { + Performance.setCustomRefreshRate(5, realValue) + } + } + } + } + } + Item { Layout.preferredWidth: parent.width Layout.preferredHeight: 35 diff --git a/interface/src/RefreshRateManager.cpp b/interface/src/RefreshRateManager.cpp index 09428da5ea..9ba446750b 100644 --- a/interface/src/RefreshRateManager.cpp +++ b/interface/src/RefreshRateManager.cpp @@ -48,12 +48,13 @@ static const int VR_TARGET_RATE = 90; * "Interactive"Medium refresh rate, which is reduced when Interface doesn't have focus or is * minimized. * "Realtime"High refresh rate, even when Interface doesn't have focus or is minimized. + * "Custom"Custom refresh rate for full control over the refresh rate in all states. * * * @typedef {string} RefreshRateProfileName */ static const std::array REFRESH_RATE_PROFILE_TO_STRING = - { { "Eco", "Interactive", "Realtime" } }; + { { "Eco", "Interactive", "Realtime", "Custom" } }; /*@jsdoc *

Interface states that affect the refresh rate.

@@ -94,7 +95,8 @@ static const std::array UX_MODE static const std::map REFRESH_RATE_PROFILE_FROM_STRING = { { "Eco", RefreshRateManager::RefreshRateProfile::ECO }, { "Interactive", RefreshRateManager::RefreshRateProfile::INTERACTIVE }, - { "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME } }; + { "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME }, + { "Custom", RefreshRateManager::RefreshRateProfile::CUSTOM } }; // Porfile regimes are: @@ -107,10 +109,12 @@ static const std::array { { 30, 20, 10, 2, 30, 30 } }; static const std::array REALTIME_PROFILE = - { { 60, 60, 60, 2, 30, 30} }; + { { 60, 60, 60, 2, 30, 30 } }; -static const std::array, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILES = - { { ECO_PROFILE, INTERACTIVE_PROFILE, REALTIME_PROFILE } }; +static const std::array CUSTOM_PROFILE = REALTIME_PROFILE; // derived from settings and modified by scripts below + +static std::array, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILES = + { { ECO_PROFILE, INTERACTIVE_PROFILE, REALTIME_PROFILE, CUSTOM_PROFILE } }; static const int INACTIVE_TIMER_LIMIT = 3000; @@ -134,6 +138,10 @@ std::string RefreshRateManager::uxModeToString(RefreshRateManager::RefreshRateMa RefreshRateManager::RefreshRateManager() { _refreshRateProfile = (RefreshRateManager::RefreshRateProfile) _refreshRateProfileSetting.get(); + for (size_t i = 0; i < _customRefreshRateSettings.size(); i++) { + REFRESH_RATE_PROFILES[CUSTOM][i] = _customRefreshRateSettings[i].get(); + } + _inactiveTimer->setInterval(INACTIVE_TIMER_LIMIT); _inactiveTimer->setSingleShot(true); QObject::connect(_inactiveTimer.get(), &QTimer::timeout, [&] { @@ -168,6 +176,25 @@ void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRatePr } } +int RefreshRateManager::getCustomRefreshRate(RefreshRateRegime regime) { + if (isValidRefreshRateRegime(regime)) { + return REFRESH_RATE_PROFILES[RefreshRateProfile::CUSTOM][regime]; + } + + return 0; +} + +void RefreshRateManager::setCustomRefreshRate(RefreshRateRegime regime, int value) { + value = std::max(value, 1); + if (isValidRefreshRateRegime(regime)) { + _refreshRateProfileSettingLock.withWriteLock([&] { + REFRESH_RATE_PROFILES[RefreshRateProfile::CUSTOM][regime] = value; + _customRefreshRateSettings[regime].set(value); + }); + updateRefreshRateController(); + } +} + RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile() const { RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME; @@ -191,7 +218,6 @@ void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateReg _refreshRateRegime = refreshRateRegime; updateRefreshRateController(); } - } void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) { diff --git a/interface/src/RefreshRateManager.h b/interface/src/RefreshRateManager.h index 4b91f0c45e..6b94a94a4a 100644 --- a/interface/src/RefreshRateManager.h +++ b/interface/src/RefreshRateManager.h @@ -32,10 +32,11 @@ public: ECO = 0, INTERACTIVE, REALTIME, + CUSTOM, PROFILE_NUM }; Q_ENUM(RefreshRateProfile) - static bool isValidRefreshRateProfile(RefreshRateProfile value) { return (value >= RefreshRateProfile::ECO && value <= RefreshRateProfile::REALTIME); } + static bool isValidRefreshRateProfile(RefreshRateProfile value) { return (value >= 0 && value < RefreshRateProfile::PROFILE_NUM); } /*@jsdoc *

Interface states that affect the refresh rate.

@@ -106,6 +107,9 @@ public: // query the refresh rate target at the specified combination int queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const; + int getCustomRefreshRate(RefreshRateRegime regime); + void setCustomRefreshRate(RefreshRateRegime regime, int value); + void resetInactiveTimer(); void toggleInactive(); @@ -121,7 +125,15 @@ private: UXMode _uxMode { UXMode::DESKTOP }; mutable ReadWriteLockable _refreshRateProfileSettingLock; - Setting::Handle _refreshRateProfileSetting { "refreshRateProfile", RefreshRateProfile::INTERACTIVE }; + Setting::Handle _refreshRateProfileSetting{ "refreshRateProfile", RefreshRateProfile::INTERACTIVE }; + std::array, REGIME_NUM> _customRefreshRateSettings { { + { "customRefreshRateFocusActive", 60 }, + { "customRefreshRateFocusInactive", 60 }, + { "customRefreshRateUnfocus", 60 }, + { "customRefreshRateMinimized", 2 }, + { "customRefreshRateStartup", 30 }, + { "customRefreshRateShutdown", 30 } + } }; std::function _refreshRateOperator { nullptr }; diff --git a/interface/src/scripting/PerformanceScriptingInterface.cpp b/interface/src/scripting/PerformanceScriptingInterface.cpp index 9f3534b3e8..f619729eff 100644 --- a/interface/src/scripting/PerformanceScriptingInterface.cpp +++ b/interface/src/scripting/PerformanceScriptingInterface.cpp @@ -56,12 +56,22 @@ void PerformanceScriptingInterface::setRefreshRateProfile(RefreshRateProfile ref emit settingsChanged(); } +void PerformanceScriptingInterface::setCustomRefreshRate(RefreshRateManager::RefreshRateRegime refreshRateRegime, int value) +{ + qApp->getRefreshRateManager().setCustomRefreshRate(refreshRateRegime, value); + emit settingsChanged(); +} + +int PerformanceScriptingInterface::getCustomRefreshRate(RefreshRateManager::RefreshRateRegime refreshRateRegime) const { + return qApp->getRefreshRateManager().getCustomRefreshRate(refreshRateRegime); +} + PerformanceScriptingInterface::RefreshRateProfile PerformanceScriptingInterface::getRefreshRateProfile() const { return (PerformanceScriptingInterface::RefreshRateProfile)qApp->getRefreshRateManager().getRefreshRateProfile(); } QStringList PerformanceScriptingInterface::getRefreshRateProfileNames() const { - static const QStringList refreshRateProfileNames = { "ECO", "INTERACTIVE", "REALTIME" }; + static const QStringList refreshRateProfileNames = { "ECO", "INTERACTIVE", "REALTIME", "CUSTOM" }; return refreshRateProfileNames; } diff --git a/interface/src/scripting/PerformanceScriptingInterface.h b/interface/src/scripting/PerformanceScriptingInterface.h index 86350c8a1f..05e2c1d7bc 100644 --- a/interface/src/scripting/PerformanceScriptingInterface.h +++ b/interface/src/scripting/PerformanceScriptingInterface.h @@ -127,6 +127,22 @@ public slots: */ void setRefreshRateProfile(RefreshRateProfile refreshRateProfile); + /*@jsdoc + * Sets a custom refresh rate. + * @function Performance.setCustomRefreshRate + * @param {RefreshRateRegime} refreshRateRegime - The refresh rate regime + * @param {int} value - The value for the regime + */ + void setCustomRefreshRate(RefreshRateManager::RefreshRateRegime refreshRateRegime, int value); + + /*@jsdoc + * Gets the value for a specific RefreshRateRegime. + * @function Performance.getCustomRefreshRate + * @param {RefreshRateRegime} - The regime to get the value from + * @returns {int} - The value from the specified regime + */ + int getCustomRefreshRate(RefreshRateManager::RefreshRateRegime regime) const; + /*@jsdoc * Gets the current refresh rate profile in use. * @function Performance.getRefreshRateProfile diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 8597cb5717..ce5a588776 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -100,7 +100,8 @@ void setupPreferences() { QStringList refreshRateProfiles { QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::ECO)), QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::INTERACTIVE)), - QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::REALTIME)) }; + QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::REALTIME)), + QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::CUSTOM)) }; preference->setItems(refreshRateProfiles); preferences->addPreference(preference);