OLD | NEW |
(Empty) | |
| 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. |
| 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) OVERRIDE { |
| 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 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598. |
| 113 typedef std::vector<WebGestureEvent> Gestures; |
| 114 Gestures BuildScrollSequence(size_t steps, |
| 115 gfx::Vector2dF origin, |
| 116 gfx::Vector2dF distance) { |
| 117 Gestures gestures; |
| 118 const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps); |
| 119 |
| 120 WebGestureEvent gesture; |
| 121 gesture.type = WebInputEvent::GestureScrollBegin; |
| 122 gesture.x = origin.x(); |
| 123 gesture.y = origin.y(); |
| 124 gestures.push_back(gesture); |
| 125 |
| 126 gesture.type = WebInputEvent::GestureScrollUpdate; |
| 127 gesture.data.scrollUpdate.deltaX = delta.x(); |
| 128 gesture.data.scrollUpdate.deltaY = delta.y(); |
| 129 for (size_t i = 0; i < steps; ++i) { |
| 130 gesture.x += delta.x(); |
| 131 gesture.y += delta.y(); |
| 132 gestures.push_back(gesture); |
| 133 } |
| 134 |
| 135 gesture.type = WebInputEvent::GestureScrollEnd; |
| 136 gestures.push_back(gesture); |
| 137 return gestures; |
| 138 } |
| 139 |
| 140 typedef std::vector<WebTouchEvent> Touches; |
| 141 Touches BuildTouchSequence(size_t steps, |
| 142 gfx::Vector2dF origin, |
| 143 gfx::Vector2dF distance) { |
| 144 Touches touches; |
| 145 const gfx::Vector2dF delta = ScaleVector2d(distance, 1.f / steps); |
| 146 |
| 147 WebTouchEvent touch; |
| 148 touch.touchesLength = 1; |
| 149 touch.type = WebInputEvent::TouchStart; |
| 150 touch.touches[0].id = 0; |
| 151 touch.touches[0].state = WebTouchPoint::StatePressed; |
| 152 touch.touches[0].position.x = origin.x(); |
| 153 touch.touches[0].position.y = origin.y(); |
| 154 touch.touches[0].screenPosition.x = origin.x(); |
| 155 touch.touches[0].screenPosition.y = origin.y(); |
| 156 touches.push_back(touch); |
| 157 |
| 158 touch.type = WebInputEvent::TouchMove; |
| 159 touch.touches[0].state = WebTouchPoint::StateMoved; |
| 160 for (size_t i = 0; i < steps; ++i) { |
| 161 touch.touches[0].position.x += delta.x(); |
| 162 touch.touches[0].position.y += delta.y(); |
| 163 touch.touches[0].screenPosition.x += delta.x(); |
| 164 touch.touches[0].screenPosition.y += delta.y(); |
| 165 touches.push_back(touch); |
| 166 } |
| 167 |
| 168 touch.type = WebInputEvent::TouchEnd; |
| 169 touch.touches[0].state = WebTouchPoint::StateReleased; |
| 170 touches.push_back(touch); |
| 171 return touches; |
| 172 } |
| 173 |
| 174 class InputEventTimer { |
| 175 public: |
| 176 InputEventTimer(const char* test_name, int64 event_count) |
| 177 : test_name_(test_name), |
| 178 event_count_(event_count), |
| 179 start_(base::TimeTicks::Now()) {} |
| 180 |
| 181 ~InputEventTimer() { |
| 182 perf_test::PrintResult( |
| 183 "avg_time_per_event", |
| 184 "", |
| 185 test_name_, |
| 186 static_cast<size_t>(((base::TimeTicks::Now() - start_) / event_count_) |
| 187 .InMicroseconds()), |
| 188 "us", |
| 189 true); |
| 190 } |
| 191 |
| 192 private: |
| 193 const char* test_name_; |
| 194 int64 event_count_; |
| 195 base::TimeTicks start_; |
| 196 DISALLOW_COPY_AND_ASSIGN(InputEventTimer); |
| 197 }; |
| 198 |
| 199 } // namespace |
| 200 |
| 201 class InputRouterImplPerfTest : public testing::Test { |
| 202 public: |
| 203 InputRouterImplPerfTest() : last_input_id_(0) {} |
| 204 virtual ~InputRouterImplPerfTest() {} |
| 205 |
| 206 protected: |
| 207 // testing::Test |
| 208 virtual void SetUp() OVERRIDE { |
| 209 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 210 switches::kDisableGestureDebounce)) { |
| 211 CommandLine::ForCurrentProcess()->AppendSwitch( |
| 212 switches::kDisableGestureDebounce); |
| 213 } |
| 214 |
| 215 sender_.reset(new NullIPCSender()); |
| 216 client_.reset(new NullInputRouterClient()); |
| 217 ack_handler_.reset(new NullInputAckHandler()); |
| 218 input_router_.reset(new InputRouterImpl( |
| 219 sender_.get(), client_.get(), ack_handler_.get(), MSG_ROUTING_NONE)); |
| 220 } |
| 221 |
| 222 virtual void TearDown() OVERRIDE { |
| 223 base::MessageLoop::current()->RunUntilIdle(); |
| 224 |
| 225 input_router_.reset(); |
| 226 ack_handler_.reset(); |
| 227 client_.reset(); |
| 228 sender_.reset(); |
| 229 } |
| 230 |
| 231 void SendEvent(const WebGestureEvent& gesture, |
| 232 const ui::LatencyInfo& latency) { |
| 233 input_router_->SendGestureEvent( |
| 234 GestureEventWithLatencyInfo(gesture, latency)); |
| 235 } |
| 236 |
| 237 void SendEvent(const WebTouchEvent& touch, const ui::LatencyInfo& latency) { |
| 238 input_router_->SendTouchEvent(TouchEventWithLatencyInfo(touch, latency)); |
| 239 } |
| 240 |
| 241 void SendEventAck(blink::WebInputEvent::Type type, |
| 242 InputEventAckState ack_result) { |
| 243 InputHostMsg_HandleInputEvent_ACK response( |
| 244 0, type, ack_result, ui::LatencyInfo()); |
| 245 input_router_->OnMessageReceived(response); |
| 246 } |
| 247 |
| 248 void OnHasTouchEventHandlers(bool has_handlers) { |
| 249 input_router_->OnMessageReceived( |
| 250 ViewHostMsg_HasTouchEventHandlers(0, has_handlers)); |
| 251 } |
| 252 |
| 253 size_t GetAndResetSentEventCount() { |
| 254 return sender_->GetAndResetSentEventCount(); |
| 255 } |
| 256 |
| 257 size_t GetAndResetAckCount() { return ack_handler_->GetAndResetAckCount(); } |
| 258 |
| 259 size_t AckCount() const { return ack_handler_->ack_count(); } |
| 260 |
| 261 int64 NextLatencyID() { return ++last_input_id_; } |
| 262 |
| 263 ui::LatencyInfo CreateLatencyInfo() { |
| 264 ui::LatencyInfo latency; |
| 265 latency.AddLatencyNumber( |
| 266 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, 1, 0); |
| 267 latency.AddLatencyNumber( |
| 268 ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT, |
| 269 1, |
| 270 NextLatencyID()); |
| 271 return latency; |
| 272 } |
| 273 |
| 274 // TODO(jdduke): Use synthetic gesture pipeline, crbug.com/344598. |
| 275 template <typename EventType> |
| 276 void SimulateEventSequence(const char* test_name, |
| 277 const std::vector<EventType>& events, |
| 278 bool ack_delay, |
| 279 size_t iterations) { |
| 280 OnHasTouchEventHandlers(true); |
| 281 |
| 282 const size_t event_count = events.size(); |
| 283 const size_t total_event_count = event_count * iterations; |
| 284 |
| 285 InputEventTimer timer(test_name, total_event_count); |
| 286 while (iterations--) { |
| 287 size_t i = 0, ack_i = 0; |
| 288 if (ack_delay) |
| 289 SendEvent(events[i++], CreateLatencyInfo()); |
| 290 |
| 291 for (; i < event_count; ++i, ++ack_i) { |
| 292 SendEvent(events[i], CreateLatencyInfo()); |
| 293 SendEventAck(events[ack_i].type, INPUT_EVENT_ACK_STATE_CONSUMED); |
| 294 } |
| 295 |
| 296 if (ack_delay) |
| 297 SendEventAck(events.back().type, INPUT_EVENT_ACK_STATE_CONSUMED); |
| 298 |
| 299 EXPECT_EQ(event_count, GetAndResetSentEventCount()); |
| 300 EXPECT_EQ(event_count, GetAndResetAckCount()); |
| 301 } |
| 302 } |
| 303 |
| 304 void SimulateTouchAndScrollEventSequence(const char* test_name, |
| 305 size_t steps, |
| 306 gfx::Vector2dF origin, |
| 307 gfx::Vector2dF distance, |
| 308 size_t iterations) { |
| 309 OnHasTouchEventHandlers(true); |
| 310 |
| 311 Gestures gestures = BuildScrollSequence(steps, origin, distance); |
| 312 Touches touches = BuildTouchSequence(steps, origin, distance); |
| 313 ASSERT_EQ(touches.size(), gestures.size()); |
| 314 |
| 315 const size_t event_count = gestures.size(); |
| 316 const size_t total_event_count = event_count * iterations * 2; |
| 317 |
| 318 InputEventTimer timer(test_name, total_event_count); |
| 319 while (iterations--) { |
| 320 for (size_t i = 0; i < event_count; ++i) { |
| 321 SendEvent(touches[i], CreateLatencyInfo()); |
| 322 // Touches may not be forwarded after the scroll sequence has begun, so |
| 323 // only ack if necessary. |
| 324 if (!AckCount()) { |
| 325 SendEventAck(touches[i].type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| 326 } |
| 327 |
| 328 SendEvent(gestures[i], CreateLatencyInfo()); |
| 329 SendEventAck(gestures[i].type, INPUT_EVENT_ACK_STATE_CONSUMED); |
| 330 EXPECT_EQ(2U, GetAndResetAckCount()); |
| 331 } |
| 332 } |
| 333 } |
| 334 |
| 335 private: |
| 336 int64 last_input_id_; |
| 337 scoped_ptr<NullIPCSender> sender_; |
| 338 scoped_ptr<NullInputRouterClient> client_; |
| 339 scoped_ptr<NullInputAckHandler> ack_handler_; |
| 340 scoped_ptr<InputRouterImpl> input_router_; |
| 341 base::MessageLoopForUI message_loop_; |
| 342 }; |
| 343 |
| 344 const size_t kDefaultSteps(100); |
| 345 const size_t kDefaultIterations(100); |
| 346 const gfx::Vector2dF kDefaultOrigin(100, 100); |
| 347 const gfx::Vector2dF kDefaultDistance(500, 500); |
| 348 |
| 349 TEST_F(InputRouterImplPerfTest, TouchSwipe) { |
| 350 SimulateEventSequence( |
| 351 "TouchSwipe ", |
| 352 BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
| 353 false, |
| 354 kDefaultIterations); |
| 355 } |
| 356 |
| 357 TEST_F(InputRouterImplPerfTest, TouchSwipeDelayedAck) { |
| 358 SimulateEventSequence( |
| 359 "TouchSwipeDelayedAck ", |
| 360 BuildTouchSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
| 361 true, |
| 362 kDefaultIterations); |
| 363 } |
| 364 |
| 365 TEST_F(InputRouterImplPerfTest, GestureScroll) { |
| 366 SimulateEventSequence( |
| 367 "GestureScroll ", |
| 368 BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
| 369 false, |
| 370 kDefaultIterations); |
| 371 } |
| 372 |
| 373 TEST_F(InputRouterImplPerfTest, GestureScrollDelayedAck) { |
| 374 SimulateEventSequence( |
| 375 "GestureScrollDelayedAck ", |
| 376 BuildScrollSequence(kDefaultSteps, kDefaultOrigin, kDefaultDistance), |
| 377 true, |
| 378 kDefaultIterations); |
| 379 } |
| 380 |
| 381 TEST_F(InputRouterImplPerfTest, TouchSwipeToGestureScroll) { |
| 382 SimulateTouchAndScrollEventSequence("TouchSwipeToGestureScroll ", |
| 383 kDefaultSteps, |
| 384 kDefaultOrigin, |
| 385 kDefaultDistance, |
| 386 kDefaultIterations); |
| 387 } |
| 388 |
| 389 } // namespace content |
OLD | NEW |