Merge "SurfaceFlinger: enhance refresh rate selection"
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9a7eabd..1b1e889 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -165,7 +165,9 @@
"Scheduler/EventThread.cpp",
"Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
+ "Scheduler/LayerHistoryV2.cpp",
"Scheduler/LayerInfo.cpp",
+ "Scheduler/LayerInfoV2.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/PhaseOffsets.cpp",
"Scheduler/RefreshRateConfigs.cpp",
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index b55e62b..e85281d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -129,8 +129,11 @@
return frameRateChanged;
}
-float BufferQueueLayer::getFrameRate() const {
- return mLatchedFrameRate;
+std::optional<float> BufferQueueLayer::getFrameRate() const {
+ if (mLatchedFrameRate > 0.f || mLatchedFrameRate == FRAME_RATE_NO_VOTE)
+ return mLatchedFrameRate;
+
+ return {};
}
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 0777953..2bd1e3d 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -57,7 +57,7 @@
bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
bool setFrameRate(float frameRate) override;
- float getFrameRate() const override;
+ std::optional<float> getFrameRate() const override;
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 287fe89..cd4227c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -253,7 +253,8 @@
FrameTracer::FrameEvent::POST);
mCurrentState.desiredPresentTime = desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
+ mFlinger->mScheduler->recordLayerHistory(this,
+ desiredPresentTime <= 0 ? 0 : desiredPresentTime);
return true;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 605e7c8..ff48ecd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -227,8 +227,10 @@
public:
DisplayRenderArea(const sp<const DisplayDevice>& display,
RotationFlags rotation = ui::Transform::ROT_0)
- : DisplayRenderArea(display, display->getBounds(), display->getWidth(),
- display->getHeight(), display->getCompositionDataSpace(), rotation) {}
+ : DisplayRenderArea(display, display->getBounds(),
+ static_cast<uint32_t>(display->getWidth()),
+ static_cast<uint32_t>(display->getHeight()),
+ display->getCompositionDataSpace(), rotation) {}
DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index eef0c8b..aa43f09 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -23,6 +23,10 @@
#include <utility>
#include <vector>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
@@ -36,6 +40,9 @@
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
namespace android {
namespace Hwc2 {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index a0dabb4..effe43b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -27,7 +27,13 @@
#include <android-base/thread_annotations.h>
#include <ui/Fence.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
+
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 67810fc..6f8df95 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1256,8 +1256,11 @@
return true;
}
-float Layer::getFrameRate() const {
- return getDrawingState().frameRate;
+std::optional<float> Layer::getFrameRate() const {
+ const auto frameRate = getDrawingState().frameRate;
+ if (frameRate > 0.f || frameRate == FRAME_RATE_NO_VOTE) return frameRate;
+
+ return {};
}
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fae976d..c75a570 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -744,8 +744,9 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
+ constexpr static auto FRAME_RATE_NO_VOTE = -1.0f;
virtual bool setFrameRate(float frameRate);
- virtual float getFrameRate() const;
+ virtual std::optional<float> getFrameRate() const;
protected:
// constant
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 33e5796..d3d9d3a 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -155,6 +155,7 @@
Mutex::Autolock _l(mFlinger.mStateLock);
mLayer = mClient->getLayerUser(mIBinder);
+ mLayer->setFrameRate(Layer::FRAME_RATE_NO_VOTE);
// setting Layer's Z requires resorting layersSortedByZ
ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index a7a6dd5..9b3a9f4 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -71,8 +71,8 @@
RotationFlags getRotationFlags() const { return mRotationFlags; }
// Returns the size of the physical render area.
- int getReqWidth() const { return mReqWidth; }
- int getReqHeight() const { return mReqHeight; }
+ int getReqWidth() const { return static_cast<int>(mReqWidth); }
+ int getReqHeight() const { return static_cast<int>(mReqHeight); }
// Returns the composition data space of the render area.
ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
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
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d716c54..29d281c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -543,6 +543,11 @@
const auto& performanceRefreshRate = mRefreshRateConfigs->getMaxRefreshRateByPolicy();
changeRefreshRateLocked(performanceRefreshRate, Scheduler::ConfigEvent::None);
}
+
+ if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
+ mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+ }
}));
}
@@ -1782,7 +1787,13 @@
// Layers need to get updated (in the previous line) before we can use them for
// choosing the refresh rate.
- mScheduler->chooseRefreshRateForContent();
+ // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
+ // and may eventually call to ~Layer() if it holds the last reference
+ {
+ Mutex::Autolock _l(mStateLock);
+ mScheduler->chooseRefreshRateForContent();
+ }
+
if (performSetActiveConfig()) {
break;
}
@@ -2391,6 +2402,9 @@
}
if (state.width != draw[i].width || state.height != draw[i].height) {
display->setDisplaySize(state.width, state.height);
+ if (display->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(state.width * state.height);
+ }
}
}
}
@@ -2463,6 +2477,12 @@
LOG_ALWAYS_FATAL_IF(!displayId);
dispatchDisplayHotplugEvent(displayId->value, true);
}
+
+ const auto displayDevice = mDisplays[displayToken];
+ if (displayDevice->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
+ displayDevice->getHeight());
+ }
}
}
}
@@ -2660,8 +2680,12 @@
}
void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
- Scheduler::ConfigEvent event) {
- Mutex::Autolock lock(mStateLock);
+ Scheduler::ConfigEvent event) NO_THREAD_SAFETY_ANALYSIS {
+ // If this is called from the main thread mStateLock must be locked before
+ // Currently the only way to call this function from the main thread is from
+ // Sheduler::chooseRefreshRateForContent
+
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
changeRefreshRateLocked(refreshRate, event);
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index e12d31a..45889a5 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -75,7 +75,9 @@
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
ISchedulerCallback& schedulerCallback) {
- return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback);
+ return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
+ property_get_bool("debug.sf.use_content_detection_v2",
+ false));
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1eaf2dd..d046f76 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -44,6 +44,7 @@
"EventThreadTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
+ "LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
"PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index ca51cb6..9ca1b70 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -73,7 +73,7 @@
HI_FPS_PERIOD},
},
HwcConfigIndexType(0)};
- TestableScheduler* const mScheduler{new TestableScheduler(mConfigs)};
+ TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
TestableSurfaceFlinger mFlinger;
const nsecs_t mTime = systemTime();
@@ -85,53 +85,36 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- // 0 FPS is returned if no layers are active.
- EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ // no layers are returned if no layers are active.
+ ASSERT_TRUE(history().summarize(mTime).empty());
EXPECT_EQ(0, activeLayerCount());
- // 0 FPS is returned if active layers have insufficient history.
+ // no layers are returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
history().record(layer.get(), 0, mTime);
- EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
+ ASSERT_TRUE(history().summarize(mTime).empty());
EXPECT_EQ(1, activeLayerCount());
}
// High FPS is returned once enough history has been recorded.
for (int i = 0; i < 10; i++) {
history().record(layer.get(), 0, mTime);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
}
}
-TEST_F(LayerHistoryTest, oneHDRLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- history().record(layer.get(), 0, mTime);
- auto summary = history().summarize(mTime);
- EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-
- summary = history().summarize(mTime);
- EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- 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, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -142,7 +125,8 @@
time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
@@ -154,13 +138,15 @@
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-
+ EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
nsecs_t time = mTime;
EXPECT_EQ(3, layerCount());
@@ -173,7 +159,8 @@
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -186,7 +173,9 @@
// layer1 is still active but infrequent.
history().record(layer1.get(), time, time);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ 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);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -197,7 +186,8 @@
time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -213,19 +203,24 @@
time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
history().record(layer3.get(), time, time);
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ 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);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
// layer1 expires.
layer1.clear();
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ 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);
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -237,13 +232,14 @@
time += LO_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer2 expires.
layer2.clear();
- EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ ASSERT_TRUE(history().summarize(time).empty());
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
@@ -254,14 +250,15 @@
time += HI_FPS_PERIOD;
}
- EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
// layer3 expires.
layer3.clear();
- EXPECT_FLOAT_EQ(0, history().summarize(time).maxRefreshRate);
+ ASSERT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, layerCount());
EXPECT_EQ(0, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
new file mode 100644
index 0000000..11ace05
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -0,0 +1,418 @@
+/*
+ * 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 <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"
+
+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 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); }
+
+ 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); });
+ }
+
+ void setLayerInfoVote(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);
+ info->setLayerVote(vote, 0);
+ return;
+ }
+ }
+ }
+
+ auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+ RefreshRateConfigs mConfigs{true,
+ {
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
+ HwcConfigGroupType(0),
+ LO_FPS_PERIOD},
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(1),
+ HwcConfigGroupType(0),
+ HI_FPS_PERIOD},
+ },
+ HwcConfigIndexType(0)};
+ TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
+ TestableSurfaceFlinger mFlinger;
+
+ const nsecs_t mTime = systemTime();
+};
+
+namespace {
+
+TEST_F(LayerHistoryTestV2, oneLayer) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(history().summarize(mTime).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, mTime);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[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, mTime);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ }
+}
+
+TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ history().record(layer.get(), 0, mTime);
+ auto summary = history().summarize(mTime);
+ ASSERT_EQ(1, history().summarize(mTime).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+ summary = history().summarize(mTime);
+ EXPECT_TRUE(history().summarize(mTime).empty());
+ EXPECT_EQ(0, activeLayerCount());
+}
+
+TEST_F(LayerHistoryTestV2, explicitTimestamp) {
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ 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, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ 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, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ 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, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ 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, getFrameRate()).WillRepeatedly(Return(73.4f));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = mTime;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Explicit, 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
+ setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(history().summarize(time).empty());
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ 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, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(std::nullopt));
+
+ nsecs_t time = mTime;
+
+ EXPECT_EQ(3, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer1 is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer1.get(), time, time);
+ 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));
+
+ // layer2 is frequent and has high refresh rate.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer2.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // layer1 is still active but infrequent.
+ history().record(layer1.get(), time, time);
+
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[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++) {
+ history().record(layer2.get(), time, time);
+ 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));
+
+ // 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);
+ }
+
+ history().record(layer3.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(2, 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(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer3 becomes recently active.
+ history().record(layer3.get(), time, time);
+ ASSERT_EQ(2, 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(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+ EXPECT_EQ(2, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer1 expires.
+ layer1.clear();
+ ASSERT_EQ(2, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[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);
+ 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));
+
+ // layer2 expires.
+ layer2.clear();
+ EXPECT_TRUE(history().summarize(time).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++) {
+ history().record(layer3.get(), time, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+ EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer3 expires.
+ layer3.clear();
+ EXPECT_TRUE(history().summarize(time).empty());
+ EXPECT_EQ(0, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 86aa8fb..78009b8 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -31,16 +31,24 @@
namespace scheduler {
using RefreshRate = RefreshRateConfigs::RefreshRate;
+using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+using LayerRequirement = RefreshRateConfigs::LayerRequirement;
class RefreshRateConfigsTest : public testing::Test {
protected:
static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
- static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(1);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(2);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
- static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr auto VSYNC_30 = static_cast<int64_t>(1e9f / 30);
+ static constexpr auto VSYNC_60 = static_cast<int64_t>(1e9f / 60);
+ static constexpr auto VSYNC_72 = static_cast<int64_t>(1e9f / 72);
+ static constexpr auto VSYNC_90 = static_cast<int64_t>(1e9f / 90);
+ static constexpr auto VSYNC_120 = static_cast<int64_t>(1e9f / 120);
static constexpr int64_t VSYNC_60_POINT_4 = 16666665;
- static constexpr int64_t VSYNC_90 = 11111111;
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
@@ -212,31 +220,476 @@
RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
+ return {{"testLayer", LayerVoteType::Heuristic, refreshRate, 1.0f}};
+ };
+
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
- ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
- ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_60_90) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_60_72_90) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60_72_90_120) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+ {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+ RefreshRate expected120Config = {HWC_CONFIG_ID_120, VSYNC_120, HWC_GROUP_ID_0, "120fps", 120};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 48.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.desiredRefreshRate = 24.0f;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 48.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60_72_90) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 90.0f;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 30.0f;
+ EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_PriorityTest) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+ {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Max;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 24.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Max;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Max;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 15.0f;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 30.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 45.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_24FpsVideo) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Explicit;
+ for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = fps;
+ const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+ printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+ EXPECT_EQ(expected60Config, refreshRate);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ 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::Explicit;
+ lr2.desiredRefreshRate = 90.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Explicit) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ 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::Explicit;
+ lr2.desiredRefreshRate = 90.0f;
+ EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 90.0f;
+ lr2.vote = LayerVoteType::Explicit;
+ lr2.desiredRefreshRate = 60.0f;
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index b1ecf4d..82a00ee 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -76,7 +76,7 @@
scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
/*currentConfig=*/HwcConfigIndexType(0));
- mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs);
+ mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
auto eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a67c24c..52da34b 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -28,16 +28,25 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
- explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
- : Scheduler([](bool) {}, configs, *this) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2) {
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ }
}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this) {
- mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+ : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
+ useContentDetectionV2) {
+ if (mUseContentDetectionV2) {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+ } else {
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+ }
}
// Used to inject mock event thread.
@@ -46,7 +55,13 @@
}
size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())->mLayerInfos.size();
+ if (mUseContentDetectionV2) {
+ return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
+ ->mLayerInfos.size();
+ } else {
+ return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
+ ->mLayerInfos.size();
+ }
}
/* ------------------------------------------------------------------------
@@ -60,6 +75,9 @@
auto mutableLayerHistory() {
return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
}
+ auto mutableLayerHistoryV2() {
+ return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
+ }
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8ddb872..2491533 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -198,7 +198,8 @@
void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
- std::unique_ptr<EventThread> sfEventThread) {
+ std::unique_ptr<EventThread> sfEventThread,
+ bool useContentDetectionV2 = false) {
std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
{{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
mFlinger->mRefreshRateConfigs = std::make_unique<
@@ -213,7 +214,7 @@
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
- *mFlinger->mRefreshRateConfigs);
+ *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -443,7 +444,7 @@
static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
- static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
+ static constexpr hwc2_config_t DEFAULT_ACTIVE_CONFIG = 0;
static constexpr int32_t DEFAULT_POWER_MODE = 2;
FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType,
@@ -465,7 +466,7 @@
return *this;
}
- auto& setRefreshRate(uint32_t refreshRate) {
+ auto& setRefreshRate(int32_t refreshRate) {
mRefreshRate = refreshRate;
return *this;
}
@@ -480,7 +481,7 @@
return *this;
}
- auto& setActiveConfig(int32_t config) {
+ auto& setActiveConfig(hwc2_config_t config) {
mActiveConfig = config;
return *this;
}
@@ -513,7 +514,7 @@
config.setDpiX(mDpiX);
config.setDpiY(mDpiY);
config.setConfigGroup(mConfigGroup);
- display->mutableConfigs().emplace(mActiveConfig, config.build());
+ display->mutableConfigs().emplace(static_cast<int32_t>(mActiveConfig), config.build());
display->mutableIsConnected() = true;
display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
@@ -534,11 +535,11 @@
hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
int32_t mWidth = DEFAULT_WIDTH;
int32_t mHeight = DEFAULT_HEIGHT;
- uint32_t mRefreshRate = DEFAULT_REFRESH_RATE;
+ int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
int32_t mDpiX = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
int32_t mDpiY = DEFAULT_DPI;
- int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
+ hwc2_config_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
int32_t mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr;
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 1fd0e61..494e73d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,6 +31,7 @@
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
+ MOCK_CONST_METHOD0(getFrameRate, std::optional<float>());
};
} // namespace android::mock