xref: /aosp_15_r20/frameworks/native/include/input/Resampler.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /**
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 
19 #include <array>
20 #include <chrono>
21 #include <iterator>
22 #include <map>
23 #include <optional>
24 #include <vector>
25 
26 #include <android-base/logging.h>
27 #include <ftl/mixins.h>
28 #include <input/CoordinateFilter.h>
29 #include <input/Input.h>
30 #include <input/InputTransport.h>
31 #include <input/RingBuffer.h>
32 #include <utils/Timers.h>
33 
34 namespace 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  */
40 struct Resampler {
41     virtual ~Resampler() = default;
42 
43     /**
44      * Tries to resample motionEvent at frameTime. The provided frameTime must be greater than
45      * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
46      * frameTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
47      * 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      */
54     virtual void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
55                                      const InputMessage* futureSample) = 0;
56 
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;
63 };
64 
65 class LegacyResampler final : public Resampler {
66 public:
67     /**
68      * Tries to resample `motionEvent` at `frameTime` by adding a resampled sample at the end of
69      * `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
74      * `motionEvent` is unmodified. Furthermore, motionEvent is not resampled if resampleTime equals
75      * the last sample eventTime of motionEvent.
76      */
77     void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
78                              const InputMessage* futureSample) override;
79 
80     std::chrono::nanoseconds getResampleLatency() const override;
81 
82 private:
83     struct Pointer {
84         PointerProperties properties;
85         PointerCoords coords;
86     };
87 
88     /**
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 
iterator(pointer element)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 
PointerMap()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          */
begin()145         iterator<Pointer> begin() { return iterator<Pointer>{&pointers[0]}; }
146 
begin()147         iterator<const Pointer> begin() const { return iterator<const Pointer>{&pointers[0]}; }
148 
end()149         iterator<Pointer> end() { return iterator<Pointer>{&pointers[nextPointerIndex]}; }
150 
end()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          */
insert(const Pointer & pointer)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          */
find(PointerId id)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 
202     struct Sample {
203         std::chrono::nanoseconds eventTime;
204         PointerMap pointerMap;
205 
asPointerCoordsSample206         std::vector<PointerCoords> asPointerCoords() const {
207             std::vector<PointerCoords> pointersCoords;
208             for (const Pointer& pointer : pointerMap) {
209                 pointersCoords.push_back(pointer.coords);
210             }
211             return pointersCoords;
212         }
213     };
214 
215     /**
216      * 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     /**
223      * Latest sample in mLatestSamples after resampling motion event.
224      */
225     std::optional<Sample> mLastRealSample;
226 
227     /**
228      * Latest prediction. That is, the latest extrapolated sample.
229      */
230     std::optional<Sample> mPreviousPrediction;
231 
232     /**
233      * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
234      * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
235      * added to mLatestSamples.
236      */
237     void updateLatestSamples(const MotionEvent& motionEvent);
238 
239     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 
248     /**
249      * 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.
252      */
253     bool canInterpolate(const InputMessage& futureSample) const;
254 
255     /**
256      * Returns a sample interpolated between the latest sample of mLatestSamples and futureMessage,
257      * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
258      * mLatestSamples must have at least one sample when attemptInterpolation is called.
259      */
260     std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
261                                                const InputMessage& futureMessage) const;
262 
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 
277     /**
278      * Iterates through motion event samples, and replaces real coordinates with resampled
279      * coordinates to avoid jerkiness in certain conditions.
280      */
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 
289     /**
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 
295     inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
296 };
297 
298 /**
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  */
302 class FilteredLegacyResampler final : public Resampler {
303 public:
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 
315 private:
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 
337 } // namespace android
338