OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/renderer_host/render_widget_host_latency_tracker.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/metrics/histogram.h" | |
9 #include "content/browser/renderer_host/render_widget_host_impl.h" | |
10 | |
11 using blink::WebGestureEvent; | |
12 using blink::WebInputEvent; | |
13 using blink::WebMouseEvent; | |
14 using blink::WebMouseWheelEvent; | |
15 using blink::WebTouchEvent; | |
16 using ui::LatencyInfo; | |
17 | |
18 namespace content { | |
19 namespace { | |
20 | |
21 const uint32 kMaxInputCoordinates = LatencyInfo::kMaxInputCoordinates; | |
22 | |
23 void UpdateLatencyCoordinatesImpl(const blink::WebTouchEvent& touch, | |
24 LatencyInfo* latency) { | |
25 latency->input_coordinates_size = | |
26 std::min(kMaxInputCoordinates, touch.touchesLength); | |
27 for (uint32 i = 0; i < latency->input_coordinates_size; ++i) { | |
28 latency->input_coordinates[i] = LatencyInfo::InputCoordinate( | |
29 touch.touches[i].position.x, touch.touches[i].position.y); | |
30 } | |
31 } | |
32 | |
33 void UpdateLatencyCoordinatesImpl(const WebGestureEvent& gesture, | |
34 LatencyInfo* latency) { | |
35 latency->input_coordinates_size = 1; | |
36 latency->input_coordinates[0] = | |
37 LatencyInfo::InputCoordinate(gesture.x, gesture.y); | |
38 } | |
39 | |
40 void UpdateLatencyCoordinatesImpl(const WebMouseEvent& mouse, | |
41 LatencyInfo* latency) { | |
42 latency->input_coordinates_size = 1; | |
43 latency->input_coordinates[0] = | |
44 LatencyInfo::InputCoordinate(mouse.x, mouse.y); | |
45 } | |
46 | |
47 void UpdateLatencyCoordinatesImpl(const WebMouseWheelEvent& wheel, | |
48 LatencyInfo* latency) { | |
49 latency->input_coordinates_size = 1; | |
50 latency->input_coordinates[0] = | |
51 LatencyInfo::InputCoordinate(wheel.x, wheel.y); | |
52 } | |
53 | |
54 void UpdateLatencyCoordinates(const WebInputEvent& event, | |
55 float device_scale_factor, | |
56 LatencyInfo* latency) { | |
57 if (WebInputEvent::isMouseEventType(event.type)) { | |
58 UpdateLatencyCoordinatesImpl(static_cast<const WebMouseEvent&>(event), | |
59 latency); | |
60 } else if (WebInputEvent::isGestureEventType(event.type)) { | |
61 UpdateLatencyCoordinatesImpl(static_cast<const WebGestureEvent&>(event), | |
62 latency); | |
63 } else if (WebInputEvent::isTouchEventType(event.type)) { | |
64 UpdateLatencyCoordinatesImpl(static_cast<const WebTouchEvent&>(event), | |
65 latency); | |
66 } else if (event.type == WebInputEvent::MouseWheel) { | |
67 UpdateLatencyCoordinatesImpl(static_cast<const WebMouseWheelEvent&>(event), | |
68 latency); | |
69 } | |
70 if (device_scale_factor == 1) | |
71 return; | |
72 for (uint32 i = 0; i < latency->input_coordinates_size; ++i) { | |
73 latency->input_coordinates[i].x *= device_scale_factor; | |
74 latency->input_coordinates[i].y *= device_scale_factor; | |
75 } | |
76 } | |
77 | |
78 void ComputeInputLatencyHistograms(WebInputEvent::Type type, | |
79 int64 latency_component_id, | |
80 const LatencyInfo& latency) { | |
81 LatencyInfo::LatencyComponent rwh_component; | |
82 if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, | |
83 latency_component_id, &rwh_component)) { | |
84 return; | |
85 } | |
86 DCHECK_EQ(rwh_component.event_count, 1u); | |
87 | |
88 LatencyInfo::LatencyComponent ui_component; | |
89 if (latency.FindLatency(ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 0, | |
90 &ui_component)) { | |
91 DCHECK_EQ(ui_component.event_count, 1u); | |
92 base::TimeDelta ui_delta = | |
93 rwh_component.event_time - ui_component.event_time; | |
94 switch (type) { | |
95 case blink::WebInputEvent::MouseWheel: | |
96 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
97 "Event.Latency.Browser.WheelUI", | |
98 ui_delta.InMicroseconds(), 1, 20000, 100); | |
99 break; | |
100 case blink::WebInputEvent::TouchTypeFirst: | |
101 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
102 "Event.Latency.Browser.TouchUI", | |
103 ui_delta.InMicroseconds(), 1, 20000, 100); | |
104 break; | |
105 default: | |
106 NOTREACHED(); | |
107 break; | |
108 } | |
109 } | |
110 | |
111 LatencyInfo::LatencyComponent acked_component; | |
112 if (latency.FindLatency(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, | |
113 &acked_component)) { | |
114 DCHECK_EQ(acked_component.event_count, 1u); | |
115 base::TimeDelta acked_delta = | |
116 acked_component.event_time - rwh_component.event_time; | |
117 switch (type) { | |
118 case blink::WebInputEvent::MouseWheel: | |
119 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
120 "Event.Latency.Browser.WheelAcked", | |
121 acked_delta.InMicroseconds(), 1, 1000000, 100); | |
122 break; | |
123 case blink::WebInputEvent::TouchTypeFirst: | |
124 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
125 "Event.Latency.Browser.TouchAcked", | |
126 acked_delta.InMicroseconds(), 1, 1000000, 100); | |
127 break; | |
128 default: | |
129 NOTREACHED(); | |
130 break; | |
131 } | |
132 } | |
133 } | |
134 | |
135 // LatencyComponents generated in the renderer must have component IDs | |
136 // provided to them by the browser process. This function adds the correct | |
137 // component ID where necessary. | |
138 void AddLatencyInfoComponentIds(LatencyInfo* latency, | |
139 int64 latency_component_id) { | |
140 LatencyInfo::LatencyMap new_components; | |
141 auto lc = latency->latency_components.begin(); | |
142 while (lc != latency->latency_components.end()) { | |
143 ui::LatencyComponentType component_type = lc->first.first; | |
144 if (component_type == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT || | |
145 component_type == ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT) { | |
146 // Generate a new component entry with the correct component ID | |
147 auto key = std::make_pair(component_type, latency_component_id); | |
148 new_components[key] = lc->second; | |
149 | |
150 // Remove the old entry | |
151 latency->latency_components.erase(lc++); | |
152 } else { | |
153 ++lc; | |
154 } | |
155 } | |
156 | |
157 // Add newly generated components into the latency info | |
158 for (lc = new_components.begin(); lc != new_components.end(); ++lc) { | |
159 latency->latency_components[lc->first] = lc->second; | |
160 } | |
161 } | |
162 | |
163 } // namespace | |
164 | |
165 RenderWidgetHostLatencyTracker::RenderWidgetHostLatencyTracker() | |
166 : last_event_id_(0), latency_component_id_(0) { | |
167 } | |
168 | |
169 RenderWidgetHostLatencyTracker::~RenderWidgetHostLatencyTracker() { | |
170 } | |
171 | |
172 void RenderWidgetHostLatencyTracker::Initialize(int routing_id, | |
173 int process_id) { | |
174 DCHECK_EQ(0, last_event_id_); | |
175 DCHECK_EQ(0, latency_component_id_); | |
176 last_event_id_ = static_cast<int64>(process_id) << 32; | |
177 latency_component_id_ = routing_id | last_event_id_; | |
178 } | |
179 | |
180 void RenderWidgetHostLatencyTracker::OnInputEvent( | |
181 const blink::WebInputEvent& event, | |
182 float device_scale_factor, | |
183 LatencyInfo* latency) { | |
184 DCHECK(latency); | |
185 if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, | |
186 latency_component_id_, NULL)) { | |
187 return; | |
188 } | |
189 | |
190 latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, | |
191 latency_component_id_, ++last_event_id_); | |
192 latency->TraceEventType(WebInputEventTraits::GetName(event.type)); | |
193 UpdateLatencyCoordinates(event, device_scale_factor, latency); | |
194 | |
195 if (event.type == blink::WebInputEvent::GestureScrollUpdate) { | |
196 latency->AddLatencyNumber( | |
197 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT, | |
198 latency_component_id_, ++last_event_id_); | |
199 | |
200 // Make a copy of the INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT with a | |
201 // different name INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT. | |
202 // So we can track the latency specifically for scroll update events. | |
203 LatencyInfo::LatencyComponent original_component; | |
204 if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, | |
205 &original_component)) { | |
206 latency->AddLatencyNumberWithTimestamp( | |
207 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, | |
208 latency_component_id_, original_component.sequence_number, | |
209 original_component.event_time, original_component.event_count); | |
210 } | |
211 } | |
212 } | |
213 | |
214 void RenderWidgetHostLatencyTracker::OnInputEventAck( | |
215 const blink::WebInputEvent& event, | |
216 LatencyInfo* latency) { | |
217 DCHECK(latency); | |
218 latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, 0); | |
Yufeng Shen (Slow to review)
2014/12/04 23:18:57
GestureEvent latency might already inherit the INP
jdduke (slow)
2014/12/05 18:36:10
Oh good catch, I completely glossed over that.
| |
219 | |
220 // Latency ends when it is acked but does not cause render scheduling. | |
221 bool rendering_scheduled = latency->FindLatency( | |
222 ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL); | |
223 if (rendering_scheduled) | |
224 return; | |
225 | |
226 if (WebInputEvent::isGestureEventType(event.type)) { | |
227 latency->AddLatencyNumber( | |
228 ui::INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT, 0, 0); | |
229 // TODO(jdduke): Consider exposing histograms for gesture event types. | |
230 return; | |
231 } | |
232 | |
233 if (WebInputEvent::isTouchEventType(event.type)) { | |
234 latency->AddLatencyNumber( | |
235 ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, 0); | |
236 ComputeInputLatencyHistograms(WebInputEvent::TouchTypeFirst, | |
237 latency_component_id_, *latency); | |
238 return; | |
239 } | |
240 | |
241 if (event.type == WebInputEvent::MouseWheel) { | |
242 latency->AddLatencyNumber( | |
243 ui::INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT, 0, 0); | |
244 ComputeInputLatencyHistograms(WebInputEvent::MouseWheel, | |
245 latency_component_id_, *latency); | |
246 return; | |
247 } | |
248 | |
249 // TODO(jdduke): Determine if mouse and keyboard events are worth hooking | |
250 // into LatencyInfo. | |
251 } | |
252 | |
253 void RenderWidgetHostLatencyTracker::OnSwapCompositorFrame( | |
254 std::vector<LatencyInfo>* latencies) { | |
255 DCHECK(latencies); | |
256 for (LatencyInfo& latency : *latencies) | |
257 AddLatencyInfoComponentIds(&latency, latency_component_id_); | |
258 } | |
259 | |
260 void RenderWidgetHostLatencyTracker::OnFrameSwapped( | |
261 const ui::LatencyInfo& latency) { | |
262 LatencyInfo::LatencyComponent swap_component; | |
263 if (!latency.FindLatency( | |
264 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, | |
265 &swap_component)) { | |
266 return; | |
267 } | |
268 | |
269 LatencyInfo::LatencyComponent tab_switch_component; | |
270 if (latency.FindLatency(ui::TAB_SHOW_COMPONENT, latency_component_id_, | |
271 &tab_switch_component)) { | |
272 base::TimeDelta delta = | |
273 swap_component.event_time - tab_switch_component.event_time; | |
274 for (size_t i = 0; i < tab_switch_component.event_count; i++) { | |
275 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", delta); | |
276 } | |
277 } | |
278 | |
279 LatencyInfo::LatencyComponent rwh_component; | |
280 if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, | |
281 latency_component_id_, &rwh_component)) { | |
282 return; | |
283 } | |
284 | |
285 LatencyInfo::LatencyComponent original_component; | |
286 if (latency.FindLatency( | |
287 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, | |
288 latency_component_id_, &original_component)) { | |
289 // This UMA metric tracks the time from when the original touch event is | |
290 // created (averaged if there are multiple) to when the scroll gesture | |
291 // results in final frame swap. | |
292 for (size_t i = 0; i < original_component.event_count; i++) { | |
293 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
294 "Event.Latency.TouchToScrollUpdateSwap", | |
295 (swap_component.event_time - original_component.event_time) | |
296 .InMicroseconds(), | |
297 1, | |
298 1000000, | |
299 100); | |
300 } | |
301 } | |
302 } | |
303 | |
304 } // namespace content | |
OLD | NEW |