Chromium Code Reviews| Index: content/renderer/input/main_thread_event_queue.cc |
| diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc |
| index 39dd5e56c1ba7750f63e2ae8a4acb73dc5a38ea1..225c76b4411552b5d10a73090c8e9f4ba28b03de 100644 |
| --- a/content/renderer/input/main_thread_event_queue.cc |
| +++ b/content/renderer/input/main_thread_event_queue.cc |
| @@ -14,24 +14,200 @@ namespace content { |
| namespace { |
| -// Time interval at which touchmove events will be skipped during rAF signal. |
| -const base::TimeDelta kAsyncTouchMoveInterval = |
| - base::TimeDelta::FromMilliseconds(200); |
| - |
| const size_t kTenSeconds = 10 * 1000 * 1000; |
| -bool IsContinuousEvent(const std::unique_ptr<EventWithDispatchType>& event) { |
| - switch (event->event().type()) { |
| - case blink::WebInputEvent::MouseMove: |
| - case blink::WebInputEvent::MouseWheel: |
| - case blink::WebInputEvent::TouchMove: |
| - return true; |
| - default: |
| +class QueuedClosure : public QueuedItem { |
| + public: |
| + QueuedClosure(const base::Closure& closure) : closure_(closure) {} |
| + |
| + ~QueuedClosure() override {} |
| + |
| + bool CanCoalesceWith(const QueuedItem&) override { return false; } |
| + |
| + void CoalesceWith(const QueuedItem&) override { NOTREACHED(); } |
| + |
| + bool IsSameEventClass(const QueuedItem&) const override { |
| + // Say they are the same event class as an optimization to |
| + // because coalescing will always fail. |
| + return true; |
| + } |
| + |
| + bool IsEvent() const override { return false; } |
|
mustaq
2017/03/24 15:31:18
I think it would be unwise to consider QueuedClosu
dtapuska
2017/03/24 20:16:31
How about a QueuedClosure and a QueuedWebInputEven
|
| + |
| + void Dispatch(int routing_id, MainThreadEventQueueClient*) override { |
| + closure_.Run(); |
| + } |
| + |
| + void EventHandled(int routing_id, |
| + blink::scheduler::RendererScheduler* renderer_scheduler, |
| + MainThreadEventQueueClient* client, |
| + blink::WebInputEvent::Type type, |
| + blink::WebInputEventResult result, |
| + InputEventAckState ack_result) override {} |
| + |
| + private: |
| + base::Closure closure_; |
| +}; |
| + |
| +class EventWithDispatchType : public ScopedWebInputEventWithLatencyInfo, |
| + public QueuedItem { |
| + public: |
| + EventWithDispatchType(ui::WebScopedInputEvent event, |
| + const ui::LatencyInfo& latency, |
| + InputEventDispatchType dispatch_type, |
| + bool originally_cancelable) |
| + : ScopedWebInputEventWithLatencyInfo(std::move(event), latency), |
| + dispatch_type_(dispatch_type), |
| + non_blocking_coalesced_count_(0), |
| + creation_timestamp_(base::TimeTicks::Now()), |
| + last_coalesced_timestamp_(creation_timestamp_), |
| + originally_cancelable_(originally_cancelable) {} |
| + |
| + ~EventWithDispatchType() override {} |
| + |
| + bool CanCoalesceWith(const QueuedItem& other_item) override { |
| + if (!other_item.IsEvent()) |
| return false; |
| + const EventWithDispatchType& other_event = |
| + static_cast<const EventWithDispatchType&>(other_item); |
| + return ScopedWebInputEventWithLatencyInfo::CanCoalesceWith(other_event); |
| + } |
| + |
| + void CoalesceWith(const QueuedItem& other_item) override { |
| + const EventWithDispatchType& other_event = |
| + static_cast<const EventWithDispatchType&>(other_item); |
| + // If this event was blocking push the event id to the blocking |
| + // list before updating the dispatch_type of this event. |
| + if (dispatch_type_ == DISPATCH_TYPE_BLOCKING) { |
| + blocking_coalesced_event_ids_.push_back( |
| + ui::WebInputEventTraits::GetUniqueTouchEventId(event())); |
| + } else { |
| + non_blocking_coalesced_count_++; |
| + } |
| + ScopedWebInputEventWithLatencyInfo::CoalesceWith(other_event); |
| + dispatch_type_ = other_event.dispatch_type_; |
| + last_coalesced_timestamp_ = base::TimeTicks::Now(); |
| + originally_cancelable_ = other_event.originally_cancelable_; |
| + } |
| + |
| + bool IsSameEventClass(const QueuedItem& other_item) const override { |
| + if (!other_item.IsEvent()) |
| + return false; |
| + const EventWithDispatchType& other_event = |
| + static_cast<const EventWithDispatchType&>(other_item); |
| + return event().isSameEventClass(other_event.event()); |
| + } |
| + |
| + bool IsEvent() const override { return true; } |
| + |
| + void Dispatch(int routing_id, MainThreadEventQueueClient* client) override { |
| + // Report the coalesced count only for continuous events; otherwise |
| + // the zero value would be dominated by non-continuous events. |
| + base::TimeTicks now = base::TimeTicks::Now(); |
| + if (IsContinuousEvent()) { |
| + UMA_HISTOGRAM_CUSTOM_COUNTS( |
| + "Event.MainThreadEventQueue.Continuous.QueueingTime", |
| + (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50); |
| + |
| + UMA_HISTOGRAM_CUSTOM_COUNTS( |
| + "Event.MainThreadEventQueue.Continuous.FreshnessTime", |
| + (now - lastCoalescedTimestamp()).InMicroseconds(), 1, kTenSeconds, |
| + 50); |
| + |
| + UMA_HISTOGRAM_COUNTS_1000("Event.MainThreadEventQueue.CoalescedCount", |
| + coalescedCount()); |
| + } else { |
| + UMA_HISTOGRAM_CUSTOM_COUNTS( |
| + "Event.MainThreadEventQueue.NonContinuous.QueueingTime", |
| + (now - creationTimestamp()).InMicroseconds(), 1, kTenSeconds, 50); |
| + } |
| + |
| + InputEventDispatchType dispatch_type = dispatchType(); |
| + if (!blockingCoalescedEventIds().empty()) { |
| + switch (dispatch_type) { |
| + case DISPATCH_TYPE_BLOCKING: |
| + dispatch_type = DISPATCH_TYPE_BLOCKING_NOTIFY_MAIN; |
| + break; |
| + case DISPATCH_TYPE_NON_BLOCKING: |
| + dispatch_type = DISPATCH_TYPE_NON_BLOCKING_NOTIFY_MAIN; |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| + } |
| + client->HandleEventOnMainThread(routing_id, &coalesced_event(), |
| + latencyInfo(), dispatch_type); |
| + } |
| + |
| + void EventHandled(int routing_id, |
| + blink::scheduler::RendererScheduler* renderer_scheduler, |
| + MainThreadEventQueueClient* client, |
| + blink::WebInputEvent::Type type, |
| + blink::WebInputEventResult result, |
| + InputEventAckState ack_result) override { |
| + for (const auto id : blockingCoalescedEventIds()) { |
| + client->SendInputEventAck(routing_id, type, ack_result, id); |
| + if (renderer_scheduler) { |
| + renderer_scheduler->DidHandleInputEventOnMainThread(event(), result); |
| + } |
| + } |
| + } |
| + |
| + bool originallyCancelable() const { return originally_cancelable_; } |
| + |
| + private: |
| + const std::deque<uint32_t>& blockingCoalescedEventIds() const { |
| + return blocking_coalesced_event_ids_; |
| + } |
| + InputEventDispatchType dispatchType() const { return dispatch_type_; } |
| + base::TimeTicks creationTimestamp() const { return creation_timestamp_; } |
| + base::TimeTicks lastCoalescedTimestamp() const { |
| + return last_coalesced_timestamp_; |
| + } |
| + |
| + size_t coalescedCount() const { |
| + return non_blocking_coalesced_count_ + blocking_coalesced_event_ids_.size(); |
| } |
| -} |
| -bool IsAsyncTouchMove(const std::unique_ptr<EventWithDispatchType>& event) { |
| + bool IsContinuousEvent() const { |
| + switch (event().type()) { |
| + case blink::WebInputEvent::MouseMove: |
| + case blink::WebInputEvent::MouseWheel: |
| + case blink::WebInputEvent::TouchMove: |
| + return true; |
| + default: |
| + return false; |
| + } |
| + } |
| + |
| + InputEventDispatchType dispatch_type_; |
| + |
| + // Contains the unique touch event ids to be acked. If |
| + // the events are not TouchEvents the values will be 0. More importantly for |
| + // those cases the deque ends up containing how many additional ACKs |
| + // need to be sent. |
| + std::deque<uint32_t> blocking_coalesced_event_ids_; |
| + // Contains the number of non-blocking events coalesced. |
| + size_t non_blocking_coalesced_count_; |
| + base::TimeTicks creation_timestamp_; |
| + base::TimeTicks last_coalesced_timestamp_; |
| + |
| + // Whether the received event was originally cancelable or not. The compositor |
| + // input handler can change the event based on presence of event handlers so |
| + // this is the state at which the renderer received the event from the |
| + // browser. |
| + bool originally_cancelable_; |
| +}; |
| + |
| +// Time interval at which touchmove events will be skipped during rAF signal. |
| +const base::TimeDelta kAsyncTouchMoveInterval = |
| + base::TimeDelta::FromMilliseconds(200); |
| + |
| +bool IsAsyncTouchMove(const std::unique_ptr<QueuedItem>& queued_item) { |
| + if (!queued_item->IsEvent()) |
| + return false; |
| + const EventWithDispatchType* event = |
| + static_cast<const EventWithDispatchType*>(queued_item.get()); |
| if (event->event().type() != blink::WebInputEvent::TouchMove) |
| return false; |
| const blink::WebTouchEvent& touch_event = |
| @@ -41,37 +217,8 @@ bool IsAsyncTouchMove(const std::unique_ptr<EventWithDispatchType>& event) { |
| } // namespace |
| -EventWithDispatchType::EventWithDispatchType( |
| - ui::WebScopedInputEvent event, |
| - const ui::LatencyInfo& latency, |
| - InputEventDispatchType dispatch_type, |
| - bool originally_cancelable) |
| - : ScopedWebInputEventWithLatencyInfo(std::move(event), latency), |
| - dispatch_type_(dispatch_type), |
| - non_blocking_coalesced_count_(0), |
| - creation_timestamp_(base::TimeTicks::Now()), |
| - last_coalesced_timestamp_(creation_timestamp_), |
| - originally_cancelable_(originally_cancelable) {} |
| - |
| -EventWithDispatchType::~EventWithDispatchType() {} |
| - |
| -void EventWithDispatchType::CoalesceWith(const EventWithDispatchType& other) { |
| - // If this event was blocking push the event id to the blocking |
| - // list before updating the dispatch_type of this event. |
| - if (dispatch_type_ == DISPATCH_TYPE_BLOCKING) { |
| - blocking_coalesced_event_ids_.push_back( |
| - ui::WebInputEventTraits::GetUniqueTouchEventId(event())); |
| - } else { |
| - non_blocking_coalesced_count_++; |
| - } |
| - ScopedWebInputEventWithLatencyInfo::CoalesceWith(other); |
| - dispatch_type_ = other.dispatch_type_; |
| - last_coalesced_timestamp_ = base::TimeTicks::Now(); |
| - originally_cancelable_ = other.originally_cancelable_; |
| -} |
| - |
| MainThreadEventQueue::SharedState::SharedState() |
| - : sent_main_frame_request_(false) {} |
| + : sent_main_frame_request_(false), sent_post_task_(false) {} |
| MainThreadEventQueue::SharedState::~SharedState() {} |
| @@ -206,48 +353,23 @@ bool MainThreadEventQueue::HandleEvent( |
| return non_blocking; |
| } |
| -void MainThreadEventQueue::DispatchInFlightEvent() { |
| - if (in_flight_event_) { |
| - // Report the coalesced count only for continuous events; otherwise |
| - // the zero value would be dominated by non-continuous events. |
| - base::TimeTicks now = base::TimeTicks::Now(); |
| - if (IsContinuousEvent(in_flight_event_)) { |
| - UMA_HISTOGRAM_CUSTOM_COUNTS( |
| - "Event.MainThreadEventQueue.Continuous.QueueingTime", |
| - (now - in_flight_event_->creationTimestamp()).InMicroseconds(), 1, |
| - kTenSeconds, 50); |
| - |
| - UMA_HISTOGRAM_CUSTOM_COUNTS( |
| - "Event.MainThreadEventQueue.Continuous.FreshnessTime", |
| - (now - in_flight_event_->lastCoalescedTimestamp()).InMicroseconds(), |
| - 1, kTenSeconds, 50); |
| +void MainThreadEventQueue::QueueClosure(const base::Closure& closure) { |
| + bool needs_post_task = false; |
| + std::unique_ptr<QueuedClosure> item(new QueuedClosure(closure)); |
| + { |
| + base::AutoLock lock(shared_state_lock_); |
| + shared_state_.events_.Queue(std::move(item)); |
| + needs_post_task = !shared_state_.sent_post_task_; |
| + shared_state_.sent_post_task_ = true; |
| + } |
| - UMA_HISTOGRAM_COUNTS_1000("Event.MainThreadEventQueue.CoalescedCount", |
| - in_flight_event_->coalescedCount()); |
| - } else { |
| - UMA_HISTOGRAM_CUSTOM_COUNTS( |
| - "Event.MainThreadEventQueue.NonContinuous.QueueingTime", |
| - (now - in_flight_event_->creationTimestamp()).InMicroseconds(), 1, |
| - kTenSeconds, 50); |
| - } |
| + if (needs_post_task) |
| + SendEventNotificationToMainThread(); |
| +} |
| - InputEventDispatchType dispatch_type = in_flight_event_->dispatchType(); |
| - if (!in_flight_event_->blockingCoalescedEventIds().empty()) { |
| - switch (dispatch_type) { |
| - case DISPATCH_TYPE_BLOCKING: |
| - dispatch_type = DISPATCH_TYPE_BLOCKING_NOTIFY_MAIN; |
| - break; |
| - case DISPATCH_TYPE_NON_BLOCKING: |
| - dispatch_type = DISPATCH_TYPE_NON_BLOCKING_NOTIFY_MAIN; |
| - break; |
| - default: |
| - NOTREACHED(); |
| - } |
| - } |
| - client_->HandleEventOnMainThread( |
| - routing_id_, &in_flight_event_->coalesced_event(), |
| - in_flight_event_->latencyInfo(), dispatch_type); |
| - } |
| +void MainThreadEventQueue::DispatchInFlightEvent() { |
| + if (in_flight_event_) |
| + in_flight_event_->Dispatch(routing_id_, client_); |
| in_flight_event_.reset(); |
| } |
| @@ -260,7 +382,7 @@ void MainThreadEventQueue::PossiblyScheduleMainFrame() { |
| base::AutoLock lock(shared_state_lock_); |
| if (!shared_state_.sent_main_frame_request_ && |
| !shared_state_.events_.empty() && |
| - IsRafAlignedEvent(shared_state_.events_.front()->event())) { |
| + IsRafAlignedEvent(shared_state_.events_.front())) { |
| needs_main_frame = !shared_state_.sent_main_frame_request_; |
| shared_state_.sent_main_frame_request_ = false; |
| } |
| @@ -269,15 +391,28 @@ void MainThreadEventQueue::PossiblyScheduleMainFrame() { |
| client_->NeedsMainFrame(routing_id_); |
| } |
| -void MainThreadEventQueue::DispatchSingleEvent() { |
| +void MainThreadEventQueue::DispatchEvents() { |
| + std::deque<std::unique_ptr<QueuedItem>> events_to_process; |
| { |
| base::AutoLock lock(shared_state_lock_); |
| - if (shared_state_.events_.empty()) |
| - return; |
| + shared_state_.sent_post_task_ = false; |
| - in_flight_event_ = shared_state_.events_.Pop(); |
| + shared_state_.events_.swap(&events_to_process); |
| + |
| + // Now take any raf aligned events that are at the tail of the queue |
| + // and put them back. |
| + while (!events_to_process.empty()) { |
| + if (!IsRafAlignedEvent(events_to_process.back())) |
| + break; |
| + shared_state_.events_.emplace_front(std::move(events_to_process.back())); |
| + events_to_process.pop_back(); |
| + } |
| + } |
| + while (!events_to_process.empty()) { |
| + in_flight_event_ = std::move(events_to_process.front()); |
| + events_to_process.pop_front(); |
| + DispatchInFlightEvent(); |
| } |
| - DispatchInFlightEvent(); |
| PossiblyScheduleMainFrame(); |
| } |
| @@ -285,13 +420,8 @@ void MainThreadEventQueue::EventHandled(blink::WebInputEvent::Type type, |
| blink::WebInputEventResult result, |
| InputEventAckState ack_result) { |
| if (in_flight_event_) { |
| - for (const auto id : in_flight_event_->blockingCoalescedEventIds()) { |
| - client_->SendInputEventAck(routing_id_, type, ack_result, id); |
| - if (renderer_scheduler_) { |
| - renderer_scheduler_->DidHandleInputEventOnMainThread( |
| - in_flight_event_->event(), result); |
| - } |
| - } |
| + in_flight_event_->EventHandled(routing_id_, renderer_scheduler_, client_, |
| + type, result, ack_result); |
| } |
| } |
| @@ -299,24 +429,23 @@ void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) { |
| if (IsRafAlignedInputDisabled()) |
| return; |
| - std::deque<std::unique_ptr<EventWithDispatchType>> events_to_process; |
| + std::deque<std::unique_ptr<QueuedItem>> events_to_process; |
| { |
| base::AutoLock lock(shared_state_lock_); |
| shared_state_.sent_main_frame_request_ = false; |
| while (!shared_state_.events_.empty()) { |
| - if (!IsRafAlignedEvent(shared_state_.events_.front()->event())) |
| - break; |
| - |
| - // Throttle touchmoves that are async. |
| - if (handle_raf_aligned_touch_input_ && |
| - IsAsyncTouchMove(shared_state_.events_.front())) { |
| - if (shared_state_.events_.size() == 1 && |
| - frame_time < shared_state_.last_async_touch_move_timestamp_ + |
| - kAsyncTouchMoveInterval) { |
| - break; |
| + if (IsRafAlignedEvent(shared_state_.events_.front())) { |
| + // Throttle touchmoves that are async. |
| + if (handle_raf_aligned_touch_input_ && |
| + IsAsyncTouchMove(shared_state_.events_.front())) { |
| + if (shared_state_.events_.size() == 1 && |
| + frame_time < shared_state_.last_async_touch_move_timestamp_ + |
| + kAsyncTouchMoveInterval) { |
| + break; |
| + } |
| + shared_state_.last_async_touch_move_timestamp_ = frame_time; |
| } |
| - shared_state_.last_async_touch_move_timestamp_ = frame_time; |
| } |
| events_to_process.emplace_back(shared_state_.events_.Pop()); |
| } |
| @@ -332,14 +461,13 @@ void MainThreadEventQueue::DispatchRafAlignedInput(base::TimeTicks frame_time) { |
| void MainThreadEventQueue::SendEventNotificationToMainThread() { |
|
mustaq
2017/03/24 15:31:18
Since there is really no notification here, may be
dtapuska
2017/03/24 20:16:33
That would mean that we are somehow transferring t
|
| main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&MainThreadEventQueue::DispatchSingleEvent, this)); |
| + FROM_HERE, base::Bind(&MainThreadEventQueue::DispatchEvents, this)); |
| } |
| -void MainThreadEventQueue::QueueEvent( |
| - std::unique_ptr<EventWithDispatchType> event) { |
| - bool is_raf_aligned = IsRafAlignedEvent(event->event()); |
| - size_t send_notification_count = 0; |
| +void MainThreadEventQueue::QueueEvent(std::unique_ptr<QueuedItem> event) { |
| + bool is_raf_aligned = IsRafAlignedEvent(event); |
| bool needs_main_frame = false; |
| + bool needs_post_task = false; |
| { |
|
mustaq
2017/03/24 15:31:18
I will take a look at this block again.
dtapuska
2017/03/24 20:16:31
Acknowledged.
|
| base::AutoLock lock(shared_state_lock_); |
| size_t size_before = shared_state_.events_.size(); |
| @@ -348,24 +476,15 @@ void MainThreadEventQueue::QueueEvent( |
| bool was_raf_aligned = false; |
| if (size_before > 0) { |
| was_raf_aligned = |
| - IsRafAlignedEvent(shared_state_.events_.at(size_before - 1)->event()); |
| + IsRafAlignedEvent(shared_state_.events_.at(size_before - 1)); |
| } |
| shared_state_.events_.Queue(std::move(event)); |
| size_t size_after = shared_state_.events_.size(); |
| if (size_before != size_after) { |
| - if (IsRafAlignedInputDisabled()) { |
| - send_notification_count = 1; |
| - } else if (!is_raf_aligned) { |
| - send_notification_count = 1; |
| - // If we had just enqueued a non-rAF input event we will send a series |
| - // of normal post messages to ensure they are all handled right away. |
| - for (size_t pos = size_after - 1; pos >= 1; --pos) { |
| - if (IsRafAlignedEvent(shared_state_.events_.at(pos - 1)->event())) |
| - send_notification_count++; |
| - else |
| - break; |
| - } |
| + if (IsRafAlignedInputDisabled() || !is_raf_aligned) { |
| + needs_post_task = !shared_state_.sent_post_task_; |
| + shared_state_.sent_post_task_ = true; |
| } else { |
| needs_main_frame = !shared_state_.sent_main_frame_request_; |
| shared_state_.sent_main_frame_request_ = true; |
| @@ -375,25 +494,31 @@ void MainThreadEventQueue::QueueEvent( |
| // the rAF alignment of the event may have and we need to schedule |
| // a notification. |
| bool is_coalesced_raf_aligned = |
| - IsRafAlignedEvent(shared_state_.events_.at(size_before - 1)->event()); |
| - if (was_raf_aligned != is_coalesced_raf_aligned) |
| - send_notification_count = 1; |
| + IsRafAlignedEvent(shared_state_.events_.at(size_before - 1)); |
| + if (was_raf_aligned != is_coalesced_raf_aligned) { |
| + needs_post_task = !shared_state_.sent_post_task_; |
| + shared_state_.sent_post_task_ = true; |
| + } |
| } |
| } |
| - for (size_t i = 0; i < send_notification_count; ++i) |
| + if (needs_post_task) |
| SendEventNotificationToMainThread(); |
| if (needs_main_frame) |
| client_->NeedsMainFrame(routing_id_); |
| } |
| -bool MainThreadEventQueue::IsRafAlignedInputDisabled() { |
| +bool MainThreadEventQueue::IsRafAlignedInputDisabled() const { |
| return !handle_raf_aligned_mouse_input_ && !handle_raf_aligned_touch_input_; |
| } |
| bool MainThreadEventQueue::IsRafAlignedEvent( |
| - const blink::WebInputEvent& event) { |
| - switch (event.type()) { |
| + const std::unique_ptr<QueuedItem>& item) const { |
| + if (!item->IsEvent()) |
| + return false; |
| + const EventWithDispatchType* event = |
| + static_cast<const EventWithDispatchType*>(item.get()); |
| + switch (event->event().type()) { |
| case blink::WebInputEvent::MouseMove: |
| case blink::WebInputEvent::MouseWheel: |
| return handle_raf_aligned_mouse_input_; |