blob: 155097732c286749d834b3df343eca2a2568d45f [file] [log] [blame]
Paul Ramirezbe9c5442024-07-10 00:12:41 +00001/**
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
Paul Ramirez29ee27c2024-10-02 08:18:53 +000019#include <array>
Paul Ramirezbe9c5442024-07-10 00:12:41 +000020#include <chrono>
Paul Ramirez29ee27c2024-10-02 08:18:53 +000021#include <iterator>
Paul Ramirez08ee1992024-10-10 18:02:15 +000022#include <map>
Paul Ramirezbe9c5442024-07-10 00:12:41 +000023#include <optional>
Paul Ramirezcf1b06e2024-08-01 17:11:58 +000024#include <vector>
Paul Ramirezbe9c5442024-07-10 00:12:41 +000025
Paul Ramirez29ee27c2024-10-02 08:18:53 +000026#include <android-base/logging.h>
27#include <ftl/mixins.h>
Paul Ramirez08ee1992024-10-10 18:02:15 +000028#include <input/CoordinateFilter.h>
Paul Ramirezbe9c5442024-07-10 00:12:41 +000029#include <input/Input.h>
30#include <input/InputTransport.h>
31#include <input/RingBuffer.h>
32#include <utils/Timers.h>
33
34namespace android {
35
36/**
37 * Resampler is an interface for resampling MotionEvents. Every resampling implementation
38 * must use this interface to enable resampling inside InputConsumer's logic.
39 */
40struct Resampler {
41 virtual ~Resampler() = default;
42
43 /**
Paul Ramirez6affbdb2024-09-11 22:20:26 +000044 * Tries to resample motionEvent at frameTime. The provided frameTime must be greater than
Paul Ramirezbe9c5442024-07-10 00:12:41 +000045 * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
Paul Ramirez6affbdb2024-09-11 22:20:26 +000046 * frameTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
Paul Ramirezbe9c5442024-07-10 00:12:41 +000047 * may be resampled by another method, or not resampled at all. Furthermore, it is the
48 * implementer's responsibility to guarantee the following:
49 * - If resampling occurs, a single additional sample should be added to motionEvent. That is,
50 * if motionEvent had N samples before being passed to Resampler, then it will have N + 1
51 * samples by the end of the resampling. No other field of motionEvent should be modified.
52 * - If resampling does not occur, then motionEvent must not be modified in any way.
53 */
Paul Ramirez6affbdb2024-09-11 22:20:26 +000054 virtual void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
Paul Ramirezbe9c5442024-07-10 00:12:41 +000055 const InputMessage* futureSample) = 0;
Paul Ramirezcd7488c2024-09-13 23:01:12 +000056
57 /**
58 * Returns resample latency. Resample latency is the time difference between frame time and
59 * resample time. More precisely, let frameTime and resampleTime be two timestamps, and
60 * frameTime > resampleTime. Resample latency is defined as frameTime - resampleTime.
61 */
62 virtual std::chrono::nanoseconds getResampleLatency() const = 0;
Paul Ramirezbe9c5442024-07-10 00:12:41 +000063};
64
65class LegacyResampler final : public Resampler {
66public:
67 /**
Paul Ramirez6affbdb2024-09-11 22:20:26 +000068 * Tries to resample `motionEvent` at `frameTime` by adding a resampled sample at the end of
Paul Ramirezbe9c5442024-07-10 00:12:41 +000069 * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by
70 * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if
71 * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
72 * not null, interpolation will occur. If `futureSample` is null and there is enough historical
73 * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
Paul Ramirez7f1efed2024-09-29 23:55:23 +000074 * `motionEvent` is unmodified. Furthermore, motionEvent is not resampled if resampleTime equals
75 * the last sample eventTime of motionEvent.
Paul Ramirezbe9c5442024-07-10 00:12:41 +000076 */
Paul Ramirez6affbdb2024-09-11 22:20:26 +000077 void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
Paul Ramirezbe9c5442024-07-10 00:12:41 +000078 const InputMessage* futureSample) override;
79
Paul Ramirezcd7488c2024-09-13 23:01:12 +000080 std::chrono::nanoseconds getResampleLatency() const override;
81
Paul Ramirezbe9c5442024-07-10 00:12:41 +000082private:
83 struct Pointer {
84 PointerProperties properties;
85 PointerCoords coords;
86 };
87
Paul Ramirez29ee27c2024-10-02 08:18:53 +000088 /**
89 * Container that stores pointers as an associative array, supporting O(1) lookup by pointer id,
90 * as well as forward iteration in the order in which the pointer or pointers were inserted in
91 * the container. PointerMap has a maximum capacity equal to MAX_POINTERS.
92 */
93 class PointerMap {
94 public:
95 struct PointerId : ftl::DefaultConstructible<PointerId, int32_t>,
96 ftl::Equatable<PointerId> {
97 using DefaultConstructible::DefaultConstructible;
98 };
99
100 /**
101 * Custom iterator to enable use of range-based for loops.
102 */
103 template <typename T>
104 class iterator {
105 public:
106 using iterator_category = std::forward_iterator_tag;
107 using value_type = T;
108 using difference_type = std::ptrdiff_t;
109 using pointer = T*;
110 using reference = T&;
111
112 explicit iterator(pointer element) : mElement{element} {}
113
114 friend bool operator==(const iterator& lhs, const iterator& rhs) {
115 return lhs.mElement == rhs.mElement;
116 }
117
118 friend bool operator!=(const iterator& lhs, const iterator& rhs) {
119 return !(lhs == rhs);
120 }
121
122 iterator operator++() {
123 ++mElement;
124 return *this;
125 }
126
127 reference operator*() const { return *mElement; }
128
129 private:
130 pointer mElement;
131 };
132
133 PointerMap() {
134 idToIndex.fill(std::nullopt);
135 for (Pointer& pointer : pointers) {
136 pointer.properties.clear();
137 pointer.coords.clear();
138 }
139 }
140
141 /**
142 * Forward iterators to traverse the pointers in `pointers`. The order of the pointers is
143 * determined by the order in which they were inserted (not by id).
144 */
145 iterator<Pointer> begin() { return iterator<Pointer>{&pointers[0]}; }
146
147 iterator<const Pointer> begin() const { return iterator<const Pointer>{&pointers[0]}; }
148
149 iterator<Pointer> end() { return iterator<Pointer>{&pointers[nextPointerIndex]}; }
150
151 iterator<const Pointer> end() const {
152 return iterator<const Pointer>{&pointers[nextPointerIndex]};
153 }
154
155 /**
156 * Inserts the given pointer into the PointerMap. Precondition: The current number of
157 * contained pointers must be less than MAX_POINTERS when this function is called. It
158 * fatally logs if the user tries to insert more than MAX_POINTERS, or if pointer id is out
159 * of bounds.
160 */
161 void insert(const Pointer& pointer) {
162 LOG_IF(FATAL, nextPointerIndex >= pointers.size())
163 << "Cannot insert more than " << MAX_POINTERS << " in PointerMap.";
164 LOG_IF(FATAL, (pointer.properties.id < 0) || (pointer.properties.id > MAX_POINTER_ID))
165 << "Invalid pointer id.";
166 idToIndex[pointer.properties.id] = std::optional<size_t>{nextPointerIndex};
167 pointers[nextPointerIndex] = pointer;
168 ++nextPointerIndex;
169 }
170
171 /**
172 * Returns the pointer associated with the provided id if it exists.
173 * Otherwise, std::nullopt is returned.
174 */
175 std::optional<Pointer> find(PointerId id) const {
176 const int32_t idValue = ftl::to_underlying(id);
177 LOG_IF(FATAL, (idValue < 0) || (idValue > MAX_POINTER_ID)) << "Invalid pointer id.";
178 const std::optional<size_t> index = idToIndex[idValue];
179 return index.has_value() ? std::optional{pointers[*index]} : std::nullopt;
180 }
181
182 private:
183 /**
184 * The index at which a pointer is inserted in `pointers`. Likewise, it represents the
185 * number of pointers in PointerMap.
186 */
187 size_t nextPointerIndex{0};
188
189 /**
190 * Sequentially stores pointers. Each pointer's position is determined by the value of
191 * nextPointerIndex at insertion time.
192 */
193 std::array<Pointer, MAX_POINTERS + 1> pointers;
194
195 /**
196 * Maps each pointer id to its associated index in pointers. If no pointer with the id
197 * exists in pointers, the mapped value is std::nullopt.
198 */
199 std::array<std::optional<size_t>, MAX_POINTER_ID + 1> idToIndex;
200 };
201
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000202 struct Sample {
203 std::chrono::nanoseconds eventTime;
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000204 PointerMap pointerMap;
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000205
206 std::vector<PointerCoords> asPointerCoords() const {
207 std::vector<PointerCoords> pointersCoords;
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000208 for (const Pointer& pointer : pointerMap) {
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000209 pointersCoords.push_back(pointer.coords);
210 }
211 return pointersCoords;
212 }
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000213 };
214
215 /**
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000216 * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called.
217 * Note: We store up to two samples in order to simplify the implementation. Although,
218 * calculations are possible with only one previous sample.
219 */
220 RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
221
222 /**
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000223 * Latest sample in mLatestSamples after resampling motion event.
Paul Ramirez4d3b03a2024-09-30 01:39:00 +0000224 */
225 std::optional<Sample> mLastRealSample;
226
227 /**
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000228 * Latest prediction. That is, the latest extrapolated sample.
Paul Ramirez4d3b03a2024-09-30 01:39:00 +0000229 */
230 std::optional<Sample> mPreviousPrediction;
231
232 /**
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000233 * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000234 * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000235 * added to mLatestSamples.
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000236 */
237 void updateLatestSamples(const MotionEvent& motionEvent);
238
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000239 static Sample messageToSample(const InputMessage& message);
240
241 /**
242 * Checks if auxiliary sample has the same pointer properties of target sample. That is,
243 * auxiliary pointer IDs must appear in the same order as target pointer IDs, their toolType
244 * must match and be resampleable.
245 */
246 static bool pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary);
247
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000248 /**
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000249 * Checks if there are necessary conditions to interpolate. For example, interpolation cannot
250 * take place if samples are too far apart in time. mLatestSamples must have at least one sample
251 * when canInterpolate is invoked.
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000252 */
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000253 bool canInterpolate(const InputMessage& futureSample) const;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000254
255 /**
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000256 * Returns a sample interpolated between the latest sample of mLatestSamples and futureMessage,
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000257 * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
258 * mLatestSamples must have at least one sample when attemptInterpolation is called.
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000259 */
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000260 std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000261 const InputMessage& futureMessage) const;
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000262
263 /**
264 * Checks if there are necessary conditions to extrapolate. That is, there are at least two
265 * samples in mLatestSamples, and delta is bounded within a time interval.
266 */
267 bool canExtrapolate() const;
268
269 /**
270 * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from
271 * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime,
272 * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false,
273 * this function returns nullopt.
274 */
275 std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
276
Paul Ramirez4d3b03a2024-09-30 01:39:00 +0000277 /**
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000278 * Iterates through motion event samples, and replaces real coordinates with resampled
279 * coordinates to avoid jerkiness in certain conditions.
Paul Ramirez4d3b03a2024-09-30 01:39:00 +0000280 */
281 void overwriteMotionEventSamples(MotionEvent& motionEvent) const;
282
283 /**
284 * Overwrites with resampled data the pointer coordinates that did not move between motion event
285 * samples, that is, both x and y values are identical to mLastRealSample.
286 */
287 void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
288
Paul Ramirez4679e552024-10-01 01:17:39 +0000289 /**
290 * Overwrites the pointer coordinates of a sample with event time older than
291 * that of mPreviousPrediction.
292 */
293 void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
294
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000295 inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000296};
Paul Ramirez29ee27c2024-10-02 08:18:53 +0000297
Paul Ramirez08ee1992024-10-10 18:02:15 +0000298/**
299 * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters
300 * the X and Y coordinates with a pair of One Euro filters.
301 */
302class FilteredLegacyResampler final : public Resampler {
303public:
304 /**
305 * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro
306 * filters.
307 */
308 explicit FilteredLegacyResampler(float minCutoffFreq, float beta);
309
310 void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent,
311 const InputMessage* futureMessage) override;
312
313 std::chrono::nanoseconds getResampleLatency() const override;
314
315private:
316 LegacyResampler mResampler;
317
318 /**
319 * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a
320 * more detailed explanation.
321 */
322 const float mMinCutoffFreq;
323
324 /**
325 * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a
326 * more detailed explanation.
327 */
328 const float mBeta;
329
330 /*
331 * Note: an associative array with constant insertion and lookup times would be more efficient.
332 * When this was implemented, there was no container with these properties.
333 */
334 std::map<int32_t /*pointerId*/, CoordinateFilter> mFilteredPointers;
335};
336
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000337} // namespace android