OLD | NEW |
---|---|
(Empty) | |
1 // 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.
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/basictypes.h" | |
6 #include "base/command_line.h" | |
7 #include "base/memory/scoped_ptr.h" | |
8 #include "content/browser/renderer_host/input/input_ack_handler.h" | |
9 #include "content/browser/renderer_host/input/input_router_client.h" | |
10 #include "content/browser/renderer_host/input/input_router_impl.h" | |
11 #include "content/common/input/web_input_event_traits.h" | |
12 #include "content/common/input_messages.h" | |
13 #include "content/common/view_messages.h" | |
14 #include "content/public/common/content_switches.h" | |
15 #include "ipc/ipc_sender.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 #include "testing/perf/perf_test.h" | |
18 #include "ui/gfx/geometry/vector2d_f.h" | |
19 | |
20 using base::TimeDelta; | |
21 using blink::WebGestureEvent; | |
22 using blink::WebInputEvent; | |
23 using blink::WebTouchEvent; | |
24 using blink::WebTouchPoint; | |
25 | |
26 namespace content { | |
27 | |
28 namespace { | |
29 | |
30 class NullInputAckHandler : public InputAckHandler { | |
31 public: | |
32 NullInputAckHandler() : ack_count_(0) {} | |
33 virtual ~NullInputAckHandler() {} | |
34 | |
35 // InputAckHandler | |
36 virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event, | |
37 InputEventAckState ack_result) OVERRIDE { | |
38 ++ack_count_; | |
39 } | |
40 virtual void OnWheelEventAck(const MouseWheelEventWithLatencyInfo& event, | |
41 InputEventAckState ack_result) OVERRIDE { | |
42 ++ack_count_; | |
43 } | |
44 virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event, | |
45 InputEventAckState ack_result) OVERRIDE { | |
46 ++ack_count_; | |
47 } | |
48 virtual void OnGestureEventAck(const GestureEventWithLatencyInfo& event, | |
49 InputEventAckState ack_result) OVERRIDE { | |
50 ++ack_count_; | |
51 } | |
52 virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE { | |
53 ++ack_count_; | |
54 } | |
55 | |
56 size_t GetAndResetAckCount() { | |
57 size_t ack_count = ack_count_; | |
58 ack_count_ = 0; | |
59 return ack_count; | |
60 } | |
61 | |
62 size_t ack_count() const { return ack_count_; } | |
63 | |
64 private: | |
65 size_t ack_count_; | |
66 }; | |
67 | |
68 class NullInputRouterClient : public InputRouterClient { | |
69 public: | |
70 NullInputRouterClient() {} | |
71 virtual ~NullInputRouterClient() {} | |
72 | |
73 // InputRouterClient | |
74 virtual InputEventAckState FilterInputEvent( | |
75 const blink::WebInputEvent& input_event, | |
76 const ui::LatencyInfo& latency_info) OVERRIDE { | |
77 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
78 } | |
79 virtual void IncrementInFlightEventCount() OVERRIDE {} | |
80 virtual void DecrementInFlightEventCount() OVERRIDE {} | |
81 virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE {} | |
82 virtual OverscrollController* GetOverscrollController() const OVERRIDE { | |
83 return NULL; | |
84 } | |
85 virtual void DidFlush() OVERRIDE {} | |
86 virtual void SetNeedsFlush() OVERRIDE {} | |
87 }; | |
88 | |
89 class NullIPCSender : public IPC::Sender { | |
90 public: | |
91 NullIPCSender() : sent_count_(0) {} | |
92 virtual ~NullIPCSender() {} | |
93 | |
94 virtual bool Send(IPC::Message* message) { | |
95 delete message; | |
96 ++sent_count_; | |
97 return true; | |
98 } | |
99 | |
100 size_t GetAndResetSentEventCount() { | |
101 size_t message_count = sent_count_; | |
102 sent_count_ = 0; | |
103 return message_count; | |
104 } | |
105 | |
106 bool HasMessages() const { return sent_count_ > 0; } | |
107 | |
108 private: | |
109 size_t sent_count_; | |
110 }; | |
111 | |
112 typedef std::vector<WebGestureEvent> Gestures; | |
113 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
| |
114 gfx::Vector2dF origin, | |
115 gfx::Vector2dF distance) { | |
116 Gestures gestures; | |
117 const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps); | |
118 | |
119 WebGestureEvent gesture; | |
120 gesture.type = WebInputEvent::GestureScrollBegin; | |
121 gesture.x = origin.x(); | |
122 gesture.y = origin.y(); | |
123 gestures.push_back(gesture); | |
124 | |
125 gesture.type = WebInputEvent::GestureScrollUpdate; | |
126 gesture.data.scrollUpdate.deltaX = delta.x(); | |
127 gesture.data.scrollUpdate.deltaY = delta.y(); | |
128 for (size_t i = 0; i < steps; ++i) { | |
129 gesture.x += delta.x(); | |
130 gesture.y += delta.y(); | |
131 gestures.push_back(gesture); | |
132 } | |
133 | |
134 gesture.type = WebInputEvent::GestureScrollEnd; | |
135 gestures.push_back(gesture); | |
136 return gestures; | |
137 } | |
138 | |
139 typedef std::vector<WebTouchEvent> Touches; | |
140 Touches BuildTouchSequence(size_t steps, | |
141 gfx::Vector2dF origin, | |
142 gfx::Vector2dF distance) { | |
143 Touches touches; | |
144 const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps); | |
145 | |
146 WebTouchEvent touch; | |
147 touch.touchesLength = 1; | |
148 touch.type = WebInputEvent::TouchStart; | |
149 touch.touches[0].id = 0; | |
150 touch.touches[0].state = WebTouchPoint::StatePressed; | |
151 touch.touches[0].position.x = origin.x(); | |
152 touch.touches[0].position.y = origin.y(); | |
153 touch.touches[0].screenPosition.x = origin.x(); | |
154 touch.touches[0].screenPosition.y = origin.y(); | |
155 touches.push_back(touch); | |
156 | |
157 touch.type = WebInputEvent::TouchMove; | |
158 touch.touches[0].state = WebTouchPoint::StateMoved; | |
159 for (size_t i = 0; i < steps; ++i) { | |
160 touch.touches[0].position.x += delta.x(); | |
161 touch.touches[0].position.y += delta.y(); | |
162 touch.touches[0].screenPosition.x += delta.x(); | |
163 touch.touches[0].screenPosition.y += delta.y(); | |
164 touches.push_back(touch); | |
165 } | |
166 | |
167 touch.type = WebInputEvent::TouchEnd; | |
168 touch.touches[0].state = WebTouchPoint::StateReleased; | |
169 touches.push_back(touch); | |
170 return touches; | |
171 } | |
172 | |
173 class InputEventTimer { | |
174 public: | |
175 InputEventTimer(const char* test_name, int64 event_count) | |
176 : test_name_(test_name), | |
177 event_count_(event_count), | |
178 start_(base::TimeTicks::Now()) {} | |
179 | |
180 ~InputEventTimer() { | |
181 perf_test::PrintResult( | |
182 "avg_time_per_event", | |
183 "", | |
184 test_name_, | |
185 static_cast<size_t>(((base::TimeTicks::Now() - start_) / event_count_) | |
186 .InMicroseconds()), | |
187 "us", | |
188 true); | |
189 } | |
190 | |
191 private: | |
192 const char* test_name_; | |
193 int64 event_count_; | |
194 base::TimeTicks start_; | |
195 DISALLOW_COPY_AND_ASSIGN(InputEventTimer); | |
196 }; | |
197 | |
198 } // namespace | |
199 | |
200 class InputRouterImplPerfTest : public testing::Test { | |
201 public: | |
202 InputRouterImplPerfTest() | |
203 : last_sent_event_type_(WebInputEvent::Undefined), | |
204 last_input_id_(0) {} | |
205 | |
206 virtual ~InputRouterImplPerfTest() {} | |
207 | |
208 protected: | |
209 // testing::Test | |
210 virtual void SetUp() OVERRIDE { | |
211 if (!CommandLine::ForCurrentProcess()->HasSwitch( | |
212 switches::kDisableGestureDebounce)) { | |
213 CommandLine::ForCurrentProcess()->AppendSwitch( | |
214 switches::kDisableGestureDebounce); | |
215 } | |
216 | |
217 sender_.reset(new NullIPCSender()); | |
218 client_.reset(new NullInputRouterClient()); | |
219 ack_handler_.reset(new NullInputAckHandler()); | |
220 input_router_.reset(new InputRouterImpl( | |
221 sender_.get(), client_.get(), ack_handler_.get(), MSG_ROUTING_NONE)); | |
222 } | |
223 | |
224 virtual void TearDown() OVERRIDE { | |
225 base::MessageLoop::current()->RunUntilIdle(); | |
226 | |
227 input_router_.reset(); | |
228 ack_handler_.reset(); | |
229 client_.reset(); | |
230 sender_.reset(); | |
231 } | |
232 | |
233 void SendEvent(const WebGestureEvent& gesture, | |
234 const ui::LatencyInfo& latency) { | |
235 input_router_->SendGestureEvent( | |
236 GestureEventWithLatencyInfo(gesture, latency)); | |
237 } | |
238 | |
239 void SendEvent(const WebTouchEvent& touch, const ui::LatencyInfo& latency) { | |
240 input_router_->SendTouchEvent(TouchEventWithLatencyInfo(touch, latency)); | |
241 } | |
242 | |
243 void SendInputEventACK(blink::WebInputEvent::Type type, | |
244 InputEventAckState ack_result) { | |
245 InputHostMsg_HandleInputEvent_ACK response( | |
246 0, type, ack_result, ui::LatencyInfo()); | |
247 input_router_->OnMessageReceived(response); | |
248 } | |
249 | |
250 InputRouterImpl* input_router() const { return input_router_.get(); } | |
251 | |
252 void OnHasTouchEventHandlers(bool has_handlers) { | |
253 input_router_->OnMessageReceived( | |
254 ViewHostMsg_HasTouchEventHandlers(0, has_handlers)); | |
255 } | |
256 | |
257 size_t GetAndResetSentEventCount() { | |
258 return sender_->GetAndResetSentEventCount(); | |
259 } | |
260 | |
261 size_t GetAndResetAckCount() { return ack_handler_->GetAndResetAckCount(); } | |
262 | |
263 size_t AckCount() const { return ack_handler_->ack_count(); } | |
264 | |
265 int64 NextLatencyID() { return ++last_input_id_; } | |
266 | |
267 ui::LatencyInfo CreateLatencyInfo() { | |
268 ui::LatencyInfo latency; | |
269 latency.AddLatencyNumber( | |
270 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 1, 0); | |
271 latency.AddLatencyNumber( | |
272 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT, | |
273 1, | |
274 NextLatencyID()); | |
275 return latency; | |
276 } | |
277 | |
278 // TODO(jdduke): Use synthetic gesture pipeline. | |
279 template <typename EventType> | |
280 void RunTest(const char* test_name, | |
281 const std::vector<EventType>& events, | |
282 bool ack_delay, | |
283 size_t iterations) { | |
284 OnHasTouchEventHandlers(true); | |
285 | |
286 const size_t event_count = events.size(); | |
287 const size_t total_event_count = event_count * iterations; | |
288 ASSERT_LT(ack_delay, event_count); | |
289 | |
290 InputEventTimer timer(test_name, total_event_count); | |
291 while (iterations--) { | |
292 size_t i = 0, ack_i = 0; | |
293 if (ack_delay) | |
294 SendEvent(events[i++], CreateLatencyInfo()); | |
295 | |
296 for (; i < event_count; ++i, ++ack_i) { | |
297 SendEvent(events[i], CreateLatencyInfo()); | |
298 SendInputEventACK(events[ack_i].type, INPUT_EVENT_ACK_STATE_CONSUMED); | |
299 } | |
300 | |
301 if (ack_delay) | |
302 SendInputEventACK(events.back().type, INPUT_EVENT_ACK_STATE_CONSUMED); | |
303 | |
304 EXPECT_EQ(event_count, GetAndResetSentEventCount()); | |
305 EXPECT_EQ(event_count, GetAndResetAckCount()); | |
306 } | |
307 } | |
308 | |
309 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.
| |
310 size_t steps, | |
311 gfx::Vector2dF origin, | |
312 gfx::Vector2dF distance, | |
313 size_t iterations) { | |
314 OnHasTouchEventHandlers(true); | |
315 | |
316 Gestures gestures = BuildScrollSequence(steps, origin, distance); | |
317 Touches touches = BuildTouchSequence(steps, origin, distance); | |
318 ASSERT_EQ(touches.size(), gestures.size()); | |
319 | |
320 const size_t event_count = gestures.size(); | |
321 const size_t total_event_count = event_count * iterations * 2; | |
322 | |
323 InputEventTimer timer(test_name, total_event_count); | |
324 while (iterations--) { | |
325 for (size_t i = 0; i < event_count; ++i) { | |
326 SendEvent(touches[i], CreateLatencyInfo()); | |
327 // Touches may not be forwarded after the scroll sequence has begun, so | |
328 // only ack if necessary. | |
329 if (!AckCount()) { | |
330 SendInputEventACK(touches[i].type, | |
331 INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | |
332 } | |
333 | |
334 SendEvent(gestures[i], CreateLatencyInfo()); | |
335 SendInputEventACK(gestures[i].type, INPUT_EVENT_ACK_STATE_CONSUMED); | |
336 EXPECT_EQ(2U, GetAndResetAckCount()); | |
337 } | |
338 } | |
339 } | |
340 | |
341 private: | |
342 WebInputEvent::Type last_sent_event_type_; | |
343 int64 last_input_id_; | |
344 scoped_ptr<NullIPCSender> sender_; | |
345 scoped_ptr<NullInputRouterClient> client_; | |
346 scoped_ptr<NullInputAckHandler> ack_handler_; | |
347 scoped_ptr<InputRouterImpl> input_router_; | |
348 base::MessageLoopForUI message_loop_; | |
349 }; | |
350 | |
351 const size_t kDefaultSteps(100); | |
352 const size_t kDefaultIterations(100); | |
353 const gfx::Vector2dF kDefaultOrigin(100, 100); | |
354 const gfx::Vector2dF kDefaultDistance(500, 500); | |
355 | |
356 TEST_F(InputRouterImplPerfTest, TouchSwipe) { | |
357 RunTest("TouchSwipe ", | |
358 BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), | |
359 false, | |
360 kDefaultIterations); | |
361 } | |
362 | |
363 TEST_F(InputRouterImplPerfTest, TouchSwipeDelayedAck) { | |
364 RunTest("TouchSwipeDelayedAck ", | |
365 BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), | |
366 true, | |
367 kDefaultIterations); | |
368 } | |
369 | |
370 TEST_F(InputRouterImplPerfTest, GestureScroll) { | |
371 RunTest("GestureScroll ", | |
372 BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), | |
373 false, | |
374 kDefaultIterations); | |
375 } | |
376 | |
377 TEST_F(InputRouterImplPerfTest, GestureScrollDelayedAck) { | |
378 RunTest("GestureScrollDelayedAck ", | |
379 BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), | |
380 true, | |
381 kDefaultIterations); | |
382 } | |
383 | |
384 TEST_F(InputRouterImplPerfTest, TouchSwipeToGestureScroll) { | |
385 RunTest("TouchSwipeToGestureScroll ", | |
386 kDefaultSteps, | |
387 kDefaultOrigin, | |
388 kDefaultDistance, | |
389 kDefaultIterations); | |
390 } | |
391 | |
392 } // namespace content | |
OLD | NEW |