OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/input/render_widget_host_latency_tracker
.h" | 5 #include "content/browser/renderer_host/input/render_widget_host_latency_tracker
.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/metrics/histogram_functions.h" | 10 #include "base/metrics/histogram_functions.h" |
11 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
12 #include "build/build_config.h" | 12 #include "build/build_config.h" |
13 #include "components/rappor/public/rappor_utils.h" | 13 #include "components/rappor/public/rappor_utils.h" |
14 #include "content/browser/renderer_host/render_widget_host_delegate.h" | 14 #include "content/browser/renderer_host/render_widget_host_delegate.h" |
15 #include "content/public/browser/content_browser_client.h" | 15 #include "content/public/browser/content_browser_client.h" |
16 #include "content/public/common/content_client.h" | 16 #include "content/public/common/content_client.h" |
17 #include "ui/events/blink/web_input_event_traits.h" | 17 #include "ui/events/blink/web_input_event_traits.h" |
| 18 #include "ui/latency/latency_histogram_macros.h" |
18 | 19 |
19 using blink::WebGestureEvent; | 20 using blink::WebGestureEvent; |
20 using blink::WebInputEvent; | 21 using blink::WebInputEvent; |
21 using blink::WebMouseEvent; | 22 using blink::WebMouseEvent; |
22 using blink::WebMouseWheelEvent; | 23 using blink::WebMouseWheelEvent; |
23 using blink::WebTouchEvent; | 24 using blink::WebTouchEvent; |
24 using ui::LatencyInfo; | 25 using ui::LatencyInfo; |
25 | 26 |
26 namespace content { | 27 namespace content { |
27 namespace { | 28 namespace { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 latency, device_scale_factor); | 72 latency, device_scale_factor); |
72 } else if (WebInputEvent::isTouchEventType(event.type())) { | 73 } else if (WebInputEvent::isTouchEventType(event.type())) { |
73 UpdateLatencyCoordinatesImpl(static_cast<const WebTouchEvent&>(event), | 74 UpdateLatencyCoordinatesImpl(static_cast<const WebTouchEvent&>(event), |
74 latency, device_scale_factor); | 75 latency, device_scale_factor); |
75 } else if (event.type() == WebInputEvent::MouseWheel) { | 76 } else if (event.type() == WebInputEvent::MouseWheel) { |
76 UpdateLatencyCoordinatesImpl(static_cast<const WebMouseWheelEvent&>(event), | 77 UpdateLatencyCoordinatesImpl(static_cast<const WebMouseWheelEvent&>(event), |
77 latency, device_scale_factor); | 78 latency, device_scale_factor); |
78 } | 79 } |
79 } | 80 } |
80 | 81 |
81 // Check valid timing for start and end latency components. | |
82 #define CONFIRM_VALID_TIMING(start, end) \ | |
83 DCHECK(!start.first_event_time.is_null()); \ | |
84 DCHECK(!end.last_event_time.is_null()); \ | |
85 DCHECK_GE(end.last_event_time, start.first_event_time); | |
86 | |
87 // Event latency that is mostly under 1 second. We should only use 100 buckets | |
88 // when needed. | |
89 #define UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS(name, start, \ | |
90 end) \ | |
91 CONFIRM_VALID_TIMING(start, end) \ | |
92 base::UmaHistogramCustomCounts( \ | |
93 name, (end.last_event_time - start.first_event_time).InMicroseconds(), \ | |
94 1, 1000000, 100); | |
95 | |
96 #define UMA_HISTOGRAM_INPUT_LATENCY_MILLISECONDS(name, start, end) \ | |
97 CONFIRM_VALID_TIMING(start, end) \ | |
98 base::UmaHistogramCustomCounts( \ | |
99 name, (end.last_event_time - start.first_event_time).InMilliseconds(), \ | |
100 1, 1000, 50); | |
101 | |
102 // Touch/wheel to scroll latency using Rappor. | |
103 #define RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY(delegate, name, start, end) \ | |
104 CONFIRM_VALID_TIMING(start, end) \ | |
105 rappor::RapporService* rappor_service = \ | |
106 GetContentClient()->browser()->GetRapporService(); \ | |
107 if (rappor_service && delegate) { \ | |
108 std::unique_ptr<rappor::Sample> sample = \ | |
109 rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); \ | |
110 delegate->AddDomainInfoToRapporSample(sample.get()); \ | |
111 sample->SetUInt64Field( \ | |
112 "Latency", \ | |
113 (end.last_event_time - start.first_event_time).InMicroseconds(), \ | |
114 rappor::NO_NOISE); \ | |
115 rappor_service->RecordSample(name, std::move(sample)); \ | |
116 } | |
117 | |
118 // Long touch/wheel scroll latency component that is mostly under 200ms. | |
119 #define UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2(name, start, end) \ | |
120 CONFIRM_VALID_TIMING(start, end) \ | |
121 base::Histogram::FactoryGet(name, 1000, 200000, 50, \ | |
122 base::HistogramBase::kUmaTargetedHistogramFlag) \ | |
123 ->Add((end.last_event_time - start.first_event_time).InMicroseconds()); | |
124 | |
125 // Short touch/wheel scroll latency component that is mostly under 50ms. | |
126 #define UMA_HISTOGRAM_SCROLL_LATENCY_SHORT_2(name, start, end) \ | |
127 CONFIRM_VALID_TIMING(start, end) \ | |
128 base::Histogram::FactoryGet(name, 1, 50000, 50, \ | |
129 base::HistogramBase::kUmaTargetedHistogramFlag) \ | |
130 ->Add((end.last_event_time - start.first_event_time).InMicroseconds()); | |
131 | |
132 std::string LatencySourceEventTypeToInputModalityString( | |
133 ui::SourceEventType type) { | |
134 switch (type) { | |
135 case ui::SourceEventType::WHEEL: | |
136 return "Wheel"; | |
137 case ui::SourceEventType::TOUCH: | |
138 return "Touch"; | |
139 default: | |
140 return ""; | |
141 } | |
142 } | |
143 | |
144 std::string WebInputEventTypeToInputModalityString(WebInputEvent::Type type) { | 82 std::string WebInputEventTypeToInputModalityString(WebInputEvent::Type type) { |
145 if (type == blink::WebInputEvent::MouseWheel) { | 83 if (type == blink::WebInputEvent::MouseWheel) { |
146 return "Wheel"; | 84 return "Wheel"; |
147 } else if (WebInputEvent::isKeyboardEventType(type)) { | 85 } else if (WebInputEvent::isKeyboardEventType(type)) { |
148 return "Key"; | 86 return "Key"; |
149 } else if (WebInputEvent::isMouseEventType(type)) { | 87 } else if (WebInputEvent::isMouseEventType(type)) { |
150 return "Mouse"; | 88 return "Mouse"; |
151 } else if (WebInputEvent::isTouchEventType(type)) { | 89 } else if (WebInputEvent::isTouchEventType(type)) { |
152 return "Touch"; | 90 return "Touch"; |
153 } | 91 } |
154 return ""; | 92 return ""; |
155 } | 93 } |
156 | 94 |
157 void ComputeScrollLatencyHistograms( | |
158 const LatencyInfo::LatencyComponent& gpu_swap_begin_component, | |
159 const LatencyInfo::LatencyComponent& gpu_swap_end_component, | |
160 int64_t latency_component_id, | |
161 const LatencyInfo& latency) { | |
162 DCHECK(!latency.coalesced()); | |
163 if (latency.coalesced()) | |
164 return; | |
165 | |
166 DCHECK(!gpu_swap_begin_component.event_time.is_null()); | |
167 DCHECK(!gpu_swap_end_component.event_time.is_null()); | |
168 LatencyInfo::LatencyComponent original_component; | |
169 if (latency.FindLatency( | |
170 ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, | |
171 latency_component_id, &original_component)) { | |
172 // This UMA metric tracks the time between the final frame swap for the | |
173 // first scroll event in a sequence and the original timestamp of that | |
174 // scroll event's underlying touch event. | |
175 for (size_t i = 0; i < original_component.event_count; i++) { | |
176 UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS( | |
177 "Event.Latency.TouchToFirstScrollUpdateSwapBegin", | |
178 original_component, gpu_swap_begin_component); | |
179 } | |
180 } else if (!latency.FindLatency( | |
181 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, | |
182 latency_component_id, &original_component)) { | |
183 return; | |
184 } | |
185 | |
186 // This UMA metric tracks the time from when the original touch event is | |
187 // created to when the scroll gesture results in final frame swap. | |
188 for (size_t i = 0; i < original_component.event_count; i++) { | |
189 UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS( | |
190 "Event.Latency.TouchToScrollUpdateSwapBegin", original_component, | |
191 gpu_swap_begin_component); | |
192 } | |
193 } | |
194 | |
195 void ComputeTouchAndWheelScrollLatencyHistograms( | |
196 RenderWidgetHostDelegate* render_widget_host_delegate, | |
197 const ui::LatencyInfo::LatencyComponent& gpu_swap_begin_component, | |
198 const ui::LatencyInfo::LatencyComponent& gpu_swap_end_component, | |
199 int64_t latency_component_id, | |
200 const ui::LatencyInfo& latency) { | |
201 DCHECK(!latency.coalesced()); | |
202 if (latency.coalesced()) | |
203 return; | |
204 | |
205 LatencyInfo::LatencyComponent original_component; | |
206 std::string scroll_name = "ScrollUpdate"; | |
207 | |
208 const std::string input_modality = | |
209 LatencySourceEventTypeToInputModalityString(latency.source_event_type()); | |
210 | |
211 if (latency.FindLatency( | |
212 ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, | |
213 latency_component_id, &original_component)) { | |
214 scroll_name = "ScrollBegin"; | |
215 // This UMA metric tracks the time between the final frame swap for the | |
216 // first scroll event in a sequence and the original timestamp of that | |
217 // scroll event's underlying touch/wheel event. | |
218 UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS( | |
219 "Event.Latency.ScrollBegin." + input_modality + | |
220 ".TimeToScrollUpdateSwapBegin2", | |
221 original_component, gpu_swap_begin_component); | |
222 | |
223 RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY( | |
224 render_widget_host_delegate, | |
225 "Event.Latency.ScrollBegin." + input_modality + | |
226 ".TimeToScrollUpdateSwapBegin2", | |
227 original_component, gpu_swap_begin_component); | |
228 | |
229 // TODO(lanwei): Will remove them when M56 is stable, see | |
230 // https://crbug.com/669618. | |
231 UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS( | |
232 "Event.Latency.ScrollUpdate." + input_modality + | |
233 ".TimeToFirstScrollUpdateSwapBegin2", | |
234 original_component, gpu_swap_begin_component); | |
235 } else if (latency.FindLatency( | |
236 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, | |
237 latency_component_id, &original_component)) { | |
238 // This UMA metric tracks the time from when the original touch event is | |
239 // created to when the scroll gesture results in final frame swap. | |
240 // First scroll events are excluded from this metric. | |
241 if (input_modality == "Touch") { | |
242 UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS( | |
243 "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2", | |
244 original_component, gpu_swap_begin_component); | |
245 | |
246 RAPPOR_TOUCH_WHEEL_TO_SCROLL_LATENCY( | |
247 render_widget_host_delegate, | |
248 "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2", | |
249 original_component, gpu_swap_begin_component); | |
250 } | |
251 } else { | |
252 // No original component found. | |
253 return; | |
254 } | |
255 | |
256 LatencyInfo::LatencyComponent rendering_scheduled_component; | |
257 bool rendering_scheduled_on_main = latency.FindLatency( | |
258 ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT, 0, | |
259 &rendering_scheduled_component); | |
260 if (!rendering_scheduled_on_main) { | |
261 if (!latency.FindLatency( | |
262 ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT, 0, | |
263 &rendering_scheduled_component)) | |
264 return; | |
265 } | |
266 | |
267 const std::string thread_name = rendering_scheduled_on_main ? "Main" : "Impl"; | |
268 | |
269 UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( | |
270 "Event.Latency." + scroll_name + "." + input_modality + | |
271 ".TimeToHandled2_" + thread_name, | |
272 original_component, rendering_scheduled_component); | |
273 | |
274 LatencyInfo::LatencyComponent renderer_swap_component; | |
275 if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, | |
276 &renderer_swap_component)) | |
277 return; | |
278 | |
279 UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( | |
280 "Event.Latency." + scroll_name + "." + input_modality + | |
281 ".HandledToRendererSwap2_" + thread_name, | |
282 rendering_scheduled_component, renderer_swap_component); | |
283 | |
284 LatencyInfo::LatencyComponent browser_received_swap_component; | |
285 if (!latency.FindLatency( | |
286 ui::INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT, 0, | |
287 &browser_received_swap_component)) | |
288 return; | |
289 | |
290 UMA_HISTOGRAM_SCROLL_LATENCY_SHORT_2( | |
291 "Event.Latency." + scroll_name + "." + input_modality + | |
292 ".RendererSwapToBrowserNotified2", | |
293 renderer_swap_component, browser_received_swap_component); | |
294 | |
295 UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( | |
296 "Event.Latency." + scroll_name + "." + input_modality + | |
297 ".BrowserNotifiedToBeforeGpuSwap2", | |
298 browser_received_swap_component, gpu_swap_begin_component); | |
299 | |
300 UMA_HISTOGRAM_SCROLL_LATENCY_SHORT_2( | |
301 "Event.Latency." + scroll_name + "." + input_modality + ".GpuSwap2", | |
302 gpu_swap_begin_component, gpu_swap_end_component); | |
303 } | |
304 // LatencyComponents generated in the renderer must have component IDs | 95 // LatencyComponents generated in the renderer must have component IDs |
305 // provided to them by the browser process. This function adds the correct | 96 // provided to them by the browser process. This function adds the correct |
306 // component ID where necessary. | 97 // component ID where necessary. |
307 void AddLatencyInfoComponentIds(LatencyInfo* latency, | 98 void AddLatencyInfoComponentIds(LatencyInfo* latency, |
308 int64_t latency_component_id) { | 99 int64_t latency_component_id) { |
309 std::vector<std::pair<ui::LatencyComponentType, int64_t>> new_components_key; | 100 std::vector<std::pair<ui::LatencyComponentType, int64_t>> new_components_key; |
310 std::vector<LatencyInfo::LatencyComponent> new_components_value; | 101 std::vector<LatencyInfo::LatencyComponent> new_components_value; |
311 for (const auto& lc : latency->latency_components()) { | 102 for (const auto& lc : latency->latency_components()) { |
312 ui::LatencyComponentType component_type = lc.first.first; | 103 ui::LatencyComponentType component_type = lc.first.first; |
313 if (component_type == ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT) { | 104 if (component_type == ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT) { |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
538 ComputeInputLatencyHistograms(event.type(), latency_component_id_, *latency, | 329 ComputeInputLatencyHistograms(event.type(), latency_component_id_, *latency, |
539 ack_result); | 330 ack_result); |
540 } | 331 } |
541 | 332 |
542 void RenderWidgetHostLatencyTracker::OnSwapCompositorFrame( | 333 void RenderWidgetHostLatencyTracker::OnSwapCompositorFrame( |
543 std::vector<LatencyInfo>* latencies) { | 334 std::vector<LatencyInfo>* latencies) { |
544 DCHECK(latencies); | 335 DCHECK(latencies); |
545 for (LatencyInfo& latency : *latencies) { | 336 for (LatencyInfo& latency : *latencies) { |
546 AddLatencyInfoComponentIds(&latency, latency_component_id_); | 337 AddLatencyInfoComponentIds(&latency, latency_component_id_); |
547 latency.AddLatencyNumber( | 338 latency.AddLatencyNumber( |
548 ui::INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT, 0, 0); | 339 ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, 0, 0); |
549 } | 340 } |
550 } | 341 } |
551 | 342 |
552 void RenderWidgetHostLatencyTracker::OnGpuSwapBuffersCompleted( | |
553 const LatencyInfo& latency) { | |
554 LatencyInfo::LatencyComponent gpu_swap_end_component; | |
555 if (!latency.FindLatency( | |
556 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, | |
557 &gpu_swap_end_component)) { | |
558 return; | |
559 } | |
560 | |
561 LatencyInfo::LatencyComponent gpu_swap_begin_component; | |
562 if (!latency.FindLatency(ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, | |
563 &gpu_swap_begin_component)) { | |
564 return; | |
565 } | |
566 | |
567 LatencyInfo::LatencyComponent tab_switch_component; | |
568 if (latency.FindLatency(ui::TAB_SHOW_COMPONENT, latency_component_id_, | |
569 &tab_switch_component)) { | |
570 base::TimeDelta delta = | |
571 gpu_swap_end_component.event_time - tab_switch_component.event_time; | |
572 for (size_t i = 0; i < tab_switch_component.event_count; i++) { | |
573 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", delta); | |
574 } | |
575 } | |
576 | |
577 if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, | |
578 latency_component_id_, nullptr)) { | |
579 return; | |
580 } | |
581 | |
582 ui::SourceEventType source_event_type = latency.source_event_type(); | |
583 if (source_event_type == ui::SourceEventType::WHEEL || | |
584 source_event_type == ui::SourceEventType::TOUCH) { | |
585 ComputeTouchAndWheelScrollLatencyHistograms( | |
586 render_widget_host_delegate_, gpu_swap_begin_component, | |
587 gpu_swap_end_component, latency_component_id_, latency); | |
588 } | |
589 | |
590 // Compute the old scroll update latency metrics. They are exclusively | |
591 // calculated for touch scrolls, and will be deprecated on M56. | |
592 // (https://crbug.com/649754) | |
593 LatencyInfo::LatencyComponent mouse_wheel_scroll_update_component; | |
594 if (!latency.FindLatency( | |
595 ui::INPUT_EVENT_LATENCY_GENERATE_SCROLL_UPDATE_FROM_MOUSE_WHEEL, 0, | |
596 &mouse_wheel_scroll_update_component)) { | |
597 ComputeScrollLatencyHistograms(gpu_swap_begin_component, | |
598 gpu_swap_end_component, | |
599 latency_component_id_, latency); | |
600 } | |
601 } | |
602 | |
603 void RenderWidgetHostLatencyTracker::SetDelegate( | 343 void RenderWidgetHostLatencyTracker::SetDelegate( |
604 RenderWidgetHostDelegate* delegate) { | 344 RenderWidgetHostDelegate* delegate) { |
605 render_widget_host_delegate_ = delegate; | 345 render_widget_host_delegate_ = delegate; |
606 } | 346 } |
607 | 347 |
| 348 void RenderWidgetHostLatencyTracker::ReportRapporScrollLatency( |
| 349 const std::string& name, |
| 350 const LatencyInfo::LatencyComponent& start_component, |
| 351 const LatencyInfo::LatencyComponent& end_component) { |
| 352 CONFIRM_VALID_TIMING(start_component, end_component) |
| 353 rappor::RapporService* rappor_service = |
| 354 GetContentClient()->browser()->GetRapporService(); |
| 355 if (rappor_service && render_widget_host_delegate_) { |
| 356 std::unique_ptr<rappor::Sample> sample = |
| 357 rappor_service->CreateSample(rappor::UMA_RAPPOR_TYPE); |
| 358 render_widget_host_delegate_->AddDomainInfoToRapporSample(sample.get()); |
| 359 sample->SetUInt64Field( |
| 360 "Latency", |
| 361 (end_component.last_event_time - start_component.first_event_time) |
| 362 .InMicroseconds(), |
| 363 rappor::NO_NOISE); |
| 364 rappor_service->RecordSample(name, std::move(sample)); |
| 365 } |
| 366 } |
| 367 |
608 } // namespace content | 368 } // namespace content |
OLD | NEW |