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.
5 #include "ui/events/gesture_detection/motion_event_buffer.h"
7 #include "base/trace_event/trace_event.h"
8 #include "ui/events/gesture_detection/motion_event_generic.h"
13 // Latency added during resampling. A few milliseconds doesn't hurt much but
14 // reduces the impact of mispredicted touch positions.
15 const int kResampleLatencyMs
= 5;
17 // Minimum time difference between consecutive samples before attempting to
19 const int kResampleMinDeltaMs
= 2;
21 // Maximum time to predict forward from the last known state, to avoid
22 // predicting too far into the future. This time is further bounded by 50% of
23 // the last time delta.
24 const int kResampleMaxPredictionMs
= 8;
26 typedef ScopedVector
<MotionEventGeneric
> MotionEventVector
;
28 float Lerp(float a
, float b
, float alpha
) {
29 return a
+ alpha
* (b
- a
);
32 bool CanAddSample(const MotionEvent
& event0
, const MotionEvent
& event1
) {
33 DCHECK_EQ(event0
.GetAction(), MotionEvent::ACTION_MOVE
);
34 if (event1
.GetAction() != MotionEvent::ACTION_MOVE
)
37 const size_t pointer_count
= event0
.GetPointerCount();
38 if (pointer_count
!= event1
.GetPointerCount())
41 for (size_t event0_i
= 0; event0_i
< pointer_count
; ++event0_i
) {
42 const int id
= event0
.GetPointerId(event0_i
);
43 const int event1_i
= event1
.FindPointerIndexOfId(id
);
46 if (event0
.GetToolType(event0_i
) != event1
.GetToolType(event1_i
))
53 bool ShouldResampleTool(MotionEvent::ToolType tool
) {
54 return tool
== MotionEvent::TOOL_TYPE_UNKNOWN
||
55 tool
== MotionEvent::TOOL_TYPE_FINGER
;
58 size_t CountSamplesNoLaterThan(const MotionEventVector
& batch
,
59 base::TimeTicks time
) {
61 while (count
< batch
.size() && batch
[count
]->GetEventTime() <= time
)
66 MotionEventVector
ConsumeSamplesNoLaterThan(MotionEventVector
* batch
,
67 base::TimeTicks time
) {
69 size_t count
= CountSamplesNoLaterThan(*batch
, time
);
70 DCHECK_GE(batch
->size(), count
);
72 return MotionEventVector();
74 if (count
== batch
->size())
77 // TODO(jdduke): Use a ScopedDeque to work around this mess.
78 MotionEventVector unconsumed_batch
;
79 unconsumed_batch
.insert(
80 unconsumed_batch
.begin(), batch
->begin() + count
, batch
->end());
81 batch
->weak_erase(batch
->begin() + count
, batch
->end());
83 unconsumed_batch
.swap(*batch
);
84 DCHECK_GE(unconsumed_batch
.size(), 1U);
85 return unconsumed_batch
.Pass();
88 // Linearly interpolate the pointer position between two MotionEvent samples.
89 // Only pointers of finger or unknown type will be resampled.
90 PointerProperties
ResamplePointer(const MotionEvent
& event0
,
91 const MotionEvent
& event1
,
92 size_t event0_pointer_index
,
93 size_t event1_pointer_index
,
95 DCHECK_EQ(event0
.GetPointerId(event0_pointer_index
),
96 event1
.GetPointerId(event1_pointer_index
));
97 // If the tool should not be resampled, use the latest event in the valid
98 // horizon (i.e., the event no later than the time interpolated by alpha).
99 if (!ShouldResampleTool(event0
.GetToolType(event0_pointer_index
))) {
101 return PointerProperties(event1
, event1_pointer_index
);
103 return PointerProperties(event0
, event0_pointer_index
);
106 PointerProperties
p(event0
, event0_pointer_index
);
107 p
.x
= Lerp(p
.x
, event1
.GetX(event1_pointer_index
), alpha
);
108 p
.y
= Lerp(p
.y
, event1
.GetY(event1_pointer_index
), alpha
);
109 p
.raw_x
= Lerp(p
.raw_x
, event1
.GetRawX(event1_pointer_index
), alpha
);
110 p
.raw_y
= Lerp(p
.raw_y
, event1
.GetRawY(event1_pointer_index
), alpha
);
114 // Linearly interpolate the pointers between two event samples using the
115 // provided |resample_time|.
116 scoped_ptr
<MotionEventGeneric
> ResampleMotionEvent(
117 const MotionEvent
& event0
,
118 const MotionEvent
& event1
,
119 base::TimeTicks resample_time
) {
120 DCHECK_EQ(MotionEvent::ACTION_MOVE
, event0
.GetAction());
121 DCHECK_EQ(event0
.GetPointerCount(), event1
.GetPointerCount());
123 const base::TimeTicks time0
= event0
.GetEventTime();
124 const base::TimeTicks time1
= event1
.GetEventTime();
125 DCHECK(time0
< time1
);
126 DCHECK(time0
<= resample_time
);
128 const float alpha
= (resample_time
- time0
).InMillisecondsF() /
129 (time1
- time0
).InMillisecondsF();
131 scoped_ptr
<MotionEventGeneric
> event
;
132 const size_t pointer_count
= event0
.GetPointerCount();
133 DCHECK_EQ(pointer_count
, event1
.GetPointerCount());
134 for (size_t event0_i
= 0; event0_i
< pointer_count
; ++event0_i
) {
135 int event1_i
= event1
.FindPointerIndexOfId(event0
.GetPointerId(event0_i
));
136 DCHECK_NE(event1_i
, -1);
137 PointerProperties pointer
= ResamplePointer(
138 event0
, event1
, event0_i
, static_cast<size_t>(event1_i
), alpha
);
141 event
.reset(new MotionEventGeneric(
142 MotionEvent::ACTION_MOVE
, resample_time
, pointer
));
144 event
->PushPointer(pointer
);
149 event
->set_button_state(event0
.GetButtonState());
153 // Synthesize a compound MotionEventGeneric event from a sequence of events.
154 // Events must be in non-decreasing (time) order.
155 scoped_ptr
<MotionEventGeneric
> ConsumeSamples(MotionEventVector events
) {
156 DCHECK(!events
.empty());
157 scoped_ptr
<MotionEventGeneric
> event(events
.back());
158 for (size_t i
= 0; i
+ 1 < events
.size(); ++i
)
159 event
->PushHistoricalEvent(scoped_ptr
<MotionEvent
>(events
[i
]));
164 // Consume a series of event samples, attempting to synthesize a new, synthetic
165 // event if the samples and sample time meet certain interpolation/extrapolation
166 // conditions. If such conditions are met, the provided samples will be added
167 // to the synthetic event's history, otherwise, the samples will be used to
168 // generate a basic, compound event.
169 // TODO(jdduke): Revisit resampling to handle cases where alternating frames
170 // are resampled or resampling is otherwise inconsistent, e.g., a 90hz input
171 // and 60hz frame signal could phase-align such that even frames yield an
172 // extrapolated event and odd frames are not resampled, crbug.com/399381.
173 scoped_ptr
<MotionEventGeneric
> ConsumeSamplesAndTryResampling(
174 base::TimeTicks resample_time
,
175 MotionEventVector events
,
176 const MotionEvent
* next
) {
177 const ui::MotionEvent
* event0
= nullptr;
178 const ui::MotionEvent
* event1
= nullptr;
180 DCHECK(resample_time
< next
->GetEventTime());
181 // Interpolate between current sample and future sample.
182 event0
= events
.back();
184 } else if (events
.size() >= 2) {
185 // Extrapolate future sample using current sample and past sample.
186 event0
= events
[events
.size() - 2];
187 event1
= events
[events
.size() - 1];
189 const base::TimeTicks time1
= event1
->GetEventTime();
190 base::TimeTicks max_predict
=
192 std::min((event1
->GetEventTime() - event0
->GetEventTime()) / 2,
193 base::TimeDelta::FromMilliseconds(kResampleMaxPredictionMs
));
194 if (resample_time
> max_predict
) {
195 TRACE_EVENT_INSTANT2("input",
196 "MotionEventBuffer::TryResample prediction adjust",
197 TRACE_EVENT_SCOPE_THREAD
,
199 (resample_time
- time1
).InMilliseconds(),
201 (max_predict
- time1
).InMilliseconds());
202 resample_time
= max_predict
;
205 TRACE_EVENT_INSTANT0("input",
206 "MotionEventBuffer::TryResample insufficient data",
207 TRACE_EVENT_SCOPE_THREAD
);
208 return ConsumeSamples(events
.Pass());
213 const base::TimeTicks time0
= event0
->GetEventTime();
214 const base::TimeTicks time1
= event1
->GetEventTime();
215 base::TimeDelta delta
= time1
- time0
;
216 if (delta
< base::TimeDelta::FromMilliseconds(kResampleMinDeltaMs
)) {
217 TRACE_EVENT_INSTANT1("input",
218 "MotionEventBuffer::TryResample failure",
219 TRACE_EVENT_SCOPE_THREAD
,
220 "event_delta_too_small(ms)",
221 delta
.InMilliseconds());
222 return ConsumeSamples(events
.Pass());
225 scoped_ptr
<MotionEventGeneric
> resampled_event
=
226 ResampleMotionEvent(*event0
, *event1
, resample_time
);
227 for (size_t i
= 0; i
< events
.size(); ++i
)
228 resampled_event
->PushHistoricalEvent(scoped_ptr
<MotionEvent
>(events
[i
]));
230 return resampled_event
.Pass();
235 MotionEventBuffer::MotionEventBuffer(MotionEventBufferClient
* client
,
236 bool enable_resampling
)
237 : client_(client
), resample_(enable_resampling
) {
240 MotionEventBuffer::~MotionEventBuffer() {
243 void MotionEventBuffer::OnMotionEvent(const MotionEvent
& event
) {
244 DCHECK_EQ(0U, event
.GetHistorySize());
245 if (event
.GetAction() != MotionEvent::ACTION_MOVE
) {
246 last_extrapolated_event_time_
= base::TimeTicks();
247 if (!buffered_events_
.empty())
248 FlushWithoutResampling(buffered_events_
.Pass());
249 client_
->ForwardMotionEvent(event
);
253 // Guard against events that are *older* than the last one that may have been
254 // artificially synthesized.
255 if (!last_extrapolated_event_time_
.is_null()) {
256 DCHECK(buffered_events_
.empty());
257 if (event
.GetEventTime() < last_extrapolated_event_time_
)
259 last_extrapolated_event_time_
= base::TimeTicks();
262 scoped_ptr
<MotionEventGeneric
> clone
= MotionEventGeneric::CloneEvent(event
);
263 if (buffered_events_
.empty()) {
264 buffered_events_
.push_back(clone
.Pass());
265 client_
->SetNeedsFlush();
269 if (CanAddSample(*buffered_events_
.front(), *clone
)) {
270 DCHECK(buffered_events_
.back()->GetEventTime() <= clone
->GetEventTime());
272 FlushWithoutResampling(buffered_events_
.Pass());
275 buffered_events_
.push_back(clone
.Pass());
276 // No need to request another flush as the first event will have requested it.
279 void MotionEventBuffer::Flush(base::TimeTicks frame_time
) {
280 if (buffered_events_
.empty())
283 // Shifting the sample time back slightly minimizes the potential for
284 // misprediction when extrapolating events.
286 frame_time
-= base::TimeDelta::FromMilliseconds(kResampleLatencyMs
);
288 // TODO(jdduke): Use a persistent MotionEventVector vector for temporary
290 MotionEventVector
events(
291 ConsumeSamplesNoLaterThan(&buffered_events_
, frame_time
));
292 if (events
.empty()) {
293 DCHECK(!buffered_events_
.empty());
294 client_
->SetNeedsFlush();
298 if (!resample_
|| (events
.size() == 1 && buffered_events_
.empty())) {
299 FlushWithoutResampling(events
.Pass());
300 if (!buffered_events_
.empty())
301 client_
->SetNeedsFlush();
305 FlushWithResampling(events
.Pass(), frame_time
);
308 void MotionEventBuffer::FlushWithResampling(MotionEventVector events
,
309 base::TimeTicks resample_time
) {
310 DCHECK(!events
.empty());
311 base::TimeTicks original_event_time
= events
.back()->GetEventTime();
312 const MotionEvent
* next_event
=
313 !buffered_events_
.empty() ? buffered_events_
.front() : nullptr;
315 scoped_ptr
<MotionEventGeneric
> resampled_event
=
316 ConsumeSamplesAndTryResampling(resample_time
, events
.Pass(), next_event
);
317 DCHECK(resampled_event
);
319 // Log the extrapolated event time, guarding against subsequently queued
320 // events that might have an earlier timestamp.
321 if (!next_event
&& resampled_event
->GetEventTime() > original_event_time
) {
322 last_extrapolated_event_time_
= resampled_event
->GetEventTime();
324 last_extrapolated_event_time_
= base::TimeTicks();
327 client_
->ForwardMotionEvent(*resampled_event
);
328 if (!buffered_events_
.empty())
329 client_
->SetNeedsFlush();
332 void MotionEventBuffer::FlushWithoutResampling(MotionEventVector events
) {
333 last_extrapolated_event_time_
= base::TimeTicks();
337 client_
->ForwardMotionEvent(*ConsumeSamples(events
.Pass()));