diff options
author | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-19 20:58:59 +0000 |
---|---|---|
committer | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-19 20:58:59 +0000 |
commit | 35afe7467e97f573eed41804ccbd2d7509322a1a (patch) | |
tree | fab54040b01c80eeb215148d289feb9a1d1208b8 /content | |
parent | ffc735e33b2d004dcf5da7c96f9a5ac34236d053 (diff) | |
download | chromium_src-35afe7467e97f573eed41804ccbd2d7509322a1a.zip chromium_src-35afe7467e97f573eed41804ccbd2d7509322a1a.tar.gz chromium_src-35afe7467e97f573eed41804ccbd2d7509322a1a.tar.bz2 |
Add InputRouter perftests for touches and scroll gestures
Add some basic perftests that track how long a given WebTouchEvent or
WebGestureEvent takes to propagate through the InputRouter. This includes both
the time to send and the time to ack a given event, averaged over a typical
touch swipe or scroll gesture sequence.
BUG=339877
TBR=jam@chromium.org
Review URL: https://codereview.chromium.org/170913002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@252079 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/renderer_host/input/input_router_impl_perftest.cc | 389 | ||||
-rw-r--r-- | content/content_tests.gypi | 22 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 2 |
3 files changed, 412 insertions, 1 deletions
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 0000000..717ba4f --- /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 diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 69a839d..db64b35 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -867,6 +867,7 @@ 'type': '<(gtest_target_type)', 'defines!': ['CONTENT_IMPLEMENTATION'], 'dependencies': [ + 'content.gyp:content_browser', 'content.gyp:content_common', 'test_support_content', '../base/base.gyp:test_support_base', @@ -881,9 +882,17 @@ '..', ], 'sources': [ + 'browser/renderer_host/input/input_router_impl_perftest.cc', 'common/cc_messages_perftest.cc', 'test/run_all_perftests.cc', ], + 'conditions': [ + ['OS == "android" and gtest_target_type == "shared_library"', { + 'dependencies': [ + '../testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], }, { 'target_name': 'content_browser_test_support', @@ -1424,6 +1433,19 @@ 'includes': [ '../build/java_apk.gypi' ], }, { + 'target_name': 'content_perftests_apk', + 'type': 'none', + 'dependencies': [ + 'content.gyp:content_java', + 'content_perftests', + ], + 'variables': { + 'test_suite_name': 'content_perftests', + 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)content_perftests<(SHARED_LIB_SUFFIX)', + }, + 'includes': [ '../build/apk_test.gypi' ], + }, + { 'target_name': 'chromium_linker_test_apk', 'type': 'none', 'dependencies': [ diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index eb173c4..913ed5a 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -64,7 +64,7 @@ CONTENT_EXPORT extern const char kDisableFlash3d[]; CONTENT_EXPORT extern const char kDisableFlashStage3d[]; CONTENT_EXPORT extern const char kDisableForceCompositingMode[]; CONTENT_EXPORT extern const char kDisableFullScreen[]; -extern const char kDisableGestureDebounce[]; +CONTENT_EXPORT extern const char kDisableGestureDebounce[]; CONTENT_EXPORT extern const char kDisableGestureTapHighlight[]; CONTENT_EXPORT extern const char kDisableGLMultisampling[]; CONTENT_EXPORT extern const char kDisableGpu[]; |