summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/Scheduler/LayerInfo.h
blob: 820624b9059f5e66e3bc2b86f6ddea63544058d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
 * 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 <utils/Timers.h>

#include <chrono>
#include <deque>

#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<float> 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<nsecs_t> 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