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