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), device_scale_factor_(1) { |
| 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 LatencyInfo* latency) { |
| 183 DCHECK(latency); |
| 184 if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
| 185 latency_component_id_, NULL)) { |
| 186 return; |
| 187 } |
| 188 |
| 189 latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
| 190 latency_component_id_, ++last_event_id_); |
| 191 latency->TraceEventType(WebInputEventTraits::GetName(event.type)); |
| 192 UpdateLatencyCoordinates(event, device_scale_factor_, latency); |
| 193 |
| 194 if (event.type == blink::WebInputEvent::GestureScrollUpdate) { |
| 195 latency->AddLatencyNumber( |
| 196 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT, |
| 197 latency_component_id_, ++last_event_id_); |
| 198 |
| 199 // Make a copy of the INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT with a |
| 200 // different name INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT. |
| 201 // So we can track the latency specifically for scroll update events. |
| 202 LatencyInfo::LatencyComponent original_component; |
| 203 if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, |
| 204 &original_component)) { |
| 205 latency->AddLatencyNumberWithTimestamp( |
| 206 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, |
| 207 latency_component_id_, original_component.sequence_number, |
| 208 original_component.event_time, original_component.event_count); |
| 209 } |
| 210 } |
| 211 } |
| 212 |
| 213 void RenderWidgetHostLatencyTracker::OnInputEventAck( |
| 214 const blink::WebInputEvent& event, |
| 215 LatencyInfo* latency) { |
| 216 DCHECK(latency); |
| 217 |
| 218 // Latency ends when it is acked but does not cause render scheduling. |
| 219 bool rendering_scheduled = latency->FindLatency( |
| 220 ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL); |
| 221 |
| 222 if (WebInputEvent::isGestureEventType(event.type)) { |
| 223 if (rendering_scheduled) { |
| 224 latency->AddLatencyNumber( |
| 225 ui::INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT, 0, 0); |
| 226 // TODO(jdduke): Consider exposing histograms for gesture event types. |
| 227 } |
| 228 return; |
| 229 } |
| 230 |
| 231 if (WebInputEvent::isTouchEventType(event.type)) { |
| 232 latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, 0); |
| 233 if (rendering_scheduled) { |
| 234 latency->AddLatencyNumber( |
| 235 ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, 0); |
| 236 ComputeInputLatencyHistograms(WebInputEvent::TouchTypeFirst, |
| 237 latency_component_id_, *latency); |
| 238 } |
| 239 return; |
| 240 } |
| 241 |
| 242 if (event.type == WebInputEvent::MouseWheel) { |
| 243 latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, 0); |
| 244 if (rendering_scheduled) { |
| 245 latency->AddLatencyNumber( |
| 246 ui::INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT, 0, 0); |
| 247 ComputeInputLatencyHistograms(WebInputEvent::MouseWheel, |
| 248 latency_component_id_, *latency); |
| 249 } |
| 250 return; |
| 251 } |
| 252 |
| 253 // TODO(jdduke): Determine if mouse and keyboard events are worth hooking |
| 254 // into LatencyInfo. |
| 255 } |
| 256 |
| 257 void RenderWidgetHostLatencyTracker::OnSwapCompositorFrame( |
| 258 std::vector<LatencyInfo>* latencies) { |
| 259 DCHECK(latencies); |
| 260 for (LatencyInfo& latency : *latencies) |
| 261 AddLatencyInfoComponentIds(&latency, latency_component_id_); |
| 262 } |
| 263 |
| 264 void RenderWidgetHostLatencyTracker::OnFrameSwapped( |
| 265 const ui::LatencyInfo& latency) { |
| 266 LatencyInfo::LatencyComponent swap_component; |
| 267 if (!latency.FindLatency( |
| 268 ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, |
| 269 &swap_component)) { |
| 270 return; |
| 271 } |
| 272 |
| 273 LatencyInfo::LatencyComponent tab_switch_component; |
| 274 if (latency.FindLatency(ui::TAB_SHOW_COMPONENT, latency_component_id_, |
| 275 &tab_switch_component)) { |
| 276 base::TimeDelta delta = |
| 277 swap_component.event_time - tab_switch_component.event_time; |
| 278 for (size_t i = 0; i < tab_switch_component.event_count; i++) { |
| 279 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", delta); |
| 280 } |
| 281 } |
| 282 |
| 283 LatencyInfo::LatencyComponent rwh_component; |
| 284 if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
| 285 latency_component_id_, &rwh_component)) { |
| 286 return; |
| 287 } |
| 288 |
| 289 LatencyInfo::LatencyComponent original_component; |
| 290 if (latency.FindLatency( |
| 291 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, |
| 292 latency_component_id_, &original_component)) { |
| 293 // This UMA metric tracks the time from when the original touch event is |
| 294 // created (averaged if there are multiple) to when the scroll gesture |
| 295 // results in final frame swap. |
| 296 for (size_t i = 0; i < original_component.event_count; i++) { |
| 297 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 298 "Event.Latency.TouchToScrollUpdateSwap", |
| 299 (swap_component.event_time - original_component.event_time) |
| 300 .InMicroseconds(), |
| 301 1, |
| 302 1000000, |
| 303 100); |
| 304 } |
| 305 } |
| 306 } |
| 307 |
| 308 } // namespace content |
OLD | NEW |