1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "content/browser/renderer_host/input/input_ack_handler.h"
8 #include "content/browser/renderer_host/input/input_router_client.h"
9 #include "content/browser/renderer_host/input/input_router_impl.h"
10 #include "content/common/input/web_input_event_traits.h"
11 #include "content/common/input_messages.h"
12 #include "content/common/view_messages.h"
13 #include "ipc/ipc_sender.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "testing/perf/perf_test.h"
16 #include "ui/gfx/geometry/vector2d_f.h"
18 using base::TimeDelta
;
19 using blink::WebGestureEvent
;
20 using blink::WebInputEvent
;
21 using blink::WebTouchEvent
;
22 using blink::WebTouchPoint
;
28 class NullInputAckHandler
: public InputAckHandler
{
30 NullInputAckHandler() : ack_count_(0) {}
31 ~NullInputAckHandler() override
{}
34 void OnKeyboardEventAck(const NativeWebKeyboardEvent
& event
,
35 InputEventAckState ack_result
) override
{
38 void OnWheelEventAck(const MouseWheelEventWithLatencyInfo
& event
,
39 InputEventAckState ack_result
) override
{
42 void OnTouchEventAck(const TouchEventWithLatencyInfo
& event
,
43 InputEventAckState ack_result
) override
{
46 void OnGestureEventAck(const GestureEventWithLatencyInfo
& event
,
47 InputEventAckState ack_result
) override
{
50 void OnUnexpectedEventAck(UnexpectedEventAckType type
) override
{
54 size_t GetAndResetAckCount() {
55 size_t ack_count
= ack_count_
;
60 size_t ack_count() const { return ack_count_
; }
66 class NullInputRouterClient
: public InputRouterClient
{
68 NullInputRouterClient() {}
69 ~NullInputRouterClient() override
{}
72 InputEventAckState
FilterInputEvent(
73 const blink::WebInputEvent
& input_event
,
74 const ui::LatencyInfo
& latency_info
) override
{
75 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED
;
77 void IncrementInFlightEventCount() override
{}
78 void DecrementInFlightEventCount() override
{}
79 void OnHasTouchEventHandlers(bool has_handlers
) override
{}
80 void DidFlush() override
{}
81 void DidOverscroll(const DidOverscrollParams
& params
) override
{}
82 void DidStopFlinging() override
{}
85 class NullIPCSender
: public IPC::Sender
{
87 NullIPCSender() : sent_count_(0) {}
88 ~NullIPCSender() override
{}
90 bool Send(IPC::Message
* message
) override
{
96 size_t GetAndResetSentEventCount() {
97 size_t message_count
= sent_count_
;
102 bool HasMessages() const { return sent_count_
> 0; }
108 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
109 typedef std::vector
<WebGestureEvent
> Gestures
;
110 Gestures
BuildScrollSequence(size_t steps
,
111 const gfx::Vector2dF
& origin
,
112 const gfx::Vector2dF
& distance
) {
114 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
116 WebGestureEvent gesture
;
117 gesture
.type
= WebInputEvent::GestureScrollBegin
;
118 gesture
.x
= origin
.x();
119 gesture
.y
= origin
.y();
120 gestures
.push_back(gesture
);
122 gesture
.type
= WebInputEvent::GestureScrollUpdate
;
123 gesture
.data
.scrollUpdate
.deltaX
= delta
.x();
124 gesture
.data
.scrollUpdate
.deltaY
= delta
.y();
125 for (size_t i
= 0; i
< steps
; ++i
) {
126 gesture
.x
+= delta
.x();
127 gesture
.y
+= delta
.y();
128 gestures
.push_back(gesture
);
131 gesture
.type
= WebInputEvent::GestureScrollEnd
;
132 gestures
.push_back(gesture
);
136 typedef std::vector
<WebTouchEvent
> Touches
;
137 Touches
BuildTouchSequence(size_t steps
,
138 const gfx::Vector2dF
& origin
,
139 const gfx::Vector2dF
& distance
) {
141 const gfx::Vector2dF delta
= ScaleVector2d(distance
, 1.f
/ steps
);
144 touch
.touchesLength
= 1;
145 touch
.type
= WebInputEvent::TouchStart
;
146 touch
.touches
[0].id
= 0;
147 touch
.touches
[0].state
= WebTouchPoint::StatePressed
;
148 touch
.touches
[0].position
.x
= origin
.x();
149 touch
.touches
[0].position
.y
= origin
.y();
150 touch
.touches
[0].screenPosition
.x
= origin
.x();
151 touch
.touches
[0].screenPosition
.y
= origin
.y();
152 touches
.push_back(touch
);
154 touch
.type
= WebInputEvent::TouchMove
;
155 touch
.touches
[0].state
= WebTouchPoint::StateMoved
;
156 for (size_t i
= 0; i
< steps
; ++i
) {
157 touch
.touches
[0].position
.x
+= delta
.x();
158 touch
.touches
[0].position
.y
+= delta
.y();
159 touch
.touches
[0].screenPosition
.x
+= delta
.x();
160 touch
.touches
[0].screenPosition
.y
+= delta
.y();
161 touches
.push_back(touch
);
164 touch
.type
= WebInputEvent::TouchEnd
;
165 touch
.touches
[0].state
= WebTouchPoint::StateReleased
;
166 touches
.push_back(touch
);
170 class InputEventTimer
{
172 InputEventTimer(const char* test_name
, int64 event_count
)
173 : test_name_(test_name
),
174 event_count_(event_count
),
175 start_(base::TimeTicks::Now()) {}
178 perf_test::PrintResult(
179 "avg_time_per_event",
182 static_cast<size_t>(((base::TimeTicks::Now() - start_
) / event_count_
)
189 const char* test_name_
;
191 base::TimeTicks start_
;
192 DISALLOW_COPY_AND_ASSIGN(InputEventTimer
);
197 class InputRouterImplPerfTest
: public testing::Test
{
199 InputRouterImplPerfTest() : last_input_id_(0) {}
200 ~InputRouterImplPerfTest() override
{}
204 void SetUp() override
{
205 sender_
.reset(new NullIPCSender());
206 client_
.reset(new NullInputRouterClient());
207 ack_handler_
.reset(new NullInputAckHandler());
208 input_router_
.reset(new InputRouterImpl(sender_
.get(),
212 InputRouterImpl::Config()));
215 void TearDown() override
{
216 base::MessageLoop::current()->RunUntilIdle();
218 input_router_
.reset();
219 ack_handler_
.reset();
224 void SendEvent(const WebGestureEvent
& gesture
,
225 const ui::LatencyInfo
& latency
) {
226 input_router_
->SendGestureEvent(
227 GestureEventWithLatencyInfo(gesture
, latency
));
230 void SendEvent(const WebTouchEvent
& touch
, const ui::LatencyInfo
& latency
) {
231 input_router_
->SendTouchEvent(TouchEventWithLatencyInfo(touch
, latency
));
234 void SendEventAckIfNecessary(const blink::WebInputEvent
& event
,
235 InputEventAckState ack_result
) {
236 if (!WebInputEventTraits::WillReceiveAckFromRenderer(event
))
238 InputEventAck
ack(event
.type
, ack_result
);
239 InputHostMsg_HandleInputEvent_ACK
response(0, ack
);
240 input_router_
->OnMessageReceived(response
);
243 void OnHasTouchEventHandlers(bool has_handlers
) {
244 input_router_
->OnMessageReceived(
245 ViewHostMsg_HasTouchEventHandlers(0, has_handlers
));
248 size_t GetAndResetSentEventCount() {
249 return sender_
->GetAndResetSentEventCount();
252 size_t GetAndResetAckCount() { return ack_handler_
->GetAndResetAckCount(); }
254 size_t AckCount() const { return ack_handler_
->ack_count(); }
256 int64
NextLatencyID() { return ++last_input_id_
; }
258 ui::LatencyInfo
CreateLatencyInfo() {
259 ui::LatencyInfo latency
;
260 latency
.AddLatencyNumber(
261 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT
, 1, 0);
262 latency
.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT
, 1,
267 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598.
268 template <typename EventType
>
269 void SimulateEventSequence(const char* test_name
,
270 const std::vector
<EventType
>& events
,
273 OnHasTouchEventHandlers(true);
275 const size_t event_count
= events
.size();
276 const size_t total_event_count
= event_count
* iterations
;
278 InputEventTimer
timer(test_name
, total_event_count
);
279 while (iterations
--) {
280 size_t i
= 0, ack_i
= 0;
282 SendEvent(events
[i
++], CreateLatencyInfo());
284 for (; i
< event_count
; ++i
, ++ack_i
) {
285 SendEvent(events
[i
], CreateLatencyInfo());
286 SendEventAckIfNecessary(events
[ack_i
], INPUT_EVENT_ACK_STATE_CONSUMED
);
290 SendEventAckIfNecessary(events
.back(), INPUT_EVENT_ACK_STATE_CONSUMED
);
292 EXPECT_EQ(event_count
, GetAndResetSentEventCount());
293 EXPECT_EQ(event_count
, GetAndResetAckCount());
297 void SimulateTouchAndScrollEventSequence(const char* test_name
,
299 const gfx::Vector2dF
& origin
,
300 const gfx::Vector2dF
& distance
,
302 OnHasTouchEventHandlers(true);
304 Gestures gestures
= BuildScrollSequence(steps
, origin
, distance
);
305 Touches touches
= BuildTouchSequence(steps
, origin
, distance
);
306 ASSERT_EQ(touches
.size(), gestures
.size());
308 const size_t event_count
= gestures
.size();
309 const size_t total_event_count
= event_count
* iterations
* 2;
311 InputEventTimer
timer(test_name
, total_event_count
);
312 while (iterations
--) {
313 for (size_t i
= 0; i
< event_count
; ++i
) {
314 SendEvent(touches
[i
], CreateLatencyInfo());
315 // Touches may not be forwarded after the scroll sequence has begun, so
316 // only ack if necessary.
318 SendEventAckIfNecessary(touches
[i
],
319 INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
322 SendEvent(gestures
[i
], CreateLatencyInfo());
323 SendEventAckIfNecessary(gestures
[i
], INPUT_EVENT_ACK_STATE_CONSUMED
);
324 EXPECT_EQ(2U, GetAndResetAckCount());
330 int64 last_input_id_
;
331 scoped_ptr
<NullIPCSender
> sender_
;
332 scoped_ptr
<NullInputRouterClient
> client_
;
333 scoped_ptr
<NullInputAckHandler
> ack_handler_
;
334 scoped_ptr
<InputRouterImpl
> input_router_
;
335 base::MessageLoopForUI message_loop_
;
338 const size_t kDefaultSteps(100);
339 const size_t kDefaultIterations(100);
340 const gfx::Vector2dF
kDefaultOrigin(100, 100);
341 const gfx::Vector2dF
kDefaultDistance(500, 500);
343 TEST_F(InputRouterImplPerfTest
, TouchSwipe
) {
344 SimulateEventSequence(
346 BuildTouchSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
351 TEST_F(InputRouterImplPerfTest
, TouchSwipeDelayedAck
) {
352 SimulateEventSequence(
353 "TouchSwipeDelayedAck ",
354 BuildTouchSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
359 TEST_F(InputRouterImplPerfTest
, GestureScroll
) {
360 SimulateEventSequence(
362 BuildScrollSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
367 TEST_F(InputRouterImplPerfTest
, GestureScrollDelayedAck
) {
368 SimulateEventSequence(
369 "GestureScrollDelayedAck ",
370 BuildScrollSequence(kDefaultSteps
, kDefaultOrigin
, kDefaultDistance
),
375 TEST_F(InputRouterImplPerfTest
, TouchSwipeToGestureScroll
) {
376 SimulateTouchAndScrollEventSequence("TouchSwipeToGestureScroll ",
383 } // namespace content