SF: Optimize layer history in scheduler
Register layers with LayerHistory by storing a weak pointer rather than
allocating a LayerHandle. Query layer visibility when needed instead of
synchronizing it to LayerInfo whenever Layer::isVisible is called. Store
active/inactive layers in contiguous memory instead of two hash maps for
cache efficiency, and minimal allocation and run time of operations like
clearing history. Remove redundant ref-counting, locking, and frequency-
period conversion in LayerInfo. Avoid sleeping in unit tests.
This is also prework for per-display layer history.
Bug: 130554049
Bug: 134772048
Test: go/wm-smoke with debug.sf.layer_history_trace
Test: C2/F2 test cases from spreadsheet
Test: LayerHistoryTest with new test cases
Change-Id: Ibfcfe46cd76ebd93b916d4a0c737a19e837d4ff1
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 5473db6..8b71728 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -14,183 +14,150 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "LayerHistory"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "LayerHistory.h"
-#include <cinttypes>
-#include <cstdint>
-#include <limits>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "LayerInfo.h"
#include "SchedulerUtils.h"
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
-std::atomic<int64_t> LayerHistory::sNextId = 0;
+namespace {
-LayerHistory::LayerHistory() {
+bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+ return layer.isVisible() && (info.isHDR() || info.getLastUpdatedTime() >= threshold);
+}
+
+bool traceEnabled() {
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.layer_history_trace", value, "0");
- mTraceEnabled = static_cast<bool>(atoi(value));
+ return atoi(value);
}
+void trace(const wp<Layer>& weak, int fps) {
+ const auto layer = weak.promote();
+ if (!layer) return;
+
+ const auto& name = layer->getName();
+ const auto tag = "LFPS " + name;
+ ATRACE_INT(tag.c_str(), fps);
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+
+} // namespace
+
+LayerHistory::LayerHistory() : mTraceEnabled(traceEnabled()) {}
LayerHistory::~LayerHistory() = default;
-std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
- float minRefreshRate,
- float maxRefreshRate) {
- const int64_t id = sNextId++;
-
+void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate) {
+ auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
std::lock_guard lock(mLock);
- mInactiveLayerInfos.emplace(id,
- std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate));
- return std::make_unique<LayerHistory::LayerHandle>(*this, id);
+ mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::destroyLayer(const int64_t id) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, bool isHDR, nsecs_t now) {
std::lock_guard lock(mLock);
- auto it = mActiveLayerInfos.find(id);
- if (it != mActiveLayerInfos.end()) {
- mActiveLayerInfos.erase(it);
- }
- it = mInactiveLayerInfos.find(id);
- if (it != mInactiveLayerInfos.end()) {
- mInactiveLayerInfos.erase(it);
+ const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+ [layer](const auto& pair) { return pair.first == layer; });
+ LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+ const auto& info = it->second;
+ info->setLastPresentTime(presentTime, now);
+ info->setIsHDR(isHDR);
+
+ // Activate layer if inactive.
+ if (const auto end = activeLayers().end(); it >= end) {
+ std::iter_swap(it, end);
+ mActiveLayersEnd++;
}
}
-void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime,
- bool isHdr) {
- std::shared_ptr<LayerInfo> layerInfo;
- {
- std::lock_guard lock(mLock);
- auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mInactiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- mInactiveLayerInfos.erase(layerInfoIterator);
- mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
- } else {
- layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mActiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- } else {
- ALOGW("Inserting information about layer that is not registered: %" PRId64,
- layerHandle->mId);
- return;
- }
- }
- }
- layerInfo->setLastPresentTime(presentTime);
- layerInfo->setHDRContent(isHdr);
-}
-
-void LayerHistory::setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible) {
- std::shared_ptr<LayerInfo> layerInfo;
- {
- std::lock_guard lock(mLock);
- auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mInactiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- if (visible) {
- mInactiveLayerInfos.erase(layerInfoIterator);
- mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
- }
- } else {
- layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
- if (layerInfoIterator != mActiveLayerInfos.end()) {
- layerInfo = layerInfoIterator->second;
- } else {
- ALOGW("Inserting information about layer that is not registered: %" PRId64,
- layerHandle->mId);
- return;
- }
- }
- }
- layerInfo->setVisibility(visible);
-}
-
-std::pair<float, bool> LayerHistory::getDesiredRefreshRateAndHDR() {
+LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
+ float maxRefreshRate = 0;
bool isHDR = false;
- float newRefreshRate = 0.f;
+
std::lock_guard lock(mLock);
- removeIrrelevantLayers();
+ partitionLayers(now);
- // Iterate through all layers that have been recently updated, and find the max refresh rate.
- for (const auto& [layerId, layerInfo] : mActiveLayerInfos) {
- const bool recent = layerInfo->isRecentlyActive();
+ // Find the maximum refresh rate among recently active layers.
+ for (const auto& [layer, info] : activeLayers()) {
+ const bool recent = info->isRecentlyActive(now);
if (recent || CC_UNLIKELY(mTraceEnabled)) {
- const float refreshRate = layerInfo->getDesiredRefreshRate();
- if (recent && refreshRate > newRefreshRate) {
- newRefreshRate = refreshRate;
+ const float refreshRate = info->getRefreshRate(now);
+ if (recent && refreshRate > maxRefreshRate) {
+ maxRefreshRate = refreshRate;
}
if (CC_UNLIKELY(mTraceEnabled)) {
- std::string name = "LFPS " + layerInfo->getName();
- const float rate = std::round(refreshRate);
- ATRACE_INT(name.c_str(), rate);
- ALOGD("%s: %f", name.c_str(), rate);
+ trace(layer, std::round(refreshRate));
}
}
- isHDR |= layerInfo->getHDRContent();
+ isHDR |= info->isHDR();
}
if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
+ ALOGD("%s: maxRefreshRate=%.2f, isHDR=%d", __FUNCTION__, maxRefreshRate, isHDR);
}
- return {newRefreshRate, isHDR};
+ return {maxRefreshRate, isHDR};
}
-void LayerHistory::removeIrrelevantLayers() {
- const int64_t obsoleteEpsilon = systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
- // Iterator pointing to first element in map
- auto it = mActiveLayerInfos.begin();
- while (it != mActiveLayerInfos.end()) {
- // If last updated was before the obsolete time, remove it.
- // Keep HDR layer around as long as they are visible.
- if (!it->second->isVisible() ||
- (!it->second->getHDRContent() && it->second->getLastUpdatedTime() < obsoleteEpsilon)) {
- // erase() function returns the iterator of the next
- // to last deleted element.
- if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("Layer %s obsolete", it->second->getName().c_str());
- // Make sure to update systrace to indicate that the layer was erased.
- std::string layerName = "LFPS " + it->second->getName();
- ATRACE_INT(layerName.c_str(), 0);
- }
- auto id = it->first;
- auto layerInfo = it->second;
- layerInfo->clearHistory();
- mInactiveLayerInfos.insert({id, layerInfo});
- it = mActiveLayerInfos.erase(it);
+void LayerHistory::partitionLayers(nsecs_t now) {
+ const nsecs_t threshold = getActiveLayerThreshold(now);
+
+ // Collect expired and inactive layers after active layers.
+ size_t i = 0;
+ while (i < mActiveLayersEnd) {
+ auto& [weak, info] = mLayerInfos[i];
+ if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+ i++;
+ continue;
+ }
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(weak, 0);
+ }
+
+ info->clearHistory();
+ std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+ }
+
+ // Collect expired layers after inactive layers.
+ size_t end = mLayerInfos.size();
+ while (i < end) {
+ if (mLayerInfos[i].first.promote()) {
+ i++;
} else {
- ++it;
+ std::swap(mLayerInfos[i], mLayerInfos[--end]);
}
}
+
+ mLayerInfos.erase(mLayerInfos.begin() + end, mLayerInfos.end());
}
-void LayerHistory::clearHistory() {
+void LayerHistory::clear() {
std::lock_guard lock(mLock);
- auto it = mActiveLayerInfos.begin();
- while (it != mActiveLayerInfos.end()) {
- auto id = it->first;
- auto layerInfo = it->second;
- layerInfo->clearHistory();
- mInactiveLayerInfos.insert({id, layerInfo});
- it = mActiveLayerInfos.erase(it);
+ for (const auto& [layer, info] : activeLayers()) {
+ info->clearHistory();
}
+
+ mActiveLayersEnd = 0;
}
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler