/* * 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 #include #include #include "SchedulerUtils.h" namespace android { class Layer; namespace scheduler { using namespace std::chrono_literals; // Maximum period between presents for a layer to be considered active. constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms; // Earliest present time for a layer to be considered active. constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) { return now - MAX_ACTIVE_LAYER_PERIOD_NS.count(); } // Stores history of present times and refresh rates for a layer. class LayerInfo { // Layer is considered frequent if the earliest value in the window of most recent present times // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3; static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms; /** * 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(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {} void insertRefreshRate(float refreshRate) { mElements.push_back(refreshRate); if (mElements.size() > HISTORY_SIZE) { mElements.pop_front(); } } float getRefreshRateAvg() const { return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements); } void clearHistory() { mElements.clear(); } private: const float mHighRefreshRate; static constexpr size_t HISTORY_SIZE = 30; std::deque mElements; }; /** * 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: static constexpr size_t HISTORY_SIZE = 90; void insertPresentTime(nsecs_t presentTime) { mElements.push_back(presentTime); if (mElements.size() > HISTORY_SIZE) { mElements.pop_front(); } } // Returns whether the earliest present time is within the active threshold. bool isRecentlyActive(nsecs_t now) const { if (mElements.size() < 2) { return false; } // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates if (mElements.size() < HISTORY_SIZE && mElements.back() - mElements.front() < HISTORY_DURATION.count()) { return false; } return mElements.back() >= getActiveLayerThreshold(now); } bool isFrequent(nsecs_t now) const { // Assume layer is infrequent if too few present times have been recorded. if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) { return false; } // Layer is frequent if the earliest value in the window of most recent present times is // within threshold. const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE; const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count(); return *it >= threshold; } void clearHistory() { mElements.clear(); } private: std::deque mElements; static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s; }; friend class LayerHistoryTest; public: LayerInfo(float lowRefreshRate, float highRefreshRate); 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, nsecs_t now); bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); } bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); } float getRefreshRate(nsecs_t now) const { return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate; } // Return the last updated time. If the present time is farther in the future than the // updated time, the updated time is the present time. nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } void clearHistory() { mRefreshRateHistory.clearHistory(); mPresentTimeHistory.clearHistory(); } private: const float mLowRefreshRate; const float mHighRefreshRate; nsecs_t mLastUpdatedTime = 0; nsecs_t mLastPresentTime = 0; RefreshRateHistory mRefreshRateHistory{mHighRefreshRate}; PresentTimeHistory mPresentTimeHistory; }; } // namespace scheduler } // namespace android