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 
19 #include <utils/Timers.h>
20 
21 #include <chrono>
22 #include <deque>
23 
24 #include "SchedulerUtils.h"
25 
26 namespace android {
27 
28 class Layer;
29 
30 namespace scheduler {
31 
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.
getActiveLayerThreshold(nsecs_t now)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.
43 class LayerInfo {
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 
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:
RefreshRateHistory(float highRefreshRate)57         explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
58 
insertRefreshRate(float refreshRate)59         void insertRefreshRate(float refreshRate) {
60             mElements.push_back(refreshRate);
61             if (mElements.size() > HISTORY_SIZE) {
62                 mElements.pop_front();
63             }
64         }
65 
getRefreshRateAvg()66         float getRefreshRateAvg() const {
67             return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
68         }
69 
clearHistory()70         void clearHistory() { mElements.clear(); }
71 
72     private:
73         const float mHighRefreshRate;
74 
75         static constexpr size_t HISTORY_SIZE = 30;
76         std::deque<float> mElements;
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:
86         static constexpr size_t HISTORY_SIZE = 90;
87 
insertPresentTime(nsecs_t presentTime)88         void insertPresentTime(nsecs_t presentTime) {
89             mElements.push_back(presentTime);
90             if (mElements.size() > HISTORY_SIZE) {
91                 mElements.pop_front();
92             }
93         }
94 
95         // Returns whether the earliest present time is within the active threshold.
isRecentlyActive(nsecs_t now)96         bool isRecentlyActive(nsecs_t now) const {
97             if (mElements.size() < 2) {
98                 return false;
99             }
100 
101             // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
102             if (mElements.size() < HISTORY_SIZE &&
103                 mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
104                 return false;
105             }
106 
107             return mElements.back() >= getActiveLayerThreshold(now);
108         }
109 
isFrequent(nsecs_t now)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) {
113                 return false;
114             }
115 
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;
121         }
122 
clearHistory()123         void clearHistory() { mElements.clear(); }
124 
125     private:
126         std::deque<nsecs_t> mElements;
127         static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
128     };
129 
130     friend class LayerHistoryTest;
131 
132 public:
133     LayerInfo(float lowRefreshRate, float highRefreshRate);
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.
141     void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
142 
isRecentlyActive(nsecs_t now)143     bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
isFrequent(nsecs_t now)144     bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
145 
getRefreshRate(nsecs_t now)146     float getRefreshRate(nsecs_t now) const {
147         return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
148     }
149 
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.
getLastUpdatedTime()152     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
153 
clearHistory()154     void clearHistory() {
155         mRefreshRateHistory.clearHistory();
156         mPresentTimeHistory.clearHistory();
157     }
158 
159 private:
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;
167 };
168 
169 } // namespace scheduler
170 } // namespace android
171