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/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 603a9f7..a25709c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -107,11 +107,8 @@
}
bool BufferLayer::isVisible() const {
- bool visible = !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
+ return !isHiddenByPolicy() && getAlpha() > 0.0f &&
(mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
- mFlinger->mScheduler->setLayerVisibility(mSchedulerLayerHandle, visible);
-
- return visible;
}
bool BufferLayer::isFixedSize() const {
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 8cc1831..c24cdaa 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -389,8 +389,8 @@
{ // Autolock scope
if (mFlinger->mUseSmart90ForVideo) {
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
- item.mHdrMetadata.validTypes != 0);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+ item.mHdrMetadata.validTypes != 0);
}
Mutex::Autolock lock(mQueueItemLock);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 85d2253..5768edd 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -249,8 +249,8 @@
if (mFlinger->mUseSmart90ForVideo) {
const nsecs_t presentTime = (desiredPresentTime == -1) ? 0 : desiredPresentTime;
- mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
- mCurrentState.hdrMetadata.validTypes != 0);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+ mCurrentState.hdrMetadata.validTypes != 0);
}
return true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 37caab2..e19b79b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -118,10 +118,9 @@
mFrameEventHistory.initializeCompositorTiming(compositorTiming);
mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
- mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType);
mCallingPid = args.callingPid;
mCallingUid = args.callingUid;
- mFlinger->onLayerCreated();
+ mFlinger->onLayerCreated(this);
}
Layer::~Layer() {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ad81920..fdac98f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -221,6 +221,8 @@
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
+ int getWindowType() const { return mWindowType; }
+
void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
@@ -886,9 +888,6 @@
// Window types from WindowManager.LayoutParams
const int mWindowType;
- // This is populated if the layer is registered with Scheduler for tracking purposes.
- std::unique_ptr<scheduler::LayerHistory::LayerHandle> mSchedulerLayerHandle;
-
private:
/**
* Returns an unsorted vector of all layers that are part of this tree.
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
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5598cc1..15ac8ca 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -16,76 +16,78 @@
#pragma once
-#include <array>
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
+#include <android-base/thread_annotations.h>
+#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include "LayerInfo.h"
-#include "SchedulerUtils.h"
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
namespace android {
+
+class Layer;
+
namespace scheduler {
-/*
- * This class represents information about layers that are considered current. We keep an
- * unordered map between layer name and LayerInfo.
- */
+class LayerInfo;
+
+// Records per-layer history of scheduling-related information (primarily present time),
+// heuristically categorizes layers as active or inactive, and summarizes stats about
+// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
class LayerHistory {
public:
- // Handle for each layer we keep track of.
- class LayerHandle {
- public:
- LayerHandle(LayerHistory& lh, int64_t id) : mId(id), mLayerHistory(lh) {}
- ~LayerHandle() { mLayerHistory.destroyLayer(mId); }
-
- const int64_t mId;
-
- private:
- LayerHistory& mLayerHistory;
- };
-
LayerHistory();
~LayerHistory();
- // When the layer is first created, register it.
- std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate,
- float maxRefreshRate);
+ // Layers are unregistered when the weak reference expires.
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate);
- // Method for inserting layers and their requested present time into the unordered map.
- void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
- // Method for setting layer visibility
- void setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible);
+ // Marks the layer as active, and records the given state to its history.
+ void record(Layer*, nsecs_t presentTime, bool isHDR, nsecs_t now);
- // Returns the desired refresh rate, which is a max refresh rate of all the current
- // layers. See go/content-fps-detection-in-scheduler for more information.
- std::pair<float, bool> getDesiredRefreshRateAndHDR();
+ struct Summary {
+ float maxRefreshRate; // Maximum refresh rate among recently active layers.
+ bool isHDR; // True if any recently active layer has HDR content.
+ };
- // Clears all layer history.
- void clearHistory();
+ // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+ Summary summarize(nsecs_t now);
- // Removes the handle and the object from the map.
- void destroyLayer(const int64_t id);
+ void clear();
private:
- // Removes the layers that have been idle for a given amount of time from mLayerInfos.
- void removeIrrelevantLayers() REQUIRES(mLock);
+ friend class LayerHistoryTest;
- // Information about currently active layers.
- std::mutex mLock;
- std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mActiveLayerInfos GUARDED_BY(mLock);
- std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mInactiveLayerInfos GUARDED_BY(mLock);
+ using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+ using LayerInfos = std::vector<LayerPair>;
- // Each layer has it's own ID. This variable keeps track of the count.
- static std::atomic<int64_t> sNextId;
+ struct ActiveLayers {
+ LayerInfos& infos;
+ const size_t index;
- // Flag whether to log layer FPS in systrace
- bool mTraceEnabled = false;
+ auto begin() { return infos.begin(); }
+ auto end() { return begin() + index; }
+ };
+
+ ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+ // Iterates over layers in a single pass, swapping pairs such that active layers precede
+ // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+ // truncating after inactive layers.
+ void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+ mutable std::mutex mLock;
+
+ // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+ // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+ LayerInfos mLayerInfos GUARDED_BY(mLock);
+ size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+ // Whether to emit systrace output and debug logs.
+ const bool mTraceEnabled;
};
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 723d71f..f3b0d56 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -16,27 +16,17 @@
#include "LayerInfo.h"
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
+#include <algorithm>
+#include <utility>
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
-LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate)
- : mName(name),
- mMinRefreshDuration(1e9f / maxRefreshRate),
- mLowActivityRefreshDuration(1e9f / minRefreshRate),
- mRefreshRateHistory(mMinRefreshDuration) {}
+LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
+ : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
-LayerInfo::~LayerInfo() = default;
-
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) {
- std::lock_guard lock(mLock);
-
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
// Buffers can come with a present time far in the future. That keeps them relevant.
- mLastUpdatedTime = std::max(lastPresentTime, systemTime());
+ mLastUpdatedTime = std::max(lastPresentTime, now);
mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
if (mLastPresentTime == 0) {
@@ -45,14 +35,13 @@
return;
}
- const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
+ const nsecs_t period = lastPresentTime - mLastPresentTime;
mLastPresentTime = lastPresentTime;
// Ignore time diff that are too high - those are stale values
- if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
- const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration);
- const int fps = 1e9f / refreshDuration;
+ if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+
+ const float fps = std::min(1e9f / period, mHighRefreshRate);
mRefreshRateHistory.insertRefreshRate(fps);
}
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 17afdda..b86709f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,27 +16,37 @@
#pragma once
-#include <cinttypes>
-#include <cstdint>
-#include <deque>
-#include <mutex>
-#include <numeric>
-#include <string>
-
-#include <log/log.h>
-
-#include <utils/Mutex.h>
#include <utils/Timers.h>
+#include <chrono>
+#include <deque>
+
#include "SchedulerUtils.h"
namespace android {
+
+class Layer;
+
namespace scheduler {
-/*
- * This class represents information about individial layers.
- */
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+ return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
class LayerInfo {
+ // Layer is considered frequent if the earliest value in the window of most recent present times
+ // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+ // favor of a low refresh rate.
+ static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+ static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
+
/**
* Struct that keeps the information about the refresh rate for last
* HISTORY_SIZE frames. This is used to better determine the refresh rate
@@ -44,9 +54,9 @@
*/
class RefreshRateHistory {
public:
- explicit RefreshRateHistory(nsecs_t minRefreshDuration)
- : mMinRefreshDuration(minRefreshDuration) {}
- void insertRefreshRate(int refreshRate) {
+ explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
+
+ void insertRefreshRate(float refreshRate) {
mElements.push_back(refreshRate);
if (mElements.size() > HISTORY_SIZE) {
mElements.pop_front();
@@ -54,19 +64,16 @@
}
float getRefreshRateAvg() const {
- if (mElements.empty()) {
- return 1e9f / mMinRefreshDuration;
- }
-
- return scheduler::calculate_mean(mElements);
+ return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
}
void clearHistory() { mElements.clear(); }
private:
- std::deque<nsecs_t> mElements;
+ const float mHighRefreshRate;
+
static constexpr size_t HISTORY_SIZE = 30;
- const nsecs_t mMinRefreshDuration;
+ std::deque<float> mElements;
};
/**
@@ -76,6 +83,8 @@
*/
class PresentTimeHistory {
public:
+ static constexpr size_t HISTORY_SIZE = 90;
+
void insertPresentTime(nsecs_t presentTime) {
mElements.push_back(presentTime);
if (mElements.size() > HISTORY_SIZE) {
@@ -83,60 +92,45 @@
}
}
- // Checks whether the present time that was inserted HISTORY_SIZE ago is within a
- // certain threshold: TIME_EPSILON_NS.
- bool isRelevant() const {
+ // Returns whether the earliest present time is within the active threshold.
+ bool isRecentlyActive(nsecs_t now) const {
if (mElements.size() < 2) {
return false;
}
// The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
- if (mElements.size() != HISTORY_SIZE &&
- mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) {
+ if (mElements.size() < HISTORY_SIZE &&
+ mElements.back() - mElements.front() < HISTORY_TIME.count()) {
return false;
}
- // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
- const int64_t obsoleteEpsilon =
- systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
- if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
- return false;
- }
-
- return true;
+ return mElements.back() >= getActiveLayerThreshold(now);
}
- bool isLowActivityLayer() const {
- // We want to make sure that we received more than two frames from the layer
- // in order to check low activity.
- if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
+ bool isFrequent(nsecs_t now) const {
+ // Assume layer is infrequent if too few present times have been recorded.
+ if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) {
return false;
}
- const int64_t obsoleteEpsilon =
- systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
- // Check the frame before last to determine whether there is low activity.
- // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
- // infrequent updates.
- if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
- obsoleteEpsilon) {
- return true;
- }
-
- return false;
+ // Layer is frequent if the earliest value in the window of most recent present times is
+ // within threshold.
+ const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE;
+ const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ return *it >= threshold;
}
void clearHistory() { mElements.clear(); }
private:
std::deque<nsecs_t> mElements;
- static constexpr size_t HISTORY_SIZE = 90;
static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
};
+ friend class LayerHistoryTest;
+
public:
- LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
- ~LayerInfo();
+ LayerInfo(float lowRefreshRate, float highRefreshRate);
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
@@ -144,71 +138,37 @@
// Records the last requested oresent time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- void setLastPresentTime(nsecs_t lastPresentTime);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
- void setHDRContent(bool isHdr) {
- std::lock_guard lock(mLock);
- mIsHDR = isHdr;
- }
+ bool isHDR() const { return mIsHDR; }
+ void setIsHDR(bool isHDR) { mIsHDR = isHDR; }
- void setVisibility(bool visible) {
- std::lock_guard lock(mLock);
- mIsVisible = visible;
- }
+ bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
+ bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
- // Checks the present time history to see whether the layer is relevant.
- bool isRecentlyActive() const {
- std::lock_guard lock(mLock);
- return mPresentTimeHistory.isRelevant();
- }
-
- // Calculate the average refresh rate.
- float getDesiredRefreshRate() const {
- std::lock_guard lock(mLock);
-
- if (mPresentTimeHistory.isLowActivityLayer()) {
- return 1e9f / mLowActivityRefreshDuration;
- }
- return mRefreshRateHistory.getRefreshRateAvg();
- }
-
- bool getHDRContent() {
- std::lock_guard lock(mLock);
- return mIsHDR;
- }
-
- bool isVisible() {
- std::lock_guard lock(mLock);
- return mIsVisible;
+ float getRefreshRate(nsecs_t now) const {
+ return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
}
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
- nsecs_t getLastUpdatedTime() {
- std::lock_guard lock(mLock);
- return mLastUpdatedTime;
- }
-
- std::string getName() const { return mName; }
+ nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
void clearHistory() {
- std::lock_guard lock(mLock);
mRefreshRateHistory.clearHistory();
mPresentTimeHistory.clearHistory();
}
private:
- const std::string mName;
- const nsecs_t mMinRefreshDuration;
- const nsecs_t mLowActivityRefreshDuration;
- mutable std::mutex mLock;
- nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
- nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
- RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
- PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
- bool mIsHDR GUARDED_BY(mLock) = false;
- bool mIsVisible GUARDED_BY(mLock) = false;
+ const float mLowRefreshRate;
+ const float mHighRefreshRate;
+
+ nsecs_t mLastUpdatedTime = 0;
+ nsecs_t mLastPresentTime = 0;
+ RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
+ PresentTimeHistory mPresentTimeHistory;
+ bool mIsHDR = false;
};
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d60e101..71b3500 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,13 +20,6 @@
#include "Scheduler.h"
-#include <algorithm>
-#include <cinttypes>
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <numeric>
-
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
@@ -37,6 +30,14 @@
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <numeric>
+
+#include "../Layer.h"
#include "DispSync.h"
#include "DispSyncSource.h"
#include "EventControlThread.h"
@@ -325,37 +326,27 @@
return mPrimaryDispSync->expectedPresentTime();
}
-std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
- std::string const& name, int windowType) {
+void Scheduler::registerLayer(Layer* layer) {
uint32_t defaultFps, performanceFps;
if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
- performanceFps =
- mRefreshRateConfigs
- .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE)
- .fps;
+ const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE;
+ performanceFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
} else {
defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
performanceFps = defaultFps;
}
- return mLayerHistory.createLayer(name, defaultFps, performanceFps);
+ mLayerHistory.registerLayer(layer, defaultFps, performanceFps);
}
-void Scheduler::addLayerPresentTimeAndHDR(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
- nsecs_t presentTime, bool isHDR) {
- mLayerHistory.insert(layerHandle, presentTime, isHDR);
-}
-
-void Scheduler::setLayerVisibility(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
- mLayerHistory.setVisibility(layerHandle, visible);
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
+ mLayerHistory.record(layer, presentTime, isHDR, systemTime());
}
void Scheduler::updateFpsBasedOnContent() {
- auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
+ auto [refreshRate, isHDR] = mLayerHistory.summarize(systemTime());
const uint32_t refreshRateRound = std::round(refreshRate);
RefreshRateType newRefreshRateType;
{
@@ -402,7 +393,7 @@
// Touch event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory.clearHistory();
+ mLayerHistory.clear();
}
void Scheduler::setDisplayPowerState(bool normal) {
@@ -417,7 +408,7 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory.clearHistory();
+ mLayerHistory.clear();
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a5971fe..c983475 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -100,17 +100,11 @@
void addPresentFence(const std::shared_ptr<FenceTime>&);
void setIgnorePresentFences(bool ignore);
nsecs_t getDispSyncExpectedPresentTime();
- // Registers the layer in the scheduler, and returns the handle for future references.
- std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
- int windowType);
- // Stores present time for a layer.
- void addLayerPresentTimeAndHDR(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
- nsecs_t presentTime, bool isHDR);
- // Stores visibility for a layer.
- void setLayerVisibility(
- const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible);
+ // Layers are registered on creation, and unregistered when the weak reference expires.
+ void registerLayer(Layer*);
+ void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
+
// Updates FPS based on the most content presented.
void updateFpsBasedOnContent();
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 3b7567c..d301b99 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -16,7 +16,6 @@
#pragma once
-#include <chrono>
#include <cinttypes>
#include <numeric>
#include <unordered_map>
@@ -38,21 +37,6 @@
return lhs.id == rhs.id;
}
-using namespace std::chrono_literals;
-
-// This number is used when we try to determine how long do we keep layer information around
-// before we remove it. It is also used to determine how long the layer stays relevant.
-// This time period captures infrequent updates when playing YouTube video with static image,
-// or waiting idle in messaging app, when cursor is blinking.
-static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
-
-// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
-// LOW_ACTIVITY_EPSILON_NS apart.
-// This is helping SF to vote for lower refresh rates when there is not activity
-// in screen.
-static constexpr int LOW_ACTIVITY_BUFFERS = 2;
-static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
-
// Calculates the statistical mean (average) in the data structure (array, vector). The
// function does not modify the contents of the array.
template <typename T>
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 057669b..1acb2da 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -5505,6 +5505,16 @@
return nullptr;
}
+void SurfaceFlinger::onLayerCreated(Layer* layer) {
+ mNumLayers++;
+ mScheduler->registerLayer(layer);
+}
+
+void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+ mNumLayers--;
+ mOffscreenLayers.erase(layer);
+}
+
void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b719245..abb8b82 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -310,11 +310,8 @@
bool authenticateSurfaceTextureLocked(
const sp<IGraphicBufferProducer>& bufferProducer) const;
- inline void onLayerCreated() { mNumLayers++; }
- inline void onLayerDestroyed(Layer* layer) {
- mNumLayers--;
- mOffscreenLayers.erase(layer);
- }
+ void onLayerCreated(Layer*);
+ void onLayerDestroyed(Layer*);
TransactionCompletedThread& getTransactionCompletedThread() {
return mTransactionCompletedThread;
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8e7440c..9a962bc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,140 +1,243 @@
#undef LOG_TAG
-#define LOG_TAG "LayerHistoryUnittests"
+#define LOG_TAG "LayerHistoryTest"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
-#include <mutex>
-#include <thread>
-
#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
using testing::_;
using testing::Return;
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
class LayerHistoryTest : public testing::Test {
-public:
- LayerHistoryTest();
- ~LayerHistoryTest() override;
-
protected:
- std::unique_ptr<LayerHistory> mLayerHistory;
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
- static constexpr float MIN_REFRESH_RATE = 30.f;
- static constexpr float MAX_REFRESH_RATE = 90.f;
- static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
- static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+ static constexpr float LO_FPS = 30.f;
+ static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
- void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
- mLayerHistory->setVisibility(testLayer, true);
- for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- }
- };
-};
+ static constexpr float HI_FPS = 90.f;
+ static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
-LayerHistoryTest::LayerHistoryTest() {
- mLayerHistory = std::make_unique<LayerHistory>();
-}
-LayerHistoryTest::~LayerHistoryTest() {}
+ LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
-namespace {
-TEST_F(LayerHistoryTest, oneLayer) {
- std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer, true);
- for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
- EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+
+ size_t layerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mLayerInfos.size(); }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+ size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+ [now](const auto& pair) { return pair.second->isFrequent(now); });
}
- // Add a few more. This time we should get MAX refresh rate as the layer
- // becomes relevant
- static constexpr auto A_FEW = 10;
- for (auto i = 0u; i < A_FEW; i++) {
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+ RefreshRateConfigs mConfigs{true,
+ {RefreshRateConfigs::InputConfig{0, LO_FPS_PERIOD},
+ RefreshRateConfigs::InputConfig{1, HI_FPS_PERIOD}},
+ 0};
+ TestableScheduler* const mScheduler{new TestableScheduler(mConfigs)};
+ TestableSurfaceFlinger mFlinger;
+
+ const nsecs_t mTime = systemTime();
+};
+
+namespace {
+
+TEST_F(LayerHistoryTest, oneLayer) {
+ const auto layer = createLayer();
+ constexpr bool isHDR = false;
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // 0 FPS is returned if no layers are active.
+ EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(0, activeLayerCount());
+
+ // 0 FPS is returned if active layers have insufficient history.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ history().record(layer.get(), 0, isHDR, mTime);
+ EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ }
+
+ // High FPS is returned once enough history has been recorded.
+ for (int i = 0; i < 10; i++) {
+ history().record(layer.get(), 0, isHDR, mTime);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
}
}
TEST_F(LayerHistoryTest, oneHDRLayer) {
- std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer, true);
+ const auto layer = createLayer();
+ constexpr bool isHDR = true;
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
- EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- EXPECT_EQ(true, mLayerHistory->getDesiredRefreshRateAndHDR().second);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
- mLayerHistory->setVisibility(testLayer, false);
- EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- EXPECT_EQ(false, mLayerHistory->getDesiredRefreshRateAndHDR().second);
+ history().record(layer.get(), 0, isHDR, mTime);
+ auto summary = history().summarize(mTime);
+ EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
+ EXPECT_TRUE(summary.isHDR);
+ EXPECT_EQ(1, activeLayerCount());
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+ summary = history().summarize(mTime);
+ EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
+ EXPECT_FALSE(summary.isHDR);
+ EXPECT_EQ(0, activeLayerCount());
}
TEST_F(LayerHistoryTest, explicitTimestamp) {
- std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(test30FpsLayer, true);
+ const auto layer = createLayer();
+ constexpr bool isHDR = false;
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- nsecs_t startTime = systemTime();
- for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, isHDR, time);
+ time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
}
TEST_F(LayerHistoryTest, multipleLayers) {
- std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer, true);
- std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(test30FpsLayer, true);
- std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
- mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
- mLayerHistory->setVisibility(testLayer2, true);
+ auto layer1 = createLayer();
+ auto layer2 = createLayer();
+ auto layer3 = createLayer();
+ constexpr bool isHDR = false;
- nsecs_t startTime = systemTime();
- for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+
+ nsecs_t time = mTime;
+
+ EXPECT_EQ(3, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer1 is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer1.get(), time, isHDR, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- startTime = systemTime();
- for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer2 is frequent and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, isHDR, time);
+ time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
+ // layer1 is still active but infrequent.
+ history().record(layer1.get(), time, isHDR, time);
+
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer1 is no longer active.
+ // layer2 is frequent and has low refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, isHDR, time);
+ time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- // This frame is only around for 9 occurrences, so it doesn't throw
- // anything off.
- for (int i = 0; i < 9; i++) {
- mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 has high refresh rate but not enough history.
+ constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ if (i % RATIO == 0) {
+ history().record(layer2.get(), time, isHDR, time);
+ }
+
+ history().record(layer3.get(), time, isHDR, time);
+ time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- // After 1200 ms frames become obsolete.
- std::this_thread::sleep_for(std::chrono::milliseconds(1500));
- mLayerHistory->insert(test30FpsLayer,
- startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
- false /*isHDR*/);
- EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer3 becomes recently active.
+ history().record(layer3.get(), time, isHDR, time);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer1 expires.
+ layer1.clear();
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(2, layerCount());
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 becomes inactive.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, isHDR, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 expires.
+ layer2.clear();
+ EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer3 becomes active and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer3.get(), time, isHDR, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer3 expires.
+ layer3.clear();
+ EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ EXPECT_EQ(0, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
}
} // namespace
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ae72467..ae6aa89 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -48,6 +48,7 @@
auto& mutableEventControlThread() { return mEventControlThread; }
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+ auto& mutableLayerHistory() { return mLayerHistory; }
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c7664fb..94fc5f7 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -22,6 +22,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/mock/DisplaySurface.h>
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -173,6 +174,7 @@
class TestableSurfaceFlinger {
public:
+ SurfaceFlinger* flinger() { return mFlinger.get(); }
TestableScheduler* scheduler() { return mScheduler; }
// Extend this as needed for accessing SurfaceFlinger private (and public)
@@ -207,13 +209,15 @@
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
+ resetScheduler(mScheduler);
- mFlinger->mScheduler.reset(mScheduler);
mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
mFlinger->mSfConnectionHandle,
mFlinger->mPhaseOffsets->getCurrentOffsets());
}
+ void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+
using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
mFactory.mCreateBufferQueue = f;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
new file mode 100644
index 0000000..f375e23
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "Layer.h"
+
+namespace android::mock {
+
+class MockLayer : public Layer {
+public:
+ explicit MockLayer(SurfaceFlinger* flinger)
+ : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
+
+ MOCK_CONST_METHOD0(getType, const char*());
+ MOCK_CONST_METHOD0(isVisible, bool());
+ MOCK_METHOD0(createClone, sp<Layer>());
+};
+
+} // namespace android::mock