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_; |