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/debug/trace_event.h"
8 #include "ui/events/gesture_detection/motion_event.h"
9 #include "ui/events/gesture_detection/motion_event_generic.h"
14 // Latency added during resampling. A few milliseconds doesn't hurt much but
15 // reduces the impact of mispredicted touch positions.
16 const int kResampleLatencyMs
= 5;
18 // Minimum time difference between consecutive samples before attempting to
20 const int kResampleMinDeltaMs
= 2;
22 // Maximum time to predict forward from the last known state, to avoid
23 // predicting too far into the future. This time is further bounded by 50% of
24 // the last time delta.
25 const int kResampleMaxPredictionMs
= 8;
27 typedef ScopedVector
<MotionEvent
> MotionEventVector
;
29 float Lerp(float a
, float b
, float alpha
) {
30 return a
+ alpha
* (b
- a
);
33 bool CanAddSample(const MotionEvent
& event0
, const MotionEvent
& event1
) {
34 DCHECK_EQ(event0
.GetAction(), MotionEvent::ACTION_MOVE
);
35 if (event1
.GetAction() != MotionEvent::ACTION_MOVE
)
38 const size_t pointer_count
= event0
.GetPointerCount();
39 if (pointer_count
!= event1
.GetPointerCount())
42 for (size_t event0_i
= 0; event0_i
< pointer_count
; ++event0_i
) {
43 const int id
= event0
.GetPointerId(event0_i
);
44 const int event1_i
= event1
.FindPointerIndexOfId(id
);
47 if (event0
.GetToolType(event0_i
) != event1
.GetToolType(event1_i
))
54 bool ShouldResampleTool(MotionEvent::ToolType tool
) {
55 return tool
== MotionEvent::TOOL_TYPE_UNKNOWN
||
56 tool
== MotionEvent::TOOL_TYPE_FINGER
;
59 size_t CountSamplesNoLaterThan(const MotionEventVector
& batch
,
60 base::TimeTicks time
) {
62 while (count
< batch
.size() && batch
[count
]->GetEventTime() <= time
)
67 MotionEventVector
ConsumeSamplesNoLaterThan(MotionEventVector
* batch
,
68 base::TimeTicks time
) {
70 size_t count
= CountSamplesNoLaterThan(*batch
, time
);
71 DCHECK_GE(batch
->size(), count
);
73 return MotionEventVector();
75 if (count
== batch
->size())
78 // TODO(jdduke): Use a ScopedDeque to work around this mess.
79 MotionEventVector unconsumed_batch
;
80 unconsumed_batch
.insert(
81 unconsumed_batch
.begin(), batch
->begin() + count
, batch
->end());
82 batch
->weak_erase(batch
->begin() + count
, batch
->end());
84 unconsumed_batch
.swap(*batch
);
85 DCHECK_GE(unconsumed_batch
.size(), 1U);
86 return unconsumed_batch
.Pass();
89 PointerProperties
PointerFromMotionEvent(const MotionEvent
& event
,
90 size_t pointer_index
) {
91 PointerProperties result
;
92 result
.id
= event
.GetPointerId(pointer_index
);
93 result
.tool_type
= event
.GetToolType(pointer_index
);
94 result
.x
= event
.GetX(pointer_index
);
95 result
.y
= event
.GetY(pointer_index
);
96 result
.raw_x
= event
.GetRawX(pointer_index
);
97 result
.raw_y
= event
.GetRawY(pointer_index
);
98 result
.pressure
= event
.GetPressure(pointer_index
);
99 result
.touch_major
= event
.GetTouchMajor(pointer_index
);
103 PointerProperties
ResamplePointer(const MotionEvent
& event0
,
104 const MotionEvent
& event1
,
105 size_t event0_pointer_index
,
106 size_t event1_pointer_index
,
108 DCHECK_EQ(event0
.GetPointerId(event0_pointer_index
),
109 event1
.GetPointerId(event1_pointer_index
));
110 // If the tool should not be resampled, use the latest event in the valid
111 // horizon (i.e., the event no later than the time interpolated by alpha).
112 if (!ShouldResampleTool(event0
.GetToolType(event0_pointer_index
))) {
114 return PointerFromMotionEvent(event1
, event1_pointer_index
);
116 return PointerFromMotionEvent(event0
, event0_pointer_index
);
119 PointerProperties
p(PointerFromMotionEvent(event0
, event0_pointer_index
));
120 p
.x
= Lerp(p
.x
, event1
.GetX(event1_pointer_index
), alpha
);
121 p
.y
= Lerp(p
.y
, event1
.GetY(event1_pointer_index
), alpha
);
122 p
.raw_x
= Lerp(p
.raw_x
, event1
.GetRawX(event1_pointer_index
), alpha
);
123 p
.raw_y
= Lerp(p
.raw_y
, event1
.GetRawY(event1_pointer_index
), alpha
);
127 scoped_ptr
<MotionEvent
> ResampleMotionEvent(const MotionEvent
& event0
,
128 const MotionEvent
& event1
,
129 base::TimeTicks resample_time
) {
130 DCHECK_EQ(MotionEvent::ACTION_MOVE
, event0
.GetAction());
131 DCHECK_EQ(event0
.GetPointerCount(), event1
.GetPointerCount());
133 const base::TimeTicks time0
= event0
.GetEventTime();
134 const base::TimeTicks time1
= event1
.GetEventTime();
135 DCHECK(time0
< time1
);
136 DCHECK(time0
<= resample_time
);
138 const float alpha
= (resample_time
- time0
).InMillisecondsF() /
139 (time1
- time0
).InMillisecondsF();
141 scoped_ptr
<MotionEventGeneric
> event
;
142 const size_t pointer_count
= event0
.GetPointerCount();
143 DCHECK_EQ(pointer_count
, event1
.GetPointerCount());
144 for (size_t event0_i
= 0; event0_i
< pointer_count
; ++event0_i
) {
145 int event1_i
= event1
.FindPointerIndexOfId(event0
.GetPointerId(event0_i
));
146 DCHECK_NE(event1_i
, -1);
147 PointerProperties pointer
= ResamplePointer(
148 event0
, event1
, event0_i
, static_cast<size_t>(event1_i
), alpha
);
151 event
.reset(new MotionEventGeneric(
152 MotionEvent::ACTION_MOVE
, resample_time
, pointer
));
154 event
->PushPointer(pointer
);
159 event
->set_id(event0
.GetId());
160 event
->set_action_index(event0
.GetActionIndex());
161 event
->set_button_state(event0
.GetButtonState());
163 return event
.PassAs
<MotionEvent
>();
166 // MotionEvent implementation for storing multiple events, with the most
167 // recent event used as the base event, and prior events used as the history.
168 class CompoundMotionEvent
: public ui::MotionEvent
{
170 explicit CompoundMotionEvent(MotionEventVector events
)
171 : events_(events
.Pass()) {
172 DCHECK_GE(events_
.size(), 1U);
174 virtual ~CompoundMotionEvent() {}
176 virtual int GetId() const OVERRIDE
{ return latest().GetId(); }
177 virtual Action
GetAction() const OVERRIDE
{ return latest().GetAction(); }
178 virtual int GetActionIndex() const OVERRIDE
{
179 return latest().GetActionIndex();
181 virtual size_t GetPointerCount() const OVERRIDE
{
182 return latest().GetPointerCount();
184 virtual int GetPointerId(size_t pointer_index
) const OVERRIDE
{
185 return latest().GetPointerId(pointer_index
);
187 virtual float GetX(size_t pointer_index
) const OVERRIDE
{
188 return latest().GetX(pointer_index
);
190 virtual float GetY(size_t pointer_index
) const OVERRIDE
{
191 return latest().GetY(pointer_index
);
193 virtual float GetRawX(size_t pointer_index
) const OVERRIDE
{
194 return latest().GetRawX(pointer_index
);
196 virtual float GetRawY(size_t pointer_index
) const OVERRIDE
{
197 return latest().GetRawY(pointer_index
);
199 virtual float GetTouchMajor(size_t pointer_index
) const OVERRIDE
{
200 return latest().GetTouchMajor(pointer_index
);
202 virtual float GetPressure(size_t pointer_index
) const OVERRIDE
{
203 return latest().GetPressure(pointer_index
);
205 virtual ToolType
GetToolType(size_t pointer_index
) const OVERRIDE
{
206 return latest().GetToolType(pointer_index
);
208 virtual int GetButtonState() const OVERRIDE
{
209 return latest().GetButtonState();
211 virtual base::TimeTicks
GetEventTime() const OVERRIDE
{
212 return latest().GetEventTime();
214 virtual size_t GetHistorySize() const OVERRIDE
{ return events_
.size() - 1; }
215 virtual base::TimeTicks
GetHistoricalEventTime(
216 size_t historical_index
) const OVERRIDE
{
217 DCHECK_LT(historical_index
, GetHistorySize());
218 return events_
[historical_index
]->GetEventTime();
220 virtual float GetHistoricalTouchMajor(
221 size_t pointer_index
,
222 size_t historical_index
) const OVERRIDE
{
223 DCHECK_LT(historical_index
, GetHistorySize());
224 return events_
[historical_index
]->GetTouchMajor();
226 virtual float GetHistoricalX(size_t pointer_index
,
227 size_t historical_index
) const OVERRIDE
{
228 DCHECK_LT(historical_index
, GetHistorySize());
229 return events_
[historical_index
]->GetX(pointer_index
);
231 virtual float GetHistoricalY(size_t pointer_index
,
232 size_t historical_index
) const OVERRIDE
{
233 DCHECK_LT(historical_index
, GetHistorySize());
234 return events_
[historical_index
]->GetY(pointer_index
);
236 virtual scoped_ptr
<MotionEvent
> Clone() const OVERRIDE
{
237 MotionEventVector cloned_events
;
238 cloned_events
.reserve(events_
.size());
239 for (size_t i
= 0; i
< events_
.size(); ++i
)
240 cloned_events
.push_back(events_
[i
]->Clone().release());
241 return scoped_ptr
<MotionEvent
>(
242 new CompoundMotionEvent(cloned_events
.Pass()));
244 virtual scoped_ptr
<MotionEvent
> Cancel() const OVERRIDE
{
245 return latest().Cancel();
248 // Returns the new, resampled event, or NULL if none was created.
249 // TODO(jdduke): Revisit resampling to handle cases where alternating frames
250 // are resampled or resampling is otherwise inconsistent, e.g., a 90hz input
251 // and 60hz frame signal could phase-align such that even frames yield an
252 // extrapolated event and odd frames are not resampled, crbug.com/399381.
253 const MotionEvent
* TryResample(base::TimeTicks resample_time
,
254 const ui::MotionEvent
* next
) {
255 DCHECK_EQ(GetAction(), ACTION_MOVE
);
256 const ui::MotionEvent
* event0
= NULL
;
257 const ui::MotionEvent
* event1
= NULL
;
259 DCHECK(resample_time
< next
->GetEventTime());
260 // Interpolate between current sample and future sample.
261 event0
= events_
.back();
263 } else if (events_
.size() >= 2) {
264 // Extrapolate future sample using current sample and past sample.
265 event0
= events_
[events_
.size() - 2];
266 event1
= events_
[events_
.size() - 1];
268 const base::TimeTicks time1
= event1
->GetEventTime();
269 base::TimeTicks max_predict
=
271 std::min((event1
->GetEventTime() - event0
->GetEventTime()) / 2,
272 base::TimeDelta::FromMilliseconds(kResampleMaxPredictionMs
));
273 if (resample_time
> max_predict
) {
274 TRACE_EVENT_INSTANT2("input",
275 "MotionEventBuffer::TryResample prediction adjust",
276 TRACE_EVENT_SCOPE_THREAD
,
278 (resample_time
- time1
).InMilliseconds(),
280 (max_predict
- time1
).InMilliseconds());
281 resample_time
= max_predict
;
284 TRACE_EVENT_INSTANT0("input",
285 "MotionEventBuffer::TryResample insufficient data",
286 TRACE_EVENT_SCOPE_THREAD
);
292 const base::TimeTicks time0
= event0
->GetEventTime();
293 const base::TimeTicks time1
= event1
->GetEventTime();
294 base::TimeDelta delta
= time1
- time0
;
295 if (delta
< base::TimeDelta::FromMilliseconds(kResampleMinDeltaMs
)) {
296 TRACE_EVENT_INSTANT1("input",
297 "MotionEventBuffer::TryResample failure",
298 TRACE_EVENT_SCOPE_THREAD
,
299 "event_delta_too_small(ms)",
300 delta
.InMilliseconds());
305 ResampleMotionEvent(*event0
, *event1
, resample_time
).release());
306 return events_
.back();
309 size_t samples() const { return events_
.size(); }
312 const MotionEvent
& latest() const { return *events_
.back(); }
314 // Events are in order from oldest to newest.
315 MotionEventVector events_
;
317 DISALLOW_COPY_AND_ASSIGN(CompoundMotionEvent
);
322 MotionEventBuffer::MotionEventBuffer(MotionEventBufferClient
* client
,
323 bool enable_resampling
)
324 : client_(client
), resample_(enable_resampling
) {
327 MotionEventBuffer::~MotionEventBuffer() {
330 void MotionEventBuffer::OnMotionEvent(const MotionEvent
& event
) {
331 if (event
.GetAction() != MotionEvent::ACTION_MOVE
) {
332 last_extrapolated_event_time_
= base::TimeTicks();
333 if (!buffered_events_
.empty())
334 FlushWithoutResampling(buffered_events_
.Pass());
335 client_
->ForwardMotionEvent(event
);
339 // Guard against events that are *older* than the last one that may have been
340 // artificially synthesized.
341 if (!last_extrapolated_event_time_
.is_null()) {
342 DCHECK(buffered_events_
.empty());
343 if (event
.GetEventTime() < last_extrapolated_event_time_
)
345 last_extrapolated_event_time_
= base::TimeTicks();
348 scoped_ptr
<MotionEvent
> clone
= event
.Clone();
349 if (buffered_events_
.empty()) {
350 buffered_events_
.push_back(clone
.release());
351 client_
->SetNeedsFlush();
355 if (CanAddSample(*buffered_events_
.front(), *clone
)) {
356 DCHECK(buffered_events_
.back()->GetEventTime() <= clone
->GetEventTime());
358 FlushWithoutResampling(buffered_events_
.Pass());
361 buffered_events_
.push_back(clone
.release());
362 // No need to request another flush as the first event will have requested it.
365 void MotionEventBuffer::Flush(base::TimeTicks frame_time
) {
366 if (buffered_events_
.empty())
369 // Shifting the sample time back slightly minimizes the potential for
370 // misprediction when extrapolating events.
372 frame_time
-= base::TimeDelta::FromMilliseconds(kResampleLatencyMs
);
374 // TODO(jdduke): Use a persistent MotionEventVector vector for temporary
376 MotionEventVector
events(
377 ConsumeSamplesNoLaterThan(&buffered_events_
, frame_time
));
378 if (events
.empty()) {
379 DCHECK(!buffered_events_
.empty());
380 client_
->SetNeedsFlush();
384 if (!resample_
|| (events
.size() == 1 && buffered_events_
.empty())) {
385 FlushWithoutResampling(events
.Pass());
386 if (!buffered_events_
.empty())
387 client_
->SetNeedsFlush();
391 CompoundMotionEvent
resampled_event(events
.Pass());
392 base::TimeTicks original_event_time
= resampled_event
.GetEventTime();
393 const MotionEvent
* next_event
=
394 !buffered_events_
.empty() ? buffered_events_
.front() : NULL
;
396 // Try to interpolate/extrapolate a new event at |frame_time|. Note that
397 // |new_event|, if non-NULL, is owned by |resampled_event_|.
398 const MotionEvent
* new_event
=
399 resampled_event
.TryResample(frame_time
, next_event
);
401 // Log the extrapolated event time, guarding against subsequently queued
402 // events that might have an earlier timestamp.
403 if (!next_event
&& new_event
&&
404 new_event
->GetEventTime() > original_event_time
) {
405 last_extrapolated_event_time_
= new_event
->GetEventTime();
407 last_extrapolated_event_time_
= base::TimeTicks();
410 client_
->ForwardMotionEvent(resampled_event
);
411 if (!buffered_events_
.empty())
412 client_
->SetNeedsFlush();
415 void MotionEventBuffer::FlushWithoutResampling(MotionEventVector events
) {
416 last_extrapolated_event_time_
= base::TimeTicks();
420 if (events
.size() == 1) {
421 // Avoid CompoundEvent creation to prevent unnecessary allocations.
422 scoped_ptr
<MotionEvent
> event(events
.front());
424 client_
->ForwardMotionEvent(*event
);
428 CompoundMotionEvent
compound_event(events
.Pass());
429 client_
->ForwardMotionEvent(compound_event
);