// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/base/prediction/linear_resampling.h" #include #include "ui/base/ui_base_features.h" #include namespace ui { namespace { // Minimum time difference between last two consecutive events before attempting // to resample. constexpr auto kResampleMinDelta = base::TimeDelta::FromMilliseconds(2); // Maximum time to predict forward from the last event, to avoid predicting too // far into the future. This time is further bounded by 50% of the last time // delta. constexpr auto kResampleMaxPrediction = base::TimeDelta::FromMilliseconds(8); // Align events to a few milliseconds before frame_time. This is to make the // resampling either doing interpolation or extrapolating a closer future time // so that resampled result is more accurate and has less noise. This adds some // latency during resampling but a few ms should be fine. constexpr auto kResampleLatency = base::TimeDelta::FromMilliseconds(5); // Get position at |sample_time| by linear interpolate/extrapolate a and b. inline gfx::PointF lerp(const InputPredictor::InputData& a, const InputPredictor::InputData& b, base::TimeTicks sample_time) { const float alpha = (sample_time - a.time_stamp) / (a.time_stamp - b.time_stamp); return a.pos + gfx::ScaleVector2d(a.pos - b.pos, alpha); } } // namespace LinearResampling::LinearResampling() {} LinearResampling::~LinearResampling() {} const char* LinearResampling::GetName() const { return features::kPredictorNameLinearResampling; } void LinearResampling::Reset() { events_queue_.clear(); } void LinearResampling::Update(const InputData& new_input) { // The last input received is at least kMaxDeltaTime away, we consider it // is a new trajectory if (!events_queue_.empty() && new_input.time_stamp - events_queue_.front().time_stamp > kMaxTimeDelta) { Reset(); } // Queue the new event. events_queue_.push_front(new_input); if (events_queue_.size() > kNumEventsForResampling) events_queue_.pop_back(); DCHECK(events_queue_.size() <= kNumEventsForResampling); if (events_queue_.size() == kNumEventsForResampling) events_dt_ = events_queue_[0].time_stamp - events_queue_[1].time_stamp; } bool LinearResampling::HasPrediction() const { return events_queue_.size() == kNumEventsForResampling && events_dt_ >= kResampleMinDelta; } std::unique_ptr LinearResampling::GeneratePrediction( base::TimeTicks frame_time) const { if (!HasPrediction()) return nullptr; base::TimeTicks sample_time = frame_time - kResampleLatency; base::TimeDelta max_prediction = std::min(kResampleMaxPrediction, events_dt_ / 2.0); sample_time = std::min(sample_time, events_queue_[0].time_stamp + max_prediction); return std::make_unique( lerp(events_queue_[0], events_queue_[1], sample_time), sample_time); } base::TimeDelta LinearResampling::TimeInterval() const { if (events_queue_.size() == kNumEventsForResampling) { return events_dt_; } return kTimeInterval; } } // namespace ui