blob: 51fadf8ec114484117c6b23bf168937ed759db17 [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#define LOG_TAG "LegacyResampler"
18
19#include <algorithm>
20#include <chrono>
21
22#include <android-base/logging.h>
23#include <android-base/properties.h>
Paul Ramirezcf1b06e2024-08-01 17:11:58 +000024#include <ftl/enum.h>
Paul Ramirezbe9c5442024-07-10 00:12:41 +000025
26#include <input/Resampler.h>
27#include <utils/Timers.h>
28
29using std::chrono::nanoseconds;
30
31namespace android {
32
33namespace {
34
35const bool IS_DEBUGGABLE_BUILD =
36#if defined(__ANDROID__)
37 android::base::GetBoolProperty("ro.debuggable", false);
38#else
39 true;
40#endif
41
42bool debugResampling() {
43 if (!IS_DEBUGGABLE_BUILD) {
44 static const bool DEBUG_TRANSPORT_RESAMPLING =
45 __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
46 ANDROID_LOG_INFO);
47 return DEBUG_TRANSPORT_RESAMPLING;
48 }
49 return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
50}
51
52constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
53
54constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
55
56constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20};
57
58constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
59
Paul Ramirezcf1b06e2024-08-01 17:11:58 +000060bool canResampleTool(ToolType toolType) {
61 return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
62 toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
63}
64
Paul Ramirezbe9c5442024-07-10 00:12:41 +000065inline float lerp(float a, float b, float alpha) {
66 return a + alpha * (b - a);
67}
68
Paul Ramirez486ca6d2024-08-12 14:37:02 +000069PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
70 float alpha) {
Paul Ramirez68ca3d12024-08-12 23:00:50 +000071 // We use the value of alpha to initialize resampledCoords with the latest sample information.
72 PointerCoords resampledCoords = (alpha < 1.0f) ? a : b;
Paul Ramirezbe9c5442024-07-10 00:12:41 +000073 resampledCoords.isResampled = true;
74 resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
75 resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
76 return resampledCoords;
77}
78} // namespace
79
80void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
Paul Ramirez486ca6d2024-08-12 14:37:02 +000081 const size_t numSamples = motionEvent.getHistorySize() + 1;
Paul Ramirezcf1b06e2024-08-01 17:11:58 +000082 const size_t latestIndex = numSamples - 1;
83 const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
84 for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
85 std::vector<Pointer> pointers;
86 const size_t numPointers = motionEvent.getPointerCount();
87 for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
88 // getSamplePointerCoords is the vector representation of a getHistorySize by
89 // getPointerCount matrix.
90 const PointerCoords& pointerCoords =
91 motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
92 pointers.push_back(
93 Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
94 }
Paul Ramirez486ca6d2024-08-12 14:37:02 +000095 mLatestSamples.pushBack(
Paul Ramirez698344a2024-08-20 00:58:55 +000096 Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
Paul Ramirezbe9c5442024-07-10 00:12:41 +000097 }
98}
99
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000100LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
101 std::vector<Pointer> pointers;
102 for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
103 pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
104 message.body.motion.pointers[i].coords});
105 }
Paul Ramirez698344a2024-08-20 00:58:55 +0000106 return Sample{nanoseconds{message.body.motion.eventTime}, pointers};
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000107}
108
109bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
110 if (target.pointers.size() > auxiliary.pointers.size()) {
111 LOG_IF(INFO, debugResampling())
112 << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
113 return false;
114 }
115 for (size_t i = 0; i < target.pointers.size(); ++i) {
116 if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
117 LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
118 return false;
119 }
120 if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
121 LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
122 return false;
123 }
124 if (!canResampleTool(target.pointers[i].properties.toolType)) {
125 LOG_IF(INFO, debugResampling())
126 << "Not resampled. Cannot resample "
127 << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
128 return false;
129 }
130 }
131 return true;
132}
133
134bool LegacyResampler::canInterpolate(const InputMessage& message) const {
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000135 LOG_IF(FATAL, mLatestSamples.empty())
136 << "Not resampled. mLatestSamples must not be empty to interpolate.";
137
138 const Sample& pastSample = *(mLatestSamples.end() - 1);
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000139 const Sample& futureSample = messageToSample(message);
140
141 if (!pointerPropertiesResampleable(pastSample, futureSample)) {
142 return false;
143 }
144
145 const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000146 if (delta < RESAMPLE_MIN_DELTA) {
147 LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000148 return false;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000149 }
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000150 return true;
151}
152
153std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
154 nanoseconds resampleTime, const InputMessage& futureSample) const {
155 if (!canInterpolate(futureSample)) {
156 return std::nullopt;
157 }
158 LOG_IF(FATAL, mLatestSamples.empty())
159 << "Not resampled. mLatestSamples must not be empty to interpolate.";
160
161 const Sample& pastSample = *(mLatestSamples.end() - 1);
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000162
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000163 const nanoseconds delta =
Paul Ramirez698344a2024-08-20 00:58:55 +0000164 nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000165 const float alpha =
166 std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000167
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000168 std::vector<Pointer> resampledPointers;
169 for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
170 const PointerCoords& resampledCoords =
171 calculateResampledCoords(pastSample.pointers[i].coords,
172 futureSample.body.motion.pointers[i].coords, alpha);
173 resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
174 }
175 return Sample{resampleTime, resampledPointers};
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000176}
177
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000178bool LegacyResampler::canExtrapolate() const {
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000179 if (mLatestSamples.size() < 2) {
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000180 LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
181 return false;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000182 }
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000183
184 const Sample& pastSample = *(mLatestSamples.end() - 2);
185 const Sample& presentSample = *(mLatestSamples.end() - 1);
186
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000187 if (!pointerPropertiesResampleable(presentSample, pastSample)) {
188 return false;
189 }
190
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000191 const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000192 if (delta < RESAMPLE_MIN_DELTA) {
193 LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000194 return false;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000195 } else if (delta > RESAMPLE_MAX_DELTA) {
196 LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000197 return false;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000198 }
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000199 return true;
200}
201
202std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation(
203 nanoseconds resampleTime) const {
204 if (!canExtrapolate()) {
205 return std::nullopt;
206 }
207 LOG_IF(FATAL, mLatestSamples.size() < 2)
208 << "Not resampled. mLatestSamples must have at least two samples to extrapolate.";
209
210 const Sample& pastSample = *(mLatestSamples.end() - 2);
211 const Sample& presentSample = *(mLatestSamples.end() - 1);
212
213 const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000214 // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
215 // we use this value as the resample time target.
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000216 const nanoseconds farthestPrediction =
217 presentSample.eventTime + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000218 const nanoseconds newResampleTime =
219 (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
220 LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
221 << "Resample time is too far in the future. Adjusting prediction from "
222 << (resampleTime - presentSample.eventTime) << " to "
223 << (farthestPrediction - presentSample.eventTime) << "ns.";
224 const float alpha =
225 std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
226 delta;
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000227
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000228 std::vector<Pointer> resampledPointers;
229 for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
230 const PointerCoords& resampledCoords =
231 calculateResampledCoords(pastSample.pointers[i].coords,
232 presentSample.pointers[i].coords, alpha);
233 resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
234 }
235 return Sample{newResampleTime, resampledPointers};
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000236}
237
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000238inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
239 MotionEvent& motionEvent) {
Paul Ramirezcf1b06e2024-08-01 17:11:58 +0000240 motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(),
241 motionEvent.getId());
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000242}
243
Paul Ramirezcd7488c2024-09-13 23:01:12 +0000244nanoseconds LegacyResampler::getResampleLatency() const {
245 return RESAMPLE_LATENCY;
246}
247
Paul Ramirez6affbdb2024-09-11 22:20:26 +0000248void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000249 const InputMessage* futureSample) {
250 if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
251 mLatestSamples.clear();
252 }
253 mPreviousDeviceId = motionEvent.getDeviceId();
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000254
Paul Ramirez6affbdb2024-09-11 22:20:26 +0000255 const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
256
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000257 updateLatestSamples(motionEvent);
Paul Ramirez486ca6d2024-08-12 14:37:02 +0000258
259 const std::optional<Sample> sample = (futureSample != nullptr)
260 ? (attemptInterpolation(resampleTime, *futureSample))
261 : (attemptExtrapolation(resampleTime));
262 if (sample.has_value()) {
263 addSampleToMotionEvent(*sample, motionEvent);
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000264 }
Paul Ramirezbe9c5442024-07-10 00:12:41 +0000265}
266} // namespace android