summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/TimeStats/TimeStats.h
blob: 9e70684023767b4915e7de1a03e02f832c5d9837 (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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/*
 * Copyright 2018 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 <cstdint>

#include <../Fps.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <gui/JankInfo.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
#include <utils/String16.h>
#include <utils/Vector.h>

#include <deque>
#include <mutex>
#include <optional>
#include <unordered_map>
#include <variant>

using namespace android::surfaceflinger;

namespace android {

class TimeStats {
public:
    using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;

    virtual ~TimeStats() = default;

    // Process a pull request from statsd.
    virtual bool onPullAtom(const int atomId, std::string* pulledData);

    virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
    virtual bool isEnabled() = 0;
    virtual std::string miniDump() = 0;

    virtual void incrementTotalFrames() = 0;
    virtual void incrementMissedFrames() = 0;
    virtual void incrementClientCompositionFrames() = 0;
    virtual void incrementClientCompositionReusedFrames() = 0;
    // Increments the number of times the display refresh rate changed.
    virtual void incrementRefreshRateSwitches() = 0;
    // Increments the number of changes in composition strategy
    // The intention is to reflect the number of changes between hwc and gpu
    // composition, where "gpu composition" may also include mixed composition.
    virtual void incrementCompositionStrategyChanges() = 0;
    // Records the most up-to-date count of display event connections.
    // The stored count will be the maximum ever recoded.
    virtual void recordDisplayEventConnectionCount(int32_t count) = 0;

    // Records the start and end times for a frame.
    // The start time is the same as the beginning of a SurfaceFlinger
    // invalidate message.
    // The end time corresponds to when SurfaceFlinger finishes submitting the
    // request to HWC to present a frame.
    virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
    // Records the start time and end times for when RenderEngine begins work.
    // The start time corresponds to the beginning of RenderEngine::drawLayers.
    // The end time corresponds to when RenderEngine finishes rendering.
    virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
    // Same as above, but passes in a fence representing the end time.
    virtual void recordRenderEngineDuration(nsecs_t startTime,
                                            const std::shared_ptr<FenceTime>& readyFence) = 0;

    virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                             uid_t uid, nsecs_t postTime, int32_t gameMode) = 0;
    virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
    // Reasons why latching a particular buffer may be skipped
    enum class LatchSkipReason {
        // If the acquire fence did not fire on some devices we skip latching
        // the buffer until the fence fires.
        LateAcquire,
    };
    // Increments the counter of skipped latch buffers.
    virtual void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) = 0;
    // Increments the counter of bad desired present times for this layer.
    // Bad desired present times are "implausible" and cause SurfaceFlinger to
    // latch a buffer immediately to avoid stalling.
    virtual void incrementBadDesiredPresent(int32_t layerId) = 0;
    virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
    virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
    virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& acquireFence) = 0;
    // SetPresent{Time, Fence} are not expected to be called in the critical
    // rendering path, as they flush prior fences if those fences have fired.
    virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
                                Fps displayRefreshRate, std::optional<Fps> renderRate,
                                SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
    virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence,
                                 Fps displayRefreshRate, std::optional<Fps> renderRate,
                                 SetFrameRateVote frameRateVote, int32_t gameMode) = 0;

    // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
    // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
    // infrastructure responsible for computing jank in the system, this is expected to be called
    // from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there
    // are no jank reasons, then total frames are incremented but jank is not, for accurate
    // accounting of janky frames.
    // displayDeadlineDelta, displayPresentJitter, and appDeadlineDelta are also provided in order
    // to provide contextual information about a janky frame. These values may only be uploaded if
    // there was an associated valid jank reason, and they must be positive. When these frame counts
    // are incremented, these are also aggregated into a global reporting packet to help with data
    // validation and assessing of overall device health.
    struct JankyFramesInfo {
        Fps refreshRate;
        std::optional<Fps> renderRate;
        uid_t uid = 0;
        std::string layerName;
        int32_t gameMode = 0;
        int32_t reasons = 0;
        nsecs_t displayDeadlineDelta = 0;
        nsecs_t displayPresentJitter = 0;
        nsecs_t appDeadlineDelta = 0;

        bool operator==(const JankyFramesInfo& o) const {
            return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) &&
                    ((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
                     (renderRate != std::nullopt && o.renderRate != std::nullopt &&
                      Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
                    uid == o.uid && layerName == o.layerName && gameMode == o.gameMode &&
                    reasons == o.reasons && displayDeadlineDelta == o.displayDeadlineDelta &&
                    displayPresentJitter == o.displayPresentJitter &&
                    appDeadlineDelta == o.appDeadlineDelta;
        }

        friend std::ostream& operator<<(std::ostream& os, const JankyFramesInfo& info) {
            os << "JankyFramesInfo {";
            os << "\n    .refreshRate = " << info.refreshRate;
            os << "\n    .renderRate = "
               << (info.renderRate ? to_string(*info.renderRate) : "nullopt");
            os << "\n    .uid = " << info.uid;
            os << "\n    .layerName = " << info.layerName;
            os << "\n    .reasons = " << info.reasons;
            os << "\n    .displayDeadlineDelta = " << info.displayDeadlineDelta;
            os << "\n    .displayPresentJitter = " << info.displayPresentJitter;
            os << "\n    .appDeadlineDelta = " << info.appDeadlineDelta;
            return os << "\n}";
        }
    };

    virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0;
    // Clean up the layer record
    virtual void onDestroy(int32_t layerId) = 0;
    // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
    virtual void removeTimeRecord(int32_t layerId, uint64_t frameNumber) = 0;

    virtual void setPowerMode(
            hardware::graphics::composer::V2_4::IComposerClient::PowerMode powerMode) = 0;
    // Source of truth is RefrehRateStats.
    virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0;
    virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0;
};

namespace impl {

class TimeStats : public android::TimeStats {
    using PowerMode = android::hardware::graphics::composer::V2_4::IComposerClient::PowerMode;

    struct FrameTime {
        uint64_t frameNumber = 0;
        nsecs_t postTime = 0;
        nsecs_t latchTime = 0;
        nsecs_t acquireTime = 0;
        nsecs_t desiredTime = 0;
        nsecs_t presentTime = 0;
    };

    struct TimeRecord {
        bool ready = false;
        FrameTime frameTime;
        std::shared_ptr<FenceTime> acquireFence;
        std::shared_ptr<FenceTime> presentFence;
    };

    struct LayerRecord {
        uid_t uid;
        std::string layerName;
        int32_t gameMode = 0;
        // This is the index in timeRecords, at which the timestamps for that
        // specific frame are still not fully received. This is not waiting for
        // fences to signal, but rather waiting to receive those fences/timestamps.
        int32_t waitData = -1;
        uint32_t droppedFrames = 0;
        uint32_t lateAcquireFrames = 0;
        uint32_t badDesiredPresentFrames = 0;
        TimeRecord prevTimeRecord;
        std::deque<TimeRecord> timeRecords;
    };

    struct PowerTime {
        PowerMode powerMode = PowerMode::OFF;
        nsecs_t prevTime = 0;
    };

    struct RenderEngineDuration {
        nsecs_t startTime;
        std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
    };

    struct GlobalRecord {
        nsecs_t prevPresentTime = 0;
        std::deque<std::shared_ptr<FenceTime>> presentFences;
        std::deque<RenderEngineDuration> renderEngineDurations;
    };

public:
    TimeStats();
    // For testing only for injecting custom dependencies.
    TimeStats(std::optional<size_t> maxPulledLayers,
              std::optional<size_t> maxPulledHistogramBuckets);

    bool onPullAtom(const int atomId, std::string* pulledData) override;
    void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
    bool isEnabled() override;
    std::string miniDump() override;

    void incrementTotalFrames() override;
    void incrementMissedFrames() override;
    void incrementClientCompositionFrames() override;
    void incrementClientCompositionReusedFrames() override;
    void incrementRefreshRateSwitches() override;
    void incrementCompositionStrategyChanges() override;
    void recordDisplayEventConnectionCount(int32_t count) override;

    void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
    void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
    void recordRenderEngineDuration(nsecs_t startTime,
                                    const std::shared_ptr<FenceTime>& readyFence) override;

    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
                     nsecs_t postTime, int32_t gameMode) override;
    void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
    void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
    void incrementBadDesiredPresent(int32_t layerId) override;
    void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
    void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
    void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                         const std::shared_ptr<FenceTime>& acquireFence) override;
    void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
                        Fps displayRefreshRate, std::optional<Fps> renderRate,
                        SetFrameRateVote frameRateVote, int32_t gameMode) override;
    void setPresentFence(int32_t layerId, uint64_t frameNumber,
                         const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
                         std::optional<Fps> renderRate, SetFrameRateVote frameRateVote,
                         int32_t gameMode) override;

    void incrementJankyFrames(const JankyFramesInfo& info) override;
    // Clean up the layer record
    void onDestroy(int32_t layerId) override;
    // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
    void removeTimeRecord(int32_t layerId, uint64_t frameNumber) override;

    void setPowerMode(
            hardware::graphics::composer::V2_4::IComposerClient::PowerMode powerMode) override;
    // Source of truth is RefrehRateStats.
    void recordRefreshRate(uint32_t fps, nsecs_t duration) override;
    void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override;

    static const size_t MAX_NUM_TIME_RECORDS = 64;

private:
    bool populateGlobalAtom(std::string* pulledData);
    bool populateLayerAtom(std::string* pulledData);
    bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
    void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
                                            std::optional<Fps> renderRate,
                                            SetFrameRateVote frameRateVote, int32_t gameMode);
    void flushPowerTimeLocked();
    void flushAvailableGlobalRecordsToStatsLocked();
    bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode);

    void enable();
    void disable();
    void clearAll();
    void clearGlobalLocked();
    void clearLayersLocked();
    void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);

    std::atomic<bool> mEnabled = false;
    std::mutex mMutex;
    TimeStatsHelper::TimeStatsGlobal mTimeStats;
    // Hashmap for LayerRecord with layerId as the hash key
    std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
    PowerTime mPowerTime;
    GlobalRecord mGlobalRecord;

    static const size_t MAX_NUM_LAYER_RECORDS = 200;

    static const size_t REFRESH_RATE_BUCKET_WIDTH = 30;
    static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH;
    static const size_t MAX_NUM_LAYER_STATS = 200;
    static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS;
    size_t mMaxPulledLayers = MAX_NUM_PULLED_LAYERS;
    size_t mMaxPulledHistogramBuckets = 6;
};

} // namespace impl

} // namespace android