Merge changes from topic "deprecate-content-detection-v1"
* changes:
Move LayerHistoryV2 to LayerHistory
SF: Deprecate content detection v1
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index ed0d75b..fdb8aaf 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -156,9 +156,7 @@
"Scheduler/EventThread.cpp",
"Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
- "Scheduler/LayerHistoryV2.cpp",
"Scheduler/LayerInfo.cpp",
- "Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 359ee26..c0d00f3 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2020 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.
@@ -35,21 +35,21 @@
#include "LayerInfo.h"
#include "SchedulerUtils.h"
-namespace android::scheduler::impl {
+namespace android::scheduler {
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+ // Layers with an explicit vote are always kept active
if (layer.getFrameRateForLayerTree().rate > 0) {
- return layer.isVisible();
+ return true;
}
+
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
}
bool traceEnabled() {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.layer_history_trace", value, "0");
- return atoi(value);
+ return property_get_bool("debug.sf.layer_history_trace", false);
}
bool useFrameRatePriority() {
@@ -58,30 +58,44 @@
return atoi(value);
}
-void trace(const wp<Layer>& weak, int fps) {
+void trace(const wp<Layer>& weak, const LayerInfo& info, LayerHistory::LayerVoteType type,
+ 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);
+ const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+ ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+ };
+
+ traceType(LayerHistory::LayerVoteType::NoVote, 1);
+ traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+ traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+ traceType(LayerHistory::LayerVoteType::Min, 1);
+ traceType(LayerHistory::LayerVoteType::Max, 1);
+
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
}
} // namespace
-LayerHistory::LayerHistory()
- : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs)
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+ LayerInfo::setTraceEnabled(mTraceEnabled);
+ LayerInfo::setRefreshRateConfigs(refreshRateConfigs);
+}
+
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
- LayerVoteType /*type*/) {
- auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
+void LayerHistory::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
+ LayerVoteType type) {
+ const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
+ auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
}
void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
- LayerUpdateType /*updateType*/) {
+ LayerUpdateType updateType) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -89,7 +103,7 @@
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
- info->setLastPresentTime(presentTime, now);
+ info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -99,54 +113,42 @@
}
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
- ATRACE_CALL();
+ LayerHistory::Summary summary;
+
std::lock_guard lock(mLock);
partitionLayers(now);
- LayerHistory::Summary summary;
- for (const auto& [weakLayer, info] : activeLayers()) {
- const bool recent = info->isRecentlyActive(now);
- auto layer = weakLayer.promote();
- // Only use the layer if the reference still exists.
- if (layer || CC_UNLIKELY(mTraceEnabled)) {
- const auto layerFocused =
- Layer::isLayerFocusedBasedOnPriority(layer->getFrameRateSelectionPriority());
- // Check if frame rate was set on layer.
- const auto frameRate = layer->getFrameRateForLayerTree();
- if (frameRate.rate > 0.f) {
- const auto voteType = [&]() {
- switch (frameRate.type) {
- case Layer::FrameRateCompatibility::Default:
- return LayerVoteType::ExplicitDefault;
- case Layer::FrameRateCompatibility::ExactOrMultiple:
- return LayerVoteType::ExplicitExactOrMultiple;
- case Layer::FrameRateCompatibility::NoVote:
- return LayerVoteType::NoVote;
- }
- }();
- summary.push_back(
- RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
- .vote = voteType,
- .desiredRefreshRate = frameRate.rate,
- .seamlessness = frameRate.seamlessness,
- .weight = 1.0f,
- .focused = layerFocused});
- } else if (recent) {
- summary.push_back(
- RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
- .vote = LayerVoteType::Heuristic,
- .desiredRefreshRate =
- info->getRefreshRate(now),
- .seamlessness =
- Seamlessness::OnlySeamless,
- .weight = 1.0f,
- .focused = layerFocused});
- }
+ for (const auto& [layer, info] : activeLayers()) {
+ const auto strong = layer.promote();
+ if (!strong) {
+ continue;
+ }
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weakLayer, round<int>(frameRate.rate));
- }
+ const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+ const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
+ ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
+ frameRateSelectionPriority, layerFocused ? "" : "not");
+
+ const auto vote = info->getRefreshRateVote(now);
+ // Skip NoVote layer as those don't have any requirements
+ if (vote.type == LayerHistory::LayerVoteType::NoVote) {
+ continue;
+ }
+
+ // Compute the layer's position on the screen
+ const Rect bounds = Rect(strong->getBounds());
+ const ui::Transform transform = strong->getTransform();
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
+
+ const float layerArea = transformed.getWidth() * transformed.getHeight();
+ float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+ summary.push_back(
+ {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused});
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps)));
}
}
@@ -162,14 +164,33 @@
auto& [weak, info] = mLayerInfos[i];
if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
i++;
+ // Set layer vote if set
+ const auto frameRate = layer->getFrameRateForLayerTree();
+ const auto voteType = [&]() {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return LayerVoteType::ExplicitDefault;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return LayerVoteType::ExplicitExactOrMultiple;
+ case Layer::FrameRateCompatibility::NoVote:
+ return LayerVoteType::NoVote;
+ }
+ }();
+
+ if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+ const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+ info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
+ } else {
+ info->resetLayerVote();
+ }
continue;
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, 0);
+ trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
}
- info->clearHistory();
+ info->onLayerInactive(now);
std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
@@ -190,10 +211,8 @@
std::lock_guard lock(mLock);
for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory();
+ info->clearHistory(systemTime());
}
-
- mActiveLayersEnd = 0;
}
std::string LayerHistory::dump() const {
@@ -202,4 +221,4 @@
mActiveLayersEnd);
}
-} // namespace android::scheduler::impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 128699b..507ccc6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -36,25 +36,23 @@
namespace scheduler {
class LayerHistoryTest;
-class LayerHistoryTestV2;
class LayerInfo;
-class LayerInfoV2;
class LayerHistory {
public:
using LayerVoteType = RefreshRateConfigs::LayerVoteType;
- virtual ~LayerHistory() = default;
+ LayerHistory(const RefreshRateConfigs&);
+ ~LayerHistory();
// Layers are unregistered when the weak reference expires.
- virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
- LayerVoteType type) = 0;
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate, LayerVoteType type);
// Sets the display size. Client is responsible for synchronization.
- virtual void setDisplayArea(uint32_t displayArea) = 0;
+ void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
// Sets whether a config change is pending to be applied
- virtual void setConfigChangePending(bool pending) = 0;
+ void setConfigChangePending(bool pending) { mConfigChangePending = pending; }
// Represents which layer activity is recorded
enum class LayerUpdateType {
@@ -64,45 +62,18 @@
};
// Marks the layer as active, and records the given state to its history.
- virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
+ void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- virtual Summary summarize(nsecs_t now) = 0;
+ Summary summarize(nsecs_t now);
- virtual void clear() = 0;
- virtual std::string dump() const = 0;
-};
-
-namespace impl {
-// 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 android::scheduler::LayerHistory {
-public:
- LayerHistory();
- virtual ~LayerHistory();
-
- // Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
- LayerVoteType type) override;
-
- void setDisplayArea(uint32_t /*displayArea*/) override {}
-
- void setConfigChangePending(bool /*pending*/) override {}
-
- // Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
-
- // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
-
- void clear() override;
- std::string dump() const override;
+ void clear();
+ std::string dump() const;
private:
- friend class android::scheduler::LayerHistoryTest;
+ friend LayerHistoryTest;
friend TestableScheduler;
using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
@@ -130,65 +101,6 @@
LayerInfos mLayerInfos GUARDED_BY(mLock);
size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
- // Whether to emit systrace output and debug logs.
- const bool mTraceEnabled;
-
- // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
- const bool mUseFrameRatePriority;
-};
-
-class LayerHistoryV2 : public android::scheduler::LayerHistory {
-public:
- LayerHistoryV2(const scheduler::RefreshRateConfigs&);
- virtual ~LayerHistoryV2();
-
- // Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
- LayerVoteType type) override;
-
- // Sets the display size. Client is responsible for synchronization.
- void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
-
- void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
-
- // Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
-
- // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
-
- void clear() override;
- std::string dump() const override;
-
-private:
- friend android::scheduler::LayerHistoryTestV2;
- friend TestableScheduler;
-
- using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfoV2>>;
- using LayerInfos = std::vector<LayerPair>;
-
- struct ActiveLayers {
- LayerInfos& infos;
- const size_t index;
-
- auto begin() { return infos.begin(); }
- auto end() { return begin() + static_cast<long>(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;
-
uint32_t mDisplayArea = 0;
// Whether to emit systrace output and debug logs.
@@ -201,6 +113,5 @@
std::atomic<bool> mConfigChangePending = false;
};
-} // namespace impl
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
deleted file mode 100644
index e919d1b..0000000
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryV2"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerHistory.h"
-
-#include <android-base/stringprintf.h>
-#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 "LayerInfoV2.h"
-#include "SchedulerUtils.h"
-
-namespace android::scheduler::impl {
-
-namespace {
-
-bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
- // Layers with an explicit vote are always kept active
- if (layer.getFrameRateForLayerTree().rate > 0) {
- return true;
- }
-
- return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
-}
-
-bool traceEnabled() {
- return property_get_bool("debug.sf.layer_history_trace", false);
-}
-
-bool useFrameRatePriority() {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.use_frame_rate_priority", value, "1");
- return atoi(value);
-}
-
-void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
- int fps) {
- const auto layer = weak.promote();
- if (!layer) return;
-
- const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
- ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
- };
-
- traceType(LayerHistory::LayerVoteType::NoVote, 1);
- traceType(LayerHistory::LayerVoteType::Heuristic, fps);
- traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
- traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
- traceType(LayerHistory::LayerVoteType::Min, 1);
- traceType(LayerHistory::LayerVoteType::Max, 1);
-
- ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
-}
-} // namespace
-
-LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
- LayerInfoV2::setTraceEnabled(mTraceEnabled);
- LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
-}
-
-LayerHistoryV2::~LayerHistoryV2() = default;
-
-void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
- LayerVoteType type) {
- const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
- auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
- std::lock_guard lock(mLock);
- mLayerInfos.emplace_back(layer, std::move(info));
-}
-
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
- LayerUpdateType updateType) {
- std::lock_guard lock(mLock);
-
- 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, updateType, mConfigChangePending);
-
- // Activate layer if inactive.
- if (const auto end = activeLayers().end(); it >= end) {
- std::iter_swap(it, end);
- mActiveLayersEnd++;
- }
-}
-
-LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
- LayerHistory::Summary summary;
-
- std::lock_guard lock(mLock);
-
- partitionLayers(now);
-
- for (const auto& [layer, info] : activeLayers()) {
- const auto strong = layer.promote();
- if (!strong) {
- continue;
- }
-
- const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
- const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
- ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
- frameRateSelectionPriority, layerFocused ? "" : "not");
-
- const auto vote = info->getRefreshRateVote(now);
- // Skip NoVote layer as those don't have any requirements
- if (vote.type == LayerHistory::LayerVoteType::NoVote) {
- continue;
- }
-
- // Compute the layer's position on the screen
- const Rect bounds = Rect(strong->getBounds());
- const ui::Transform transform = strong->getTransform();
- constexpr bool roundOutwards = true;
- Rect transformed = transform.transform(bounds, roundOutwards);
-
- const float layerArea = transformed.getWidth() * transformed.getHeight();
- float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back(
- {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused});
-
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps)));
- }
- }
-
- return summary;
-}
-
-void LayerHistoryV2::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++;
- // Set layer vote if set
- const auto frameRate = layer->getFrameRateForLayerTree();
- const auto voteType = [&]() {
- switch (frameRate.type) {
- case Layer::FrameRateCompatibility::Default:
- return LayerVoteType::ExplicitDefault;
- case Layer::FrameRateCompatibility::ExactOrMultiple:
- return LayerVoteType::ExplicitExactOrMultiple;
- case Layer::FrameRateCompatibility::NoVote:
- return LayerVoteType::NoVote;
- }
- }();
-
- if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
- const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
- } else {
- info->resetLayerVote();
- }
- continue;
- }
-
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
- }
-
- info->onLayerInactive(now);
- 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 {
- std::swap(mLayerInfos[i], mLayerInfos[--end]);
- }
- }
-
- mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
-}
-
-void LayerHistoryV2::clear() {
- std::lock_guard lock(mLock);
-
- for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory(systemTime());
- }
-}
-
-std::string LayerHistoryV2::dump() const {
- std::lock_guard lock(mLock);
- return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(),
- mActiveLayersEnd);
-}
-
-} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 6d9dd43..66ac98a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,36 +14,284 @@
* limitations under the License.
*/
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "LayerInfo.h"
#include <algorithm>
#include <utility>
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfo"
+
namespace android::scheduler {
-LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
- : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
+const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
+bool LayerInfo::sTraceEnabled = false;
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+LayerInfo::LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
+ LayerHistory::LayerVoteType defaultVote)
+ : mName(name),
+ mHighRefreshRatePeriod(highRefreshRatePeriod),
+ mDefaultVote(defaultVote),
+ mLayerVote({defaultVote, 0.0f}),
+ mRefreshRateHistory(name) {}
+
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+ bool pendingConfigChange) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
- // Buffers can come with a present time far in the future. That keeps them relevant.
mLastUpdatedTime = std::max(lastPresentTime, now);
- mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+ switch (updateType) {
+ case LayerUpdateType::AnimationTX:
+ mLastAnimationTime = std::max(lastPresentTime, now);
+ break;
+ case LayerUpdateType::SetFrameRate:
+ case LayerUpdateType::Buffer:
+ FrameTimeData frameTime = {.presetTime = lastPresentTime,
+ .queueTime = mLastUpdatedTime,
+ .pendingConfigChange = pendingConfigChange};
+ mFrameTimes.push_back(frameTime);
+ if (mFrameTimes.size() > HISTORY_SIZE) {
+ mFrameTimes.pop_front();
+ }
+ break;
+ }
+}
- if (mLastPresentTime == 0) {
- // First frame
- mLastPresentTime = lastPresentTime;
- return;
+bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
+ return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+ mFrameTimeValidSince.time_since_epoch())
+ .count();
+}
+
+bool LayerInfo::isFrequent(nsecs_t now) const {
+ // If we know nothing about this layer we consider it as frequent as it might be the start
+ // of an animation.
+ if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+ return true;
}
- const nsecs_t period = lastPresentTime - mLastPresentTime;
- mLastPresentTime = lastPresentTime;
- // Ignore time diff that are too high - those are stale values
- if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+ // Find the first active frame
+ auto it = mFrameTimes.begin();
+ for (; it != mFrameTimes.end(); ++it) {
+ if (it->queueTime >= getActiveLayerThreshold(now)) {
+ break;
+ }
+ }
- const float fps = std::min(1e9f / period, mHighRefreshRate);
- mRefreshRateHistory.insertRefreshRate(fps);
+ const auto numFrames = std::distance(it, mFrameTimes.end());
+ if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
+ return false;
+ }
+
+ // Layer is considered frequent if the average frame rate is higher than the threshold
+ const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
+ return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
+}
+
+bool LayerInfo::isAnimating(nsecs_t now) const {
+ return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfo::hasEnoughDataForHeuristic() const {
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
+ if (mFrameTimes.size() < 2) {
+ ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
+ return false;
+ }
+
+ if (!isFrameTimeValid(mFrameTimes.front())) {
+ ALOGV("stale frames still captured");
+ return false;
+ }
+
+ const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+ if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+ ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+ totalDuration / 1e9f);
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
+ nsecs_t totalPresentTimeDeltas = 0;
+ nsecs_t totalQueueTimeDeltas = 0;
+ bool missingPresentTime = false;
+ int numFrames = 0;
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // Ignore frames captured during a config change
+ if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+ return std::nullopt;
+ }
+
+ totalQueueTimeDeltas +=
+ std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+ numFrames++;
+
+ if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
+ missingPresentTime = true;
+ // If there are no presentation timestamps and we haven't calculated
+ // one in the past then we can't calculate the refresh rate
+ if (mLastRefreshRate.reported == 0) {
+ return std::nullopt;
+ }
+ continue;
+ }
+
+ totalPresentTimeDeltas +=
+ std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ }
+
+ // Calculate the average frame time based on presentation timestamps. If those
+ // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+ // we calculated a refresh rate based on presentation timestamps in the past. The reason
+ // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+ // when implementing render ahead for specific refresh rates. When hwui no longer provides
+ // presentation timestamps we look at the queue time to see if the current refresh rate still
+ // matches the content.
+
+ const auto averageFrameTime =
+ static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
+ numFrames;
+ return static_cast<nsecs_t>(averageFrameTime);
+}
+
+std::optional<float> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) {
+ static constexpr float MARGIN = 1.0f; // 1Hz
+ if (!hasEnoughDataForHeuristic()) {
+ ALOGV("Not enough data");
+ return std::nullopt;
+ }
+
+ const auto averageFrameTime = calculateAverageFrameTime();
+ if (averageFrameTime.has_value()) {
+ const auto refreshRate = 1e9f / *averageFrameTime;
+ const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+ if (refreshRateConsistent) {
+ const auto knownRefreshRate =
+ sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
+
+ // To avoid oscillation, use the last calculated refresh rate if it is
+ // close enough
+ if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
+ mLastRefreshRate.reported != knownRefreshRate) {
+ mLastRefreshRate.calculated = refreshRate;
+ mLastRefreshRate.reported = knownRefreshRate;
+ }
+
+ ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
+ refreshRate, mLastRefreshRate.reported);
+ } else {
+ ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
+ refreshRate, mLastRefreshRate.reported);
+ }
+ }
+
+ return mLastRefreshRate.reported == 0 ? std::nullopt
+ : std::make_optional(mLastRefreshRate.reported);
+}
+
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) {
+ if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+ ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+ return mLayerVote;
+ }
+
+ if (isAnimating(now)) {
+ ALOGV("%s is animating", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
+ return {LayerHistory::LayerVoteType::Max, 0};
+ }
+
+ if (!isFrequent(now)) {
+ ALOGV("%s is infrequent", mName.c_str());
+ mLastRefreshRate.animatingOrInfrequent = true;
+ return {LayerHistory::LayerVoteType::Min, 0};
+ }
+
+ // If the layer was previously tagged as animating or infrequent, we clear
+ // the history as it is likely the layer just changed its behavior
+ // and we should not look at stale data
+ if (mLastRefreshRate.animatingOrInfrequent) {
+ clearHistory(now);
+ }
+
+ auto refreshRate = calculateRefreshRateIfPossible(now);
+ if (refreshRate.has_value()) {
+ ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
+ return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+ }
+
+ ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
+ return {LayerHistory::LayerVoteType::Max, 0};
+}
+
+const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+ if (mTraceTags.count(type) == 0) {
+ const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+ mTraceTags.emplace(type, tag);
+ }
+
+ return mTraceTags.at(type).c_str();
+}
+
+LayerInfo::RefreshRateHistory::HeuristicTraceTagData
+LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
+ const std::string prefix = "LFPS ";
+ const std::string suffix = "Heuristic ";
+ return {.min = prefix + mName + suffix + "min",
+ .max = prefix + mName + suffix + "max",
+ .consistent = prefix + mName + suffix + "consistent",
+ .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfo::RefreshRateHistory::clear() {
+ mRefreshRates.clear();
+}
+
+bool LayerInfo::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
+ mRefreshRates.push_back({refreshRate, now});
+ while (mRefreshRates.size() >= HISTORY_SIZE ||
+ now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+ mRefreshRates.pop_front();
+ }
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
+ }
+
+ return isConsistent();
+}
+
+bool LayerInfo::RefreshRateHistory::isConsistent() const {
+ if (mRefreshRates.empty()) return true;
+
+ const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+ const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
+
+ if (CC_UNLIKELY(sTraceEnabled)) {
+ if (!mHeuristicTraceTagData.has_value()) {
+ mHeuristicTraceTagData = makeHeuristicTraceTagData();
+ }
+
+ ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
+ ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
+ ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+ }
+
+ return consistent;
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 820624b..e434670 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -21,6 +21,9 @@
#include <chrono>
#include <deque>
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "Scheduler/Seamlessness.h"
#include "SchedulerUtils.h"
namespace android {
@@ -41,129 +44,179 @@
// Stores history of present times and refresh rates for a layer.
class LayerInfo {
+ using LayerUpdateType = LayerHistory::LayerUpdateType;
+
// 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
- * for individual layers.
- */
- class RefreshRateHistory {
- public:
- explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
-
- void insertRefreshRate(float refreshRate) {
- mElements.push_back(refreshRate);
- if (mElements.size() > HISTORY_SIZE) {
- mElements.pop_front();
- }
- }
-
- float getRefreshRateAvg() const {
- return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
- }
-
- void clearHistory() { mElements.clear(); }
-
- private:
- const float mHighRefreshRate;
-
- static constexpr size_t HISTORY_SIZE = 30;
- std::deque<float> mElements;
- };
-
- /**
- * Struct that keeps the information about the present time for last
- * HISTORY_SIZE frames. This is used to better determine whether the given layer
- * is still relevant and it's refresh rate should be considered.
- */
- class PresentTimeHistory {
- public:
- static constexpr size_t HISTORY_SIZE = 90;
-
- void insertPresentTime(nsecs_t presentTime) {
- mElements.push_back(presentTime);
- if (mElements.size() > HISTORY_SIZE) {
- mElements.pop_front();
- }
- }
-
- // 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_DURATION of updates
- if (mElements.size() < HISTORY_SIZE &&
- mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
- return false;
- }
-
- return mElements.back() >= getActiveLayerThreshold(now);
- }
-
- 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;
- }
-
- // 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 std::chrono::nanoseconds HISTORY_DURATION = 1s;
- };
+ static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
+ std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
friend class LayerHistoryTest;
public:
- LayerInfo(float lowRefreshRate, float highRefreshRate);
+ // Holds information about the layer vote
+ struct LayerVote {
+ LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+ float fps = 0.0f;
+ Seamlessness seamlessness = Seamlessness::Default;
+ };
+
+ static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+ static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+ sRefreshRateConfigs = &refreshRateConfigs;
+ }
+
+ LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
+ LayerHistory::LayerVoteType defaultVote);
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
- // Records the last requested oresent time. It also stores information about when
+ // Records the last requested present 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, nsecs_t now);
+ void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+ bool pendingConfigChange);
- bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
- bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
+ // Sets an explicit layer vote. This usually comes directly from the application via
+ // ANativeWindow_setFrameRate API
+ void setLayerVote(LayerVote vote) { mLayerVote = vote; }
- float getRefreshRate(nsecs_t now) const {
- return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
- }
+ // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
+ // This is used for layers that called to setLayerVote() and then removed the vote, so that the
+ // layer can go back to whatever vote it had before the app voted for it.
+ void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+
+ // Resets the layer vote to its default.
+ void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, Seamlessness::Default}; }
+
+ LayerVote getRefreshRateVote(nsecs_t now);
// 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() const { return mLastUpdatedTime; }
- void clearHistory() {
- mRefreshRateHistory.clearHistory();
- mPresentTimeHistory.clearHistory();
+ // Returns a C string for tracing a vote
+ const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
+ void onLayerInactive(nsecs_t now) {
+ // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+ // We are not deleting the old frame to keep track of whether we should treat the first
+ // buffer as Max as we don't know anything about this layer or Min as this layer is
+ // posting infrequent updates.
+ const auto timePoint = std::chrono::nanoseconds(now);
+ mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
+ mLastRefreshRate = {};
+ mRefreshRateHistory.clear();
+ }
+
+ void clearHistory(nsecs_t now) {
+ onLayerInactive(now);
+ mFrameTimes.clear();
}
private:
- const float mLowRefreshRate;
- const float mHighRefreshRate;
+ // Used to store the layer timestamps
+ struct FrameTimeData {
+ nsecs_t presetTime; // desiredPresentTime, if provided
+ nsecs_t queueTime; // buffer queue time
+ bool pendingConfigChange;
+ };
+
+ // Holds information about the calculated and reported refresh rate
+ struct RefreshRateHeuristicData {
+ // Rate calculated on the layer
+ float calculated = 0.0f;
+ // Last reported rate for LayerInfo::getRefreshRate()
+ float reported = 0.0f;
+ // Whether the last reported rate for LayerInfo::getRefreshRate()
+ // was due to animation or infrequent updates
+ bool animatingOrInfrequent = false;
+ };
+
+ // Class to store past calculated refresh rate and determine whether
+ // the refresh rate calculated is consistent with past values
+ class RefreshRateHistory {
+ public:
+ static constexpr auto HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+ RefreshRateHistory(const std::string& name) : mName(name) {}
+
+ // Clears History
+ void clear();
+
+ // Adds a new refresh rate and returns true if it is consistent
+ bool add(float refreshRate, nsecs_t now);
+
+ private:
+ friend class LayerHistoryTest;
+
+ // Holds the refresh rate when it was calculated
+ struct RefreshRateData {
+ float refreshRate = 0.0f;
+ nsecs_t timestamp = 0;
+
+ bool operator<(const RefreshRateData& other) const {
+ return refreshRate < other.refreshRate;
+ }
+ };
+
+ // Holds tracing strings
+ struct HeuristicTraceTagData {
+ std::string min;
+ std::string max;
+ std::string consistent;
+ std::string average;
+ };
+
+ bool isConsistent() const;
+ HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+ const std::string mName;
+ mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+ std::deque<RefreshRateData> mRefreshRates;
+ static constexpr float MARGIN_FPS = 1.0;
+ };
+
+ bool isFrequent(nsecs_t now) const;
+ bool isAnimating(nsecs_t now) const;
+ bool hasEnoughDataForHeuristic() const;
+ std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+ std::optional<nsecs_t> calculateAverageFrameTime() const;
+ bool isFrameTimeValid(const FrameTimeData&) const;
+
+ const std::string mName;
+
+ // Used for sanitizing the heuristic data
+ const nsecs_t mHighRefreshRatePeriod;
+ LayerHistory::LayerVoteType mDefaultVote;
+
+ LayerVote mLayerVote;
nsecs_t mLastUpdatedTime = 0;
- nsecs_t mLastPresentTime = 0;
- RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
- PresentTimeHistory mPresentTimeHistory;
+
+ nsecs_t mLastAnimationTime = 0;
+
+ RefreshRateHeuristicData mLastRefreshRate;
+
+ std::deque<FrameTimeData> mFrameTimes;
+ std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+ std::chrono::steady_clock::now();
+ static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+ RefreshRateHistory mRefreshRateHistory;
+
+ mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+ // Shared for all LayerInfo instances
+ static const RefreshRateConfigs* sRefreshRateConfigs;
+ static bool sTraceEnabled;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
deleted file mode 100644
index 94e7e20..0000000
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-// #define LOG_NDEBUG 0
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerInfoV2.h"
-
-#include <algorithm>
-#include <utility>
-
-#include <cutils/compiler.h>
-#include <cutils/trace.h>
-
-#undef LOG_TAG
-#define LOG_TAG "LayerInfoV2"
-
-namespace android::scheduler {
-
-const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
-bool LayerInfoV2::sTraceEnabled = false;
-
-LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
- LayerHistory::LayerVoteType defaultVote)
- : mName(name),
- mHighRefreshRatePeriod(highRefreshRatePeriod),
- mDefaultVote(defaultVote),
- mLayerVote({defaultVote, 0.0f}),
- mRefreshRateHistory(name) {}
-
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
- LayerUpdateType updateType, bool pendingConfigChange) {
- lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
-
- mLastUpdatedTime = std::max(lastPresentTime, now);
- switch (updateType) {
- case LayerUpdateType::AnimationTX:
- mLastAnimationTime = std::max(lastPresentTime, now);
- break;
- case LayerUpdateType::SetFrameRate:
- case LayerUpdateType::Buffer:
- FrameTimeData frameTime = {.presetTime = lastPresentTime,
- .queueTime = mLastUpdatedTime,
- .pendingConfigChange = pendingConfigChange};
- mFrameTimes.push_back(frameTime);
- if (mFrameTimes.size() > HISTORY_SIZE) {
- mFrameTimes.pop_front();
- }
- break;
- }
-}
-
-bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
- return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
- mFrameTimeValidSince.time_since_epoch())
- .count();
-}
-
-bool LayerInfoV2::isFrequent(nsecs_t now) const {
- // If we know nothing about this layer we consider it as frequent as it might be the start
- // of an animation.
- if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
- return true;
- }
-
- // Find the first active frame
- auto it = mFrameTimes.begin();
- for (; it != mFrameTimes.end(); ++it) {
- if (it->queueTime >= getActiveLayerThreshold(now)) {
- break;
- }
- }
-
- const auto numFrames = std::distance(it, mFrameTimes.end());
- if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
- return false;
- }
-
- // Layer is considered frequent if the average frame rate is higher than the threshold
- const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
- return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
-}
-
-bool LayerInfoV2::isAnimating(nsecs_t now) const {
- return mLastAnimationTime >= getActiveLayerThreshold(now);
-}
-
-bool LayerInfoV2::hasEnoughDataForHeuristic() const {
- // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
- if (mFrameTimes.size() < 2) {
- ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
- return false;
- }
-
- if (!isFrameTimeValid(mFrameTimes.front())) {
- ALOGV("stale frames still captured");
- return false;
- }
-
- const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
- if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
- ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
- totalDuration / 1e9f);
- return false;
- }
-
- return true;
-}
-
-std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
- nsecs_t totalPresentTimeDeltas = 0;
- nsecs_t totalQueueTimeDeltas = 0;
- bool missingPresentTime = false;
- int numFrames = 0;
- for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
- // Ignore frames captured during a config change
- if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
- return std::nullopt;
- }
-
- totalQueueTimeDeltas +=
- std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
- numFrames++;
-
- if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
- missingPresentTime = true;
- // If there are no presentation timestamps and we haven't calculated
- // one in the past then we can't calculate the refresh rate
- if (mLastRefreshRate.reported == 0) {
- return std::nullopt;
- }
- continue;
- }
-
- totalPresentTimeDeltas +=
- std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
- }
-
- // Calculate the average frame time based on presentation timestamps. If those
- // doesn't exist, we look at the time the buffer was queued only. We can do that only if
- // we calculated a refresh rate based on presentation timestamps in the past. The reason
- // we look at the queue time is to handle cases where hwui attaches presentation timestamps
- // when implementing render ahead for specific refresh rates. When hwui no longer provides
- // presentation timestamps we look at the queue time to see if the current refresh rate still
- // matches the content.
-
- const auto averageFrameTime =
- static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
- numFrames;
- return static_cast<nsecs_t>(averageFrameTime);
-}
-
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
- static constexpr float MARGIN = 1.0f; // 1Hz
- if (!hasEnoughDataForHeuristic()) {
- ALOGV("Not enough data");
- return std::nullopt;
- }
-
- const auto averageFrameTime = calculateAverageFrameTime();
- if (averageFrameTime.has_value()) {
- const auto refreshRate = 1e9f / *averageFrameTime;
- const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
- if (refreshRateConsistent) {
- const auto knownRefreshRate =
- sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
-
- // To avoid oscillation, use the last calculated refresh rate if it is
- // close enough
- if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
- mLastRefreshRate.reported != knownRefreshRate) {
- mLastRefreshRate.calculated = refreshRate;
- mLastRefreshRate.reported = knownRefreshRate;
- }
-
- ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
- refreshRate, mLastRefreshRate.reported);
- } else {
- ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
- refreshRate, mLastRefreshRate.reported);
- }
- }
-
- return mLastRefreshRate.reported == 0 ? std::nullopt
- : std::make_optional(mLastRefreshRate.reported);
-}
-
-LayerInfoV2::LayerVote LayerInfoV2::getRefreshRateVote(nsecs_t now) {
- if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
- ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
- return mLayerVote;
- }
-
- if (isAnimating(now)) {
- ALOGV("%s is animating", mName.c_str());
- mLastRefreshRate.animatingOrInfrequent = true;
- return {LayerHistory::LayerVoteType::Max, 0};
- }
-
- if (!isFrequent(now)) {
- ALOGV("%s is infrequent", mName.c_str());
- mLastRefreshRate.animatingOrInfrequent = true;
- return {LayerHistory::LayerVoteType::Min, 0};
- }
-
- // If the layer was previously tagged as animating or infrequent, we clear
- // the history as it is likely the layer just changed its behavior
- // and we should not look at stale data
- if (mLastRefreshRate.animatingOrInfrequent) {
- clearHistory(now);
- }
-
- auto refreshRate = calculateRefreshRateIfPossible(now);
- if (refreshRate.has_value()) {
- ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
- return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
- }
-
- ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
- return {LayerHistory::LayerVoteType::Max, 0};
-}
-
-const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
- if (mTraceTags.count(type) == 0) {
- const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
- mTraceTags.emplace(type, tag);
- }
-
- return mTraceTags.at(type).c_str();
-}
-
-LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
-LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
- const std::string prefix = "LFPS ";
- const std::string suffix = "Heuristic ";
- return {.min = prefix + mName + suffix + "min",
- .max = prefix + mName + suffix + "max",
- .consistent = prefix + mName + suffix + "consistent",
- .average = prefix + mName + suffix + "average"};
-}
-
-void LayerInfoV2::RefreshRateHistory::clear() {
- mRefreshRates.clear();
-}
-
-bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
- mRefreshRates.push_back({refreshRate, now});
- while (mRefreshRates.size() >= HISTORY_SIZE ||
- now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
- mRefreshRates.pop_front();
- }
-
- if (CC_UNLIKELY(sTraceEnabled)) {
- if (!mHeuristicTraceTagData.has_value()) {
- mHeuristicTraceTagData = makeHeuristicTraceTagData();
- }
-
- ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
- }
-
- return isConsistent();
-}
-
-bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
- if (mRefreshRates.empty()) return true;
-
- const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
-
- if (CC_UNLIKELY(sTraceEnabled)) {
- if (!mHeuristicTraceTagData.has_value()) {
- mHeuristicTraceTagData = makeHeuristicTraceTagData();
- }
-
- ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
- ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
- ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
- }
-
- return consistent;
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
deleted file mode 100644
index f94f4ab..0000000
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright 2020 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 <utils/Timers.h>
-
-#include <chrono>
-#include <deque>
-
-#include "LayerHistory.h"
-#include "RefreshRateConfigs.h"
-#include "Scheduler/Seamlessness.h"
-#include "SchedulerUtils.h"
-
-namespace android {
-
-class Layer;
-
-namespace scheduler {
-
-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 LayerInfoV2 {
- using LayerUpdateType = LayerHistory::LayerUpdateType;
-
- // 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 float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
- std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
-
- friend class LayerHistoryTestV2;
-
-public:
- // Holds information about the layer vote
- struct LayerVote {
- LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
- float fps = 0.0f;
- Seamlessness seamlessness = Seamlessness::Default;
- };
-
- static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
-
- static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
- sRefreshRateConfigs = &refreshRateConfigs;
- }
-
- LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
- LayerHistory::LayerVoteType defaultVote);
-
- LayerInfoV2(const LayerInfo&) = delete;
- LayerInfoV2& operator=(const LayerInfoV2&) = delete;
-
- // Records the last requested present 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, nsecs_t now, LayerUpdateType updateType,
- bool pendingConfigChange);
-
- // Sets an explicit layer vote. This usually comes directly from the application via
- // ANativeWindow_setFrameRate API
- void setLayerVote(LayerVote vote) { mLayerVote = vote; }
-
- // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
- // This is used for layers that called to setLayerVote() and then removed the vote, so that the
- // layer can go back to whatever vote it had before the app voted for it.
- void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
-
- // Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, Seamlessness::Default}; }
-
- LayerVote getRefreshRateVote(nsecs_t now);
-
- // 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() const { return mLastUpdatedTime; }
-
- // Returns a C string for tracing a vote
- const char* getTraceTag(LayerHistory::LayerVoteType type) const;
-
- void onLayerInactive(nsecs_t now) {
- // Mark mFrameTimeValidSince to now to ignore all previous frame times.
- // We are not deleting the old frame to keep track of whether we should treat the first
- // buffer as Max as we don't know anything about this layer or Min as this layer is
- // posting infrequent updates.
- const auto timePoint = std::chrono::nanoseconds(now);
- mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
- mLastRefreshRate = {};
- mRefreshRateHistory.clear();
- }
-
- void clearHistory(nsecs_t now) {
- onLayerInactive(now);
- mFrameTimes.clear();
- }
-
-private:
- // Used to store the layer timestamps
- struct FrameTimeData {
- nsecs_t presetTime; // desiredPresentTime, if provided
- nsecs_t queueTime; // buffer queue time
- bool pendingConfigChange;
- };
-
- // Holds information about the calculated and reported refresh rate
- struct RefreshRateHeuristicData {
- // Rate calculated on the layer
- float calculated = 0.0f;
- // Last reported rate for LayerInfoV2::getRefreshRate()
- float reported = 0.0f;
- // Whether the last reported rate for LayerInfoV2::getRefreshRate()
- // was due to animation or infrequent updates
- bool animatingOrInfrequent = false;
- };
-
- // Class to store past calculated refresh rate and determine whether
- // the refresh rate calculated is consistent with past values
- class RefreshRateHistory {
- public:
- static constexpr auto HISTORY_SIZE = 90;
- static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
-
- RefreshRateHistory(const std::string& name) : mName(name) {}
-
- // Clears History
- void clear();
-
- // Adds a new refresh rate and returns true if it is consistent
- bool add(float refreshRate, nsecs_t now);
-
- private:
- friend class LayerHistoryTestV2;
-
- // Holds the refresh rate when it was calculated
- struct RefreshRateData {
- float refreshRate = 0.0f;
- nsecs_t timestamp = 0;
-
- bool operator<(const RefreshRateData& other) const {
- return refreshRate < other.refreshRate;
- }
- };
-
- // Holds tracing strings
- struct HeuristicTraceTagData {
- std::string min;
- std::string max;
- std::string consistent;
- std::string average;
- };
-
- bool isConsistent() const;
- HeuristicTraceTagData makeHeuristicTraceTagData() const;
-
- const std::string mName;
- mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
- std::deque<RefreshRateData> mRefreshRates;
- static constexpr float MARGIN_FPS = 1.0;
- };
-
- bool isFrequent(nsecs_t now) const;
- bool isAnimating(nsecs_t now) const;
- bool hasEnoughDataForHeuristic() const;
- std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
- std::optional<nsecs_t> calculateAverageFrameTime() const;
- bool isFrameTimeValid(const FrameTimeData&) const;
-
- const std::string mName;
-
- // Used for sanitizing the heuristic data
- const nsecs_t mHighRefreshRatePeriod;
- LayerHistory::LayerVoteType mDefaultVote;
-
- LayerVote mLayerVote;
-
- nsecs_t mLastUpdatedTime = 0;
-
- nsecs_t mLastAnimationTime = 0;
-
- RefreshRateHeuristicData mLastRefreshRate;
-
- std::deque<FrameTimeData> mFrameTimes;
- std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
- std::chrono::steady_clock::now();
- static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
- static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
-
- RefreshRateHistory mRefreshRateHistory;
-
- mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
-
- // Shared for all LayerInfo instances
- static const RefreshRateConfigs* sRefreshRateConfigs;
- static bool sTraceEnabled;
-};
-
-} // namespace scheduler
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b14e482..83fa20e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -69,60 +69,6 @@
primaryRange.max, appRequestRange.min, appRequestRange.max);
}
-const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
- const std::vector<LayerRequirement>& layers) const {
- std::lock_guard lock(mLock);
- int contentFramerate = 0;
- int explicitContentFramerate = 0;
- for (const auto& layer : layers) {
- const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
- if (layer.vote == LayerVoteType::ExplicitDefault ||
- layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
- if (desiredRefreshRateRound > explicitContentFramerate) {
- explicitContentFramerate = desiredRefreshRateRound;
- }
- } else {
- if (desiredRefreshRateRound > contentFramerate) {
- contentFramerate = desiredRefreshRateRound;
- }
- }
- }
-
- if (explicitContentFramerate != 0) {
- contentFramerate = explicitContentFramerate;
- } else if (contentFramerate == 0) {
- contentFramerate = round<int>(mMaxSupportedRefreshRate->getFps());
- }
- ATRACE_INT("ContentFPS", contentFramerate);
-
- // Find the appropriate refresh rate with minimal error
- auto iter = min_element(mPrimaryRefreshRates.cbegin(), mPrimaryRefreshRates.cend(),
- [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
- return std::abs(lhs->fps - contentFramerate) <
- std::abs(rhs->fps - contentFramerate);
- });
-
- // 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 != mPrimaryRefreshRates.cend()) {
- ratio = (*iter)->fps / contentFramerate;
-
- if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
- bestSoFar = *iter;
- break;
- }
- ++iter;
- }
- }
-
- return *bestSoFar;
-}
-
std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
nsecs_t displayPeriod) const {
auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e84091e..8ebbd74 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -239,10 +239,6 @@
bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
};
- // Returns the refresh rate that fits best to the given layers.
- const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
- EXCLUDES(mLock);
-
// Global state describing signals that affect refresh rate choice.
struct GlobalSignals {
// Whether the user touched the screen recently. Used to apply touch boost.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f6d712e..52bf483 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -89,9 +89,8 @@
timerSlack.count(), vsyncMoveThreshold.count());
}
-const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
- if (!useContentDetection) return "off";
- return useContentDetectionV2 ? "V2" : "V1";
+const char* toContentDetectionString(bool useContentDetection) {
+ return useContentDetection ? "on" : "off";
}
} // namespace
@@ -119,14 +118,13 @@
Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
: Scheduler(configs, callback,
{.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
- .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false),
- .useContentDetectionV2 =
- base::GetBoolProperty("debug.sf.use_content_detection_v2"s, true)}) {}
+ .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
+}
Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
Options options)
: Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
- createLayerHistory(configs, options.useContentDetectionV2), options) {
+ createLayerHistory(configs), options) {
using namespace sysprop;
const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
@@ -195,14 +193,10 @@
}
std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
- const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
+ const scheduler::RefreshRateConfigs& configs) {
if (!configs.canSwitch()) return nullptr;
- if (useContentDetectionV2) {
- return std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
- }
-
- return std::make_unique<scheduler::impl::LayerHistory>();
+ return std::make_unique<scheduler::LayerHistory>(configs);
}
std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
@@ -528,14 +522,6 @@
// still need to be registered.
mLayerHistory->registerLayer(layer, minFps, maxFps,
scheduler::LayerHistory::LayerVoteType::Max);
- } else if (!mOptions.useContentDetectionV2) {
- // In V1 of content detection, all layers are registered as Heuristic (unless it's
- // wallpaper).
- const auto highFps =
- layer->getWindowType() == InputWindowInfo::Type::WALLPAPER ? minFps : maxFps;
-
- mLayerHistory->registerLayer(layer, minFps, highFps,
- scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
@@ -574,8 +560,6 @@
return;
}
mFeatures.contentRequirements = summary;
- mFeatures.contentDetectionV1 =
- !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
@@ -682,8 +666,7 @@
StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Content detection: %s %s\n\n",
- toContentDetectionString(mOptions.useContentDetection,
- mOptions.useContentDetectionV2),
+ toContentDetectionString(mOptions.useContentDetection),
mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
}
@@ -740,29 +723,6 @@
const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
- if (!mOptions.useContentDetectionV2) {
- // As long as touch is active we want to be in performance mode.
- if (touchActive) {
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
- }
-
- // If timer has expired as it means there is no new content on the screen.
- if (idle) {
- if (consideredSignals) consideredSignals->idle = true;
- return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
- }
-
- // If content detection is off we choose performance as we don't know the content fps.
- if (mFeatures.contentDetectionV1 == ContentDetectionState::Off) {
- // NOTE: V1 always calls this, but this is not a default behavior for V2.
- return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
- }
-
- // Content detection is on, find the appropriate refresh rate with minimal error
- return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
- .getConfigId();
- }
-
return mRefreshRateConfigs
.getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
consideredSignals)
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 76e8f57..f16e1f9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -183,8 +183,6 @@
bool supportKernelTimer;
// Whether to use content detection at all.
bool useContentDetection;
- // Whether to use improved content detection.
- bool useContentDetectionV2;
};
struct VsyncSchedule {
@@ -201,8 +199,7 @@
std::unique_ptr<LayerHistory>, Options);
static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
- static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
- bool useContentDetectionV2);
+ static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -270,7 +267,6 @@
std::mutex mFeatureStateLock;
struct {
- ContentDetectionState contentDetectionV1 = ContentDetectionState::Off;
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 18f3745..7427e27 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -48,7 +48,6 @@
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
- "LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
"MessageQueueTest.cpp",
"PromiseTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 0fbe8ec..fbb4637 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 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.
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "LayerHistoryTest"
+#include <Layer.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -39,32 +36,74 @@
class LayerHistoryTest : public testing::Test {
protected:
- static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::FREQUENT_LAYER_WINDOW_SIZE;
+ static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
+ static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+ LayerInfo::RefreshRateHistory::HISTORY_DURATION;
static constexpr float LO_FPS = 30.f;
- static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
+ static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
static constexpr float HI_FPS = 90.f;
- static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
+ static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
- impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
- const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+ LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
size_t layerCount() const { return mScheduler->layerHistorySize(); }
size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
- size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
[now](const auto& pair) { return pair.second->isFrequent(now); });
}
+ auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mLayerInfos;
+ return std::count_if(infos.begin(),
+ infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+ [now](const auto& pair) { return pair.second->isAnimating(now); });
+ }
+
+ void setDefaultLayerVote(Layer* layer,
+ LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+ for (auto& [weak, info] : history().mLayerInfos) {
+ if (auto strong = weak.promote(); strong && strong.get() == layer) {
+ info->setDefaultLayerVote(vote);
+ return;
+ }
+ }
+ }
+
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+ auto createLayer(std::string name) {
+ return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+ }
+
+ void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
+ float desiredRefreshRate, int numFrames) {
+ const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
+ LayerHistory::Summary summary;
+ for (int i = 0; i < numFrames; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += framePeriod;
+
+ summary = history().summarize(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
+ << "Frame rate is " << frameRate;
+ }
Hwc2::mock::Display mDisplay;
RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
@@ -78,14 +117,10 @@
HwcConfigIndexType(0)};
mock::NoOpSchedulerCallback mSchedulerCallback;
- static constexpr bool kUseContentDetectionV2 = false;
- TestableScheduler* const mScheduler =
- new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
+ TestableScheduler* const mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
TestableSurfaceFlinger mFlinger;
-
- const nsecs_t mTime = systemTime();
};
namespace {
@@ -93,83 +128,259 @@
TEST_F(LayerHistoryTest, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- // no layers are returned if no layers are active.
- ASSERT_TRUE(history().summarize(mTime).empty());
+ const nsecs_t time = systemTime();
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount());
- // no layers are returned if active layers have insufficient history.
+ // Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_TRUE(history().summarize(mTime).empty());
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
- // High FPS is returned once enough history has been recorded.
+ // Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
}
+TEST_F(LayerHistoryTest, oneInvisibleLayer) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+
+ history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ auto summary = history().summarize(time);
+ ASSERT_EQ(1, history().summarize(time).size());
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+ summary = history().summarize(time);
+ EXPECT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+}
+
TEST_F(LayerHistoryTest, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- nsecs_t time = mTime;
+ nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
- ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, oneLayerNoVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerMinVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerMaxVote) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitVote) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryTest, multipleLayers) {
auto layer1 = createLayer();
auto layer2 = createLayer();
auto layer3 = createLayer();
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- nsecs_t time = mTime;
+
+ nsecs_t time = systemTime();
EXPECT_EQ(3, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
+ LayerHistory::Summary summary;
+
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -177,26 +388,30 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
// layer1 is still active but infrequent.
history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
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++) {
+ for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -210,26 +425,36 @@
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer1 expires.
layer1.clear();
- ASSERT_EQ(2, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ summary = history().summarize(time);
+ ASSERT_EQ(2, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -239,42 +464,290 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer2 expires.
layer2.clear();
- ASSERT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
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++) {
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
+ summary = history().summarize(time);
}
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+ ASSERT_EQ(1, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer3 expires.
layer3.clear();
- ASSERT_TRUE(history().summarize(time).empty());
+ summary = history().summarize(time);
+ EXPECT_TRUE(summary.empty());
EXPECT_EQ(0, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, inactiveLayers) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ // the very first updates makes the layer frequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // advance the time for the previous frame to be inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+ // Now event if we post a quick few frame we should stay infrequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
+
+ // More quick frames will get us to frequent again
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
+ auto explicitVisiblelayer = createLayer();
+ auto explicitInvisiblelayer = createLayer();
+
+ EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(
+ Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+
+ EXPECT_EQ(2, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // another update with the same cadence keep in infrequent
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // an update as animation will immediately vote for Max
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+ recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ }
+}
+
+TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+class LayerHistoryTestParameterized : public LayerHistoryTest,
+ public testing::WithParamInterface<std::chrono::nanoseconds> {
+};
+
+TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) {
+ std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+ auto heuristicLayer = createLayer("HeuristicLayer");
+
+ EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ auto infrequentLayer = createLayer("InfrequentLayer");
+ EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate()));
+
+ const nsecs_t startTime = systemTime();
+
+ const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+ history().record(heuristicLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer.get(), startTime, startTime,
+ LayerHistory::LayerUpdateType::Buffer);
+
+ nsecs_t time = startTime;
+ nsecs_t lastInfrequentUpdate = startTime;
+ const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+ int infrequentLayerUpdates = 0;
+ while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+ time += heuristicUpdateDelta.count();
+ history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+ if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+ ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+ totalInfrequentLayerUpdates);
+ lastInfrequentUpdate = time;
+ history().record(infrequentLayer.get(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ infrequentLayerUpdates++;
+ }
+
+ if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+ ASSERT_NE(0, history().summarize(time).size());
+ ASSERT_GE(2, history().summarize(time).size());
+
+ bool max = false;
+ bool min = false;
+ float heuristic = 0;
+ for (const auto& layer : history().summarize(time)) {
+ if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+ heuristic = layer.desiredRefreshRate;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+ max = true;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+ min = true;
+ }
+ }
+
+ if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+ EXPECT_FLOAT_EQ(24.0f, heuristic);
+ EXPECT_FALSE(max);
+ if (history().summarize(time).size() == 2) {
+ EXPECT_TRUE(min);
+ }
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized,
+ ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
} // namespace
} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
deleted file mode 100644
index 7bfec9a..0000000
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryTestV2"
-
-#include <Layer.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#include "Scheduler/LayerHistory.h"
-#include "Scheduler/LayerInfoV2.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/MockLayer.h"
-#include "mock/MockSchedulerCallback.h"
-
-using testing::_;
-using testing::Return;
-
-namespace android::scheduler {
-
-class LayerHistoryTestV2 : public testing::Test {
-protected:
- static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
- static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
- static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
- static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
- static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
- LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
-
- static constexpr float LO_FPS = 30.f;
- static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
-
- static constexpr float HI_FPS = 90.f;
- static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
-
- LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
-
- void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
-
- impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
- const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
-
- size_t layerCount() const { return mScheduler->layerHistorySize(); }
- size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
-
- auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isFrequent(now); });
- }
-
- auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isAnimating(now); });
- }
-
- void setDefaultLayerVote(Layer* layer,
- LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
- for (auto& [weak, info] : history().mLayerInfos) {
- if (auto strong = weak.promote(); strong && strong.get() == layer) {
- info->setDefaultLayerVote(vote);
- return;
- }
- }
- }
-
- auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
- auto createLayer(std::string name) {
- return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
- }
-
- void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
- float desiredRefreshRate, int numFrames) {
- const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
- impl::LayerHistoryV2::Summary summary;
- for (int i = 0; i < numFrames; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += framePeriod;
-
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
- << "Frame rate is " << frameRate;
- }
-
- Hwc2::mock::Display mDisplay;
- RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
- .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
- .setConfigGroup(0)
- .build(),
- HWC2::Display::Config::Builder(mDisplay, 1)
- .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
- .setConfigGroup(0)
- .build()},
- HwcConfigIndexType(0)};
-
- mock::NoOpSchedulerCallback mSchedulerCallback;
- static constexpr bool kUseContentDetectionV2 = true;
-
- TestableScheduler* const mScheduler =
- new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
-
- TestableSurfaceFlinger mFlinger;
-};
-
-namespace {
-
-TEST_F(LayerHistoryTestV2, oneLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- const nsecs_t time = systemTime();
-
- // No layers returned if no layers are active.
- EXPECT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- // Max returned if active layers have insufficient history.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- }
-
- // Max is returned since we have enough history but there is no timestamp votes.
- for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- }
-}
-
-TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
- auto summary = history().summarize(time);
- ASSERT_EQ(1, history().summarize(time).size());
- // Layer is still considered inactive so we expect to get Min
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-
- summary = history().summarize(time);
- EXPECT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTestV2, explicitTimestamp) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(history().summarize(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, multipleLayers) {
- auto layer1 = createLayer();
- auto layer2 = createLayer();
- auto layer3 = createLayer();
-
- EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(3, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- impl::LayerHistoryV2::Summary summary;
-
- // layer1 is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- 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, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- // layer1 is still active but infrequent.
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
- 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 < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- 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, time, LayerHistory::LayerUpdateType::Buffer);
- }
-
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer3 becomes recently active.
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- summary = history().summarize(time);
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer1 expires.
- layer1.clear();
- summary = history().summarize(time);
- ASSERT_EQ(2, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
- 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, time, LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer2 expires.
- layer2.clear();
- summary = history().summarize(time);
- EXPECT_TRUE(summary.empty());
- 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 + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = history().summarize(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer3 expires.
- layer3.clear();
- summary = history().summarize(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(0, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, inactiveLayers) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- // the very first updates makes the layer frequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // advance the time for the previous frame to be inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-
- // Now event if we post a quick few frame we should stay infrequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- }
-
- // More quick frames will get us to frequent again
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
- auto explicitVisiblelayer = createLayer();
- auto explicitInvisiblelayer = createLayer();
-
- EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- nsecs_t time = systemTime();
-
- // Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- EXPECT_EQ(2, layerCount());
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- history().summarize(time)[0].vote);
- EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // another update with the same cadence keep in infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // an update as animation will immediately vote for Max
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, history().summarize(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(1, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
- for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
- recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
- }
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
- recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-
- recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-}
-
-class LayerHistoryTestV2Parameterized
- : public LayerHistoryTestV2,
- public testing::WithParamInterface<std::chrono::nanoseconds> {};
-
-TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
- std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
- auto heuristicLayer = createLayer("HeuristicLayer");
-
- EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- auto infrequentLayer = createLayer("InfrequentLayer");
- EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- const nsecs_t startTime = systemTime();
-
- const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(infrequentLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
-
- nsecs_t time = startTime;
- nsecs_t lastInfrequentUpdate = startTime;
- const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
- int infrequentLayerUpdates = 0;
- while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
- time += heuristicUpdateDelta.count();
- history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-
- if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
- ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
- totalInfrequentLayerUpdates);
- lastInfrequentUpdate = time;
- history().record(infrequentLayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- infrequentLayerUpdates++;
- }
-
- if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
- ASSERT_NE(0, history().summarize(time).size());
- ASSERT_GE(2, history().summarize(time).size());
-
- bool max = false;
- bool min = false;
- float heuristic = 0;
- for (const auto& layer : history().summarize(time)) {
- if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
- heuristic = layer.desiredRefreshRate;
- } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
- max = true;
- } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
- min = true;
- }
- }
-
- if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
- EXPECT_FLOAT_EQ(24.0f, heuristic);
- EXPECT_FALSE(max);
- if (history().summarize(time).size() == 2) {
- EXPECT_TRUE(min);
- }
- }
- }
- }
-}
-
-INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
- ::testing::Values(1s, 2s, 3s, 4s, 5s));
-
-} // namespace
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 338e015..f2b7191 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -304,63 +304,6 @@
}
}
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
- return {{"testLayer", LayerVoteType::Heuristic, refreshRate, Seamlessness::OnlySeamless,
- /*weight*/ 1.0f, /*focused*/ false}};
- };
-
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-}
-
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
@@ -847,29 +790,6 @@
}
}
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m60_90Device,
- /*currentConfigId=*/HWC_CONFIG_ID_60);
-
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 60.0f;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 90.0f;
- EXPECT_EQ(mExpected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 90.0f;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60.0f;
- EXPECT_EQ(mExpected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
-}
-
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90Device,
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 647689b..fb02a33 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -68,8 +68,7 @@
}
} mExpectDisableVsync{mSchedulerCallback};
- static constexpr bool kUseContentDetectionV2 = false;
- TestableScheduler mScheduler{mConfigs, mSchedulerCallback, kUseContentDetectionV2};
+ TestableScheduler mScheduler{mConfigs, mSchedulerCallback};
Scheduler::ConnectionHandle mConnectionHandle;
mock::EventThread* mEventThread;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1b6e9ed..2192977 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,21 +32,16 @@
class TestableScheduler : public Scheduler {
public:
- TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
- bool useContentDetectionV2)
+ TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_unique<mock::VSyncTracker>(), configs, callback,
- useContentDetectionV2) {}
+ std::make_unique<mock::VSyncTracker>(), configs, callback) {}
TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
- const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
- bool useContentDetectionV2)
+ const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
: Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
- callback, createLayerHistory(configs, useContentDetectionV2),
- {.supportKernelTimer = false,
- .useContentDetection = true,
- .useContentDetectionV2 = useContentDetectionV2}) {}
+ callback, createLayerHistory(configs),
+ {.supportKernelTimer = false, .useContentDetection = true}) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -62,20 +57,11 @@
bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
- auto* mutableLayerHistory() {
- LOG_ALWAYS_FATAL_IF(mOptions.useContentDetectionV2);
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
- }
-
- auto* mutableLayerHistoryV2() {
- LOG_ALWAYS_FATAL_IF(!mOptions.useContentDetectionV2);
- return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
- }
+ auto* mutableLayerHistory() { return mLayerHistory.get(); }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
if (!mLayerHistory) return 0;
- return mOptions.useContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
- : mutableLayerHistory()->mLayerInfos.size();
+ return mutableLayerHistory()->mLayerInfos.size();
}
void replaceTouchTimer(int64_t millis) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index eba3f8e..8a0276a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -227,10 +227,8 @@
mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
- constexpr bool kUseContentDetectionV2 = false;
mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
- *mFlinger->mRefreshRateConfigs, *(callback ?: this),
- kUseContentDetectionV2);
+ *mFlinger->mRefreshRateConfigs, *(callback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));