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
ash / hud_display / fps_graph_page_view.cc [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.
#include "ash/hud_display/fps_graph_page_view.h"
#include <algorithm>
#include <cmath>
#include <numeric>
#include "ash/hud_display/hud_constants.h"
#include "ash/hud_display/reference_lines.h"
#include "ash/shell.h"
#include "base/functional/bind.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace hud_display {
namespace {
// Draw tick on the vertical axis every 5 frames.
constexpr float kVerticalTickFrames = 5;
} // namespace
////////////////////////////////////////////////////////////////////////////////
// FPSGraphPageView, public:
BEGIN_METADATA(FPSGraphPageView)
END_METADATA
FPSGraphPageView::FPSGraphPageView(const base::TimeDelta refresh_interval)
: frame_rate_1s_(kHUDGraphWidth,
Graph::Baseline::kBaselineBottom,
Graph::Fill::kNone,
Graph::Style::kSkyline,
SkColorSetA(SK_ColorYELLOW, kHUDAlpha)),
frame_rate_500ms_(kHUDGraphWidth,
Graph::Baseline::kBaselineBottom,
Graph::Fill::kNone,
Graph::Style::kSkyline,
SkColorSetA(SK_ColorCYAN, kHUDAlpha)),
refresh_rate_(kHUDGraphWidth,
Graph::Baseline::kBaselineBottom,
Graph::Fill::kNone,
Graph::Style::kSkyline,
kHUDBackground /*not drawn*/) {
const int data_width = frame_rate_1s_.max_data_points();
// Verical ticks are drawn every 5 frames (5/60 interval).
constexpr float vertical_ticks_interval = kVerticalTickFrames / 60.F;
// max_data_points left label, 60fps top, 0 seconds on the right, 0fps on the
// bottom. Seconds and fps are dimensions. Number of data points is
// data_width, horizontal tick marks are drawn every 10 frames.
reference_lines_ = CreateReferenceLines(
/*left=*/data_width,
/*top=*/60, /*right=*/0, /*bottom=*/0,
/*x_unit=*/u"frames",
/*y_unit=*/u"fps",
/*horizontal_points_number=*/data_width,
/*horizontal_ticks_interval=*/10, vertical_ticks_interval);
Legend::Formatter formatter_float = base::BindRepeating([](float value) {
return base::ASCIIToUTF16(base::StringPrintf("%.1f", value));
});
Legend::Formatter formatter_int = base::BindRepeating([](float value) {
return base::ASCIIToUTF16(base::StringPrintf("%d", (int)value));
});
const std::vector<Legend::Entry> legend(
{{refresh_rate_, u"Refresh rate", u"Actual display refresh rate.",
formatter_int},
{frame_rate_1s_, u"1s FPS",
u"Number of frames successfully presented per 1 second.",
formatter_float},
{frame_rate_500ms_, u".5s FPS",
u"Number of frames successfully presented per 0.5 second scaled to a "
u"second.",
formatter_float}});
CreateLegend(legend);
}
FPSGraphPageView::~FPSGraphPageView() = default;
////////////////////////////////////////////////////////////////////////////////
void FPSGraphPageView::AddedToWidget() {
GraphPageViewBase::AddedToWidget();
GetWidget()->AddObserver(this);
}
void FPSGraphPageView::RemovedFromWidget() {
GetWidget()->RemoveObserver(this);
GraphPageViewBase::RemovedFromWidget();
}
void FPSGraphPageView::OnPaint(gfx::Canvas* canvas) {
// TODO: Should probably update last graph point more often than shift graph.
// Layout graphs.
gfx::Rect rect = GetContentsBounds();
// Adjust bounds to not overlap with bordering reference lines.
rect.Inset(kHUDGraphReferenceLineWidth);
frame_rate_500ms_.Layout(rect, /*base=*/nullptr);
frame_rate_1s_.Layout(rect, /*base=*/nullptr);
frame_rate_500ms_.Draw(canvas);
frame_rate_1s_.Draw(canvas);
// Refresh rate graph is not drawn, it's just used in Legend display and
// reference line calculations.
}
void FPSGraphPageView::OnDidPresentCompositorFrame(
uint32_t frame_token,
const gfx::PresentationFeedback& feedback) {
UpdateStats(feedback);
float frame_rate_1s = frame_rate_for_last_second();
float frame_rate_500ms = frame_rate_for_last_half_second();
float refresh_rate = GetWidget()->GetCompositor()->refresh_rate();
UpdateTopLabel(refresh_rate);
frame_rate_1s_.AddValue(frame_rate_1s / reference_lines_->top_label(),
frame_rate_1s);
frame_rate_500ms_.AddValue(frame_rate_500ms / reference_lines_->top_label(),
frame_rate_500ms);
const float max_refresh_rate =
std::max(refresh_rate, refresh_rate_.GetUnscaledValueAt(0));
refresh_rate_.AddValue(max_refresh_rate / reference_lines_->top_label(),
max_refresh_rate);
// Legend update is expensive. Do it synchronously on regular intervals only.
if (GetVisible())
SchedulePaint();
}
void FPSGraphPageView::UpdateData(const DataSource::Snapshot& snapshot) {
if (!GetWidget()->GetNativeWindow()->HasObserver(this)) {
GetWidget()->GetNativeWindow()->AddObserver(this);
GetWidget()->GetCompositor()->AddObserver(this);
}
// Graph moves only on FramePresented.
// Update legend only.
RefreshLegendValues();
}
void FPSGraphPageView::OnWidgetDestroying(views::Widget* widget) {
DCHECK_EQ(widget, GetWidget());
// Remove observe for destruction.
GetWidget()->GetNativeWindow()->RemoveObserver(this);
GetWidget()->GetCompositor()->RemoveObserver(this);
}
void FPSGraphPageView::OnWindowAddedToRootWindow(aura::Window* window) {
GetWidget()->GetCompositor()->AddObserver(this);
}
void FPSGraphPageView::OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) {
if (GetWidget() && GetWidget()->GetCompositor() &&
GetWidget()->GetCompositor()->HasObserver(this)) {
GetWidget()->GetCompositor()->RemoveObserver(this);
}
}
void FPSGraphPageView::UpdateStats(const gfx::PresentationFeedback& feedback) {
constexpr base::TimeDelta kOneSec = base::Seconds(1);
constexpr base::TimeDelta k500ms = base::Milliseconds(500);
if (!feedback.failed())
presented_times_.push_back(feedback.timestamp);
const base::TimeTicks deadline_1s = feedback.timestamp - kOneSec;
while (!presented_times_.empty() && presented_times_.front() <= deadline_1s)
presented_times_.pop_front();
const base::TimeTicks deadline_500ms = feedback.timestamp - k500ms;
frame_rate_for_last_half_second_ = 0;
for (auto i = presented_times_.crbegin();
(i != presented_times_.crend()) && (*i > deadline_500ms); ++i) {
++frame_rate_for_last_half_second_;
}
frame_rate_for_last_half_second_ *= 2;
}
void FPSGraphPageView::UpdateTopLabel(float refresh_rate) {
const float refresh_rate_rounded_10 =
ceilf(unsigned(refresh_rate) * 0.1F) * 10.F;
if (reference_lines_->top_label() != refresh_rate_rounded_10) {
frame_rate_1s_.Reset();
frame_rate_500ms_.Reset();
reference_lines_->SetTopLabel(refresh_rate_rounded_10);
reference_lines_->SetVerticalTicksInterval(kVerticalTickFrames /
refresh_rate_rounded_10);
}
}
} // namespace hud_display
} // namespace ash