Index: content/browser/renderer_host/input/input_router_impl_perftest.cc |
diff --git a/content/browser/renderer_host/input/input_router_impl_perftest.cc b/content/browser/renderer_host/input/input_router_impl_perftest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9e4b3c5ca473bb4679ca8b15e0e8a77f3683bedf |
--- /dev/null |
+++ b/content/browser/renderer_host/input/input_router_impl_perftest.cc |
@@ -0,0 +1,392 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
tdresser
2014/02/18 19:46:00
2014
jdduke (slow)
2014/02/18 20:16:10
Done.
|
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/basictypes.h" |
+#include "base/command_line.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "content/browser/renderer_host/input/input_ack_handler.h" |
+#include "content/browser/renderer_host/input/input_router_client.h" |
+#include "content/browser/renderer_host/input/input_router_impl.h" |
+#include "content/common/input/web_input_event_traits.h" |
+#include "content/common/input_messages.h" |
+#include "content/common/view_messages.h" |
+#include "content/public/common/content_switches.h" |
+#include "ipc/ipc_sender.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/perf/perf_test.h" |
+#include "ui/gfx/geometry/vector2d_f.h" |
+ |
+using base::TimeDelta; |
+using blink::WebGestureEvent; |
+using blink::WebInputEvent; |
+using blink::WebTouchEvent; |
+using blink::WebTouchPoint; |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+class NullInputAckHandler : public InputAckHandler { |
+ public: |
+ NullInputAckHandler() : ack_count_(0) {} |
+ virtual ~NullInputAckHandler() {} |
+ |
+ // InputAckHandler |
+ virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, |
+ InputEventAckState ack_result) OVERRIDE { |
+ ++ack_count_; |
+ } |
+ virtual void OnWheelEventAck(const MouseWheelEventWithLatencyInfo& event, |
+ InputEventAckState ack_result) OVERRIDE { |
+ ++ack_count_; |
+ } |
+ virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, |
+ InputEventAckState ack_result) OVERRIDE { |
+ ++ack_count_; |
+ } |
+ virtual void OnGestureEventAck(const GestureEventWithLatencyInfo& event, |
+ InputEventAckState ack_result) OVERRIDE { |
+ ++ack_count_; |
+ } |
+ virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE { |
+ ++ack_count_; |
+ } |
+ |
+ size_t GetAndResetAckCount() { |
+ size_t ack_count = ack_count_; |
+ ack_count_ = 0; |
+ return ack_count; |
+ } |
+ |
+ size_t ack_count() const { return ack_count_; } |
+ |
+ private: |
+ size_t ack_count_; |
+}; |
+ |
+class NullInputRouterClient : public InputRouterClient { |
+ public: |
+ NullInputRouterClient() {} |
+ virtual ~NullInputRouterClient() {} |
+ |
+ // InputRouterClient |
+ virtual InputEventAckState FilterInputEvent( |
+ const blink::WebInputEvent& input_event, |
+ const ui::LatencyInfo& latency_info) OVERRIDE { |
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
+ } |
+ virtual void IncrementInFlightEventCount() OVERRIDE {} |
+ virtual void DecrementInFlightEventCount() OVERRIDE {} |
+ virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE {} |
+ virtual OverscrollController* GetOverscrollController() const OVERRIDE { |
+ return NULL; |
+ } |
+ virtual void DidFlush() OVERRIDE {} |
+ virtual void SetNeedsFlush() OVERRIDE {} |
+}; |
+ |
+class NullIPCSender : public IPC::Sender { |
+ public: |
+ NullIPCSender() : sent_count_(0) {} |
+ virtual ~NullIPCSender() {} |
+ |
+ virtual bool Send(IPC::Message* message) { |
+ delete message; |
+ ++sent_count_; |
+ return true; |
+ } |
+ |
+ size_t GetAndResetSentEventCount() { |
+ size_t message_count = sent_count_; |
+ sent_count_ = 0; |
+ return message_count; |
+ } |
+ |
+ bool HasMessages() const { return sent_count_ > 0; } |
+ |
+ private: |
+ size_t sent_count_; |
+}; |
+ |
+typedef std::vector<WebGestureEvent> Gestures; |
+Gestures BuildScrollSequence(size_t steps, |
tdresser
2014/02/18 19:46:00
Could this use and/or be a part of synthetic_web_i
jdduke (slow)
2014/02/18 20:16:10
Yeah, I have a TODO below to that effect, but I'll
|
+ gfx::Vector2dF origin, |
+ gfx::Vector2dF distance) { |
+ Gestures gestures; |
+ const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps); |
+ |
+ WebGestureEvent gesture; |
+ gesture.type = WebInputEvent::GestureScrollBegin; |
+ gesture.x = origin.x(); |
+ gesture.y = origin.y(); |
+ gestures.push_back(gesture); |
+ |
+ gesture.type = WebInputEvent::GestureScrollUpdate; |
+ gesture.data.scrollUpdate.deltaX = delta.x(); |
+ gesture.data.scrollUpdate.deltaY = delta.y(); |
+ for (size_t i = 0; i < steps; ++i) { |
+ gesture.x += delta.x(); |
+ gesture.y += delta.y(); |
+ gestures.push_back(gesture); |
+ } |
+ |
+ gesture.type = WebInputEvent::GestureScrollEnd; |
+ gestures.push_back(gesture); |
+ return gestures; |
+} |
+ |
+typedef std::vector<WebTouchEvent> Touches; |
+Touches BuildTouchSequence(size_t steps, |
+ gfx::Vector2dF origin, |
+ gfx::Vector2dF distance) { |
+ Touches touches; |
+ const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps); |
+ |
+ WebTouchEvent touch; |
+ touch.touchesLength = 1; |
+ touch.type = WebInputEvent::TouchStart; |
+ touch.touches[0].id = 0; |
+ touch.touches[0].state = WebTouchPoint::StatePressed; |
+ touch.touches[0].position.x = origin.x(); |
+ touch.touches[0].position.y = origin.y(); |
+ touch.touches[0].screenPosition.x = origin.x(); |
+ touch.touches[0].screenPosition.y = origin.y(); |
+ touches.push_back(touch); |
+ |
+ touch.type = WebInputEvent::TouchMove; |
+ touch.touches[0].state = WebTouchPoint::StateMoved; |
+ for (size_t i = 0; i < steps; ++i) { |
+ touch.touches[0].position.x += delta.x(); |
+ touch.touches[0].position.y += delta.y(); |
+ touch.touches[0].screenPosition.x += delta.x(); |
+ touch.touches[0].screenPosition.y += delta.y(); |
+ touches.push_back(touch); |
+ } |
+ |
+ touch.type = WebInputEvent::TouchEnd; |
+ touch.touches[0].state = WebTouchPoint::StateReleased; |
+ touches.push_back(touch); |
+ return touches; |
+} |
+ |
+class InputEventTimer { |
+ public: |
+ InputEventTimer(const char* test_name, int64 event_count) |
+ : test_name_(test_name), |
+ event_count_(event_count), |
+ start_(base::TimeTicks::Now()) {} |
+ |
+ ~InputEventTimer() { |
+ perf_test::PrintResult( |
+ "avg_time_per_event", |
+ "", |
+ test_name_, |
+ static_cast<size_t>(((base::TimeTicks::Now() - start_) / event_count_) |
+ .InMicroseconds()), |
+ "us", |
+ true); |
+ } |
+ |
+ private: |
+ const char* test_name_; |
+ int64 event_count_; |
+ base::TimeTicks start_; |
+ DISALLOW_COPY_AND_ASSIGN(InputEventTimer); |
+}; |
+ |
+} // namespace |
+ |
+class InputRouterImplPerfTest : public testing::Test { |
+ public: |
+ InputRouterImplPerfTest() |
+ : last_sent_event_type_(WebInputEvent::Undefined), |
+ last_input_id_(0) {} |
+ |
+ virtual ~InputRouterImplPerfTest() {} |
+ |
+ protected: |
+ // testing::Test |
+ virtual void SetUp() OVERRIDE { |
+ if (!CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kDisableGestureDebounce)) { |
+ CommandLine::ForCurrentProcess()->AppendSwitch( |
+ switches::kDisableGestureDebounce); |
+ } |
+ |
+ sender_.reset(new NullIPCSender()); |
+ client_.reset(new NullInputRouterClient()); |
+ ack_handler_.reset(new NullInputAckHandler()); |
+ input_router_.reset(new InputRouterImpl( |
+ sender_.get(), client_.get(), ack_handler_.get(), MSG_ROUTING_NONE)); |
+ } |
+ |
+ virtual void TearDown() OVERRIDE { |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ |
+ input_router_.reset(); |
+ ack_handler_.reset(); |
+ client_.reset(); |
+ sender_.reset(); |
+ } |
+ |
+ void SendEvent(const WebGestureEvent& gesture, |
+ const ui::LatencyInfo& latency) { |
+ input_router_->SendGestureEvent( |
+ GestureEventWithLatencyInfo(gesture, latency)); |
+ } |
+ |
+ void SendEvent(const WebTouchEvent& touch, const ui::LatencyInfo& latency) { |
+ input_router_->SendTouchEvent(TouchEventWithLatencyInfo(touch, latency)); |
+ } |
+ |
+ void SendInputEventACK(blink::WebInputEvent::Type type, |
+ InputEventAckState ack_result) { |
+ InputHostMsg_HandleInputEvent_ACK response( |
+ 0, type, ack_result, ui::LatencyInfo()); |
+ input_router_->OnMessageReceived(response); |
+ } |
+ |
+ InputRouterImpl* input_router() const { return input_router_.get(); } |
+ |
+ void OnHasTouchEventHandlers(bool has_handlers) { |
+ input_router_->OnMessageReceived( |
+ ViewHostMsg_HasTouchEventHandlers(0, has_handlers)); |
+ } |
+ |
+ size_t GetAndResetSentEventCount() { |
+ return sender_->GetAndResetSentEventCount(); |
+ } |
+ |
+ size_t GetAndResetAckCount() { return ack_handler_->GetAndResetAckCount(); } |
+ |
+ size_t AckCount() const { return ack_handler_->ack_count(); } |
+ |
+ int64 NextLatencyID() { return ++last_input_id_; } |
+ |
+ ui::LatencyInfo CreateLatencyInfo() { |
+ ui::LatencyInfo latency; |
+ latency.AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 1, 0); |
+ latency.AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT, |
+ 1, |
+ NextLatencyID()); |
+ return latency; |
+ } |
+ |
+ // TODO(jdduke): Use synthetic gesture pipeline. |
+ template <typename EventType> |
+ void RunTest(const char* test_name, |
+ const std::vector<EventType>& events, |
+ bool ack_delay, |
+ size_t iterations) { |
+ OnHasTouchEventHandlers(true); |
+ |
+ const size_t event_count = events.size(); |
+ const size_t total_event_count = event_count * iterations; |
+ ASSERT_LT(ack_delay, event_count); |
+ |
+ InputEventTimer timer(test_name, total_event_count); |
+ while (iterations--) { |
+ size_t i = 0, ack_i = 0; |
+ if (ack_delay) |
+ SendEvent(events[i++], CreateLatencyInfo()); |
+ |
+ for (; i < event_count; ++i, ++ack_i) { |
+ SendEvent(events[i], CreateLatencyInfo()); |
+ SendInputEventACK(events[ack_i].type, INPUT_EVENT_ACK_STATE_CONSUMED); |
+ } |
+ |
+ if (ack_delay) |
+ SendInputEventACK(events.back().type, INPUT_EVENT_ACK_STATE_CONSUMED); |
+ |
+ EXPECT_EQ(event_count, GetAndResetSentEventCount()); |
+ EXPECT_EQ(event_count, GetAndResetAckCount()); |
+ } |
+ } |
+ |
+ void RunTest(const char* test_name, |
tdresser
2014/02/18 19:46:00
I'd prefer it if the "RunTest" methods had unique
jdduke (slow)
2014/02/18 20:16:10
Done.
|
+ size_t steps, |
+ gfx::Vector2dF origin, |
+ gfx::Vector2dF distance, |
+ size_t iterations) { |
+ OnHasTouchEventHandlers(true); |
+ |
+ Gestures gestures = BuildScrollSequence(steps, origin, distance); |
+ Touches touches = BuildTouchSequence(steps, origin, distance); |
+ ASSERT_EQ(touches.size(), gestures.size()); |
+ |
+ const size_t event_count = gestures.size(); |
+ const size_t total_event_count = event_count * iterations * 2; |
+ |
+ InputEventTimer timer(test_name, total_event_count); |
+ while (iterations--) { |
+ for (size_t i = 0; i < event_count; ++i) { |
+ SendEvent(touches[i], CreateLatencyInfo()); |
+ // Touches may not be forwarded after the scroll sequence has begun, so |
+ // only ack if necessary. |
+ if (!AckCount()) { |
+ SendInputEventACK(touches[i].type, |
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
+ } |
+ |
+ SendEvent(gestures[i], CreateLatencyInfo()); |
+ SendInputEventACK(gestures[i].type, INPUT_EVENT_ACK_STATE_CONSUMED); |
+ EXPECT_EQ(2U, GetAndResetAckCount()); |
+ } |
+ } |
+ } |
+ |
+ private: |
+ WebInputEvent::Type last_sent_event_type_; |
+ int64 last_input_id_; |
+ scoped_ptr<NullIPCSender> sender_; |
+ scoped_ptr<NullInputRouterClient> client_; |
+ scoped_ptr<NullInputAckHandler> ack_handler_; |
+ scoped_ptr<InputRouterImpl> input_router_; |
+ base::MessageLoopForUI message_loop_; |
+}; |
+ |
+const size_t kDefaultSteps(100); |
+const size_t kDefaultIterations(100); |
+const gfx::Vector2dF kDefaultOrigin(100, 100); |
+const gfx::Vector2dF kDefaultDistance(500, 500); |
+ |
+TEST_F(InputRouterImplPerfTest, TouchSwipe) { |
+ RunTest("TouchSwipe ", |
+ BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
+ false, |
+ kDefaultIterations); |
+} |
+ |
+TEST_F(InputRouterImplPerfTest, TouchSwipeDelayedAck) { |
+ RunTest("TouchSwipeDelayedAck ", |
+ BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
+ true, |
+ kDefaultIterations); |
+} |
+ |
+TEST_F(InputRouterImplPerfTest, GestureScroll) { |
+ RunTest("GestureScroll ", |
+ BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
+ false, |
+ kDefaultIterations); |
+} |
+ |
+TEST_F(InputRouterImplPerfTest, GestureScrollDelayedAck) { |
+ RunTest("GestureScrollDelayedAck ", |
+ BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
+ true, |
+ kDefaultIterations); |
+} |
+ |
+TEST_F(InputRouterImplPerfTest, TouchSwipeToGestureScroll) { |
+ RunTest("TouchSwipeToGestureScroll ", |
+ kDefaultSteps, |
+ kDefaultOrigin, |
+ kDefaultDistance, |
+ kDefaultIterations); |
+} |
+ |
+} // namespace content |