Merge changes from topic "scheduler_fps" into qt-dev
* changes:
SurfaceFlinger: use std::set for DisplayConfigs
SurfaceFlinger: better fit content to refresh rate
SurfaceFlinger: handle wallpaper scenario in Scheduler
SF: Updating content FPS tracking
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 776c6e6..e226136 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -190,6 +190,7 @@
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC),
front->mFrameNumber, maxFrameNumber);
+ ATRACE_NAME("PRESENT_LATER");
return PRESENT_LATER;
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 52c68df..9080d29 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -147,6 +147,7 @@
"Scheduler/EventThread.cpp",
"Scheduler/IdleTimer.cpp",
"Scheduler/LayerHistory.cpp",
+ "Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 1b2b180..a08ae8c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -413,6 +413,7 @@
// If the head buffer's acquire fence hasn't signaled yet, return and
// try again later
if (!fenceHasSignaled()) {
+ ATRACE_NAME("!fenceHasSignaled()");
mFlinger->signalLayerUpdate();
return false;
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index b623991..ff5f271 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -398,8 +398,8 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
if (mFlinger->mUseSmart90ForVideo) {
- // Report mApi ID for each layer.
- mFlinger->mScheduler->addNativeWindowApi(item.mApi);
+ const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+ mFlinger->mScheduler->addLayerPresentTime(mSchedulerLayerHandle, presentTime);
}
Mutex::Autolock lock(mQueueItemLock);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 64dfdc3..2cbb917 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -209,7 +209,8 @@
return true;
}
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) {
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
+ nsecs_t desiredPresentTime) {
if (mCurrentState.buffer) {
mReleasePreviousBuffer = true;
}
@@ -217,6 +218,15 @@
mCurrentState.buffer = buffer;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
+
+ mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
+ mDesiredPresentTime = desiredPresentTime;
+
+ if (mFlinger->mUseSmart90ForVideo) {
+ const nsecs_t presentTime = (mDesiredPresentTime == -1) ? 0 : mDesiredPresentTime;
+ mFlinger->mScheduler->addLayerPresentTime(mSchedulerLayerHandle, presentTime);
+ }
+
return true;
}
@@ -348,14 +358,6 @@
return parentBounds;
}
-void BufferStateLayer::setPostTime(nsecs_t postTime) {
- mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
-}
-
-void BufferStateLayer::setDesiredPresentTime(nsecs_t desiredPresentTime) {
- mDesiredPresentTime = desiredPresentTime;
-}
-
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 668830a..864a15d 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -63,7 +63,8 @@
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
bool setFrame(const Rect& frame) override;
- bool setBuffer(const sp<GraphicBuffer>& buffer) override;
+ bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
+ nsecs_t desiredPresentTime) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -90,8 +91,6 @@
Rect getBufferSize(const State& s) const override;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
- void setPostTime(nsecs_t postTime) override;
- void setDesiredPresentTime(nsecs_t desiredPresentTime) override;
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d88702c..8736fa5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -70,7 +70,10 @@
std::atomic<int32_t> Layer::sSequence{1};
Layer::Layer(const LayerCreationArgs& args)
- : mFlinger(args.flinger), mName(args.name), mClientRef(args.client) {
+ : mFlinger(args.flinger),
+ mName(args.name),
+ mClientRef(args.client),
+ mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
mCurrentCrop.makeInvalid();
uint32_t layerFlags = 0;
@@ -115,6 +118,8 @@
mFrameEventHistory.initializeCompositorTiming(compositorTiming);
mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
+ mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType);
+
mFlinger->onLayerCreated();
}
@@ -1297,6 +1302,7 @@
result.append("-----------------------------\n");
result.append(" Layer name\n");
result.append(" Z | ");
+ result.append(" Window Type | ");
result.append(" Comp Type | ");
result.append(" Transform | ");
result.append(" Disp Frame (LTRB) | ");
@@ -1333,6 +1339,7 @@
} else {
StringAppendF(&result, " %10d | ", layerState.z);
}
+ StringAppendF(&result, " %10d | ", mWindowType);
StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
StringAppendF(&result, "%10s | ",
toString(getCompositionLayer() ? compositionState.bufferTransform
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8348ce1..f4545e0 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -311,7 +311,10 @@
virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
virtual bool setCrop(const Rect& /*crop*/) { return false; };
virtual bool setFrame(const Rect& /*frame*/) { return false; };
- virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/) { return false; };
+ virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, nsecs_t /*postTime*/,
+ nsecs_t /*desiredPresentTime*/) {
+ return false;
+ }
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
@@ -450,9 +453,6 @@
}
virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
- virtual void setPostTime(nsecs_t /*postTime*/) {}
- virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {}
-
protected:
virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform, Region& clearRegion,
@@ -885,6 +885,12 @@
// Can only be accessed with the SF state lock held.
bool mChildrenChanged{false};
+ // Window types from WindowManager.LayoutParams
+ const int mWindowType;
+
+ // This is populated if the layer is registered with Scheduler for tracking purposes.
+ std::unique_ptr<scheduler::LayerHistory::LayerHandle> mSchedulerLayerHandle;
+
private:
/**
* Returns an unsorted vector of all layers that are part of this tree.
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index d5ccbe1..8e36ae9 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -14,14 +14,18 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "LayerHistory.h"
#include <cinttypes>
#include <cstdint>
+#include <limits>
#include <numeric>
#include <string>
#include <unordered_map>
+#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -29,28 +33,114 @@
#include "SchedulerUtils.h"
namespace android {
+namespace scheduler {
-LayerHistory::LayerHistory() {}
+std::atomic<int64_t> LayerHistory::sNextId = 0;
+
+LayerHistory::LayerHistory() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.layer_history_trace", value, "0");
+ mTraceEnabled = bool(atoi(value));
+}
LayerHistory::~LayerHistory() = default;
-void LayerHistory::insert(const std::string layerName, nsecs_t presentTime) {
- mElements[mCounter].insert(std::make_pair(layerName, presentTime));
+std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
+ float maxRefreshRate) {
+ const int64_t id = sNextId++;
+
+ std::lock_guard lock(mLock);
+ mInactiveLayerInfos.emplace(id, std::make_shared<LayerInfo>(name, maxRefreshRate));
+ return std::make_unique<LayerHistory::LayerHandle>(*this, id);
}
-void LayerHistory::incrementCounter() {
- mCounter++;
- mCounter = mCounter % scheduler::ARRAY_SIZE;
- // Clear all the previous data from the history. This is a ring buffer, so we are
- // reusing memory.
- mElements[mCounter].clear();
+void LayerHistory::destroyLayer(const int64_t id) {
+ std::lock_guard lock(mLock);
+ auto it = mActiveLayerInfos.find(id);
+ if (it != mActiveLayerInfos.end()) {
+ mActiveLayerInfos.erase(it);
+ }
+
+ it = mInactiveLayerInfos.find(id);
+ if (it != mInactiveLayerInfos.end()) {
+ mInactiveLayerInfos.erase(it);
+ }
}
-const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
- // For the purposes of the layer history, the index = 0 always needs to start at the
- // current counter, and then decrement to access the layers in correct historical order.
- return mElements.at((scheduler::ARRAY_SIZE + (mCounter - (index % scheduler::ARRAY_SIZE))) %
- scheduler::ARRAY_SIZE);
+void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime) {
+ std::shared_ptr<LayerInfo> layerInfo;
+ {
+ std::lock_guard lock(mLock);
+ auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
+ if (layerInfoIterator != mInactiveLayerInfos.end()) {
+ layerInfo = layerInfoIterator->second;
+ mInactiveLayerInfos.erase(layerInfoIterator);
+ mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
+ } else {
+ layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
+ if (layerInfoIterator != mActiveLayerInfos.end()) {
+ layerInfo = layerInfoIterator->second;
+ } else {
+ ALOGW("Inserting information about layer that is not registered: %" PRId64,
+ layerHandle->mId);
+ return;
+ }
+ }
+ }
+ layerInfo->setLastPresentTime(presentTime);
}
+float LayerHistory::getDesiredRefreshRate() {
+ float newRefreshRate = 0.f;
+ std::lock_guard lock(mLock);
+
+ removeIrrelevantLayers();
+
+ // Iterate through all layers that have been recently updated, and find the max refresh rate.
+ for (const auto& [layerId, layerInfo] : mActiveLayerInfos) {
+ const float layerRefreshRate = layerInfo->getDesiredRefreshRate();
+ if (mTraceEnabled) {
+ // Store the refresh rate in traces for easy debugging.
+ std::string layerName = "LFPS " + layerInfo->getName();
+ ATRACE_INT(layerName.c_str(), std::round(layerRefreshRate));
+ ALOGD("%s: %f", layerName.c_str(), std::round(layerRefreshRate));
+ }
+ if (layerInfo->isRecentlyActive() && layerRefreshRate > newRefreshRate) {
+ newRefreshRate = layerRefreshRate;
+ }
+ }
+ if (mTraceEnabled) {
+ ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
+ }
+
+ return newRefreshRate;
+}
+
+void LayerHistory::removeIrrelevantLayers() {
+ const int64_t obsoleteEpsilon = systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
+ // Iterator pointing to first element in map
+ auto it = mActiveLayerInfos.begin();
+ while (it != mActiveLayerInfos.end()) {
+ // If last updated was before the obsolete time, remove it.
+ if (it->second->getLastUpdatedTime() < obsoleteEpsilon) {
+ // erase() function returns the iterator of the next
+ // to last deleted element.
+ if (mTraceEnabled) {
+ ALOGD("Layer %s obsolete", it->second->getName().c_str());
+ // Make sure to update systrace to indicate that the layer was erased.
+ std::string layerName = "LFPS " + it->second->getName();
+ ATRACE_INT(layerName.c_str(), 0);
+ }
+ auto id = it->first;
+ auto layerInfo = it->second;
+ layerInfo->clearHistory();
+ mInactiveLayerInfos.insert({id, layerInfo});
+ it = mActiveLayerInfos.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index c6fab07..39061e7 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -25,40 +25,60 @@
#include <utils/Timers.h>
+#include "LayerInfo.h"
#include "SchedulerUtils.h"
namespace android {
+namespace scheduler {
/*
- * This class represents a circular buffer in which we keep layer history for
- * the past ARRAY_SIZE frames. Each time, a signal for new frame comes, the counter
- * gets incremented and includes all the layers that are requested to draw in that
- * frame.
- *
- * Once the buffer reaches the end of the array, it starts overriding the elements
- * at the beginning of the array.
+ * This class represents information about layers that are considered current. We keep an
+ * unordered map between layer name and LayerInfo.
*/
class LayerHistory {
public:
+ // Handle for each layer we keep track of.
+ class LayerHandle {
+ public:
+ LayerHandle(LayerHistory& lh, int64_t id) : mId(id), mLayerHistory(lh) {}
+ ~LayerHandle() { mLayerHistory.destroyLayer(mId); }
+
+ const int64_t mId;
+
+ private:
+ LayerHistory& mLayerHistory;
+ };
+
LayerHistory();
~LayerHistory();
- // Method for inserting layers and their requested present time into the ring buffer.
- // The elements are going to be inserted into an unordered_map at the position 'now'.
- void insert(const std::string layerName, nsecs_t presentTime);
- // Method for incrementing the current slot in the ring buffer. It also clears the
- // unordered_map, if it was created previously.
- void incrementCounter();
- // Returns unordered_map at the given at index. The index is decremented from 'now'. For
- // example, 0 is now, 1 is previous frame.
- const std::unordered_map<std::string, nsecs_t>& get(size_t index) const;
- // Returns the total size of the ring buffer. The value is always the same regardless
- // of how many slots we filled in.
- static constexpr size_t getSize() { return scheduler::ARRAY_SIZE; }
+ // When the layer is first created, register it.
+ std::unique_ptr<LayerHandle> createLayer(const std::string name, float maxRefreshRate);
+
+ // Method for inserting layers and their requested present time into the unordered map.
+ void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime);
+ // Returns the desired refresh rate, which is a max refresh rate of all the current
+ // layers. See go/content-fps-detection-in-scheduler for more information.
+ float getDesiredRefreshRate();
+
+ // Removes the handle and the object from the map.
+ void destroyLayer(const int64_t id);
private:
- size_t mCounter = 0;
- std::array<std::unordered_map<std::string, nsecs_t>, scheduler::ARRAY_SIZE> mElements;
+ // Removes the layers that have been idle for a given amount of time from mLayerInfos.
+ void removeIrrelevantLayers() REQUIRES(mLock);
+
+ // Information about currently active layers.
+ std::mutex mLock;
+ std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mActiveLayerInfos GUARDED_BY(mLock);
+ std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mInactiveLayerInfos GUARDED_BY(mLock);
+
+ // Each layer has it's own ID. This variable keeps track of the count.
+ static std::atomic<int64_t> sNextId;
+
+ // Flag whether to log layer FPS in systrace
+ bool mTraceEnabled = false;
};
+} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
new file mode 100644
index 0000000..95d7d31
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerInfo.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <numeric>
+#include <string>
+
+namespace android {
+namespace scheduler {
+
+LayerInfo::LayerInfo(const std::string name, float maxRefreshRate)
+ : mName(name),
+ mMinRefreshDuration(1e9f / maxRefreshRate),
+ mRefreshRateHistory(mMinRefreshDuration) {}
+
+LayerInfo::~LayerInfo() = default;
+
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) {
+ std::lock_guard lock(mLock);
+
+ // Buffers can come with a present time far in the future. That keeps them relevant.
+ mLastUpdatedTime = std::max(lastPresentTime, systemTime());
+ mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+
+ const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
+ mLastPresentTime = lastPresentTime;
+ // Ignore time diff that are too high - those are stale values
+ if (timeDiff > TIME_EPSILON_NS.count()) return;
+ const nsecs_t refreshDuration = (timeDiff > 0) ? timeDiff : mMinRefreshDuration;
+ mRefreshRateHistory.insertRefreshRate(refreshDuration);
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
new file mode 100644
index 0000000..1970a47
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cinttypes>
+#include <cstdint>
+#include <deque>
+#include <mutex>
+#include <numeric>
+#include <string>
+
+#include <log/log.h>
+
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+#include "SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/*
+ * This class represents information about individial layers.
+ */
+class LayerInfo {
+ /**
+ * Struct that keeps the information about the refresh rate for last
+ * HISTORY_SIZE frames. This is used to better determine the refresh rate
+ * for individual layers.
+ */
+ class RefreshRateHistory {
+ public:
+ explicit RefreshRateHistory(nsecs_t minRefreshDuration)
+ : mMinRefreshDuration(minRefreshDuration) {}
+ void insertRefreshRate(nsecs_t refreshRate) {
+ mElements.push_back(refreshRate);
+ if (mElements.size() > HISTORY_SIZE) {
+ mElements.pop_front();
+ }
+ }
+
+ float getRefreshRateAvg() const {
+ nsecs_t refreshDuration = mMinRefreshDuration;
+ if (mElements.size() == HISTORY_SIZE) {
+ refreshDuration = scheduler::calculate_mean(mElements);
+ }
+
+ return 1e9f / refreshDuration;
+ }
+ void clearHistory() { mElements.clear(); }
+
+ private:
+ std::deque<nsecs_t> mElements;
+ static constexpr size_t HISTORY_SIZE = 30;
+ const nsecs_t mMinRefreshDuration;
+ };
+
+ /**
+ * Struct that keeps the information about the present time for last
+ * HISTORY_SIZE frames. This is used to better determine whether the given layer
+ * is still relevant and it's refresh rate should be considered.
+ */
+ class PresentTimeHistory {
+ public:
+ void insertPresentTime(nsecs_t presentTime) {
+ mElements.push_back(presentTime);
+ if (mElements.size() > HISTORY_SIZE) {
+ mElements.pop_front();
+ }
+ }
+
+ // Checks whether the present time that was inserted HISTORY_SIZE ago is within a
+ // certain threshold: TIME_EPSILON_NS.
+ bool isRelevant() const {
+ const int64_t obsoleteEpsilon = systemTime() - scheduler::TIME_EPSILON_NS.count();
+ // The layer had to publish at least HISTORY_SIZE of updates, and the first
+ // update should not be older than TIME_EPSILON_NS nanoseconds.
+ if (mElements.size() == HISTORY_SIZE &&
+ mElements.at(HISTORY_SIZE - 1) > obsoleteEpsilon) {
+ return true;
+ }
+ return false;
+ }
+
+ void clearHistory() { mElements.clear(); }
+
+ private:
+ std::deque<nsecs_t> mElements;
+ static constexpr size_t HISTORY_SIZE = 10;
+ };
+
+public:
+ LayerInfo(const std::string name, float maxRefreshRate);
+ ~LayerInfo();
+
+ LayerInfo(const LayerInfo&) = delete;
+ LayerInfo& operator=(const LayerInfo&) = delete;
+
+ // Records the last requested oresent time. It also stores information about when
+ // the layer was last updated. If the present time is farther in the future than the
+ // updated time, the updated time is the present time.
+ void setLastPresentTime(nsecs_t lastPresentTime);
+
+ // Checks the present time history to see whether the layer is relevant.
+ bool isRecentlyActive() const {
+ std::lock_guard lock(mLock);
+ return mPresentTimeHistory.isRelevant();
+ }
+
+ // Calculate the average refresh rate.
+ float getDesiredRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return mRefreshRateHistory.getRefreshRateAvg();
+ }
+
+ // Return the last updated time. If the present time is farther in the future than the
+ // updated time, the updated time is the present time.
+ nsecs_t getLastUpdatedTime() {
+ std::lock_guard lock(mLock);
+ return mLastUpdatedTime;
+ }
+
+ std::string getName() const { return mName; }
+
+ void clearHistory() {
+ std::lock_guard lock(mLock);
+ mRefreshRateHistory.clearHistory();
+ mPresentTimeHistory.clearHistory();
+ }
+
+private:
+ const std::string mName;
+ const nsecs_t mMinRefreshDuration;
+ mutable std::mutex mLock;
+ nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
+ nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
+ RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
+ PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 5874066..eb9ae35 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -54,7 +54,7 @@
const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
return mRefreshRates;
}
- std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) {
+ std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
const auto& refreshRate = mRefreshRates.find(type);
if (refreshRate != mRefreshRates.end()) {
return refreshRate->second;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0063c8a..b1c39ef 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,6 +28,7 @@
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
#include <cutils/properties.h>
+#include <input/InputWindow.h>
#include <system/window.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Timers.h>
@@ -39,6 +40,7 @@
#include "EventThread.h"
#include "IdleTimer.h"
#include "InjectVSyncSource.h"
+#include "LayerInfo.h"
#include "SchedulerUtils.h"
#include "SurfaceFlingerProperties.h"
@@ -55,11 +57,13 @@
std::atomic<int64_t> Scheduler::sNextId = 0;
-Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function)
+Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
+ const scheduler::RefreshRateConfigs& refreshRateConfig)
: mHasSyncFramework(running_without_sync_framework(true)),
mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
mPrimaryHWVsyncEnabled(false),
- mHWVsyncAvailable(false) {
+ mHWVsyncAvailable(false),
+ mRefreshRateConfigs(refreshRateConfig) {
// Note: We create a local temporary with the real DispSync implementation
// type temporarily so we can initialize it with the configured values,
// before storing it for more generic use using the interface type.
@@ -297,32 +301,34 @@
mPrimaryDispSync->dump(result);
}
-void Scheduler::addNativeWindowApi(int apiId) {
- std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
- mWindowApiHistory[mApiHistoryCounter] = apiId;
- mApiHistoryCounter++;
- mApiHistoryCounter = mApiHistoryCounter % scheduler::ARRAY_SIZE;
+std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
+ std::string const& name, int windowType) {
+ RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
+ ? RefreshRateType::DEFAULT
+ : RefreshRateType::PERFORMANCE;
+
+ const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
+ const uint32_t fps = (refreshRate) ? refreshRate->fps : 0;
+ return mLayerHistory.createLayer(name, fps);
+}
+
+void Scheduler::addLayerPresentTime(
+ const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
+ nsecs_t presentTime) {
+ mLayerHistory.insert(layerHandle, presentTime);
}
void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
fn(*mPrimaryDispSync);
}
-void Scheduler::updateFpsBasedOnNativeWindowApi() {
- int mode;
- {
- std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
- mode = scheduler::calculate_mode(mWindowApiHistory);
- }
- ATRACE_INT("NativeWindowApiMode", mode);
-
- if (mode == NATIVE_WINDOW_API_MEDIA) {
- // TODO(b/127365162): These callback names are not accurate anymore. Update.
- mediaChangeRefreshRate(MediaFeatureState::MEDIA_PLAYING);
- ATRACE_INT("DetectedVideo", 1);
+void Scheduler::updateFpsBasedOnContent() {
+ uint32_t refreshRate = std::round(mLayerHistory.getDesiredRefreshRate());
+ ATRACE_INT("ContentFPS", refreshRate);
+ if (refreshRate > 0) {
+ contentChangeRefreshRate(ContentFeatureState::CONTENT_DETECTION_ON, refreshRate);
} else {
- mediaChangeRefreshRate(MediaFeatureState::MEDIA_OFF);
- ATRACE_INT("DetectedVideo", 0);
+ contentChangeRefreshRate(ContentFeatureState::CONTENT_DETECTION_OFF, 0);
}
}
@@ -365,39 +371,70 @@
return stream.str();
}
-void Scheduler::mediaChangeRefreshRate(MediaFeatureState mediaFeatureState) {
- // Default playback for media is DEFAULT when media is on.
- RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
- ConfigEvent configEvent = ConfigEvent::None;
-
+void Scheduler::contentChangeRefreshRate(ContentFeatureState contentFeatureState,
+ uint32_t refreshRate) {
+ RefreshRateType newRefreshRateType;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mCurrentMediaFeatureState = mediaFeatureState;
- // Only switch to PERFORMANCE if idle timer was reset, when turning
- // media off. If the timer is IDLE, stay at DEFAULT.
- if (mediaFeatureState == MediaFeatureState::MEDIA_OFF &&
- mCurrentIdleTimerState == IdleTimerState::RESET) {
- refreshRateType = RefreshRateType::PERFORMANCE;
- }
+ mCurrentContentFeatureState = contentFeatureState;
+ mContentRefreshRate = refreshRate;
+ newRefreshRateType = calculateRefreshRateType();
}
- changeRefreshRate(refreshRateType, configEvent);
+ changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
}
void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
- RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
- ConfigEvent configEvent = ConfigEvent::None;
-
+ RefreshRateType newRefreshRateType;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
mCurrentIdleTimerState = idleTimerState;
- // Only switch to PERFOMANCE if the idle timer was reset, and media is
- // not playing. Otherwise, stay at DEFAULT.
- if (idleTimerState == IdleTimerState::RESET &&
- mCurrentMediaFeatureState == MediaFeatureState::MEDIA_OFF) {
- refreshRateType = RefreshRateType::PERFORMANCE;
+ newRefreshRateType = calculateRefreshRateType();
+ }
+ changeRefreshRate(newRefreshRateType, ConfigEvent::None);
+}
+
+Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+ // First check if timer has expired as it means there is no new content on the screen
+ if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
+ return RefreshRateType::DEFAULT;
+ }
+
+ // If content detection is off we choose performance as we don't know the content fps
+ if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
+ return RefreshRateType::PERFORMANCE;
+ }
+
+ // Content detection is on, find the appropriate refresh rate
+ // Start with the smallest refresh rate which is within a margin of the content
+ RefreshRateType currRefreshRateType = RefreshRateType::PERFORMANCE;
+ constexpr float MARGIN = 0.05f;
+ auto iter = mRefreshRateConfigs.getRefreshRates().cbegin();
+ while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
+ if (iter->second->fps >= mContentRefreshRate * (1 - MARGIN)) {
+ currRefreshRateType = iter->first;
+ break;
+ }
+ ++iter;
+ }
+
+ // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+ // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+ // align well with both
+ float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
+ float(mContentRefreshRate);
+ if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+ while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
+ ratio = iter->second->fps / float(mContentRefreshRate);
+
+ if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+ currRefreshRateType = iter->first;
+ break;
+ }
+ ++iter;
}
}
- changeRefreshRate(refreshRateType, configEvent);
+
+ return currRefreshRateType;
}
void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 73896d5..1318fbb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -90,7 +90,8 @@
std::atomic<nsecs_t> lastResyncTime = 0;
};
- explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function);
+ explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
+ const scheduler::RefreshRateConfigs& refreshRateConfig);
virtual ~Scheduler();
@@ -145,16 +146,22 @@
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
nsecs_t expectedPresentTime();
- // apiId indicates the API (NATIVE_WINDOW_API_xxx) that queues the buffer.
- // TODO(b/123956502): Remove this call with V1 go/content-fps-detection-in-scheduler.
- void addNativeWindowApi(int apiId);
- // Updates FPS based on the most occured request for Native Window API.
- void updateFpsBasedOnNativeWindowApi();
+ // Registers the layer in the scheduler, and returns the handle for future references.
+ std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
+ int windowType);
+
+ // Stores present time for a layer.
+ void addLayerPresentTime(
+ const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
+ nsecs_t presentTime);
+ // Updates FPS based on the most content presented.
+ void updateFpsBasedOnContent();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
// Returns whether idle timer is enabled or not
bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
+
// Returns relevant information about Scheduler for dumpsys purposes.
std::string doDump();
@@ -171,7 +178,7 @@
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
- enum class MediaFeatureState { MEDIA_PLAYING, MEDIA_OFF };
+ enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
enum class IdleTimerState { EXPIRED, RESET };
// Creates a connection on the given EventThread and forwards the given callbacks.
@@ -188,12 +195,17 @@
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
// Media feature's function to change the refresh rate.
- void mediaChangeRefreshRate(MediaFeatureState mediaFeatureState);
+ void contentChangeRefreshRate(ContentFeatureState contentFeatureState, uint32_t refreshRate);
// Idle timer feature's function to change the refresh rate.
void timerChangeRefreshRate(IdleTimerState idleTimerState);
+ // Calculate the new refresh rate type
+ RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
+ // Helper function to calculate error frames
+ float getErrorFrames(float contentFps, float configFps);
+
// If fences from sync Framework are supported.
const bool mHasSyncFramework;
@@ -226,13 +238,8 @@
std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
size_t mCounter = 0;
- // The following few fields follow native window api bits that come with buffers. If there are
- // more buffers with NATIVE_WINDOW_API_MEDIA we render at 60Hz, otherwise we render at 90Hz.
- // There is not dependency on timestamp for V0.
- // TODO(b/123956502): Remove this when more robust logic for content fps detection is developed.
- std::mutex mWindowApiHistoryLock;
- std::array<int, scheduler::ARRAY_SIZE> mWindowApiHistory GUARDED_BY(mWindowApiHistoryLock);
- int64_t mApiHistoryCounter = 0;
+ // Historical information about individual layers. Used for predicting the refresh rate.
+ scheduler::LayerHistory mLayerHistory;
// Timer that records time between requests for next vsync. If the time is higher than a given
// interval, a callback is fired. Set this variable to >0 to use this feature.
@@ -245,9 +252,12 @@
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
std::mutex mFeatureStateLock;
- MediaFeatureState mCurrentMediaFeatureState GUARDED_BY(mFeatureStateLock) =
- MediaFeatureState::MEDIA_OFF;
+ ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) =
+ ContentFeatureState::CONTENT_DETECTION_OFF;
IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
+ uint32_t mContentRefreshRate;
+
+ const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 9e6e8c7..a935cac 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <cinttypes>
#include <numeric>
#include <unordered_map>
@@ -23,6 +24,8 @@
namespace android {
namespace scheduler {
+using namespace std::chrono_literals;
+
// This number is used to set the size of the arrays in scheduler that hold information
// about layers.
static constexpr size_t ARRAY_SIZE = 30;
@@ -32,12 +35,20 @@
// still like to keep track of time when the device is in this config.
static constexpr int SCREEN_OFF_CONFIG_ID = -1;
+// This number is used when we try to determine how long does a given layer stay relevant.
+// Currently it is set to 100ms, because that would indicate 10Hz rendering.
+static constexpr std::chrono::nanoseconds TIME_EPSILON_NS = 100ms;
+
+// This number is used when we try to determine how long do we keep layer information around
+// before we remove it. Currently it is set to 100ms.
+static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 100ms;
+
// Calculates the statistical mean (average) in the data structure (array, vector). The
// function does not modify the contents of the array.
template <typename T>
auto calculate_mean(const T& v) {
using V = typename T::value_type;
- V sum = std::accumulate(v.begin(), v.end(), 0);
+ V sum = std::accumulate(v.begin(), v.end(), static_cast<V>(0));
return sum / static_cast<V>(v.size());
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8a45d69..bd3f156 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -605,7 +605,8 @@
Mutex::Autolock _l(mStateLock);
// start the EventThread
mScheduler =
- getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); });
+ getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
+ mRefreshRateConfigs);
auto resyncCallback =
mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
@@ -702,6 +703,7 @@
setRefreshRateTo(type, event);
});
}
+
mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
@@ -1607,7 +1609,7 @@
if (mUseSmart90ForVideo) {
// This call is made each time SF wakes up and creates a new frame. It is part
// of video detection feature.
- mScheduler->updateFpsBasedOnNativeWindowApi();
+ mScheduler->updateFpsBasedOnContent();
}
if (performSetActiveConfig()) {
@@ -3115,6 +3117,7 @@
bool SurfaceFlinger::handlePageFlip()
{
+ ATRACE_CALL();
ALOGV("handlePageFlip");
nsecs_t latchTime = systemTime();
@@ -3140,6 +3143,7 @@
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.push_back(layer);
} else {
+ ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
}
} else {
@@ -3987,10 +3991,8 @@
buffer = s.buffer;
}
if (buffer) {
- if (layer->setBuffer(buffer)) {
+ if (layer->setBuffer(buffer, postTime, desiredPresentTime)) {
flags |= eTraversalNeeded;
- layer->setPostTime(postTime);
- layer->setDesiredPresentTime(desiredPresentTime);
}
}
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
@@ -4036,7 +4038,7 @@
if (metadata.has(METADATA_WINDOW_TYPE)) {
int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
if (windowType == 441731) {
- metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL
+ metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL);
primaryDisplayOnly = true;
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7dd7675..d8108c5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1109,7 +1109,7 @@
scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
// All configs are allowed if the set is empty.
- using DisplayConfigs = std::unordered_set<int32_t>;
+ using DisplayConfigs = std::set<int32_t>;
DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
std::mutex mActiveConfigLock;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 26d2c21..e425b2a 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -73,8 +73,10 @@
return std::make_unique<scheduler::impl::PhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) override {
- return std::make_unique<Scheduler>(callback);
+ std::unique_ptr<Scheduler> createScheduler(
+ std::function<void(bool)> callback,
+ const scheduler::RefreshRateConfigs& refreshRateConfig) override {
+ return std::make_unique<Scheduler>(callback, refreshRateConfig);
}
std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index fc1d0f8..c2bc808 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -71,7 +71,9 @@
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
- virtual std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) = 0;
+ virtual std::unique_ptr<Scheduler> createScheduler(
+ std::function<void(bool)> callback,
+ const scheduler::RefreshRateConfigs& refreshRateConfig) = 0;
virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ef3dfef..bebfa6c 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -125,7 +125,7 @@
}
void setupScheduler() {
- mScheduler = new TestableScheduler();
+ mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 9bf29a2..d83f1bd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -179,7 +179,7 @@
}
void DisplayTransactionTest::setupScheduler() {
- mScheduler = new TestableScheduler();
+ mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
mScheduler->mutableEventControlThread().reset(mEventControlThread);
mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 3fb1a6e..e51121f 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -7,6 +7,7 @@
#include <log/log.h>
#include <mutex>
+#include <thread>
#include "Scheduler/LayerHistory.h"
@@ -14,6 +15,7 @@
using testing::Return;
namespace android {
+namespace scheduler {
class LayerHistoryTest : public testing::Test {
public:
@@ -22,6 +24,8 @@
protected:
std::unique_ptr<LayerHistory> mLayerHistory;
+
+ static constexpr float MAX_REFRESH_RATE = 90.f;
};
LayerHistoryTest::LayerHistoryTest() {
@@ -30,145 +34,79 @@
LayerHistoryTest::~LayerHistoryTest() {}
namespace {
-TEST_F(LayerHistoryTest, simpleInsertAndGet) {
- mLayerHistory->insert("TestLayer", 0);
+TEST_F(LayerHistoryTest, oneLayer) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
- const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
- EXPECT_EQ(1, testMap.size());
- auto element = testMap.find("TestLayer");
- EXPECT_EQ("TestLayer", element->first);
- EXPECT_EQ(0, element->second);
+ mLayerHistory->insert(testLayer, 0);
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRate());
- // Testing accessing object at an empty container will return an empty map.
- const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
- EXPECT_EQ(0, emptyMap.size());
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ // This is still 0, because the layer is not considered recently active if it
+ // has been present in less than 10 frames.
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRate());
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ mLayerHistory->insert(testLayer, 0);
+ // This should be MAX_REFRESH_RATE as we have more than 10 samples
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
}
-TEST_F(LayerHistoryTest, multipleInserts) {
- mLayerHistory->insert("TestLayer0", 0);
- mLayerHistory->insert("TestLayer1", 1);
- mLayerHistory->insert("TestLayer2", 2);
- mLayerHistory->insert("TestLayer3", 3);
+TEST_F(LayerHistoryTest, explicitTimestamp) {
+ std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
+ mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
- const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0);
- // Because the counter was not incremented, all elements were inserted into the first
- // container.
- EXPECT_EQ(4, testMap.size());
- auto element = testMap.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
-
- element = testMap.find("TestLayer1");
- EXPECT_EQ("TestLayer1", element->first);
- EXPECT_EQ(1, element->second);
-
- element = testMap.find("TestLayer2");
- EXPECT_EQ("TestLayer2", element->first);
- EXPECT_EQ(2, element->second);
-
- element = testMap.find("TestLayer3");
- EXPECT_EQ("TestLayer3", element->first);
- EXPECT_EQ(3, element->second);
-
- // Testing accessing object at an empty container will return an empty map.
- const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1);
- EXPECT_EQ(0, emptyMap.size());
-}
-
-TEST_F(LayerHistoryTest, incrementingCounter) {
- mLayerHistory->insert("TestLayer0", 0);
- mLayerHistory->incrementCounter();
- mLayerHistory->insert("TestLayer1", 1);
- mLayerHistory->insert("TestLayer2", 2);
- mLayerHistory->incrementCounter();
- mLayerHistory->insert("TestLayer3", 3);
-
- // Because the counter was incremented, the elements were inserted into different
- // containers.
- // We expect the get method to access the slot at the current counter of the index
- // is 0.
- const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
- EXPECT_EQ(1, testMap1.size());
- auto element = testMap1.find("TestLayer3");
- EXPECT_EQ("TestLayer3", element->first);
- EXPECT_EQ(3, element->second);
-
- const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
- EXPECT_EQ(2, testMap2.size());
- element = testMap2.find("TestLayer1");
- EXPECT_EQ("TestLayer1", element->first);
- EXPECT_EQ(1, element->second);
- element = testMap2.find("TestLayer2");
- EXPECT_EQ("TestLayer2", element->first);
- EXPECT_EQ(2, element->second);
-
- const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
- EXPECT_EQ(1, testMap3.size());
- element = testMap3.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
-
- // Testing accessing object at an empty container will return an empty map.
- const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
- EXPECT_EQ(0, emptyMap.size());
-}
-
-TEST_F(LayerHistoryTest, clearTheMap) {
- mLayerHistory->insert("TestLayer0", 0);
-
- const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
- EXPECT_EQ(1, testMap1.size());
- auto element = testMap1.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
-
- mLayerHistory->incrementCounter();
- // The array currently contains 30 elements.
- for (int i = 1; i < 30; ++i) {
- mLayerHistory->insert("TestLayer0", i);
- mLayerHistory->incrementCounter();
- }
- // Expect the map to be cleared.
- const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(0);
- EXPECT_EQ(0, testMap2.size());
-
- mLayerHistory->insert("TestLayer30", 30);
- const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(0);
- element = testMap3.find("TestLayer30");
- EXPECT_EQ("TestLayer30", element->first);
- EXPECT_EQ(30, element->second);
- // The original element in this location does not exist anymore.
- element = testMap3.find("TestLayer0");
- EXPECT_EQ(testMap3.end(), element);
-}
-
-TEST_F(LayerHistoryTest, testingGet) {
- // The array currently contains 30 elements.
- for (int i = 0; i < 30; ++i) {
- const auto name = "TestLayer" + std::to_string(i);
- mLayerHistory->insert(name, i);
- mLayerHistory->incrementCounter();
+ nsecs_t startTime = systemTime();
+ for (int i = 0; i < 31; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333));
}
- // The counter should be set to 0, and the map at 0 should be cleared.
- const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
- EXPECT_EQ(0, testMap1.size());
+ EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRate());
+}
- // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE
- const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3);
- EXPECT_EQ(1, testMap2.size());
- auto element = testMap2.find("TestLayer27");
- EXPECT_EQ("TestLayer27", element->first);
- EXPECT_EQ(27, element->second);
+TEST_F(LayerHistoryTest, multipleLayers) {
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer =
+ mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
+ mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
+ mLayerHistory->createLayer("TestLayer2", MAX_REFRESH_RATE);
- // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first,
- // so requesting element 40 would be the same as requesting element 10.
- const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40);
- EXPECT_EQ(1, testMap3.size());
- element = testMap3.find("TestLayer20");
- EXPECT_EQ("TestLayer20", element->first);
- EXPECT_EQ(20, element->second);
+ nsecs_t startTime = systemTime();
+ for (int i = 0; i < 10; i++) {
+ mLayerHistory->insert(testLayer, 0);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+
+ startTime = systemTime();
+ for (int i = 0; i < 10; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333));
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+
+ for (int i = 10; i < 30; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333));
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+
+ // This frame is only around for 9 occurrences, so it doesn't throw
+ // anything off.
+ for (int i = 0; i < 9; i++) {
+ mLayerHistory->insert(testLayer2, 0);
+ }
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate());
+ // After 100 ms frames become obsolete.
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ // Insert the 31st frame.
+ mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333));
+ EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRate());
}
} // namespace
+} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ec76538..b960bdf 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -33,14 +33,17 @@
MOCK_METHOD0(requestNextVsync, void());
};
+ scheduler::RefreshRateConfigs mRefreshRateConfigs;
+
/**
* This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
* the same.
*/
class MockScheduler : public android::Scheduler {
public:
- MockScheduler(std::unique_ptr<EventThread> eventThread)
- : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {}
+ MockScheduler(const scheduler::RefreshRateConfigs& refreshRateConfigs,
+ std::unique_ptr<EventThread> eventThread)
+ : Scheduler([](bool) {}, refreshRateConfigs), mEventThread(std::move(eventThread)) {}
std::unique_ptr<EventThread> makeEventThread(
const char* /* connectionName */, DispSync* /* dispSync */,
@@ -71,7 +74,7 @@
std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
mEventThread = eventThread.get();
- mScheduler = std::make_unique<MockScheduler>(std::move(eventThread));
+ mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread));
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
mEventThreadConnection = new MockEventThreadConnection(mEventThread);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index dcbf973..1c397d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,13 +19,15 @@
#include <gmock/gmock.h>
#include "Scheduler/EventThread.h"
+#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/Scheduler.h"
namespace android {
class TestableScheduler : public Scheduler {
public:
- TestableScheduler() : Scheduler([](bool) {}) {}
+ TestableScheduler(const scheduler::RefreshRateConfigs& refreshRateConfig)
+ : Scheduler([](bool) {}, refreshRateConfig) {}
// Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection
// and adds it to the list of connectins. Returns the ConnectionHandle for the
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6bbcae3..df14e7e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -86,7 +86,8 @@
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override {
+ std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
+ const scheduler::RefreshRateConfigs&) override {
// TODO: Use test-fixture controlled factory
return nullptr;
}
@@ -340,6 +341,7 @@
auto& mutableScheduler() { return mFlinger->mScheduler; }
auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
+ auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does