| 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..717ba4f5b997002e6565a870ebeb0441ce2ba626
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/input_router_impl_perftest.cc
|
| @@ -0,0 +1,389 @@
|
| +// 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 "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) OVERRIDE {
|
| + 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_;
|
| +};
|
| +
|
| +// TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
|
| +typedef std::vector<WebGestureEvent> Gestures;
|
| +Gestures BuildScrollSequence(size_t steps,
|
| + 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_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 SendEventAck(blink::WebInputEvent::Type type,
|
| + InputEventAckState ack_result) {
|
| + InputHostMsg_HandleInputEvent_ACK response(
|
| + 0, type, ack_result, ui::LatencyInfo());
|
| + input_router_->OnMessageReceived(response);
|
| + }
|
| +
|
| + 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, crbug.com/344598.
|
| + template <typename EventType>
|
| + void SimulateEventSequence(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;
|
| +
|
| + 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());
|
| + SendEventAck(events[ack_i].type, INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + }
|
| +
|
| + if (ack_delay)
|
| + SendEventAck(events.back().type, INPUT_EVENT_ACK_STATE_CONSUMED);
|
| +
|
| + EXPECT_EQ(event_count, GetAndResetSentEventCount());
|
| + EXPECT_EQ(event_count, GetAndResetAckCount());
|
| + }
|
| + }
|
| +
|
| + void SimulateTouchAndScrollEventSequence(const char* test_name,
|
| + 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()) {
|
| + SendEventAck(touches[i].type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + }
|
| +
|
| + SendEvent(gestures[i], CreateLatencyInfo());
|
| + SendEventAck(gestures[i].type, INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(2U, GetAndResetAckCount());
|
| + }
|
| + }
|
| + }
|
| +
|
| + private:
|
| + 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) {
|
| + SimulateEventSequence(
|
| + "TouchSwipe ",
|
| + BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
|
| + false,
|
| + kDefaultIterations);
|
| +}
|
| +
|
| +TEST_F(InputRouterImplPerfTest, TouchSwipeDelayedAck) {
|
| + SimulateEventSequence(
|
| + "TouchSwipeDelayedAck ",
|
| + BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
|
| + true,
|
| + kDefaultIterations);
|
| +}
|
| +
|
| +TEST_F(InputRouterImplPerfTest, GestureScroll) {
|
| + SimulateEventSequence(
|
| + "GestureScroll ",
|
| + BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
|
| + false,
|
| + kDefaultIterations);
|
| +}
|
| +
|
| +TEST_F(InputRouterImplPerfTest, GestureScrollDelayedAck) {
|
| + SimulateEventSequence(
|
| + "GestureScrollDelayedAck ",
|
| + BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance),
|
| + true,
|
| + kDefaultIterations);
|
| +}
|
| +
|
| +TEST_F(InputRouterImplPerfTest, TouchSwipeToGestureScroll) {
|
| + SimulateTouchAndScrollEventSequence("TouchSwipeToGestureScroll ",
|
| + kDefaultSteps,
|
| + kDefaultOrigin,
|
| + kDefaultDistance,
|
| + kDefaultIterations);
|
| +}
|
| +
|
| +} // namespace content
|
|
|