Chromium Code Reviews| 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 |