blob: 836a97af530025573c8bf08e66754e9ad5171178 [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>
24
25#include <input/Resampler.h>
26#include <utils/Timers.h>
27
28using std::chrono::nanoseconds;
29
30namespace android {
31
32namespace {
33
34const bool IS_DEBUGGABLE_BUILD =
35#if defined(__ANDROID__)
36 android::base::GetBoolProperty("ro.debuggable", false);
37#else
38 true;
39#endif
40
41bool debugResampling() {
42 if (!IS_DEBUGGABLE_BUILD) {
43 static const bool DEBUG_TRANSPORT_RESAMPLING =
44 __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
45 ANDROID_LOG_INFO);
46 return DEBUG_TRANSPORT_RESAMPLING;
47 }
48 return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
49}
50
51constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
52
53constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
54
55constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20};
56
57constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
58
59inline float lerp(float a, float b, float alpha) {
60 return a + alpha * (b - a);
61}
62
63const PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
64 const float alpha) {
Paul Ramirez68ca3d12024-08-12 23:00:50 +000065 // We use the value of alpha to initialize resampledCoords with the latest sample information.
66 PointerCoords resampledCoords = (alpha < 1.0f) ? a : b;
Paul Ramirezbe9c5442024-07-10 00:12:41 +000067 resampledCoords.isResampled = true;
68 resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
69 resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
70 return resampledCoords;
71}
72} // namespace
73
74void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
75 const size_t motionEventSampleSize = motionEvent.getHistorySize() + 1;
76 for (size_t i = 0; i < motionEventSampleSize; ++i) {
77 Sample sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
78 *motionEvent.getPointerProperties(0),
79 motionEvent.getSamplePointerCoords()[i]};
80 mLatestSamples.pushBack(sample);
81 }
82}
83
84void LegacyResampler::interpolate(const nanoseconds resampleTime, MotionEvent& motionEvent,
85 const InputMessage& futureSample) const {
86 const Sample pastSample = mLatestSamples.back();
87 const nanoseconds delta =
88 static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
89 if (delta < RESAMPLE_MIN_DELTA) {
90 LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
91 return;
92 }
93 const float alpha =
94 std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
95
96 const PointerCoords resampledCoords =
97 calculateResampledCoords(pastSample.pointer.coords,
98 futureSample.body.motion.pointers[0].coords, alpha);
99 motionEvent.addSample(resampleTime.count(), &resampledCoords, motionEvent.getId());
100}
101
102void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& motionEvent) const {
103 if (mLatestSamples.size() < 2) {
104 return;
105 }
106 const Sample pastSample = *(mLatestSamples.end() - 2);
107 const Sample presentSample = *(mLatestSamples.end() - 1);
108 const nanoseconds delta =
109 static_cast<nanoseconds>(presentSample.eventTime - pastSample.eventTime);
110 if (delta < RESAMPLE_MIN_DELTA) {
111 LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
112 return;
113 } else if (delta > RESAMPLE_MAX_DELTA) {
114 LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
115 return;
116 }
117 // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
118 // we use this value as the resample time target.
119 const nanoseconds farthestPrediction = static_cast<nanoseconds>(presentSample.eventTime) +
120 std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
121 const nanoseconds newResampleTime =
122 (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
123 LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
124 << "Resample time is too far in the future. Adjusting prediction from "
125 << (resampleTime - presentSample.eventTime) << " to "
126 << (farthestPrediction - presentSample.eventTime) << "ns.";
127 const float alpha =
128 std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
129 delta;
130
131 const PointerCoords resampledCoords =
132 calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
133 alpha);
134 motionEvent.addSample(newResampleTime.count(), &resampledCoords, motionEvent.getId());
135}
136
137void LegacyResampler::resampleMotionEvent(const nanoseconds resampleTime, MotionEvent& motionEvent,
138 const InputMessage* futureSample) {
139 if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
140 mLatestSamples.clear();
141 }
142 mPreviousDeviceId = motionEvent.getDeviceId();
143 updateLatestSamples(motionEvent);
144 if (futureSample) {
145 interpolate(resampleTime, motionEvent, *futureSample);
146 } else {
147 extrapolate(resampleTime, motionEvent);
148 }
149 LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
150}
151} // namespace android