Index: content/browser/renderer_host/render_widget_host_latency_tracker.cc |
diff --git a/content/browser/renderer_host/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/render_widget_host_latency_tracker.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fc62885b39dbf9dfd55ba5eb30d36d3145538ea1 |
--- /dev/null |
+++ b/content/browser/renderer_host/render_widget_host_latency_tracker.cc |
@@ -0,0 +1,308 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/renderer_host/render_widget_host_latency_tracker.h" |
+ |
+#include "base/logging.h" |
+#include "base/metrics/histogram.h" |
+#include "content/browser/renderer_host/render_widget_host_impl.h" |
+ |
+using blink::WebGestureEvent; |
+using blink::WebInputEvent; |
+using blink::WebMouseEvent; |
+using blink::WebMouseWheelEvent; |
+using blink::WebTouchEvent; |
+using ui::LatencyInfo; |
+ |
+namespace content { |
+namespace { |
+ |
+const uint32 kMaxInputCoordinates = LatencyInfo::kMaxInputCoordinates; |
+ |
+void UpdateLatencyCoordinatesImpl(const blink::WebTouchEvent& touch, |
+ LatencyInfo* latency) { |
+ latency->input_coordinates_size = |
+ std::min(kMaxInputCoordinates, touch.touchesLength); |
+ for (uint32 i = 0; i < latency->input_coordinates_size; ++i) { |
+ latency->input_coordinates[i] = LatencyInfo::InputCoordinate( |
+ touch.touches[i].position.x, touch.touches[i].position.y); |
+ } |
+} |
+ |
+void UpdateLatencyCoordinatesImpl(const WebGestureEvent& gesture, |
+ LatencyInfo* latency) { |
+ latency->input_coordinates_size = 1; |
+ latency->input_coordinates[0] = |
+ LatencyInfo::InputCoordinate(gesture.x, gesture.y); |
+} |
+ |
+void UpdateLatencyCoordinatesImpl(const WebMouseEvent& mouse, |
+ LatencyInfo* latency) { |
+ latency->input_coordinates_size = 1; |
+ latency->input_coordinates[0] = |
+ LatencyInfo::InputCoordinate(mouse.x, mouse.y); |
+} |
+ |
+void UpdateLatencyCoordinatesImpl(const WebMouseWheelEvent& wheel, |
+ LatencyInfo* latency) { |
+ latency->input_coordinates_size = 1; |
+ latency->input_coordinates[0] = |
+ LatencyInfo::InputCoordinate(wheel.x, wheel.y); |
+} |
+ |
+void UpdateLatencyCoordinates(const WebInputEvent& event, |
+ float device_scale_factor, |
+ LatencyInfo* latency) { |
+ if (WebInputEvent::isMouseEventType(event.type)) { |
+ UpdateLatencyCoordinatesImpl(static_cast<const WebMouseEvent&>(event), |
+ latency); |
+ } else if (WebInputEvent::isGestureEventType(event.type)) { |
+ UpdateLatencyCoordinatesImpl(static_cast<const WebGestureEvent&>(event), |
+ latency); |
+ } else if (WebInputEvent::isTouchEventType(event.type)) { |
+ UpdateLatencyCoordinatesImpl(static_cast<const WebTouchEvent&>(event), |
+ latency); |
+ } else if (event.type == WebInputEvent::MouseWheel) { |
+ UpdateLatencyCoordinatesImpl(static_cast<const WebMouseWheelEvent&>(event), |
+ latency); |
+ } |
+ if (device_scale_factor == 1) |
+ return; |
+ for (uint32 i = 0; i < latency->input_coordinates_size; ++i) { |
+ latency->input_coordinates[i].x *= device_scale_factor; |
+ latency->input_coordinates[i].y *= device_scale_factor; |
+ } |
+} |
+ |
+void ComputeInputLatencyHistograms(WebInputEvent::Type type, |
+ int64 latency_component_id, |
+ const LatencyInfo& latency) { |
+ LatencyInfo::LatencyComponent rwh_component; |
+ if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
+ latency_component_id, &rwh_component)) { |
+ return; |
+ } |
+ DCHECK_EQ(rwh_component.event_count, 1u); |
+ |
+ LatencyInfo::LatencyComponent ui_component; |
+ if (latency.FindLatency(ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 0, |
+ &ui_component)) { |
+ DCHECK_EQ(ui_component.event_count, 1u); |
+ base::TimeDelta ui_delta = |
+ rwh_component.event_time - ui_component.event_time; |
+ switch (type) { |
+ case blink::WebInputEvent::MouseWheel: |
+ UMA_HISTOGRAM_CUSTOM_COUNTS( |
+ "Event.Latency.Browser.WheelUI", |
+ ui_delta.InMicroseconds(), 1, 20000, 100); |
+ break; |
+ case blink::WebInputEvent::TouchTypeFirst: |
+ UMA_HISTOGRAM_CUSTOM_COUNTS( |
+ "Event.Latency.Browser.TouchUI", |
+ ui_delta.InMicroseconds(), 1, 20000, 100); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ } |
+ |
+ LatencyInfo::LatencyComponent acked_component; |
+ if (latency.FindLatency(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, |
+ &acked_component)) { |
+ DCHECK_EQ(acked_component.event_count, 1u); |
+ base::TimeDelta acked_delta = |
+ acked_component.event_time - rwh_component.event_time; |
+ switch (type) { |
+ case blink::WebInputEvent::MouseWheel: |
+ UMA_HISTOGRAM_CUSTOM_COUNTS( |
+ "Event.Latency.Browser.WheelAcked", |
+ acked_delta.InMicroseconds(), 1, 1000000, 100); |
+ break; |
+ case blink::WebInputEvent::TouchTypeFirst: |
+ UMA_HISTOGRAM_CUSTOM_COUNTS( |
+ "Event.Latency.Browser.TouchAcked", |
+ acked_delta.InMicroseconds(), 1, 1000000, 100); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ } |
+} |
+ |
+// LatencyComponents generated in the renderer must have component IDs |
+// provided to them by the browser process. This function adds the correct |
+// component ID where necessary. |
+void AddLatencyInfoComponentIds(LatencyInfo* latency, |
+ int64 latency_component_id) { |
+ LatencyInfo::LatencyMap new_components; |
+ auto lc = latency->latency_components.begin(); |
+ while (lc != latency->latency_components.end()) { |
+ ui::LatencyComponentType component_type = lc->first.first; |
+ if (component_type == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT || |
+ component_type == ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT) { |
+ // Generate a new component entry with the correct component ID |
+ auto key = std::make_pair(component_type, latency_component_id); |
+ new_components[key] = lc->second; |
+ |
+ // Remove the old entry |
+ latency->latency_components.erase(lc++); |
+ } else { |
+ ++lc; |
+ } |
+ } |
+ |
+ // Add newly generated components into the latency info |
+ for (lc = new_components.begin(); lc != new_components.end(); ++lc) { |
+ latency->latency_components[lc->first] = lc->second; |
+ } |
+} |
+ |
+} // namespace |
+ |
+RenderWidgetHostLatencyTracker::RenderWidgetHostLatencyTracker() |
+ : last_event_id_(0), latency_component_id_(0), device_scale_factor_(1) { |
+} |
+ |
+RenderWidgetHostLatencyTracker::~RenderWidgetHostLatencyTracker() { |
+} |
+ |
+void RenderWidgetHostLatencyTracker::Initialize(int routing_id, |
+ int process_id) { |
+ DCHECK_EQ(0, last_event_id_); |
+ DCHECK_EQ(0, latency_component_id_); |
+ last_event_id_ = static_cast<int64>(process_id) << 32; |
+ latency_component_id_ = routing_id | last_event_id_; |
+} |
+ |
+void RenderWidgetHostLatencyTracker::OnInputEvent( |
+ const blink::WebInputEvent& event, |
+ LatencyInfo* latency) { |
+ DCHECK(latency); |
+ if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
+ latency_component_id_, NULL)) { |
+ return; |
+ } |
+ |
+ latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
+ latency_component_id_, ++last_event_id_); |
+ latency->TraceEventType(WebInputEventTraits::GetName(event.type)); |
+ UpdateLatencyCoordinates(event, device_scale_factor_, latency); |
+ |
+ if (event.type == blink::WebInputEvent::GestureScrollUpdate) { |
+ latency->AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT, |
+ latency_component_id_, ++last_event_id_); |
+ |
+ // Make a copy of the INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT with a |
+ // different name INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT. |
+ // So we can track the latency specifically for scroll update events. |
+ LatencyInfo::LatencyComponent original_component; |
+ if (latency->FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, |
+ &original_component)) { |
+ latency->AddLatencyNumberWithTimestamp( |
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, |
+ latency_component_id_, original_component.sequence_number, |
+ original_component.event_time, original_component.event_count); |
+ } |
+ } |
+} |
+ |
+void RenderWidgetHostLatencyTracker::OnInputEventAck( |
+ const blink::WebInputEvent& event, |
+ LatencyInfo* latency) { |
+ DCHECK(latency); |
+ |
+ // Latency ends when it is acked but does not cause render scheduling. |
+ bool rendering_scheduled = latency->FindLatency( |
+ ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL); |
+ |
+ if (WebInputEvent::isGestureEventType(event.type)) { |
+ if (rendering_scheduled) { |
+ latency->AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT, 0, 0); |
+ // TODO(jdduke): Consider exposing histograms for gesture event types. |
+ } |
+ return; |
+ } |
+ |
+ if (WebInputEvent::isTouchEventType(event.type)) { |
+ latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, 0); |
+ if (rendering_scheduled) { |
+ latency->AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, 0); |
+ ComputeInputLatencyHistograms(WebInputEvent::TouchTypeFirst, |
+ latency_component_id_, *latency); |
+ } |
+ return; |
+ } |
+ |
+ if (event.type == WebInputEvent::MouseWheel) { |
+ latency->AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, 0, 0); |
+ if (rendering_scheduled) { |
+ latency->AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT, 0, 0); |
+ ComputeInputLatencyHistograms(WebInputEvent::MouseWheel, |
+ latency_component_id_, *latency); |
+ } |
+ return; |
+ } |
+ |
+ // TODO(jdduke): Determine if mouse and keyboard events are worth hooking |
+ // into LatencyInfo. |
+} |
+ |
+void RenderWidgetHostLatencyTracker::OnSwapCompositorFrame( |
+ std::vector<LatencyInfo>* latencies) { |
+ DCHECK(latencies); |
+ for (LatencyInfo& latency : *latencies) |
+ AddLatencyInfoComponentIds(&latency, latency_component_id_); |
+} |
+ |
+void RenderWidgetHostLatencyTracker::OnFrameSwapped( |
+ const ui::LatencyInfo& latency) { |
+ LatencyInfo::LatencyComponent swap_component; |
+ if (!latency.FindLatency( |
+ ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, |
+ &swap_component)) { |
+ return; |
+ } |
+ |
+ LatencyInfo::LatencyComponent tab_switch_component; |
+ if (latency.FindLatency(ui::TAB_SHOW_COMPONENT, latency_component_id_, |
+ &tab_switch_component)) { |
+ base::TimeDelta delta = |
+ swap_component.event_time - tab_switch_component.event_time; |
+ for (size_t i = 0; i < tab_switch_component.event_count; i++) { |
+ UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", delta); |
+ } |
+ } |
+ |
+ LatencyInfo::LatencyComponent rwh_component; |
+ if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, |
+ latency_component_id_, &rwh_component)) { |
+ return; |
+ } |
+ |
+ LatencyInfo::LatencyComponent original_component; |
+ if (latency.FindLatency( |
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, |
+ latency_component_id_, &original_component)) { |
+ // This UMA metric tracks the time from when the original touch event is |
+ // created (averaged if there are multiple) to when the scroll gesture |
+ // results in final frame swap. |
+ for (size_t i = 0; i < original_component.event_count; i++) { |
+ UMA_HISTOGRAM_CUSTOM_COUNTS( |
+ "Event.Latency.TouchToScrollUpdateSwap", |
+ (swap_component.event_time - original_component.event_time) |
+ .InMicroseconds(), |
+ 1, |
+ 1000000, |
+ 100); |
+ } |
+ } |
+} |
+ |
+} // namespace content |