diff options
26 files changed, 603 insertions, 261 deletions
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 776c6e6c11..e2261360b6 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -190,6 +190,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, 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 52c68df814..9080d29194 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -147,6 +147,7 @@ filegroup { "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 1b2b180c52..a08ae8c7e6 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -413,6 +413,7 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) // 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 b623991742..ff5f271960 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -398,8 +398,8 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { // 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 64dfdc3d5e..2cbb917478 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -209,7 +209,8 @@ bool BufferStateLayer::setFrame(const Rect& frame) { 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 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) { 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 @@ FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) c 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 668830a3fe..864a15db1f 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -63,7 +63,8 @@ public: 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 @@ public: 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 d88702cc02..8736fa58d3 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -70,7 +70,10 @@ using base::StringAppendF; 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 @@ Layer::Layer(const LayerCreationArgs& args) mFrameEventHistory.initializeCompositorTiming(compositorTiming); mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval); + mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType); + mFlinger->onLayerCreated(); } @@ -1297,6 +1302,7 @@ void Layer::miniDumpHeader(std::string& result) { 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 @@ void Layer::miniDump(std::string& result, const sp<DisplayDevice>& displayDevice } 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 8348ce1f38..f4545e0538 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -311,7 +311,10 @@ public: 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 @@ public: } 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 @@ protected: // 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 d5ccbe186b..8e36ae9dc0 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 { + +std::atomic<int64_t> LayerHistory::sNextId = 0; -LayerHistory::LayerHistory() {} +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::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); + } +} + +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); } -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(); +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; } -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::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 c6fab07c87..39061e7fdf 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 0000000000..95d7d31564 --- /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 0000000000..1970a47701 --- /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 5874066aed..eb9ae35cec 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -54,7 +54,7 @@ public: 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 0063c8a202..b1c39ef906 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 @@ using namespace android::sysprop; 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 @@ void Scheduler::dumpPrimaryDispSync(std::string& result) const { 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 @@ std::string Scheduler::doDump() { 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; } - changeRefreshRate(refreshRateType, configEvent); + + // 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; + } + } + + return currRefreshRateType; } void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 73896d5763..1318fbb9c8 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -90,7 +90,8 @@ public: 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 @@ public: 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 @@ private: // 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 @@ private: // 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 @@ private: 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 @@ private: // 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 9e6e8c7af6..a935cac1eb 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 @@ static constexpr size_t ARRAY_SIZE = 30; // 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 8a45d69275..bd3f156b82 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -605,7 +605,8 @@ void SurfaceFlinger::init() { 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 @@ void SurfaceFlinger::init() { setRefreshRateTo(type, event); }); } + mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId())); mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId())); @@ -1607,7 +1609,7 @@ void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { 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 @@ void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Re bool SurfaceFlinger::handlePageFlip() { + ATRACE_CALL(); ALOGV("handlePageFlip"); nsecs_t latchTime = systemTime(); @@ -3140,6 +3143,7 @@ bool SurfaceFlinger::handlePageFlip() if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.push_back(layer); } else { + ATRACE_NAME("!layer->shouldPresentNow()"); layer->useEmptyDamage(); } } else { @@ -3987,10 +3991,8 @@ uint32_t SurfaceFlinger::setClientStateLocked( 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 @@ status_t SurfaceFlinger::createLayer( 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 7dd7675c81..d8108c5566 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1109,7 +1109,7 @@ private: 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 26d2c21848..e425b2a953 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp @@ -73,8 +73,10 @@ sp<SurfaceFlinger> createSurfaceFlinger() { 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 fc1d0f8b7f..c2bc808c8c 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -71,7 +71,9 @@ public: 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 ef3dfef38f..bebfa6c7ee 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -125,7 +125,7 @@ public: } 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 9bf29a212f..d83f1bdf4f 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -179,7 +179,7 @@ DisplayTransactionTest::~DisplayTransactionTest() { } 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 3fb1a6e62a..e51121fcf7 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::_; using testing::Return; namespace android { +namespace scheduler { class LayerHistoryTest : public testing::Test { public: @@ -22,6 +24,8 @@ public: protected: std::unique_ptr<LayerHistory> mLayerHistory; + + static constexpr float MAX_REFRESH_RATE = 90.f; }; LayerHistoryTest::LayerHistoryTest() { @@ -30,145 +34,79 @@ LayerHistoryTest::LayerHistoryTest() { LayerHistoryTest::~LayerHistoryTest() {} namespace { -TEST_F(LayerHistoryTest, simpleInsertAndGet) { - mLayerHistory->insert("TestLayer", 0); - - 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); - - // 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, oneLayer) { + std::unique_ptr<LayerHistory::LayerHandle> testLayer = + mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE); + + mLayerHistory->insert(testLayer, 0); + EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRate()); + + 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); - - 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, explicitTimestamp) { + std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer = + mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE); -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()); -} + nsecs_t startTime = systemTime(); + for (int i = 0; i < 31; i++) { + mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333)); + } -TEST_F(LayerHistoryTest, clearTheMap) { - mLayerHistory->insert("TestLayer0", 0); + EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRate()); +} - 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); +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); + + nsecs_t startTime = systemTime(); + for (int i = 0; i < 10; i++) { + mLayerHistory->insert(testLayer, 0); + } + EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate()); - mLayerHistory->incrementCounter(); - // The array currently contains 30 elements. - for (int i = 1; i < 30; ++i) { - mLayerHistory->insert("TestLayer0", i); - mLayerHistory->incrementCounter(); + startTime = systemTime(); + for (int i = 0; i < 10; i++) { + mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333)); } - // 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); -} + EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate()); -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(); + for (int i = 10; i < 30; i++) { + mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333)); } + EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRate()); - // 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()); - - // 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); - - // 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); + // 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 ec765382b8..b960bdf3fd 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -33,14 +33,17 @@ protected: 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 @@ SchedulerTest::SchedulerTest() { 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 dcbf973d2c..1c397d8443 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 6bbcae3a9d..df14e7e45d 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -86,7 +86,8 @@ public: 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 @@ public: 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 |