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