Commit 59886ae9 authored by jdduke's avatar jdduke Committed by Commit bot

Avoid heap allocation with uncoalesced touch events

Each enqueued event in the browser-side touch queue maintains a vector
of events that must be ack'ed, allowing each enqueued event to coalesce
with subsequent events delivered from the platform. Currently, the
vector is always populated with the first event, even if no coalescing
occurs. Instead, defer population until coalescing occurs, optimizing
the common case for most events by avoiding an extra heap allocation for
every touch. This can save ~5-10us per forwarded touch event on a typical
Android device.

BUG=384562

Review URL: https://codereview.chromium.org/565033002

Cr-Commit-Position: refs/heads/master@{#295998}
parent ab581815
...@@ -253,15 +253,9 @@ class TouchEventQueue::TouchMoveSlopSuppressor { ...@@ -253,15 +253,9 @@ class TouchEventQueue::TouchMoveSlopSuppressor {
// the Client receives the event with their original timestamp. // the Client receives the event with their original timestamp.
class CoalescedWebTouchEvent { class CoalescedWebTouchEvent {
public: public:
// Events for which |async| is true will not be ack'ed to the client after the CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
// corresponding ack is received following dispatch. bool suppress_client_ack)
CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, bool async) : coalesced_event_(event), suppress_client_ack_(suppress_client_ack) {
: coalesced_event_(event) {
if (async)
coalesced_event_.event.cancelable = false;
else
events_to_ack_.push_back(event);
TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this); TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
} }
...@@ -273,42 +267,48 @@ class CoalescedWebTouchEvent { ...@@ -273,42 +267,48 @@ class CoalescedWebTouchEvent {
// the event was coalesced. // the event was coalesced.
bool CoalesceEventIfPossible( bool CoalesceEventIfPossible(
const TouchEventWithLatencyInfo& event_with_latency) { const TouchEventWithLatencyInfo& event_with_latency) {
if (!WillDispatchAckToClient()) if (suppress_client_ack_)
return false; return false;
if (!coalesced_event_.CanCoalesceWith(event_with_latency)) if (!coalesced_event_.CanCoalesceWith(event_with_latency))
return false; return false;
// Addition of the first event to |uncoaleseced_events_to_ack_| is deferred
// until the first coalesced event, optimizing the (common) case where the
// event is not coalesced at all.
if (uncoaleseced_events_to_ack_.empty())
uncoaleseced_events_to_ack_.push_back(coalesced_event_);
TRACE_EVENT_INSTANT0( TRACE_EVENT_INSTANT0(
"input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
coalesced_event_.CoalesceWith(event_with_latency); coalesced_event_.CoalesceWith(event_with_latency);
events_to_ack_.push_back(event_with_latency); uncoaleseced_events_to_ack_.push_back(event_with_latency);
DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
return true; return true;
} }
void UpdateLatencyInfoForAck(const ui::LatencyInfo& renderer_latency_info) {
if (!WillDispatchAckToClient())
return;
for (WebTouchEventWithLatencyList::iterator iter = events_to_ack_.begin(),
end = events_to_ack_.end();
iter != end;
++iter) {
iter->latency.AddNewLatencyFrom(renderer_latency_info);
}
}
void DispatchAckToClient(InputEventAckState ack_result, void DispatchAckToClient(InputEventAckState ack_result,
const ui::LatencyInfo* optional_latency_info,
TouchEventQueueClient* client) { TouchEventQueueClient* client) {
DCHECK(client); DCHECK(client);
if (!WillDispatchAckToClient()) if (suppress_client_ack_)
return; return;
for (WebTouchEventWithLatencyList::const_iterator if (uncoaleseced_events_to_ack_.empty()) {
iter = events_to_ack_.begin(), if (optional_latency_info)
end = events_to_ack_.end(); coalesced_event_.latency.AddNewLatencyFrom(*optional_latency_info);
client->OnTouchEventAck(coalesced_event_, ack_result);
return;
}
DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
for (WebTouchEventWithLatencyList::iterator
iter = uncoaleseced_events_to_ack_.begin(),
end = uncoaleseced_events_to_ack_.end();
iter != end; iter != end;
++iter) { ++iter) {
if (optional_latency_info)
iter->latency.AddNewLatencyFrom(*optional_latency_info);
client->OnTouchEventAck(*iter, ack_result); client->OnTouchEventAck(*iter, ack_result);
} }
} }
...@@ -318,15 +318,16 @@ class CoalescedWebTouchEvent { ...@@ -318,15 +318,16 @@ class CoalescedWebTouchEvent {
} }
private: private:
bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); }
// This is the event that is forwarded to the renderer. // This is the event that is forwarded to the renderer.
TouchEventWithLatencyInfo coalesced_event_; TouchEventWithLatencyInfo coalesced_event_;
// This is the list of the original events that were coalesced, each requiring // This is the list of the original events that were coalesced, each requiring
// future ack dispatch to the client. // future ack dispatch to the client.
// Note that this will be empty if no coalescing has occurred.
typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
WebTouchEventWithLatencyList events_to_ack_; WebTouchEventWithLatencyList uncoaleseced_events_to_ack_;
bool suppress_client_ack_;
DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
}; };
...@@ -695,20 +696,19 @@ void TouchEventQueue::FlushQueue() { ...@@ -695,20 +696,19 @@ void TouchEventQueue::FlushQueue() {
} }
void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
AckTouchEventToClient(ack_result, PopTouchEvent()); AckTouchEventToClient(ack_result, PopTouchEvent(), NULL);
} }
void TouchEventQueue::PopTouchEventToClient( void TouchEventQueue::PopTouchEventToClient(
InputEventAckState ack_result, InputEventAckState ack_result,
const LatencyInfo& renderer_latency_info) { const LatencyInfo& renderer_latency_info) {
scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent(); AckTouchEventToClient(ack_result, PopTouchEvent(), &renderer_latency_info);
acked_event->UpdateLatencyInfoForAck(renderer_latency_info);
AckTouchEventToClient(ack_result, acked_event.Pass());
} }
void TouchEventQueue::AckTouchEventToClient( void TouchEventQueue::AckTouchEventToClient(
InputEventAckState ack_result, InputEventAckState ack_result,
scoped_ptr<CoalescedWebTouchEvent> acked_event) { scoped_ptr<CoalescedWebTouchEvent> acked_event,
const ui::LatencyInfo* optional_latency_info) {
DCHECK(acked_event); DCHECK(acked_event);
DCHECK(!dispatching_touch_ack_); DCHECK(!dispatching_touch_ack_);
UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result); UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result);
...@@ -717,7 +717,7 @@ void TouchEventQueue::AckTouchEventToClient( ...@@ -717,7 +717,7 @@ void TouchEventQueue::AckTouchEventToClient(
// to the renderer, or touch-events being queued. // to the renderer, or touch-events being queued.
base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack( base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack(
&dispatching_touch_ack_, acked_event.get()); &dispatching_touch_ack_, acked_event.get());
acked_event->DispatchAckToClient(ack_result, client_); acked_event->DispatchAckToClient(ack_result, optional_latency_info, client_);
} }
scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() {
......
...@@ -160,9 +160,11 @@ class CONTENT_EXPORT TouchEventQueue { ...@@ -160,9 +160,11 @@ class CONTENT_EXPORT TouchEventQueue {
void PopTouchEventToClient(InputEventAckState ack_result, void PopTouchEventToClient(InputEventAckState ack_result,
const ui::LatencyInfo& renderer_latency_info); const ui::LatencyInfo& renderer_latency_info);
// Ack all coalesced events in |acked_event| to the client with |ack_result|. // Ack all coalesced events in |acked_event| to the client with |ack_result|,
// updating the acked events with |optional_latency_info| if it exists.
void AckTouchEventToClient(InputEventAckState ack_result, void AckTouchEventToClient(InputEventAckState ack_result,
scoped_ptr<CoalescedWebTouchEvent> acked_event); scoped_ptr<CoalescedWebTouchEvent> acked_event,
const ui::LatencyInfo* optional_latency_info);
// Safely pop the head of the queue. // Safely pop the head of the queue.
scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent(); scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment