1 /*
2  * Copyright (C) 2018 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 #pragma once
17 
18 #include <gui/LayerMetadata.h>
19 #include <timestatsproto/TimeStatsProtoHeader.h>
20 #include <utils/Timers.h>
21 
22 #include <optional>
23 #include <string>
24 #include <unordered_map>
25 #include <vector>
26 
27 using android::gui::GameMode;
28 using android::gui::LayerMetadata;
29 
30 namespace android {
31 namespace surfaceflinger {
32 
33 class TimeStatsHelper {
34 public:
35     class Histogram {
36     public:
37         // Key is the delta time between timestamps
38         // Value is the number of appearances of that delta
39         std::unordered_map<int32_t, int32_t> hist;
40 
41         void insert(int32_t delta);
42         int64_t totalTime() const;
43         float averageTime() const;
44         std::string toString() const;
45     };
46 
47     struct JankPayload {
48         // note that transactions are counted for these frames.
49         int32_t totalFrames = 0;
50         int32_t totalJankyFrames = 0;
51         int32_t totalSFLongCpu = 0;
52         int32_t totalSFLongGpu = 0;
53         int32_t totalSFUnattributed = 0;
54         int32_t totalAppUnattributed = 0;
55         int32_t totalSFScheduling = 0;
56         int32_t totalSFPredictionError = 0;
57         int32_t totalAppBufferStuffing = 0;
58 
59         std::string toString() const;
60     };
61 
62     struct SetFrameRateVote {
63         float frameRate = 0;
64 
65         // Needs to be in sync with atoms.proto
66         enum class FrameRateCompatibility {
67             Undefined = 0,
68             Default = 1,
69             ExactOrMultiple = 2,
70 
71             ftl_last = ExactOrMultiple
72         } frameRateCompatibility = FrameRateCompatibility::Undefined;
73 
74         // Needs to be in sync with atoms.proto
75         enum class Seamlessness {
76             Undefined = 0,
77             ShouldBeSeamless = 1,
78             NotRequired = 2,
79 
80             ftl_last = NotRequired
81         } seamlessness = Seamlessness::Undefined;
82 
83         std::string toString() const;
84     };
85 
86     class TimeStatsLayer {
87     public:
88         uid_t uid;
89         std::string layerName;
90         std::string packageName;
91         int32_t displayRefreshRateBucket = 0;
92         int32_t renderRateBucket = 0;
93         GameMode gameMode = GameMode::Unsupported;
94         int32_t totalFrames = 0;
95         int32_t droppedFrames = 0;
96         int32_t lateAcquireFrames = 0;
97         int32_t badDesiredPresentFrames = 0;
98         JankPayload jankPayload;
99         SetFrameRateVote setFrameRateVote;
100         std::unordered_map<std::string, Histogram> deltas;
101 
102         std::string toString() const;
103         SFTimeStatsLayerProto toProto() const;
104     };
105 
106     // Lifted from SkiaGLRenderEngine's LinearEffect class.
107     // Which in turn was inspired by art/runtime/class_linker.cc
108     // Also this is what boost:hash_combine does so this is a pretty good hash.
HashCombine(size_t seed,size_t val)109     static size_t HashCombine(size_t seed, size_t val) {
110         return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
111     }
112 
113     struct TimelineStatsKey {
114         int32_t displayRefreshRateBucket = 0;
115         int32_t renderRateBucket = 0;
116 
117         struct Hasher {
operatorTimelineStatsKey::Hasher118             size_t operator()(const TimelineStatsKey& key) const {
119                 size_t result = std::hash<int32_t>{}(key.displayRefreshRateBucket);
120                 return HashCombine(result, std::hash<int32_t>{}(key.renderRateBucket));
121             }
122         };
123 
124         bool operator==(const TimelineStatsKey& o) const {
125             return displayRefreshRateBucket == o.displayRefreshRateBucket &&
126                     renderRateBucket == o.renderRateBucket;
127         }
128     };
129 
130     struct LayerStatsKey {
131         uid_t uid = 0;
132         std::string layerName;
133         GameMode gameMode = GameMode::Unsupported;
134 
135         struct Hasher {
operatorLayerStatsKey::Hasher136             size_t operator()(const LayerStatsKey& key) const {
137                 size_t uidHash = std::hash<uid_t>{}(key.uid);
138                 size_t layerNameHash = std::hash<std::string>{}(key.layerName);
139                 using T = std::underlying_type_t<GameMode>;
140                 size_t gameModeHash = std::hash<T>{}(static_cast<T>(key.gameMode));
141                 return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
142             }
143         };
144 
145         bool operator==(const LayerStatsKey& o) const {
146             return uid == o.uid && layerName == o.layerName && gameMode == o.gameMode;
147         }
148     };
149 
150     struct TimelineStats {
151         TimelineStatsKey key;
152         JankPayload jankPayload;
153         Histogram displayDeadlineDeltas;
154         Histogram displayPresentDeltas;
155         std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
156 
clearGlobalsTimelineStats157         void clearGlobals() {
158             jankPayload = {};
159             displayDeadlineDeltas = {};
160             displayPresentDeltas = {};
161         }
162     };
163 
164     class TimeStatsGlobal {
165     public:
166         // Note: these are all legacy statistics, we're keeping these around because a variety of
167         // systems and form-factors find these useful when comparing with older releases. However,
168         // the current recommendation is that the new timeline-based metrics are used, and the old
169         // ones are deprecated.
170         int64_t statsStartLegacy = 0;
171         int64_t statsEndLegacy = 0;
172         int32_t totalFramesLegacy = 0;
173         int32_t missedFramesLegacy = 0;
174         int32_t clientCompositionFramesLegacy = 0;
175         int32_t clientCompositionReusedFramesLegacy = 0;
176         int32_t refreshRateSwitchesLegacy = 0;
177         int32_t compositionStrategyChangesLegacy = 0;
178         int64_t displayOnTimeLegacy = 0;
179         Histogram presentToPresentLegacy;
180         Histogram frameDurationLegacy;
181         Histogram renderEngineTimingLegacy;
182         std::unordered_map<uint32_t, nsecs_t> refreshRateStatsLegacy;
183         int32_t compositionStrategyPredictedLegacy = 0;
184         int32_t compositionStrategyPredictionSucceededLegacy = 0;
185 
186         std::unordered_map<TimelineStatsKey, TimelineStats, TimelineStatsKey::Hasher> stats;
187 
188         std::string toString(std::optional<uint32_t> maxLayers) const;
189         SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
190 
191     private:
192         std::vector<TimeStatsLayer const*> generateDumpStats(
193                 std::optional<uint32_t> maxLayers) const;
194     };
195 };
196 
197 } // namespace surfaceflinger
198 } // namespace android
199