| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2019 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #pragma once | 
 | 18 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 19 | #include <utils/Timers.h> | 
 | 20 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 21 | #include <chrono> | 
 | 22 | #include <deque> | 
 | 23 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 24 | #include "SchedulerUtils.h" | 
 | 25 |  | 
 | 26 | namespace android { | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 27 |  | 
 | 28 | class Layer; | 
 | 29 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 30 | namespace scheduler { | 
 | 31 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 32 | using namespace std::chrono_literals; | 
 | 33 |  | 
 | 34 | // Maximum period between presents for a layer to be considered active. | 
 | 35 | constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms; | 
 | 36 |  | 
 | 37 | // Earliest present time for a layer to be considered active. | 
 | 38 | constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) { | 
 | 39 |     return now - MAX_ACTIVE_LAYER_PERIOD_NS.count(); | 
 | 40 | } | 
 | 41 |  | 
 | 42 | // Stores history of present times and refresh rates for a layer. | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 43 | class LayerInfo { | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 44 |     // Layer is considered frequent if the earliest value in the window of most recent present times | 
 | 45 |     // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in | 
 | 46 |     // favor of a low refresh rate. | 
 | 47 |     static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3; | 
 | 48 |     static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms; | 
 | 49 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 50 |     /** | 
 | 51 |      * Struct that keeps the information about the refresh rate for last | 
 | 52 |      * HISTORY_SIZE frames. This is used to better determine the refresh rate | 
 | 53 |      * for individual layers. | 
 | 54 |      */ | 
 | 55 |     class RefreshRateHistory { | 
 | 56 |     public: | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 57 |         explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {} | 
 | 58 |  | 
 | 59 |         void insertRefreshRate(float refreshRate) { | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 60 |             mElements.push_back(refreshRate); | 
 | 61 |             if (mElements.size() > HISTORY_SIZE) { | 
 | 62 |                 mElements.pop_front(); | 
 | 63 |             } | 
 | 64 |         } | 
 | 65 |  | 
 | 66 |         float getRefreshRateAvg() const { | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 67 |             return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements); | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 68 |         } | 
| Ady Abraham | 187d2d8 | 2019-07-10 16:18:48 -0700 | [diff] [blame] | 69 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 70 |         void clearHistory() { mElements.clear(); } | 
 | 71 |  | 
 | 72 |     private: | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 73 |         const float mHighRefreshRate; | 
 | 74 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 75 |         static constexpr size_t HISTORY_SIZE = 30; | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 76 |         std::deque<float> mElements; | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 77 |     }; | 
 | 78 |  | 
 | 79 |     /** | 
 | 80 |      * Struct that keeps the information about the present time for last | 
 | 81 |      * HISTORY_SIZE frames. This is used to better determine whether the given layer | 
 | 82 |      * is still relevant and it's refresh rate should be considered. | 
 | 83 |      */ | 
 | 84 |     class PresentTimeHistory { | 
 | 85 |     public: | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 86 |         static constexpr size_t HISTORY_SIZE = 90; | 
 | 87 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 88 |         void insertPresentTime(nsecs_t presentTime) { | 
 | 89 |             mElements.push_back(presentTime); | 
 | 90 |             if (mElements.size() > HISTORY_SIZE) { | 
 | 91 |                 mElements.pop_front(); | 
 | 92 |             } | 
 | 93 |         } | 
 | 94 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 95 |         // Returns whether the earliest present time is within the active threshold. | 
 | 96 |         bool isRecentlyActive(nsecs_t now) const { | 
| Ady Abraham | 616b832 | 2019-06-12 13:30:07 -0700 | [diff] [blame] | 97 |             if (mElements.size() < 2) { | 
 | 98 |                 return false; | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 99 |             } | 
| Ady Abraham | 616b832 | 2019-06-12 13:30:07 -0700 | [diff] [blame] | 100 |  | 
| Ady Abraham | 0ccd79b | 2020-06-10 10:11:17 -0700 | [diff] [blame] | 101 |             // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 102 |             if (mElements.size() < HISTORY_SIZE && | 
| Ady Abraham | 0ccd79b | 2020-06-10 10:11:17 -0700 | [diff] [blame] | 103 |                 mElements.back() - mElements.front() < HISTORY_DURATION.count()) { | 
| Ady Abraham | 616b832 | 2019-06-12 13:30:07 -0700 | [diff] [blame] | 104 |                 return false; | 
 | 105 |             } | 
 | 106 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 107 |             return mElements.back() >= getActiveLayerThreshold(now); | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 108 |         } | 
 | 109 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 110 |         bool isFrequent(nsecs_t now) const { | 
 | 111 |             // Assume layer is infrequent if too few present times have been recorded. | 
 | 112 |             if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) { | 
| Ana Krulec | ad083c4 | 2019-06-26 16:28:08 -0700 | [diff] [blame] | 113 |                 return false; | 
 | 114 |             } | 
 | 115 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 116 |             // Layer is frequent if the earliest value in the window of most recent present times is | 
 | 117 |             // within threshold. | 
 | 118 |             const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE; | 
 | 119 |             const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count(); | 
 | 120 |             return *it >= threshold; | 
| Ana Krulec | ad083c4 | 2019-06-26 16:28:08 -0700 | [diff] [blame] | 121 |         } | 
 | 122 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 123 |         void clearHistory() { mElements.clear(); } | 
 | 124 |  | 
 | 125 |     private: | 
 | 126 |         std::deque<nsecs_t> mElements; | 
| Ady Abraham | 0ccd79b | 2020-06-10 10:11:17 -0700 | [diff] [blame] | 127 |         static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s; | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 128 |     }; | 
 | 129 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 130 |     friend class LayerHistoryTest; | 
 | 131 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 132 | public: | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 133 |     LayerInfo(float lowRefreshRate, float highRefreshRate); | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 134 |  | 
 | 135 |     LayerInfo(const LayerInfo&) = delete; | 
 | 136 |     LayerInfo& operator=(const LayerInfo&) = delete; | 
 | 137 |  | 
 | 138 |     // Records the last requested oresent time. It also stores information about when | 
 | 139 |     // the layer was last updated. If the present time is farther in the future than the | 
 | 140 |     // updated time, the updated time is the present time. | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 141 |     void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now); | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 142 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 143 |     bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); } | 
 | 144 |     bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); } | 
| Ady Abraham | a315ce7 | 2019-04-24 14:35:20 -0700 | [diff] [blame] | 145 |  | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 146 |     float getRefreshRate(nsecs_t now) const { | 
 | 147 |         return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate; | 
| Ady Abraham | a315ce7 | 2019-04-24 14:35:20 -0700 | [diff] [blame] | 148 |     } | 
 | 149 |  | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 150 |     // Return the last updated time. If the present time is farther in the future than the | 
 | 151 |     // updated time, the updated time is the present time. | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 152 |     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 153 |  | 
 | 154 |     void clearHistory() { | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 155 |         mRefreshRateHistory.clearHistory(); | 
 | 156 |         mPresentTimeHistory.clearHistory(); | 
 | 157 |     } | 
 | 158 |  | 
 | 159 | private: | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 160 |     const float mLowRefreshRate; | 
 | 161 |     const float mHighRefreshRate; | 
 | 162 |  | 
 | 163 |     nsecs_t mLastUpdatedTime = 0; | 
 | 164 |     nsecs_t mLastPresentTime = 0; | 
 | 165 |     RefreshRateHistory mRefreshRateHistory{mHighRefreshRate}; | 
 | 166 |     PresentTimeHistory mPresentTimeHistory; | 
| Ady Abraham | 09bd392 | 2019-04-08 10:44:56 -0700 | [diff] [blame] | 167 | }; | 
 | 168 |  | 
 | 169 | } // namespace scheduler | 
| Dominik Laskowski | f7a09ed | 2019-10-07 13:54:18 -0700 | [diff] [blame] | 170 | } // namespace android |