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
cc / metrics / dropped_frame_counter.h [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CC_METRICS_DROPPED_FRAME_COUNTER_H_
#define CC_METRICS_DROPPED_FRAME_COUNTER_H_
#include <stddef.h>
#include <array>
#include <map>
#include <optional>
#include <queue>
#include <utility>
#include <vector>
#include "base/containers/ring_buffer.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "cc/cc_export.h"
#include "cc/metrics/frame_info.h"
#include "cc/metrics/frame_sorter.h"
#include "cc/metrics/ukm_smoothness_data.h"
namespace cc {
class TotalFrameCounter;
// This class maintains a counter for produced/dropped frames, and can be used
// to estimate the recent throughput.
class CC_EXPORT DroppedFrameCounter {
public:
enum FrameState {
kFrameStateDropped,
kFrameStatePartial,
kFrameStateComplete
};
enum SmoothnessStrategy {
kDefaultStrategy, // All threads and interactions are considered equal.
kScrollFocusedStrategy, // Scroll interactions has the highest priority.
kMainFocusedStrategy, // Reports dropped frames with main thread updates.
kCompositorFocusedStrategy, // Reports dropped frames with compositor
// thread updates.
kStrategyCount
};
class CC_EXPORT SlidingWindowHistogram {
public:
void AddPercentDroppedFrame(double percent_dropped_frame, size_t count = 1);
uint32_t GetPercentDroppedFramePercentile(double percentile) const;
double GetPercentDroppedFrameVariance() const;
std::vector<double> GetPercentDroppedFrameBuckets() const;
void Clear();
std::ostream& Dump(std::ostream& stream) const;
uint32_t total_count() const { return total_count_; }
private:
std::array<uint32_t, 101> histogram_bins_ = {0};
std::array<uint32_t, 7> smoothness_buckets_ = {0};
uint32_t total_count_ = 0;
};
DroppedFrameCounter();
~DroppedFrameCounter();
DroppedFrameCounter(const DroppedFrameCounter&) = delete;
DroppedFrameCounter& operator=(const DroppedFrameCounter&) = delete;
size_t frame_history_size() const { return ring_buffer_.BufferSize(); }
size_t total_frames() const { return total_frames_; }
size_t total_dropped() const { return total_dropped_; }
size_t total_partial() const { return total_partial_; }
size_t total_smoothness_dropped() const { return total_smoothness_dropped_; }
uint32_t GetAverageThroughput() const;
using SortedFrameCallback =
base::RepeatingCallback<void(const viz::BeginFrameArgs& args,
const FrameInfo&)>;
void SetSortedFrameCallback(SortedFrameCallback callback);
typedef base::RingBuffer<FrameState, 180> RingBufferType;
RingBufferType::Iterator Begin() const { return ring_buffer_.Begin(); }
// `End()` points to the last `FrameState`, not past it.
RingBufferType::Iterator End() const { return ring_buffer_.End(); }
void AddGoodFrame();
void AddPartialFrame();
void AddDroppedFrame();
void ReportFrames();
void ReportFramesOnEveryFrameForUI();
void OnBeginFrame(const viz::BeginFrameArgs& args);
virtual void OnEndFrame(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info);
void SetUkmSmoothnessDestination(UkmSmoothnessDataShared* smoothness_data);
void OnFirstContentfulPaintReceived();
// Reset is used on navigation, which resets frame statistics as well as
// frame sorter.
void Reset();
// ResetPendingFrames is used when we need to keep track of frame statistics,
// but should no longer wait for the pending frames (e.g. connection to
// gpu-process was reset, or the page became invisible, etc.). The pending
// frames are not considered to be dropped.
void ResetPendingFrames(base::TimeTicks timestamp);
// Enable dropped frame report for ui::Compositor..
void EnableReportForUI();
void set_total_counter(TotalFrameCounter* total_counter) {
total_counter_ = total_counter;
}
void SetTimeFirstContentfulPaintReceivedForTesting(
base::TimeTicks time_fcp_received) {
DCHECK(first_contentful_paint_received_);
time_first_contentful_paint_received_ = time_fcp_received;
}
double sliding_window_max_percent_dropped() const {
return sliding_window_max_percent_dropped_;
}
std::optional<double> max_percent_dropped_After_1_sec() const {
return sliding_window_max_percent_dropped_After_1_sec_;
}
std::optional<double> max_percent_dropped_After_2_sec() const {
return sliding_window_max_percent_dropped_After_2_sec_;
}
std::optional<double> max_percent_dropped_After_5_sec() const {
return sliding_window_max_percent_dropped_After_5_sec_;
}
uint32_t SlidingWindow95PercentilePercentDropped(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return sliding_window_histogram_[strategy].GetPercentDroppedFramePercentile(
0.95);
}
uint32_t SlidingWindowMedianPercentDropped(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return sliding_window_histogram_[strategy].GetPercentDroppedFramePercentile(
0.5);
}
double SlidingWindowPercentDroppedVariance(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return sliding_window_histogram_[strategy].GetPercentDroppedFrameVariance();
}
const SlidingWindowHistogram* GetSlidingWindowHistogram(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return &sliding_window_histogram_[strategy];
}
double sliding_window_current_percent_dropped() const {
return sliding_window_current_percent_dropped_.value_or(0);
}
private:
void NotifyFrameResult(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info);
base::TimeDelta ComputeCurrentWindowSize() const;
void PopSlidingWindow();
void UpdateMaxPercentDroppedFrame(double percent_dropped_frame);
// Adds count to dropped_frame_count_in_window_ of each strategy.
void UpdateDroppedFrameCountInWindow(const FrameInfo& frame_info, int count);
std::queue<std::pair<const viz::BeginFrameArgs, FrameInfo>> sliding_window_;
std::array<uint32_t, SmoothnessStrategy::kStrategyCount>
dropped_frame_count_in_window_ = {0};
double total_frames_in_window_ = 60.0;
std::array<SlidingWindowHistogram, SmoothnessStrategy::kStrategyCount>
sliding_window_histogram_;
base::TimeTicks latest_sliding_window_start_;
base::TimeDelta latest_sliding_window_interval_;
RingBufferType ring_buffer_;
size_t total_frames_ = 0;
size_t total_partial_ = 0;
size_t total_dropped_ = 0;
size_t total_smoothness_dropped_ = 0;
bool first_contentful_paint_received_ = false;
double sliding_window_max_percent_dropped_ = 0;
std::optional<double> sliding_window_max_percent_dropped_After_1_sec_;
std::optional<double> sliding_window_max_percent_dropped_After_2_sec_;
std::optional<double> sliding_window_max_percent_dropped_After_5_sec_;
base::TimeTicks time_first_contentful_paint_received_;
raw_ptr<UkmSmoothnessDataShared> ukm_smoothness_data_ = nullptr;
FrameSorter frame_sorter_;
raw_ptr<TotalFrameCounter> total_counter_ = nullptr;
struct {
double max_window = 0;
double p95_window = 0;
} last_reported_metrics_;
std::optional<SortedFrameCallback> sorted_frame_callback_;
bool report_for_ui_ = false;
std::optional<double> sliding_window_current_percent_dropped_;
// Sets to true on a newly dropped frame and stays true as long as the frames
// that follow are dropped. Reset when a frame is presented. It is used to
// generate asynchronous trace events that cover the duration of consecutive
// dropped frames
bool in_dropping_ = false;
};
CC_EXPORT std::ostream& operator<<(
std::ostream&,
const DroppedFrameCounter::SlidingWindowHistogram&);
} // namespace cc
#endif // CC_METRICS_DROPPED_FRAME_COUNTER_H_