SurfaceFlinger: enhance refresh rate selection
Enhance the refresh rate selection algorithm to allow having multiple
refresh rate. The new process attaches scores to each one of the available
refresh rate and chooses the refresh rate with the highest score.
This behavior is currently controlled by the sysprop flag
'debug.sf.use_content_detection_v2' and currently turned off.
This algorithm stills needs some tunings which will be done in
layer CLs.
Test: adb shell /data/nativetest64/SurfaceFlinger_test/SurfaceFlinger_test
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Test: go/90hzscenarios manual tests
Bug: 147516364
Fixes: 146068419
Change-Id: I06e07459e469482799ff80fa54fa8dd311325e0e
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index abf0cd6..ebd617f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -43,7 +43,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- if (layer.getFrameRate() > .0f) {
+ if (layer.getFrameRate().has_value()) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -77,7 +77,8 @@
: mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate) {
+void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType /*type*/) {
auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
@@ -101,8 +102,6 @@
}
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
- float maxRefreshRate = 0;
-
std::lock_guard lock(mLock);
partitionLayers(now);
@@ -113,7 +112,7 @@
if (recent || CC_UNLIKELY(mTraceEnabled)) {
const float refreshRate = info->getRefreshRate(now);
- if (recent && refreshRate > maxRefreshRate) {
+ if (recent && refreshRate > 0.0f) {
if (const auto layer = activeLayer.promote(); layer) {
const int32_t priority = layer->getFrameRateSelectionPriority();
// TODO(b/142507166): This is where the scoring algorithm should live.
@@ -124,36 +123,30 @@
}
}
+ 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)) {
- float refreshRate = 0.f;
- // Default content refresh rate is only used when dealing with recent layers.
- if (recent) {
- refreshRate = info->getRefreshRate(now);
- }
// Check if frame rate was set on layer.
- float frameRate = layer->getFrameRate();
- if (frameRate > 0.f) {
- // Override content detection refresh rate, if it was set.
- refreshRate = frameRate;
- }
- if (refreshRate > maxRefreshRate) {
- maxRefreshRate = refreshRate;
+ auto frameRate = layer->getFrameRate();
+ if (frameRate.has_value() && frameRate.value() > 0.f) {
+ summary.push_back(
+ {layer->getName(), LayerVoteType::Explicit, *frameRate, /* weight */ 1.0f});
+ } else if (recent) {
+ frameRate = info->getRefreshRate(now);
+ summary.push_back({layer->getName(), LayerVoteType::Heuristic, *frameRate,
+ /* weight */ 1.0f});
}
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(weakLayer, std::round(refreshRate));
+ trace(weakLayer, std::round(*frameRate));
}
}
}
- if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("%s: maxRefreshRate=%.2f", __FUNCTION__, maxRefreshRate);
- }
- return {maxRefreshRate};
+ return summary;
}
void LayerHistory::partitionLayers(nsecs_t now) {
@@ -199,22 +192,6 @@
mActiveLayersEnd = 0;
}
-bool LayerHistory::hasClientSpecifiedFrameRate() {
- std::lock_guard lock(mLock);
- for (const auto& [weakLayer, info] : activeLayers()) {
- auto layer = weakLayer.promote();
- if (layer) {
- float frameRate = layer->getFrameRate();
- // Found a layer that has a frame rate set on it.
- if (fabs(frameRate) > 0.f) {
- return true;
- }
- }
- }
- // Did not find any layers that have frame rate.
- return false;
-}
-
} // namespace android::scheduler::impl
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index f217134..bef04e9 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -25,6 +25,8 @@
#include <utility>
#include <vector>
+#include "RefreshRateConfigs.h"
+
namespace android {
class Layer;
@@ -33,29 +35,32 @@
namespace scheduler {
class LayerHistoryTest;
+class LayerHistoryTestV2;
class LayerInfo;
+class LayerInfoV2;
class LayerHistory {
public:
+ using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+
virtual ~LayerHistory() = default;
// Layers are unregistered when the weak reference expires.
- virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate) = 0;
+ virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType type) = 0;
+
+ // Sets the display size. Client is responsible for synchronization.
+ virtual void setDisplayArea(uint32_t displayArea) = 0;
// Marks the layer as active, and records the given state to its history.
virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
- struct Summary {
- float maxRefreshRate; // Maximum refresh rate among recently active layers.
- };
+ 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;
virtual void clear() = 0;
-
- // Checks whether any of the active layers have a desired frame rate bit set on them.
- virtual bool hasClientSpecifiedFrameRate() = 0;
};
namespace impl {
@@ -68,7 +73,10 @@
virtual ~LayerHistory();
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate) override;
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+ LayerVoteType type) override;
+
+ void setDisplayArea(uint32_t /*displayArea*/) override {}
// Marks the layer as active, and records the given state to its history.
void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
@@ -78,10 +86,6 @@
void clear() override;
- // Traverses all active layers and checks whether any of them have a desired frame
- // rate bit set on them.
- bool hasClientSpecifiedFrameRate() override;
-
private:
friend class android::scheduler::LayerHistoryTest;
friend TestableScheduler;
@@ -91,7 +95,7 @@
struct ActiveLayers {
LayerInfos& infos;
- const size_t index;
+ const long index;
auto begin() { return infos.begin(); }
auto end() { return begin() + index; }
@@ -109,8 +113,66 @@
// 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);
+ long 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();
+ 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; }
+
+ // Marks the layer as active, and records the given state to its history.
+ void record(Layer*, nsecs_t presentTime, nsecs_t now) 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;
+
+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.
const bool mTraceEnabled;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
new file mode 100644
index 0000000..884b46a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#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 "SchedulerUtils.h"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+#include "LayerInfoV2.h"
+
+namespace android::scheduler::impl {
+
+namespace {
+
+bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
+ if (layer.getFrameRate().has_value()) {
+ return layer.isVisible();
+ }
+ 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, LayerHistory::LayerVoteType type, int fps) {
+ const auto layer = weak.promote();
+ if (!layer) return;
+
+ const auto& name = layer->getName();
+ const auto noVoteTag = "LFPS NoVote " + name;
+ const auto heuristicVoteTag = "LFPS Heuristic " + name;
+ const auto explicitVoteTag = "LFPS Explicit " + name;
+ const auto minVoteTag = "LFPS Min " + name;
+ const auto maxVoteTag = "LFPS Max " + name;
+
+ ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
+ ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
+ ATRACE_INT(explicitVoteTag.c_str(), type == LayerHistory::LayerVoteType::Explicit ? fps : 0);
+ ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
+ ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
+
+ ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+
+} // namespace
+
+LayerHistoryV2::LayerHistoryV2()
+ : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+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>(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) {
+ 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);
+
+ // 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 bool recent = info->isRecentlyActive(now);
+ if (recent) {
+ const auto [type, refreshRate] = info->getRefreshRate(now);
+ // Skip NoVote layer as those don't have any requirements
+ if (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(), type, refreshRate, weight});
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(layer, type, static_cast<int>(std::round(refreshRate)));
+ }
+ } else if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(layer, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+ }
+
+ 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->getFrameRate();
+ if (frameRate.has_value()) {
+ if (*frameRate == Layer::FRAME_RATE_NO_VOTE) {
+ info->setLayerVote(LayerVoteType::NoVote, 0.f);
+ } else {
+ info->setLayerVote(LayerVoteType::Explicit, *frameRate);
+ }
+ } else {
+ info->resetLayerVote();
+ }
+ continue;
+ }
+
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+
+ info->clearHistory();
+ std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+ }
+
+ // Collect expired layers after inactive layers.
+ size_t end = mLayerInfos.size();
+ while (i < end) {
+ if (mLayerInfos[i].first.promote()) {
+ i++;
+ } else {
+ 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();
+ }
+
+ mActiveLayersEnd = 0;
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
new file mode 100644
index 0000000..d94d758
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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
+
+#include "LayerInfoV2.h"
+
+#include <algorithm>
+#include <utility>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfoV2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+namespace android::scheduler {
+
+LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
+ : mHighRefreshRatePeriod(highRefreshRatePeriod),
+ mDefaultVote(defaultVote),
+ mLayerVote({defaultVote, 0.0f}) {}
+
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+ lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
+ mLastUpdatedTime = std::max(lastPresentTime, now);
+
+ FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
+
+ mFrameTimes.push_back(frameTime);
+ if (mFrameTimes.size() > HISTORY_SIZE) {
+ mFrameTimes.pop_front();
+ }
+}
+
+// Returns whether the earliest present time is within the active threshold.
+bool LayerInfoV2::isRecentlyActive(nsecs_t now) const {
+ if (mFrameTimes.empty()) {
+ return false;
+ }
+
+ return mFrameTimes.back().queueTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfoV2::isFrequent(nsecs_t now) const {
+ // Assume layer is infrequent if too few present times have been recorded.
+ if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+ return true;
+ }
+
+ // Layer is frequent if the earliest value in the window of most recent present times is
+ // within threshold.
+ const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE;
+ const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ return it->queueTime >= threshold;
+}
+
+bool LayerInfoV2::hasEnoughDataForHeuristic() const {
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ if (mFrameTimes.size() < HISTORY_SIZE &&
+ mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+ static constexpr float MARGIN = 1.0f; // 1Hz
+
+ if (!hasEnoughDataForHeuristic()) {
+ ALOGV("Not enough data");
+ return std::nullopt;
+ }
+
+ // Calculate the refresh rate by finding the average delta between frames
+ nsecs_t totalPresentTimeDeltas = 0;
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ // If there are no presentation timestamp provided we can't calculate the refresh rate
+ if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+ return std::nullopt;
+ }
+
+ totalPresentTimeDeltas +=
+ std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ }
+ const float averageFrameTime =
+ static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
+
+ // Now once we calculated the refresh rate we need to make sure that all the frames we captured
+ // are evenly distrubuted and we don't calculate the average across some burst of frames.
+
+ for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+ const nsecs_t presentTimeDeltas =
+ std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
+ return std::nullopt;
+ }
+ }
+
+ const auto refreshRate = 1e9f / averageFrameTime;
+ if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
+ mLastReportedRefreshRate = refreshRate;
+ }
+
+ ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
+ return mLastReportedRefreshRate;
+}
+
+std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
+ if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+ return {mLayerVote.type, mLayerVote.fps};
+ }
+
+ if (!isFrequent(now)) {
+ return {LayerHistory::LayerVoteType::Min, 0};
+ }
+
+ auto refreshRate = calculateRefreshRateIfPossible();
+ if (refreshRate.has_value()) {
+ return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+ }
+
+ return {LayerHistory::LayerVoteType::Max, 0};
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
new file mode 100644
index 0000000..564f05e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -0,0 +1,117 @@
+/*
+ * 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 "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 {
+ // 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;
+
+ friend class LayerHistoryTestV2;
+
+public:
+ LayerInfoV2(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);
+
+ bool isRecentlyActive(nsecs_t now) const;
+
+ // Sets an explicit layer vote. This usually comes directly from the application via
+ // ANativeWindow_setFrameRate API
+ void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
+
+ // 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}; }
+
+ std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(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() { mFrameTimes.clear(); }
+
+private:
+ bool isFrequent(nsecs_t now) const;
+ bool hasEnoughDataForHeuristic() const;
+ std::optional<float> calculateRefreshRateIfPossible();
+
+ // Used for sanitizing the heuristic data
+ const nsecs_t mHighRefreshRatePeriod;
+ LayerHistory::LayerVoteType mDefaultVote;
+
+ nsecs_t mLastUpdatedTime = 0;
+
+ float mLastReportedRefreshRate = 0.0f;
+
+ // Holds information about the layer vote
+ struct {
+ LayerHistory::LayerVoteType type;
+ float fps;
+ } mLayerVote;
+
+ // Used to store the layer timestamps
+ struct FrameTimeData {
+ nsecs_t presetTime; // desiredPresentTime, if provided
+ nsecs_t queueTime; // buffer queue time
+ };
+ std::deque<FrameTimeData> mFrameTimes;
+ static constexpr size_t HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 45d1f23..a8f8e0e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -14,24 +14,51 @@
* limitations under the License.
*/
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-// #define LOG_NDEBUG 0
#include "RefreshRateConfigs.h"
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cmath>
+
+using namespace std::chrono_literals;
namespace android::scheduler {
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
-// Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
-// from multiple threads. This can only be called if refreshRateSwitching() returns true.
-// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
-// baking them in.
-const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFramerate) const {
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
+ const std::vector<LayerRequirement>& layers) const {
std::lock_guard lock(mLock);
+ float contentFramerate = 0.0f;
+ float explicitContentFramerate = 0.0f;
+ for (const auto& layer : layers) {
+ if (layer.vote == LayerVoteType::Explicit) {
+ if (layer.desiredRefreshRate > explicitContentFramerate) {
+ explicitContentFramerate = layer.desiredRefreshRate;
+ }
+ } else {
+ if (layer.desiredRefreshRate > contentFramerate) {
+ contentFramerate = layer.desiredRefreshRate;
+ }
+ }
+ }
+
+ if (explicitContentFramerate != 0.0f) {
+ contentFramerate = explicitContentFramerate;
+ } else if (contentFramerate == 0.0f) {
+ contentFramerate = mMaxSupportedRefreshRate->fps;
+ }
+ contentFramerate = std::round(contentFramerate);
+ ATRACE_INT("ContentFPS", contentFramerate);
+
// Find the appropriate refresh rate with minimal error
auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
[contentFramerate](const auto& lhs, const auto& rhs) -> bool {
@@ -60,6 +87,113 @@
return *bestSoFar;
}
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
+ const std::vector<LayerRequirement>& layers) const {
+ constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count();
+ ATRACE_CALL();
+ ALOGV("getRefreshRateForContent %zu layers", layers.size());
+
+ std::lock_guard lock(mLock);
+
+ int noVoteLayers = 0;
+ int minVoteLayers = 0;
+ int maxVoteLayers = 0;
+ int explicitVoteLayers = 0;
+ for (const auto& layer : layers) {
+ if (layer.vote == LayerVoteType::NoVote)
+ noVoteLayers++;
+ else if (layer.vote == LayerVoteType::Min)
+ minVoteLayers++;
+ else if (layer.vote == LayerVoteType::Max)
+ maxVoteLayers++;
+ else if (layer.vote == LayerVoteType::Explicit)
+ explicitVoteLayers++;
+ }
+
+ // Only if all layers want Min we should return Min
+ if (noVoteLayers + minVoteLayers == layers.size()) {
+ return *mAvailableRefreshRates.front();
+ }
+
+ // If we have some Max layers and no Explicit we should return Max
+ if (maxVoteLayers > 0 && explicitVoteLayers == 0) {
+ return *mAvailableRefreshRates.back();
+ }
+
+ // Find the best refresh rate based on score
+ std::vector<std::pair<const RefreshRate*, float>> scores;
+ scores.reserve(mAvailableRefreshRates.size());
+
+ for (const auto refreshRate : mAvailableRefreshRates) {
+ scores.emplace_back(refreshRate, 0.0f);
+ }
+
+ for (const auto& layer : layers) {
+ if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
+ layer.vote == LayerVoteType::Max) {
+ continue;
+ }
+
+ // If we have Explicit layers, ignore the Huristic ones
+ if (explicitVoteLayers > 0 && layer.vote == LayerVoteType::Heuristic) {
+ continue;
+ }
+
+ for (auto& [refreshRate, overallScore] : scores) {
+ const auto displayPeriod = refreshRate->vsyncPeriod;
+ const auto layerPeriod = 1e9f / layer.desiredRefreshRate;
+
+ // Calculate how many display vsyncs we need to present a single frame for this layer
+ auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
+ if (displayFramesRem <= MARGIN ||
+ std::abs(displayFramesRem - displayPeriod) <= MARGIN) {
+ displayFramesQuot++;
+ displayFramesRem = 0;
+ }
+
+ float layerScore;
+ if (displayFramesRem == 0) {
+ // Layer desired refresh rate matches the display rate.
+ layerScore = layer.weight * 1.0f;
+ } else if (displayFramesQuot == 0) {
+ // Layer desired refresh rate is higher the display rate.
+ layerScore = layer.weight * layerPeriod / displayPeriod;
+ } else {
+ // Layer desired refresh rate is lower the display rate. Check how well it fits the
+ // cadence
+ auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
+ int iter = 2;
+ static constexpr size_t MAX_ITERATOR = 10; // Stop calculating when score < 0.1
+ while (diff > MARGIN && iter < MAX_ITERATOR) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ layerScore = layer.weight * 1.0f / iter;
+ }
+
+ ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
+ layer.weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
+ overallScore += layerScore;
+ }
+ }
+
+ float max = 0;
+ const RefreshRate* bestRefreshRate = nullptr;
+ for (const auto [refreshRate, score] : scores) {
+ ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
+
+ ATRACE_INT(refreshRate->name.c_str(), std::round(score * 100));
+
+ if (score > max) {
+ max = score;
+ bestRefreshRate = refreshRate;
+ }
+ }
+
+ return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+}
+
const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
return mRefreshRates;
}
@@ -180,14 +314,21 @@
void RefreshRateConfigs::constructAvailableRefreshRates() {
// Filter configs based on current policy and sort based on vsync period
HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
- ALOGV("constructRefreshRateMap: default %d group %d min %.2f max %.2f", mDefaultConfig.value(),
- group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
+ ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
+ mDefaultConfig.value(), group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
getSortedRefreshRateList(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
return refreshRate.configGroup == group &&
refreshRate.inPolicy(mMinRefreshRateFps, mMaxRefreshRateFps);
},
&mAvailableRefreshRates);
+
+ std::string availableRefreshRates;
+ for (const auto& refreshRate : mAvailableRefreshRates) {
+ base::StringAppendF(&availableRefreshRates, "%s ", refreshRate->name.c_str());
+ }
+
+ ALOGV("Available refresh rates: %s", availableRefreshRates.c_str());
LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
"No compatible display configs for default=%d min=%.0f max=%.0f",
mDefaultConfig.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0c3369a..80d42cc 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -97,8 +97,39 @@
// Returns true if this device is doing refresh rate switching. This won't change at runtime.
bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
+ // Describes the different options the layer voted for refresh rate
+ enum class LayerVoteType {
+ NoVote, // Doesn't care about the refresh rate
+ Min, // Minimal refresh rate available
+ Max, // Maximal refresh rate available
+ Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
+ Explicit, // Specific refresh rate that was provided by the app
+ };
+
+ // Captures the layer requirements for a refresh rate. This will be used to determine the
+ // display refresh rate.
+ struct LayerRequirement {
+ std::string name; // Layer's name. Used for debugging purposes.
+ LayerVoteType vote; // Layer vote type.
+ float desiredRefreshRate; // Layer's desired refresh rate, if applicable.
+ float weight; // Layer's weight in the range of [0, 1]. The higher the weight the more
+ // impact this layer would have on choosing the refresh rate.
+
+ bool operator==(const LayerRequirement& other) const {
+ return name == other.name && vote == other.vote &&
+ desiredRefreshRate == other.desiredRefreshRate && weight == other.weight;
+ }
+
+ bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+ };
+
// Returns all available refresh rates according to the current policy.
- const RefreshRate& getRefreshRateForContent(float contentFramerate) const EXCLUDES(mLock);
+ const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
+ EXCLUDES(mLock);
+
+ // Returns all available refresh rates according to the current policy.
+ const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers) const
+ EXCLUDES(mLock);
// Returns all the refresh rates supported by the device. This won't change at runtime.
const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index a384dbe..e44cd52 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -114,7 +114,8 @@
mConfigModesTotalTime[mCurrentConfigMode] = 0;
}
mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
- fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+ fps = static_cast<uint32_t>(std::round(
+ mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps));
} else {
mScreenOffTime += timeElapsedMs;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0b645c4..6a437a2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -103,16 +103,21 @@
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig,
- ISchedulerCallback& schedulerCallback)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
: mPrimaryDispSync(createDispSync()),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(refreshRateConfig) {
+ mRefreshRateConfigs(refreshRateConfig),
+ mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ }
}
const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
@@ -120,7 +125,6 @@
if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
: &Scheduler::idleTimerCallback;
-
mIdleTimer.emplace(
std::chrono::milliseconds(millis),
[this, callback] { std::invoke(callback, this, TimerState::Reset); },
@@ -149,12 +153,13 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
: mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
mSupportKernelTimer(false),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(configs) {}
+ mRefreshRateConfigs(configs),
+ mUseContentDetectionV2(useContentDetectionV2) {}
Scheduler::~Scheduler() {
// Ensure the OneShotTimer threads are joined before we start destroying state.
@@ -375,12 +380,42 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
- const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? lowFps
- : mRefreshRateConfigs.getMaxRefreshRate().fps;
+ if (!mUseContentDetectionV2) {
+ const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+ const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? lowFps
+ : mRefreshRateConfigs.getMaxRefreshRate().fps;
- mLayerHistory->registerLayer(layer, lowFps, highFps);
+ mLayerHistory->registerLayer(layer, lowFps, highFps,
+ scheduler::LayerHistory::LayerVoteType::Heuristic);
+ } else {
+ if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::Min);
+ } else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::NoVote);
+ } else {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::Heuristic);
+ }
+
+ // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
+ // API to set desired rate
+ {
+ const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
+ if (vote != 0 &&
+ layer->getName() ==
+ "SurfaceView - "
+ "com.google.android.youtube/"
+ "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
+ layer->setFrameRate(vote);
+ }
+ }
+ }
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
@@ -392,27 +427,27 @@
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
- auto [refreshRate] = mLayerHistory->summarize(systemTime());
- const uint32_t refreshRateRound = std::round(refreshRate);
+ ATRACE_CALL();
+
+ scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRefreshRate == refreshRateRound) {
+ if (mFeatures.contentRequirements == summary) {
return;
}
- mFeatures.contentRefreshRate = refreshRateRound;
- ATRACE_INT("ContentFPS", refreshRateRound);
-
+ mFeatures.contentRequirements = summary;
mFeatures.contentDetection =
- refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
+ !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
+
newConfigId = calculateRefreshRateType();
if (mFeatures.configId == newConfigId) {
return;
}
mFeatures.configId = newConfigId;
- };
- auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ }
}
void Scheduler::resetIdleTimer() {
@@ -422,16 +457,17 @@
}
void Scheduler::notifyTouchEvent() {
+ if (!mTouchTimer) return;
+
// Touch event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection.
// NOTE: Instead of checking all the layers, we should be checking the layer
// that is currently on top. b/142507166 will give us this capability.
- if (mLayerHistory && !mLayerHistory->hasClientSpecifiedFrameRate()) {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ if (mLayerHistory && !layerHistoryHasClientSpecifiedFrameRate()) {
mLayerHistory->clear();
- if (mTouchTimer) {
- mTouchTimer->reset();
- }
+ mTouchTimer->reset();
if (mSupportKernelTimer && mIdleTimer) {
mIdleTimer->reset();
@@ -530,6 +566,16 @@
mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
}
+bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
+ for (const auto& layer : mFeatures.contentRequirements) {
+ if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::Explicit) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
HwcConfigIndexType Scheduler::calculateRefreshRateType() {
if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
return mRefreshRateConfigs.getCurrentRefreshRate().configId;
@@ -538,7 +584,7 @@
// If the layer history doesn't have the frame rate specified, use the old path. NOTE:
// if we remove the kernel idle timer, and use our internal idle timer, this code will have to
// be refactored.
- if (!mLayerHistory->hasClientSpecifiedFrameRate()) {
+ if (!layerHistoryHasClientSpecifiedFrameRate()) {
// If Display Power is not in normal operation we want to be in performance mode.
// When coming back to normal mode, a grace period is given with DisplayPowerTimer
if (!mFeatures.isDisplayPowerStateNormal ||
@@ -555,17 +601,26 @@
if (mFeatures.idleTimer == TimerState::Expired) {
return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
+ }
+ if (!mUseContentDetectionV2) {
// If content detection is off we choose performance as we don't know the content fps
if (mFeatures.contentDetection == ContentDetectionState::Off) {
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
+
+ // Content detection is on, find the appropriate refresh rate with minimal error
+ return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
}
// Content detection is on, find the appropriate refresh rate with minimal error
- return mRefreshRateConfigs
- .getRefreshRateForContent(static_cast<float>(mFeatures.contentRefreshRate))
- .configId;
+ if (mFeatures.contentDetection == ContentDetectionState::On) {
+ return mRefreshRateConfigs.getRefreshRateForContentV2(mFeatures.contentRequirements)
+ .configId;
+ }
+
+ // There are no signals for refresh rate, just leave it as is
+ return mRefreshRateConfigs.getCurrentRefreshRate().configId;
}
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
@@ -606,6 +661,12 @@
}
}
+void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
+ if (mLayerHistory) {
+ mLayerHistory->setDisplayArea(displayArea);
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c6430c3..2987424 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -58,7 +58,8 @@
enum class TransactionStart { EARLY, NORMAL };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+ bool useContentDetectionV2);
virtual ~Scheduler();
@@ -136,6 +137,9 @@
// Notifies the scheduler when the display was refreshed
void onDisplayRefreshed(nsecs_t timestamp);
+ // Notifies the scheduler when the display size has changed. Called from SF's main thread
+ void onPrimaryDisplayAreaChanged(uint32_t displayArea);
+
private:
friend class TestableScheduler;
@@ -147,7 +151,8 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+ bool useContentDetectionV2);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -170,6 +175,8 @@
HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ bool layerHistoryHasClientSpecifiedFrameRate() REQUIRES(mFeatureStateLock);
+
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -218,7 +225,7 @@
TimerState displayPowerTimer = TimerState::Expired;
std::optional<HwcConfigIndexType> configId;
- uint32_t contentRefreshRate = 0;
+ scheduler::LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
} mFeatures GUARDED_BY(mFeatureStateLock);
@@ -229,6 +236,8 @@
std::optional<HWC2::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+
+ const bool mUseContentDetectionV2;
};
} // namespace android