SurfaceFlinger: use config groups
Composer 2.4 adds a new attribute for configs groups. This change
groups configs according to their group and store them in
RefreshRateConfigs.
Test: rev up composer to 2.4 and test refresh rate switching
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Test: adb shell /data/nativetest64/SurfaceFlinger_test/SurfaceFlinger_test
Bug: 141329414
Fixes: 139751853
Change-Id: Ic0bcd3da4bf6b73efa11a60c2594948ce030362f
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 8d9adc8..ff800c3 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -36,6 +36,7 @@
#include <utils/Trace.h>
#include "EventThread.h"
+#include "HwcStrongTypes.h"
using namespace std::chrono_literals;
@@ -101,10 +102,11 @@
return event;
}
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
+ HwcConfigIndexType configId) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
- event.config.configId = configId;
+ event.config.configId = configId.value();
return event;
}
@@ -290,7 +292,7 @@
mCondition.notify_all();
}
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeConfigChanged(displayId, configId));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a029586..a42546c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -33,6 +33,7 @@
#include <private/gui/BitTube.h>
#include <utils/Errors.h>
+#include "HwcStrongTypes.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -109,7 +110,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active config and apps needs to be notified about the change
- virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+ virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) = 0;
virtual void dump(std::string& result) const = 0;
@@ -146,7 +147,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+ void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) override;
void dump(std::string& result) const override;
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
new file mode 100644
index 0000000..cfbbdfe
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "StrongTyping.h"
+
+namespace android {
+
+// Strong types for the different indexes as they are referring to a different base.
+using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
+using HwcConfigGroupType = StrongTyping<int, struct HwcConfigGroupTypeTag, Compare>;
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8b71728..146ec1b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- return layer.isVisible() && (info.isHDR() || info.getLastUpdatedTime() >= threshold);
+ return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
}
bool traceEnabled() {
@@ -69,7 +69,7 @@
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, bool isHDR, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -78,7 +78,6 @@
const auto& info = it->second;
info->setLastPresentTime(presentTime, now);
- info->setIsHDR(isHDR);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -89,7 +88,6 @@
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
float maxRefreshRate = 0;
- bool isHDR = false;
std::lock_guard lock(mLock);
@@ -108,13 +106,12 @@
trace(layer, std::round(refreshRate));
}
}
- isHDR |= info->isHDR();
}
if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("%s: maxRefreshRate=%.2f, isHDR=%d", __FUNCTION__, maxRefreshRate, isHDR);
+ ALOGD("%s: maxRefreshRate=%.2f", __FUNCTION__, maxRefreshRate);
}
- return {maxRefreshRate, isHDR};
+ return {maxRefreshRate};
}
void LayerHistory::partitionLayers(nsecs_t now) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bd9aca1..745c4c1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -46,11 +46,10 @@
void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate);
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, bool isHDR, nsecs_t now);
+ void record(Layer*, nsecs_t presentTime, nsecs_t now);
struct Summary {
float maxRefreshRate; // Maximum refresh rate among recently active layers.
- bool isHDR; // True if any recently active layer has HDR content.
};
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index b86709f..cb81ca2 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -140,9 +140,6 @@
// updated time, the updated time is the present time.
void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
- bool isHDR() const { return mIsHDR; }
- void setIsHDR(bool isHDR) { mIsHDR = isHDR; }
-
bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
@@ -167,7 +164,6 @@
nsecs_t mLastPresentTime = 0;
RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
PresentTimeHistory mPresentTimeHistory;
- bool mIsHDR = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 6be88f8..12832a6 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -48,16 +48,18 @@
getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
.value_or(std::numeric_limits<nsecs_t>::max());
- const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
- const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
-
- mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
- mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
+ mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync);
+ mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
}
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
- RefreshRateType refreshRateType) const {
- return mOffsets.at(refreshRateType);
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
+ // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic
+ // number for refresh rate
+ if (fps > 65.0f) {
+ return mHighFpsOffsets;
+ } else {
+ return mDefaultOffsets;
+ }
}
void PhaseOffsets::dump(std::string& result) const {
@@ -80,13 +82,13 @@
const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
- return {{RefreshRateType::DEFAULT, earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+ return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
- {RefreshRateType::DEFAULT, earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+ {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
- {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+ {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
thresholdForNextVsync};
}
@@ -104,13 +106,13 @@
const auto highFpsEarlyGlAppOffsetNs =
getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
- return {{RefreshRateType::PERFORMANCE, highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
+ return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
- {RefreshRateType::PERFORMANCE, highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
+ {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
- {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+ {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
thresholdForNextVsync};
}
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 2c52432..7747f0c 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -32,7 +32,6 @@
class PhaseOffsets {
public:
using Offsets = VSyncModulator::OffsetsConfig;
- using RefreshRateType = RefreshRateConfigs::RefreshRateType;
virtual ~PhaseOffsets();
@@ -43,9 +42,9 @@
}
virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(RefreshRateType) const = 0;
+ virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
- virtual void setRefreshRateType(RefreshRateType) = 0;
+ virtual void setRefreshRateFps(float fps) = 0;
virtual void dump(std::string& result) const = 0;
};
@@ -57,18 +56,14 @@
PhaseOffsets();
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(RefreshRateType) const override;
+ Offsets getOffsetsForRefreshRate(float fps) const override;
// Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override {
- return getOffsetsForRefreshRate(mRefreshRateType);
- }
+ Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
// This function should be called when the device is switching between different
// refresh rates, to properly update the offsets.
- void setRefreshRateType(RefreshRateType refreshRateType) override {
- mRefreshRateType = refreshRateType;
- }
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
@@ -77,9 +72,10 @@
static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
- std::atomic<RefreshRateType> mRefreshRateType = RefreshRateType::DEFAULT;
+ std::atomic<float> mRefreshRateFps = 0;
- std::unordered_map<RefreshRateType, Offsets> mOffsets;
+ Offsets mDefaultOffsets;
+ Offsets mHighFpsOffsets;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 7dc98cc..23fb96a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -13,135 +13,169 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+// #define LOG_NDEBUG 0
#include "RefreshRateConfigs.h"
namespace android::scheduler {
+
+using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
// Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
// from multiple threads. This can only be called if refreshRateSwitching() returns true.
// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
// baking them in.
-const std::map<RefreshRateType, RefreshRate>& RefreshRateConfigs::getRefreshRateMap() const {
- LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
- return mRefreshRateMap;
-}
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFramerate) const {
+ std::lock_guard lock(mLock);
+ // Find the appropriate refresh rate with minimal error
+ auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
+ [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
+ return std::abs(lhs->fps - contentFramerate) <
+ std::abs(rhs->fps - contentFramerate);
+ });
-const RefreshRate& RefreshRateConfigs::getRefreshRateFromType(RefreshRateType type) const {
- if (!mRefreshRateSwitchingSupported) {
- return getCurrentRefreshRate().second;
- } else {
- auto refreshRate = mRefreshRateMap.find(type);
- LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
- return refreshRate->second;
- }
-}
+ // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+ // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+ // align well with both
+ const RefreshRate* bestSoFar = *iter;
+ constexpr float MARGIN = 0.05f;
+ float ratio = (*iter)->fps / contentFramerate;
+ if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+ while (iter != mAvailableRefreshRates.cend()) {
+ ratio = (*iter)->fps / contentFramerate;
-std::pair<RefreshRateType, const RefreshRate&> RefreshRateConfigs::getCurrentRefreshRate() const {
- int currentConfig = mCurrentConfig;
- if (mRefreshRateSwitchingSupported) {
- for (const auto& [type, refresh] : mRefreshRateMap) {
- if (refresh.configId == currentConfig) {
- return {type, refresh};
+ if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+ bestSoFar = *iter;
+ break;
}
- }
- LOG_ALWAYS_FATAL();
- }
- return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
-}
-
-const RefreshRate& RefreshRateConfigs::getRefreshRateFromConfigId(int configId) const {
- LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
- return mRefreshRates[configId];
-}
-
-RefreshRateType RefreshRateConfigs::getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
- if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
-
- for (const auto& [type, refreshRate] : mRefreshRateMap) {
- if (refreshRate.hwcId == hwcId) {
- return type;
+ ++iter;
}
}
- return RefreshRateType::DEFAULT;
+ return *bestSoFar;
}
-void RefreshRateConfigs::setCurrentConfig(int config) {
- LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
- mCurrentConfig = config;
+const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
+ return mRefreshRates;
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.front();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.back();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return *mCurrentRefreshRate;
+}
+
+void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+ std::lock_guard lock(mLock);
+ mCurrentRefreshRate = &mRefreshRates.at(configId);
}
RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
- const std::vector<InputConfig>& configs, int currentConfig) {
- init(refreshRateSwitching, configs, currentConfig);
+ const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig)
+ : mRefreshRateSwitching(refreshRateSwitching) {
+ init(configs, currentHwcConfig);
}
RefreshRateConfigs::RefreshRateConfigs(
bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig) {
+ HwcConfigIndexType currentConfigId)
+ : mRefreshRateSwitching(refreshRateSwitching) {
std::vector<InputConfig> inputConfigs;
- for (const auto& config : configs) {
- inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
+ for (auto configId = HwcConfigIndexType(0); configId < HwcConfigIndexType(configs.size());
+ ++configId) {
+ auto configGroup = HwcConfigGroupType(configs[configId.value()]->getConfigGroup());
+ inputConfigs.push_back(
+ {configId, configGroup, configs[configId.value()]->getVsyncPeriod()});
}
- init(refreshRateSwitching, inputConfigs, currentConfig);
+ init(inputConfigs, currentConfigId);
}
-void RefreshRateConfigs::init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- mRefreshRateSwitchingSupported = refreshRateSwitching;
+void RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+ float maxRefreshRate) {
+ std::lock_guard lock(mLock);
+ mCurrentGroupId = mRefreshRates.at(defaultConfigId).configGroup;
+ mMinRefreshRateFps = minRefreshRate;
+ mMaxRefreshRateFps = maxRefreshRate;
+ constructAvailableRefreshRates();
+}
+
+void RefreshRateConfigs::getSortedRefreshRateList(
+ const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+ std::vector<const RefreshRate*>* outRefreshRates) {
+ outRefreshRates->clear();
+ outRefreshRates->reserve(mRefreshRates.size());
+ for (const auto& [type, refreshRate] : mRefreshRates) {
+ if (shouldAddRefreshRate(refreshRate)) {
+ ALOGV("getSortedRefreshRateList: config %d added to list policy",
+ refreshRate.configId.value());
+ outRefreshRates->push_back(&refreshRate);
+ }
+ }
+
+ std::sort(outRefreshRates->begin(), outRefreshRates->end(),
+ [](const auto refreshRate1, const auto refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+}
+
+void RefreshRateConfigs::constructAvailableRefreshRates() {
+ // Filter configs based on current policy and sort based on vsync period
+ ALOGV("constructRefreshRateMap: group %d min %.2f max %.2f", mCurrentGroupId.value(),
+ mMinRefreshRateFps, mMaxRefreshRateFps);
+ getSortedRefreshRateList(
+ [this](const RefreshRate& refreshRate) REQUIRES(mLock) {
+ return refreshRate.configGroup == mCurrentGroupId &&
+ refreshRate.fps >= mMinRefreshRateFps &&
+ refreshRate.fps <= mMaxRefreshRateFps;
+ },
+ &mAvailableRefreshRates);
+}
+
+// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
+void RefreshRateConfigs::init(const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig) NO_THREAD_SAFETY_ANALYSIS {
LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
- mCurrentConfig = currentConfig;
+ LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
- auto buildRefreshRate = [&](int configId) -> RefreshRate {
- const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
- const float fps = 1e9 / vsyncPeriod;
- return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
- vsyncPeriod, configs[configId].hwcId};
+ auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
+ const float fps = 1e9f / config.vsyncPeriod;
+ return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
+ base::StringPrintf("%2.ffps", fps), fps);
};
- for (int i = 0; i < configs.size(); ++i) {
- mRefreshRates.push_back(buildRefreshRate(i));
+ for (const auto& config : configs) {
+ mRefreshRates.emplace(config.configId, buildRefreshRate(config));
+ if (config.configId == currentHwcConfig) {
+ mCurrentRefreshRate = &mRefreshRates.at(config.configId);
+ mCurrentGroupId = config.configGroup;
+ }
}
- if (!mRefreshRateSwitchingSupported) return;
-
- auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
- if (configs.size() < 2) {
- return {};
- }
-
- std::vector<const RefreshRate*> sortedRefreshRates;
- for (const auto& refreshRate : mRefreshRates) {
- sortedRefreshRates.push_back(&refreshRate);
- }
- std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
- [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
- return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
- });
-
- // When the configs are ordered by the resync rate, we assume that
- // the first one is DEFAULT and the second one is PERFORMANCE,
- // i.e. the higher rate.
- if (sortedRefreshRates[0]->vsyncPeriod == 0 || sortedRefreshRates[1]->vsyncPeriod == 0) {
- return {};
- }
-
- return std::pair<int, int>(sortedRefreshRates[0]->configId,
- sortedRefreshRates[1]->configId);
- };
-
- auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
- if (!defaultAndPerfConfigs) {
- mRefreshRateSwitchingSupported = false;
- return;
- }
-
- mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
- mRefreshRateMap[RefreshRateType::PERFORMANCE] = mRefreshRates[defaultAndPerfConfigs->second];
+ std::vector<const RefreshRate*> sortedConfigs;
+ getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ mMinSupportedRefreshRate = sortedConfigs.front();
+ mMaxSupportedRefreshRate = sortedConfigs.back();
+ constructAvailableRefreshRates();
}
-} // namespace android::scheduler
\ No newline at end of file
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 90bba24..fb14dc7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -23,7 +23,9 @@
#include <type_traits>
#include "DisplayHardware/HWComposer.h"
+#include "HwcStrongTypes.h"
#include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
@@ -41,71 +43,123 @@
*/
class RefreshRateConfigs {
public:
- // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
- // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
- enum class RefreshRateType { DEFAULT, PERFORMANCE };
-
struct RefreshRate {
+ RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
+ HwcConfigGroupType configGroup, std::string name, float fps)
+ : configId(configId),
+ vsyncPeriod(vsyncPeriod),
+ configGroup(configGroup),
+ name(std::move(name)),
+ fps(fps) {}
// This config ID corresponds to the position of the config in the vector that is stored
// on the device.
- int configId;
- // Human readable name of the refresh rate.
- std::string name;
- // Refresh rate in frames per second, rounded to the nearest integer.
- uint32_t fps = 0;
+ const HwcConfigIndexType configId;
// Vsync period in nanoseconds.
- nsecs_t vsyncPeriod;
- // Hwc config Id (returned from HWC2::Display::Config::getId())
- hwc2_config_t hwcId;
+ const nsecs_t vsyncPeriod;
+ // This configGroup for the config.
+ const HwcConfigGroupType configGroup;
+ // Human readable name of the refresh rate.
+ const std::string name;
+ // Refresh rate in frames per second
+ const float fps = 0;
+
+ bool operator!=(const RefreshRate& other) const {
+ return configId != other.configId || vsyncPeriod != other.vsyncPeriod ||
+ configGroup != other.configGroup;
+ }
+
+ bool operator==(const RefreshRate& other) const { return !(*this != other); }
};
+ using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
+
+ // Sets the current policy to choose refresh rates.
+ void setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate, float maxRefreshRate)
+ EXCLUDES(mLock);
+
// Returns true if this device is doing refresh rate switching. This won't change at runtime.
- bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+ bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
- // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
- // from multiple threads. This can only be called if refreshRateSwitching() returns true.
- // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
- // baking them in.
- const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const;
+ // Returns all available refresh rates according to the current policy.
+ const RefreshRate& getRefreshRateForContent(float contentFramerate) const EXCLUDES(mLock);
- const RefreshRate& getRefreshRateFromType(RefreshRateType type) const;
+ // Returns all the refresh rates supported by the device. This won't change at runtime.
+ const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
- std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const;
+ // Returns the lowest refresh rate supported by the device. This won't change at runtime.
+ const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
- const RefreshRate& getRefreshRateFromConfigId(int configId) const;
+ // Returns the lowest refresh rate according to the current policy. May change in runtime.
+ const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
- RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const;
+ // Returns the highest refresh rate supported by the device. This won't change at runtime.
+ const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
- void setCurrentConfig(int config);
+ // Returns the highest refresh rate according to the current policy. May change in runtime.
+ const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+
+ // Returns the current refresh rate
+ const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+
+ // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+ // runtime.
+ const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+ return mRefreshRates.at(configId);
+ };
+
+ // Stores the current configId the device operates at
+ void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
struct InputConfig {
- hwc2_config_t hwcId = 0;
+ HwcConfigIndexType configId = HwcConfigIndexType(0);
+ HwcConfigGroupType configGroup = HwcConfigGroupType(0);
nsecs_t vsyncPeriod = 0;
};
RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig);
-
+ HwcConfigIndexType currentHwcConfig);
RefreshRateConfigs(bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig);
+ HwcConfigIndexType currentConfigId);
private:
- void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig);
- // Whether this device is doing refresh rate switching or not. This must not change after this
- // object is initialized.
- bool mRefreshRateSwitchingSupported;
+ void init(const std::vector<InputConfig>& configs, HwcConfigIndexType currentHwcConfig);
+
+ void constructAvailableRefreshRates() REQUIRES(mLock);
+
+ void getSortedRefreshRateList(
+ const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+ std::vector<const RefreshRate*>* outRefreshRates);
+
// The list of refresh rates, indexed by display config ID. This must not change after this
// object is initialized.
- std::vector<RefreshRate> mRefreshRates;
- // The mapping of refresh rate type to RefreshRate. This must not change after this object is
- // initialized.
- std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
- // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on
- // the main thread, and read by the Scheduler (and other objects) on other threads, so it's
- // atomic.
- std::atomic<int> mCurrentConfig;
+ AllRefreshRatesMapType mRefreshRates;
+
+ // The list of refresh rates which are available in the current policy, ordered by vsyncPeriod
+ // (the first element is the lowest refresh rate)
+ std::vector<const RefreshRate*> mAvailableRefreshRates GUARDED_BY(mLock);
+
+ // The current config. This will change at runtime. This is set by SurfaceFlinger on
+ // the main thread, and read by the Scheduler (and other objects) on other threads.
+ const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
+
+ // The current config group. This will change at runtime. This is set by SurfaceFlinger on
+ // the main thread, and read by the Scheduler (and other objects) on other threads.
+ HwcConfigGroupType mCurrentGroupId GUARDED_BY(mLock);
+
+ // The min and max FPS allowed by the policy. This will change at runtime and set by
+ // SurfaceFlinger on the main thread.
+ float mMinRefreshRateFps GUARDED_BY(mLock) = 0;
+ float mMaxRefreshRateFps GUARDED_BY(mLock) = std::numeric_limits<float>::max();
+
+ // The min and max refresh rates supported by the device.
+ // This will not change at runtime.
+ const RefreshRate* mMinSupportedRefreshRate;
+ const RefreshRate* mMaxSupportedRefreshRate;
+
+ const bool mRefreshRateSwitching;
+
+ mutable std::mutex mLock;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 8afc93e..a384dbe 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -25,8 +25,7 @@
#include "android-base/stringprintf.h"
#include "utils/Timers.h"
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
/**
* Class to encapsulate statistics about refresh rates that the display is using. When the power
@@ -42,10 +41,10 @@
public:
RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
- int currentConfigMode, int currentPowerMode)
+ HwcConfigIndexType currentConfigId, int currentPowerMode)
: mRefreshRateConfigs(refreshRateConfigs),
mTimeStats(timeStats),
- mCurrentConfigMode(currentConfigMode),
+ mCurrentConfigMode(currentConfigId),
mCurrentPowerMode(currentPowerMode) {}
// Sets power mode.
@@ -59,12 +58,12 @@
// Sets config mode. If the mode has changed, it records how much time was spent in the previous
// mode.
- void setConfigMode(int mode) {
- if (mCurrentConfigMode == mode) {
+ void setConfigMode(HwcConfigIndexType configId) {
+ if (mCurrentConfigMode == configId) {
return;
}
flushTime();
- mCurrentConfigMode = mode;
+ mCurrentConfigMode = configId;
}
// Returns a map between human readable refresh rate and number of seconds the device spent in
@@ -78,11 +77,11 @@
std::unordered_map<std::string, int64_t> totalTime;
// Multiple configs may map to the same name, e.g. "60fps". Add the
// times for such configs together.
- for (const auto& [config, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
+ for (const auto& [configId, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] = 0;
}
- for (const auto& [config, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+ for (const auto& [configId, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] += time;
}
totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
@@ -139,14 +138,14 @@
// Aggregate refresh rate statistics for telemetry.
TimeStats& mTimeStats;
- int mCurrentConfigMode;
+ HwcConfigIndexType mCurrentConfigMode;
int32_t mCurrentPowerMode;
- std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ std::unordered_map<HwcConfigIndexType /* configId */, int64_t /* duration in ms */>
+ mConfigModesTotalTime;
int64_t mScreenOffTime = 0;
nsecs_t mPreviousRecordedTime = systemTime();
};
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 55fd603..1d50fe1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -182,7 +182,7 @@
}
void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- int32_t configId) {
+ HwcConfigIndexType configId) {
RETURN_IF_INVALID_HANDLE(handle);
mConnections[handle].thread->onConfigChanged(displayId, configId);
}
@@ -280,8 +280,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- resyncToHardwareVsync(false,
- mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
+ resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().vsyncPeriod);
}
}
@@ -332,53 +331,49 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE;
-
- const auto lowFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
- const auto highFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
+ const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+ const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? lowFps
+ : mRefreshRateConfigs.getMaxRefreshRate().fps;
mLayerHistory->registerLayer(layer, lowFps, highFps);
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
if (mLayerHistory) {
- mLayerHistory->record(layer, presentTime, isHDR, systemTime());
+ mLayerHistory->record(layer, presentTime, systemTime());
}
}
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
- auto [refreshRate, isHDR] = mLayerHistory->summarize(systemTime());
+ auto [refreshRate] = mLayerHistory->summarize(systemTime());
const uint32_t refreshRateRound = std::round(refreshRate);
- RefreshRateType newRefreshRateType;
+ HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRefreshRate == refreshRateRound && mFeatures.isHDRContent == isHDR) {
+ if (mFeatures.contentRefreshRate == refreshRateRound) {
return;
}
mFeatures.contentRefreshRate = refreshRateRound;
ATRACE_INT("ContentFPS", refreshRateRound);
- mFeatures.isHDRContent = isHDR;
- ATRACE_INT("ContentHDR", isHDR);
-
mFeatures.contentDetection =
refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
- newRefreshRateType = calculateRefreshRateType();
- if (mFeatures.refreshRateType == newRefreshRateType) {
+ newConfigId = calculateRefreshRateType();
+ if (mFeatures.configId == newConfigId) {
return;
}
- mFeatures.refreshRateType = newRefreshRateType;
- }
- changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
+ mFeatures.configId = newConfigId;
+ };
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
-void Scheduler::setChangeRefreshRateCallback(ChangeRefreshRateCallback&& callback) {
+void Scheduler::setSchedulerCallback(android::Scheduler::ISchedulerCallback* callback) {
std::lock_guard<std::mutex> lock(mCallbackLock);
- mChangeRefreshRateCallback = std::move(callback);
+ mSchedulerCallback = callback;
}
void Scheduler::resetIdleTimer() {
@@ -423,13 +418,16 @@
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
+ // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
+ // magic number
const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
- if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) {
+ constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
+ if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
- resyncToHardwareVsync(true /* makeAvailable */, refreshRate.second.vsyncPeriod);
- } else if (state == TimerState::Expired && refreshRate.first != RefreshRateType::PERFORMANCE) {
+ resyncToHardwareVsync(true /* makeAvailable */, refreshRate.vsyncPeriod);
+ } else if (state == TimerState::Expired && refreshRate.fps <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
// need to update the DispSync model anyway.
@@ -471,96 +469,67 @@
template <class T>
void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None;
- RefreshRateType newRefreshRateType;
+ HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
return;
}
*currentState = newState;
- newRefreshRateType = calculateRefreshRateType();
- if (mFeatures.refreshRateType == newRefreshRateType) {
+ newConfigId = calculateRefreshRateType();
+ if (mFeatures.configId == newConfigId) {
return;
}
- mFeatures.refreshRateType = newRefreshRateType;
+ mFeatures.configId = newConfigId;
if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) {
event = ConfigEvent::Changed;
}
}
- changeRefreshRate(newRefreshRateType, event);
+ const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ changeRefreshRate(newRefreshRate, event);
}
-Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+HwcConfigIndexType Scheduler::calculateRefreshRateType() {
if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
- return RefreshRateType::DEFAULT;
- }
-
- // HDR content is not supported on PERFORMANCE mode
- if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
- return RefreshRateType::DEFAULT;
+ return mRefreshRateConfigs.getCurrentRefreshRate().configId;
}
// If Display Power is not in normal operation we want to be in performance mode.
// When coming back to normal mode, a grace period is given with DisplayPowerTimer
if (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// As long as touch is active we want to be in performance mode
if (mFeatures.touch == TouchState::Active) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// If timer has expired as it means there is no new content on the screen
if (mFeatures.idleTimer == TimerState::Expired) {
- return RefreshRateType::DEFAULT;
+ return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
// If content detection is off we choose performance as we don't know the content fps
if (mFeatures.contentDetection == ContentDetectionState::Off) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// Content detection is on, find the appropriate refresh rate with minimal error
- // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs)
- const float rate = static_cast<float>(mFeatures.contentRefreshRate);
- auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
- mRefreshRateConfigs.getRefreshRateMap().cend(),
- [rate](const auto& lhs, const auto& rhs) -> bool {
- return std::abs(lhs.second.fps - rate) <
- std::abs(rhs.second.fps - rate);
- });
- RefreshRateType currRefreshRateType = iter->first;
-
- // Some content aligns better on higher refresh rate. For example for 45fps we should choose
- // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
- // align well with both
- constexpr float MARGIN = 0.05f;
- float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate;
- if (std::abs(std::round(ratio) - ratio) > MARGIN) {
- while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
- ratio = iter->second.fps / rate;
-
- if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
- currRefreshRateType = iter->first;
- break;
- }
- ++iter;
- }
- }
-
- return currRefreshRateType;
+ return mRefreshRateConfigs
+ .getRefreshRateForContent(static_cast<float>(mFeatures.contentRefreshRate))
+ .configId;
}
-Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() {
+std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- return mFeatures.refreshRateType;
+ return mFeatures.configId;
}
-void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+void Scheduler::changeRefreshRate(const RefreshRate& refreshRate, ConfigEvent configEvent) {
std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- mChangeRefreshRateCallback(refreshRateType, configEvent);
+ if (mSchedulerCallback) {
+ mSchedulerCallback->changeRefreshRate(refreshRate, configEvent);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 346896c..04a8390 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -34,6 +34,8 @@
namespace android {
+using namespace std::chrono_literals;
+
class DispSync;
class FenceTime;
class InjectVSyncSource;
@@ -41,10 +43,14 @@
class Scheduler {
public:
- using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
+ class ISchedulerCallback {
+ public:
+ virtual ~ISchedulerCallback() = default;
+ virtual void changeRefreshRate(const RefreshRate&, ConfigEvent) = 0;
+ };
// Indicates whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
@@ -67,7 +73,7 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onConfigChanged(ConnectionHandle, PhysicalDisplayId, int32_t configId);
+ void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -103,13 +109,13 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
+ void recordLayerHistory(Layer*, nsecs_t presentTime);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
- // Called by Scheduler to change refresh rate.
- void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
+ // Called by Scheduler to control SurfaceFlinger operations.
+ void setSchedulerCallback(ISchedulerCallback*);
bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
void resetIdleTimer();
@@ -122,8 +128,8 @@
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
- // Get the appropriate refresh type for current conditions.
- RefreshRateType getPreferredRefreshRateType();
+ // Get the appropriate refresh for current conditions.
+ std::optional<HwcConfigIndexType> getPreferredConfigId();
private:
friend class TestableScheduler;
@@ -158,9 +164,9 @@
void setVsyncPeriod(nsecs_t period);
- RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback with given parameters.
- void changeRefreshRate(RefreshRateType, ConfigEvent);
+ void changeRefreshRate(const RefreshRate&, ConfigEvent);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -198,7 +204,7 @@
std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
std::mutex mCallbackLock;
- ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+ ISchedulerCallback* mSchedulerCallback GUARDED_BY(mCallbackLock) = nullptr;
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
@@ -210,17 +216,13 @@
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ std::optional<HwcConfigIndexType> configId;
uint32_t contentRefreshRate = 0;
- bool isHDRContent = false;
bool isDisplayPowerStateNormal = true;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
-
- // Global config to force HDR content to work on DEFAULT refreshRate
- static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index 02db022..e8ca0ba 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -51,13 +51,22 @@
inline bool operator>(T const& other) const { return !(*this < other || *this == other); }
};
+template <typename T>
+struct Hash : Ability<T, Hash> {
+ [[nodiscard]] std::size_t hash() const {
+ return std::hash<typename std::remove_const<
+ typename std::remove_reference<decltype(this->base().value())>::type>::type>{}(
+ this->base().value());
+ }
+};
+
template <typename T, typename W, template <typename> class... Ability>
struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
StrongTyping() : mValue(0) {}
explicit StrongTyping(T const& value) : mValue(value) {}
StrongTyping(StrongTyping const&) = default;
StrongTyping& operator=(StrongTyping const&) = default;
- inline operator T() const { return mValue; }
+ explicit inline operator T() const { return mValue; }
T const& value() const { return mValue; }
T& value() { return mValue; }
@@ -65,3 +74,12 @@
T mValue;
};
} // namespace android
+
+namespace std {
+template <typename T, typename W, template <typename> class... Ability>
+struct hash<android::StrongTyping<T, W, Ability...>> {
+ std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const {
+ return k.hash();
+ }
+};
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 4a4bef8..e001080 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -34,7 +34,7 @@
*/
class VSyncDispatch {
public:
- using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
+ using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
virtual ~VSyncDispatch();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index f058099..0e12e7f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -106,7 +106,8 @@
VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
- using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+ using CallbackMap =
+ std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
void timerCallback();
void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 27fd76c..8de35b1 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -134,18 +134,13 @@
return;
}
- const bool isDefault = mOffsets.fpsMode == RefreshRateType::DEFAULT;
- const bool isPerformance = mOffsets.fpsMode == RefreshRateType::PERFORMANCE;
const bool isEarly = &offsets == &mOffsetsConfig.early;
const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
const bool isLate = &offsets == &mOffsetsConfig.late;
- ATRACE_INT("Vsync-EarlyOffsetsOn", isDefault && isEarly);
- ATRACE_INT("Vsync-EarlyGLOffsetsOn", isDefault && isEarlyGl);
- ATRACE_INT("Vsync-LateOffsetsOn", isDefault && isLate);
- ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", isPerformance && isEarly);
- ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", isPerformance && isEarlyGl);
- ATRACE_INT("Vsync-HighFpsLateOffsetsOn", isPerformance && isLate);
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 727cef2..63c0feb 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -37,13 +37,10 @@
// switch in and out of gl composition.
static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
- using RefreshRateType = RefreshRateConfigs::RefreshRateType;
-
public:
// Wrapper for a collection of surfaceflinger/app offsets for a particular
// configuration.
struct Offsets {
- RefreshRateType fpsMode;
nsecs_t sf;
nsecs_t app;
};