motion_event_buffer.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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 11namespace ui { 12namespace { 13 14// Latency added during resampling. A few milliseconds doesn't hurt much but 15// reduces the impact of mispredicted touch positions. 16const int kResampleLatencyMs = 5; 17 18// Minimum time difference between consecutive samples before attempting to 19// resample. 20const 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. 25const int kResampleMaxPredictionMs = 8; 26 27typedef ScopedVector<MotionEvent> MotionEventVector; 28 29float Lerp(float a, float b, float alpha) { 30 return a + alpha * (b - a); 31} 32 33bool 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 54bool ShouldResampleTool(MotionEvent::ToolType tool) { 55 return tool == MotionEvent::TOOL_TYPE_UNKNOWN || 56 tool == MotionEvent::TOOL_TYPE_FINGER; 57} 58 59size_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 67MotionEventVector 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 89PointerProperties 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 return result; 101} 102 103PointerProperties ResamplePointer(const MotionEvent& event0, 104 const MotionEvent& event1, 105 size_t event0_pointer_index, 106 size_t event1_pointer_index, 107 float alpha) { 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))) { 113 if (alpha > 1) 114 return PointerFromMotionEvent(event1, event1_pointer_index); 115 else 116 return PointerFromMotionEvent(event0, event0_pointer_index); 117 } 118 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); 124 return p; 125} 126 127scoped_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()); 132 133 const base::TimeTicks time0 = event0.GetEventTime(); 134 const base::TimeTicks time1 = event1.GetEventTime(); 135 DCHECK(time0 < time1); 136 DCHECK(time0 <= resample_time); 137 138 const float alpha = (resample_time - time0).InMillisecondsF() / 139 (time1 - time0).InMillisecondsF(); 140 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); 149 150 if (event0_i == 0) { 151 event.reset(new MotionEventGeneric( 152 MotionEvent::ACTION_MOVE, resample_time, pointer)); 153 } else { 154 event->PushPointer(pointer); 155 } 156 } 157 158 DCHECK(event); 159 event->set_id(event0.GetId()); 160 event->set_action_index(event0.GetActionIndex()); 161 event->set_button_state(event0.GetButtonState()); 162 163 return event.PassAs<MotionEvent>(); 164} 165 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. 168class CompoundMotionEvent : public ui::MotionEvent { 169 public: 170 explicit CompoundMotionEvent(MotionEventVector events) 171 : events_(events.Pass()) { 172 DCHECK_GE(events_.size(), 1U); 173 } 174 virtual ~CompoundMotionEvent() {} 175 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(); 180 } 181 virtual size_t GetPointerCount() const OVERRIDE { 182 return latest().GetPointerCount(); 183 } 184 virtual int GetPointerId(size_t pointer_index) const OVERRIDE { 185 return latest().GetPointerId(pointer_index); 186 } 187 virtual float GetX(size_t pointer_index) const OVERRIDE { 188 return latest().GetX(pointer_index); 189 } 190 virtual float GetY(size_t pointer_index) const OVERRIDE { 191 return latest().GetY(pointer_index); 192 } 193 virtual float GetRawX(size_t pointer_index) const OVERRIDE { 194 return latest().GetRawX(pointer_index); 195 } 196 virtual float GetRawY(size_t pointer_index) const OVERRIDE { 197 return latest().GetRawY(pointer_index); 198 } 199 virtual float GetTouchMajor(size_t pointer_index) const OVERRIDE { 200 return latest().GetTouchMajor(pointer_index); 201 } 202 virtual float GetPressure(size_t pointer_index) const OVERRIDE { 203 return latest().GetPressure(pointer_index); 204 } 205 virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE { 206 return latest().GetToolType(pointer_index); 207 } 208 virtual int GetButtonState() const OVERRIDE { 209 return latest().GetButtonState(); 210 } 211 virtual base::TimeTicks GetEventTime() const OVERRIDE { 212 return latest().GetEventTime(); 213 } 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(); 219 } 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(); 225 } 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); 230 } 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); 235 } 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())); 243 } 244 virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE { 245 return latest().Cancel(); 246 } 247 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; 258 if (next) { 259 DCHECK(resample_time < next->GetEventTime()); 260 // Interpolate between current sample and future sample. 261 event0 = events_.back(); 262 event1 = next; 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]; 267 268 const base::TimeTicks time1 = event1->GetEventTime(); 269 base::TimeTicks max_predict = 270 time1 + 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, 277 "original(ms)", 278 (resample_time - time1).InMilliseconds(), 279 "adjusted(ms)", 280 (max_predict - time1).InMilliseconds()); 281 resample_time = max_predict; 282 } 283 } else { 284 TRACE_EVENT_INSTANT0("input", 285 "MotionEventBuffer::TryResample insufficient data", 286 TRACE_EVENT_SCOPE_THREAD); 287 return NULL; 288 } 289 290 DCHECK(event0); 291 DCHECK(event1); 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()); 301 return NULL; 302 } 303 304 events_.push_back( 305 ResampleMotionEvent(*event0, *event1, resample_time).release()); 306 return events_.back(); 307 } 308 309 size_t samples() const { return events_.size(); } 310 311 private: 312 const MotionEvent& latest() const { return *events_.back(); } 313 314 // Events are in order from oldest to newest. 315 MotionEventVector events_; 316 317 DISALLOW_COPY_AND_ASSIGN(CompoundMotionEvent); 318}; 319 320} // namespace 321 322MotionEventBuffer::MotionEventBuffer(MotionEventBufferClient* client, 323 bool enable_resampling) 324 : client_(client), resample_(enable_resampling) { 325} 326 327MotionEventBuffer::~MotionEventBuffer() { 328} 329 330void 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); 336 return; 337 } 338 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_) 344 return; 345 last_extrapolated_event_time_ = base::TimeTicks(); 346 } 347 348 scoped_ptr<MotionEvent> clone = event.Clone(); 349 if (buffered_events_.empty()) { 350 buffered_events_.push_back(clone.release()); 351 client_->SetNeedsFlush(); 352 return; 353 } 354 355 if (CanAddSample(*buffered_events_.front(), *clone)) { 356 DCHECK(buffered_events_.back()->GetEventTime() <= clone->GetEventTime()); 357 } else { 358 FlushWithoutResampling(buffered_events_.Pass()); 359 } 360 361 buffered_events_.push_back(clone.release()); 362 // No need to request another flush as the first event will have requested it. 363} 364 365void MotionEventBuffer::Flush(base::TimeTicks frame_time) { 366 if (buffered_events_.empty()) 367 return; 368 369 // Shifting the sample time back slightly minimizes the potential for 370 // misprediction when extrapolating events. 371 if (resample_) 372 frame_time -= base::TimeDelta::FromMilliseconds(kResampleLatencyMs); 373 374 // TODO(jdduke): Use a persistent MotionEventVector vector for temporary 375 // storage. 376 MotionEventVector events( 377 ConsumeSamplesNoLaterThan(&buffered_events_, frame_time)); 378 if (events.empty()) { 379 DCHECK(!buffered_events_.empty()); 380 client_->SetNeedsFlush(); 381 return; 382 } 383 384 if (!resample_ || (events.size() == 1 && buffered_events_.empty())) { 385 FlushWithoutResampling(events.Pass()); 386 if (!buffered_events_.empty()) 387 client_->SetNeedsFlush(); 388 return; 389 } 390 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; 395 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); 400 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(); 406 } else { 407 last_extrapolated_event_time_ = base::TimeTicks(); 408 } 409 410 client_->ForwardMotionEvent(resampled_event); 411 if (!buffered_events_.empty()) 412 client_->SetNeedsFlush(); 413} 414 415void MotionEventBuffer::FlushWithoutResampling(MotionEventVector events) { 416 last_extrapolated_event_time_ = base::TimeTicks(); 417 if (events.empty()) 418 return; 419 420 if (events.size() == 1) { 421 // Avoid CompoundEvent creation to prevent unnecessary allocations. 422 scoped_ptr<MotionEvent> event(events.front()); 423 events.weak_clear(); 424 client_->ForwardMotionEvent(*event); 425 return; 426 } 427 428 CompoundMotionEvent compound_event(events.Pass()); 429 client_->ForwardMotionEvent(compound_event); 430} 431 432} // namespace ui 433