• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ui/events/gesture_detection/motion_event_buffer.h"
6 
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"
10 
11 namespace ui {
12 namespace {
13 
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;
17 
18 // Minimum time difference between consecutive samples before attempting to
19 // resample.
20 const int kResampleMinDeltaMs = 2;
21 
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;
26 
27 typedef ScopedVector<MotionEvent> MotionEventVector;
28 
Lerp(float a,float b,float alpha)29 float Lerp(float a, float b, float alpha) {
30   return a + alpha * (b - a);
31 }
32 
CanAddSample(const MotionEvent & event0,const MotionEvent & event1)33 bool CanAddSample(const MotionEvent& event0, const MotionEvent& event1) {
34   DCHECK_EQ(event0.GetAction(), MotionEvent::ACTION_MOVE);
35   if (event1.GetAction() != MotionEvent::ACTION_MOVE)
36     return false;
37 
38   const size_t pointer_count = event0.GetPointerCount();
39   if (pointer_count != event1.GetPointerCount())
40     return false;
41 
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);
45     if (event1_i == -1)
46       return false;
47     if (event0.GetToolType(event0_i) != event1.GetToolType(event1_i))
48       return false;
49   }
50 
51   return true;
52 }
53 
ShouldResampleTool(MotionEvent::ToolType tool)54 bool ShouldResampleTool(MotionEvent::ToolType tool) {
55   return tool == MotionEvent::TOOL_TYPE_UNKNOWN ||
56          tool == MotionEvent::TOOL_TYPE_FINGER;
57 }
58 
CountSamplesNoLaterThan(const MotionEventVector & batch,base::TimeTicks time)59 size_t CountSamplesNoLaterThan(const MotionEventVector& batch,
60                                base::TimeTicks time) {
61   size_t count = 0;
62   while (count < batch.size() && batch[count]->GetEventTime() <= time)
63     ++count;
64   return count;
65 }
66 
ConsumeSamplesNoLaterThan(MotionEventVector * batch,base::TimeTicks time)67 MotionEventVector ConsumeSamplesNoLaterThan(MotionEventVector* batch,
68                                             base::TimeTicks time) {
69   DCHECK(batch);
70   size_t count = CountSamplesNoLaterThan(*batch, time);
71   DCHECK_GE(batch->size(), count);
72   if (count == 0)
73     return MotionEventVector();
74 
75   if (count == batch->size())
76     return batch->Pass();
77 
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());
83 
84   unconsumed_batch.swap(*batch);
85   DCHECK_GE(unconsumed_batch.size(), 1U);
86   return unconsumed_batch.Pass();
87 }
88 
PointerFromMotionEvent(const MotionEvent & event,size_t pointer_index)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);
100   result.touch_minor = event.GetTouchMinor(pointer_index);
101   result.orientation = event.GetOrientation(pointer_index);
102   return result;
103 }
104 
ResamplePointer(const MotionEvent & event0,const MotionEvent & event1,size_t event0_pointer_index,size_t event1_pointer_index,float alpha)105 PointerProperties ResamplePointer(const MotionEvent& event0,
106                                   const MotionEvent& event1,
107                                   size_t event0_pointer_index,
108                                   size_t event1_pointer_index,
109                                   float alpha) {
110   DCHECK_EQ(event0.GetPointerId(event0_pointer_index),
111             event1.GetPointerId(event1_pointer_index));
112   // If the tool should not be resampled, use the latest event in the valid
113   // horizon (i.e., the event no later than the time interpolated by alpha).
114   if (!ShouldResampleTool(event0.GetToolType(event0_pointer_index))) {
115     if (alpha > 1)
116       return PointerFromMotionEvent(event1, event1_pointer_index);
117     else
118       return PointerFromMotionEvent(event0, event0_pointer_index);
119   }
120 
121   PointerProperties p(PointerFromMotionEvent(event0, event0_pointer_index));
122   p.x = Lerp(p.x, event1.GetX(event1_pointer_index), alpha);
123   p.y = Lerp(p.y, event1.GetY(event1_pointer_index), alpha);
124   p.raw_x = Lerp(p.raw_x, event1.GetRawX(event1_pointer_index), alpha);
125   p.raw_y = Lerp(p.raw_y, event1.GetRawY(event1_pointer_index), alpha);
126   return p;
127 }
128 
ResampleMotionEvent(const MotionEvent & event0,const MotionEvent & event1,base::TimeTicks resample_time)129 scoped_ptr<MotionEvent> ResampleMotionEvent(const MotionEvent& event0,
130                                             const MotionEvent& event1,
131                                             base::TimeTicks resample_time) {
132   DCHECK_EQ(MotionEvent::ACTION_MOVE, event0.GetAction());
133   DCHECK_EQ(event0.GetPointerCount(), event1.GetPointerCount());
134 
135   const base::TimeTicks time0 = event0.GetEventTime();
136   const base::TimeTicks time1 = event1.GetEventTime();
137   DCHECK(time0 < time1);
138   DCHECK(time0 <= resample_time);
139 
140   const float alpha = (resample_time - time0).InMillisecondsF() /
141                       (time1 - time0).InMillisecondsF();
142 
143   scoped_ptr<MotionEventGeneric> event;
144   const size_t pointer_count = event0.GetPointerCount();
145   DCHECK_EQ(pointer_count, event1.GetPointerCount());
146   for (size_t event0_i = 0; event0_i < pointer_count; ++event0_i) {
147     int event1_i = event1.FindPointerIndexOfId(event0.GetPointerId(event0_i));
148     DCHECK_NE(event1_i, -1);
149     PointerProperties pointer = ResamplePointer(
150         event0, event1, event0_i, static_cast<size_t>(event1_i), alpha);
151 
152     if (event0_i == 0) {
153       event.reset(new MotionEventGeneric(
154           MotionEvent::ACTION_MOVE, resample_time, pointer));
155     } else {
156       event->PushPointer(pointer);
157     }
158   }
159 
160   DCHECK(event);
161   event->set_id(event0.GetId());
162   event->set_action_index(event0.GetActionIndex());
163   event->set_button_state(event0.GetButtonState());
164 
165   return event.PassAs<MotionEvent>();
166 }
167 
168 // MotionEvent implementation for storing multiple events, with the most
169 // recent event used as the base event, and prior events used as the history.
170 class CompoundMotionEvent : public ui::MotionEvent {
171  public:
CompoundMotionEvent(MotionEventVector events)172   explicit CompoundMotionEvent(MotionEventVector events)
173       : events_(events.Pass()) {
174     DCHECK_GE(events_.size(), 1U);
175   }
~CompoundMotionEvent()176   virtual ~CompoundMotionEvent() {}
177 
GetId() const178   virtual int GetId() const OVERRIDE { return latest().GetId(); }
179 
GetAction() const180   virtual Action GetAction() const OVERRIDE { return latest().GetAction(); }
181 
GetActionIndex() const182   virtual int GetActionIndex() const OVERRIDE {
183     return latest().GetActionIndex();
184   }
185 
GetPointerCount() const186   virtual size_t GetPointerCount() const OVERRIDE {
187     return latest().GetPointerCount();
188   }
189 
GetPointerId(size_t pointer_index) const190   virtual int GetPointerId(size_t pointer_index) const OVERRIDE {
191     return latest().GetPointerId(pointer_index);
192   }
193 
GetX(size_t pointer_index) const194   virtual float GetX(size_t pointer_index) const OVERRIDE {
195     return latest().GetX(pointer_index);
196   }
197 
GetY(size_t pointer_index) const198   virtual float GetY(size_t pointer_index) const OVERRIDE {
199     return latest().GetY(pointer_index);
200   }
201 
GetRawX(size_t pointer_index) const202   virtual float GetRawX(size_t pointer_index) const OVERRIDE {
203     return latest().GetRawX(pointer_index);
204   }
205 
GetRawY(size_t pointer_index) const206   virtual float GetRawY(size_t pointer_index) const OVERRIDE {
207     return latest().GetRawY(pointer_index);
208   }
209 
GetTouchMajor(size_t pointer_index) const210   virtual float GetTouchMajor(size_t pointer_index) const OVERRIDE {
211     return latest().GetTouchMajor(pointer_index);
212   }
213 
GetTouchMinor(size_t pointer_index) const214   virtual float GetTouchMinor(size_t pointer_index) const OVERRIDE {
215     return latest().GetTouchMinor(pointer_index);
216   }
217 
GetOrientation(size_t pointer_index) const218   virtual float GetOrientation(size_t pointer_index) const OVERRIDE {
219     return latest().GetOrientation(pointer_index);
220   }
221 
GetPressure(size_t pointer_index) const222   virtual float GetPressure(size_t pointer_index) const OVERRIDE {
223     return latest().GetPressure(pointer_index);
224   }
225 
GetToolType(size_t pointer_index) const226   virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE {
227     return latest().GetToolType(pointer_index);
228   }
229 
GetButtonState() const230   virtual int GetButtonState() const OVERRIDE {
231     return latest().GetButtonState();
232   }
233 
GetFlags() const234   virtual int GetFlags() const OVERRIDE { return latest().GetFlags(); }
235 
GetEventTime() const236   virtual base::TimeTicks GetEventTime() const OVERRIDE {
237     return latest().GetEventTime();
238   }
239 
GetHistorySize() const240   virtual size_t GetHistorySize() const OVERRIDE { return events_.size() - 1; }
241 
GetHistoricalEventTime(size_t historical_index) const242   virtual base::TimeTicks GetHistoricalEventTime(
243       size_t historical_index) const OVERRIDE {
244     DCHECK_LT(historical_index, GetHistorySize());
245     return events_[historical_index]->GetEventTime();
246   }
247 
GetHistoricalTouchMajor(size_t pointer_index,size_t historical_index) const248   virtual float GetHistoricalTouchMajor(
249       size_t pointer_index,
250       size_t historical_index) const OVERRIDE {
251     DCHECK_LT(historical_index, GetHistorySize());
252     return events_[historical_index]->GetTouchMajor();
253   }
254 
GetHistoricalX(size_t pointer_index,size_t historical_index) const255   virtual float GetHistoricalX(size_t pointer_index,
256                                size_t historical_index) const OVERRIDE {
257     DCHECK_LT(historical_index, GetHistorySize());
258     return events_[historical_index]->GetX(pointer_index);
259   }
260 
GetHistoricalY(size_t pointer_index,size_t historical_index) const261   virtual float GetHistoricalY(size_t pointer_index,
262                                size_t historical_index) const OVERRIDE {
263     DCHECK_LT(historical_index, GetHistorySize());
264     return events_[historical_index]->GetY(pointer_index);
265   }
266 
Clone() const267   virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE {
268     MotionEventVector cloned_events;
269     cloned_events.reserve(events_.size());
270     for (size_t i = 0; i < events_.size(); ++i)
271       cloned_events.push_back(events_[i]->Clone().release());
272     return scoped_ptr<MotionEvent>(
273         new CompoundMotionEvent(cloned_events.Pass()));
274   }
275 
Cancel() const276   virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE {
277     return latest().Cancel();
278   }
279 
280   // Returns the new, resampled event, or NULL if none was created.
281   // TODO(jdduke): Revisit resampling to handle cases where alternating frames
282   // are resampled or resampling is otherwise inconsistent, e.g., a 90hz input
283   // and 60hz frame signal could phase-align such that even frames yield an
284   // extrapolated event and odd frames are not resampled, crbug.com/399381.
TryResample(base::TimeTicks resample_time,const ui::MotionEvent * next)285   const MotionEvent* TryResample(base::TimeTicks resample_time,
286                                  const ui::MotionEvent* next) {
287     DCHECK_EQ(GetAction(), ACTION_MOVE);
288     const ui::MotionEvent* event0 = NULL;
289     const ui::MotionEvent* event1 = NULL;
290     if (next) {
291       DCHECK(resample_time < next->GetEventTime());
292       // Interpolate between current sample and future sample.
293       event0 = events_.back();
294       event1 = next;
295     } else if (events_.size() >= 2) {
296       // Extrapolate future sample using current sample and past sample.
297       event0 = events_[events_.size() - 2];
298       event1 = events_[events_.size() - 1];
299 
300       const base::TimeTicks time1 = event1->GetEventTime();
301       base::TimeTicks max_predict =
302           time1 +
303           std::min((event1->GetEventTime() - event0->GetEventTime()) / 2,
304                    base::TimeDelta::FromMilliseconds(kResampleMaxPredictionMs));
305       if (resample_time > max_predict) {
306         TRACE_EVENT_INSTANT2("input",
307                              "MotionEventBuffer::TryResample prediction adjust",
308                              TRACE_EVENT_SCOPE_THREAD,
309                              "original(ms)",
310                              (resample_time - time1).InMilliseconds(),
311                              "adjusted(ms)",
312                              (max_predict - time1).InMilliseconds());
313         resample_time = max_predict;
314       }
315     } else {
316       TRACE_EVENT_INSTANT0("input",
317                            "MotionEventBuffer::TryResample insufficient data",
318                            TRACE_EVENT_SCOPE_THREAD);
319       return NULL;
320     }
321 
322     DCHECK(event0);
323     DCHECK(event1);
324     const base::TimeTicks time0 = event0->GetEventTime();
325     const base::TimeTicks time1 = event1->GetEventTime();
326     base::TimeDelta delta = time1 - time0;
327     if (delta < base::TimeDelta::FromMilliseconds(kResampleMinDeltaMs)) {
328       TRACE_EVENT_INSTANT1("input",
329                            "MotionEventBuffer::TryResample failure",
330                            TRACE_EVENT_SCOPE_THREAD,
331                            "event_delta_too_small(ms)",
332                            delta.InMilliseconds());
333       return NULL;
334     }
335 
336     events_.push_back(
337         ResampleMotionEvent(*event0, *event1, resample_time).release());
338     return events_.back();
339   }
340 
samples() const341   size_t samples() const { return events_.size(); }
342 
343  private:
latest() const344   const MotionEvent& latest() const { return *events_.back(); }
345 
346   // Events are in order from oldest to newest.
347   MotionEventVector events_;
348 
349   DISALLOW_COPY_AND_ASSIGN(CompoundMotionEvent);
350 };
351 
352 }  // namespace
353 
MotionEventBuffer(MotionEventBufferClient * client,bool enable_resampling)354 MotionEventBuffer::MotionEventBuffer(MotionEventBufferClient* client,
355                                      bool enable_resampling)
356     : client_(client), resample_(enable_resampling) {
357 }
358 
~MotionEventBuffer()359 MotionEventBuffer::~MotionEventBuffer() {
360 }
361 
OnMotionEvent(const MotionEvent & event)362 void MotionEventBuffer::OnMotionEvent(const MotionEvent& event) {
363   if (event.GetAction() != MotionEvent::ACTION_MOVE) {
364     last_extrapolated_event_time_ = base::TimeTicks();
365     if (!buffered_events_.empty())
366       FlushWithoutResampling(buffered_events_.Pass());
367     client_->ForwardMotionEvent(event);
368     return;
369   }
370 
371   // Guard against events that are *older* than the last one that may have been
372   // artificially synthesized.
373   if (!last_extrapolated_event_time_.is_null()) {
374     DCHECK(buffered_events_.empty());
375     if (event.GetEventTime() < last_extrapolated_event_time_)
376       return;
377     last_extrapolated_event_time_ = base::TimeTicks();
378   }
379 
380   scoped_ptr<MotionEvent> clone = event.Clone();
381   if (buffered_events_.empty()) {
382     buffered_events_.push_back(clone.release());
383     client_->SetNeedsFlush();
384     return;
385   }
386 
387   if (CanAddSample(*buffered_events_.front(), *clone)) {
388     DCHECK(buffered_events_.back()->GetEventTime() <= clone->GetEventTime());
389   } else {
390     FlushWithoutResampling(buffered_events_.Pass());
391   }
392 
393   buffered_events_.push_back(clone.release());
394   // No need to request another flush as the first event will have requested it.
395 }
396 
Flush(base::TimeTicks frame_time)397 void MotionEventBuffer::Flush(base::TimeTicks frame_time) {
398   if (buffered_events_.empty())
399     return;
400 
401   // Shifting the sample time back slightly minimizes the potential for
402   // misprediction when extrapolating events.
403   if (resample_)
404     frame_time -= base::TimeDelta::FromMilliseconds(kResampleLatencyMs);
405 
406   // TODO(jdduke): Use a persistent MotionEventVector vector for temporary
407   // storage.
408   MotionEventVector events(
409       ConsumeSamplesNoLaterThan(&buffered_events_, frame_time));
410   if (events.empty()) {
411     DCHECK(!buffered_events_.empty());
412     client_->SetNeedsFlush();
413     return;
414   }
415 
416   if (!resample_ || (events.size() == 1 && buffered_events_.empty())) {
417     FlushWithoutResampling(events.Pass());
418     if (!buffered_events_.empty())
419       client_->SetNeedsFlush();
420     return;
421   }
422 
423   CompoundMotionEvent resampled_event(events.Pass());
424   base::TimeTicks original_event_time = resampled_event.GetEventTime();
425   const MotionEvent* next_event =
426       !buffered_events_.empty() ? buffered_events_.front() : NULL;
427 
428   // Try to interpolate/extrapolate a new event at |frame_time|. Note that
429   // |new_event|, if non-NULL, is owned by |resampled_event_|.
430   const MotionEvent* new_event =
431       resampled_event.TryResample(frame_time, next_event);
432 
433   // Log the extrapolated event time, guarding against subsequently queued
434   // events that might have an earlier timestamp.
435   if (!next_event && new_event &&
436       new_event->GetEventTime() > original_event_time) {
437     last_extrapolated_event_time_ = new_event->GetEventTime();
438   } else {
439     last_extrapolated_event_time_ = base::TimeTicks();
440   }
441 
442   client_->ForwardMotionEvent(resampled_event);
443   if (!buffered_events_.empty())
444     client_->SetNeedsFlush();
445 }
446 
FlushWithoutResampling(MotionEventVector events)447 void MotionEventBuffer::FlushWithoutResampling(MotionEventVector events) {
448   last_extrapolated_event_time_ = base::TimeTicks();
449   if (events.empty())
450     return;
451 
452   if (events.size() == 1) {
453     // Avoid CompoundEvent creation to prevent unnecessary allocations.
454     scoped_ptr<MotionEvent> event(events.front());
455     events.weak_clear();
456     client_->ForwardMotionEvent(*event);
457     return;
458   }
459 
460   CompoundMotionEvent compound_event(events.Pass());
461   client_->ForwardMotionEvent(compound_event);
462 }
463 
464 }  // namespace ui
465