diff options
| author | 2020-01-17 12:43:17 -0800 | |
|---|---|---|
| committer | 2020-01-23 21:27:14 -0800 | |
| commit | 8a82ba66a22238fe59c02a6309ba8bd3dae4f074 (patch) | |
| tree | 0ab0962ade34ecd0e09fab57c68d257192db623e | |
| parent | c34a8a2e960393f434d8307edeb495d0e301c70d (diff) | |
SurfaceFlinger: enhance refresh rate selection
Enhance the refresh rate selection algorithm to allow having multiple
refresh rate. The new process attaches scores to each one of the available
refresh rate and chooses the refresh rate with the highest score.
This behavior is currently controlled by the sysprop flag
'debug.sf.use_content_detection_v2' and currently turned off.
This algorithm stills needs some tunings which will be done in
layer CLs.
Test: adb shell /data/nativetest64/SurfaceFlinger_test/SurfaceFlinger_test
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Test: go/90hzscenarios manual tests
Bug: 147516364
Fixes: 146068419
Change-Id: I06e07459e469482799ff80fa54fa8dd311325e0e
31 files changed, 1877 insertions, 185 deletions
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 9a7eabd498..1b1e8890ab 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -165,7 +165,9 @@ filegroup { "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 b55e62b88c..e85281d8a9 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -129,8 +129,11 @@ bool BufferQueueLayer::setFrameRate(float frameRate) { 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 0777953c17..2bd1e3d6cc 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -57,7 +57,7 @@ public: 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 c173c66b4e..2967164764 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -251,7 +251,8 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTi 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 605e7c8ab4..ff48ecdedc 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -227,8 +227,10 @@ class DisplayRenderArea : public RenderArea { 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 6f0f38a58a..a2b7421269 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 a0dabb427d..effe43b548 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 f4d4329f6d..af906dc64c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1251,8 +1251,11 @@ bool Layer::setFrameRate(float frameRate) { 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 ffe004f9e9..b8423f0232 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -744,8 +744,9 @@ public: */ 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 33e5796e6f..d3d9d3aec7 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -155,6 +155,7 @@ bool RefreshRateOverlay::createLayer() { 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 a7a6dd5fc1..9b3a9f4309 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -71,8 +71,8 @@ public: 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 abf0cd673a..ebd617faef 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -43,7 +43,7 @@ namespace android::scheduler::impl { 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 @@ LayerHistory::LayerHistory() : 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 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) { } LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { - float maxRefreshRate = 0; - std::lock_guard lock(mLock); partitionLayers(now); @@ -113,7 +112,7 @@ LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { 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 LayerHistory::summarize(nsecs_t now) { } } + LayerHistory::Summary summary; for (const auto& [weakLayer, info] : activeLayers()) { const bool recent = info->isRecentlyActive(now); auto layer = weakLayer.promote(); // Only use the layer if the reference still exists. if (layer || CC_UNLIKELY(mTraceEnabled)) { - 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 @@ void LayerHistory::clear() { 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 f21713411a..bef04e9b42 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 @@ class TestableScheduler; 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: // 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 0000000000..884b46add2 --- /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 0000000000..d94d758ec5 --- /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 0000000000..564f05efa2 --- /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 45d1f23029..a8f8e0e245 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 @@ const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFra 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::getSortedRefreshRateList( 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 0c3369a22a..80d42cc620 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -97,8 +97,39 @@ public: // 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(const std::vector<LayerRequirement>& layers) const + EXCLUDES(mLock); + // Returns all available refresh rates according to the current policy. - const RefreshRate& getRefreshRateForContent(float contentFramerate) const EXCLUDES(mLock); + 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 a384dbe29b..e44cd528b5 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -114,7 +114,8 @@ private: 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 0b645c41c1..6a437a25ed 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -103,16 +103,21 @@ std::unique_ptr<DispSync> createDispSync() { 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 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, 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(impl::EventControlThread::SetVSyncEnabledFunction function, 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 @@ nsecs_t Scheduler::getDispSyncExpectedPresentTime() { 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::recordLayerHistory(Layer* layer, nsecs_t presentTime) { 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::resetIdleTimer() { } 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 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO 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 @@ HwcConfigIndexType Scheduler::calculateRefreshRateType() { // 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 @@ HwcConfigIndexType Scheduler::calculateRefreshRateType() { 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::onDisplayRefreshed(nsecs_t timestamp) { } } +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 c6430c30f5..2987424c99 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -58,7 +58,8 @@ public: 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 @@ public: // 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 @@ private: // 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 @@ private: 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 @@ private: 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 @@ private: 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 6d18922a78..adf5f9f4a4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -540,6 +540,11 @@ void SurfaceFlinger::bootFinished() 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()); + } })); } @@ -1779,7 +1784,13 @@ void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { // 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; } @@ -2378,6 +2389,9 @@ void SurfaceFlinger::processDisplayChangesLocked() { } 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); + } } } } @@ -2450,6 +2464,12 @@ void SurfaceFlinger::processDisplayChangesLocked() { LOG_ALWAYS_FATAL_IF(!displayId); dispatchDisplayHotplugEvent(displayId->value, true); } + + const auto displayDevice = mDisplays[displayToken]; + if (displayDevice->isPrimary()) { + mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() * + displayDevice->getHeight()); + } } } } @@ -2647,8 +2667,12 @@ void SurfaceFlinger::updateCursorAsync() } 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 e12d31a99a..45889a564e 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -75,7 +75,9 @@ std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfig 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 1eaf2dd004..d046f765e8 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -44,6 +44,7 @@ cc_test { "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 ca51cb6f6d..9ca1b70a1f 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -73,7 +73,7 @@ protected: 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 @@ TEST_F(LayerHistoryTest, oneLayer) { const auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer, 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 @@ TEST_F(LayerHistoryTest, explicitTimestamp) { 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { // 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { 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 @@ TEST_F(LayerHistoryTest, multipleLayers) { 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 0000000000..11ace0576d --- /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 86aa8fb22b..78009b8122 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -31,16 +31,24 @@ namespace android { 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 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) { 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 b1ecf4da6e..82a00ee734 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -76,7 +76,7 @@ SchedulerTest::SchedulerTest() { 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 a67c24c2d6..52da34b69a 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -28,16 +28,25 @@ namespace android { 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 @@ public: } 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 @@ public: 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 8ddb872582..2491533ea4 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -198,7 +198,8 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: return *this; } - auto& setRefreshRate(uint32_t refreshRate) { + auto& setRefreshRate(int32_t refreshRate) { mRefreshRate = refreshRate; return *this; } @@ -480,7 +481,7 @@ public: return *this; } - auto& setActiveConfig(int32_t config) { + auto& setActiveConfig(hwc2_config_t config) { mActiveConfig = config; return *this; } @@ -513,7 +514,7 @@ public: 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 @@ public: 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 1fd0e617c2..494e73dda9 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -31,6 +31,7 @@ public: 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 |