Add multiple pointer support to LegacyResampler with tests
Added multiple pointer support to LegacyResampler and included the
corresponding unit tests to ensure correctness.
Bug: 297226446
Flag: EXEMPT refactor
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="ResamplerTest*"
Change-Id: Ib639942c31311dcdcf6d72ed33a4d4476b76fe7f
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 342f7f5..a1e6e06 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <ftl/enum.h>
#include <input/Resampler.h>
#include <utils/Timers.h>
@@ -56,6 +57,11 @@
constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
+bool canResampleTool(ToolType toolType) {
+ return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
+ toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
+}
+
inline float lerp(float a, float b, float alpha) {
return a + alpha * (b - a);
}
@@ -73,21 +79,71 @@
void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
const size_t numSamples = motionEvent.getHistorySize() + 1;
- for (size_t i = 0; i < numSamples; ++i) {
+ const size_t latestIndex = numSamples - 1;
+ const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
+ for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
+ std::vector<Pointer> pointers;
+ const size_t numPointers = motionEvent.getPointerCount();
+ for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
+ // getSamplePointerCoords is the vector representation of a getHistorySize by
+ // getPointerCount matrix.
+ const PointerCoords& pointerCoords =
+ motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
+ pointers.push_back(
+ Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+ }
mLatestSamples.pushBack(
- Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
- Pointer{*motionEvent.getPointerProperties(0),
- motionEvent.getSamplePointerCoords()[i]}});
+ Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(sampleIndex)),
+ pointers});
}
}
-bool LegacyResampler::canInterpolate(const InputMessage& futureSample) const {
+LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
+ std::vector<Pointer> pointers;
+ for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
+ pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
+ message.body.motion.pointers[i].coords});
+ }
+ return Sample{static_cast<nanoseconds>(message.body.motion.eventTime), pointers};
+}
+
+bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
+ if (target.pointers.size() > auxiliary.pointers.size()) {
+ LOG_IF(INFO, debugResampling())
+ << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
+ return false;
+ }
+ for (size_t i = 0; i < target.pointers.size(); ++i) {
+ if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
+ return false;
+ }
+ if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
+ return false;
+ }
+ if (!canResampleTool(target.pointers[i].properties.toolType)) {
+ LOG_IF(INFO, debugResampling())
+ << "Not resampled. Cannot resample "
+ << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LegacyResampler::canInterpolate(const InputMessage& message) const {
LOG_IF(FATAL, mLatestSamples.empty())
<< "Not resampled. mLatestSamples must not be empty to interpolate.";
const Sample& pastSample = *(mLatestSamples.end() - 1);
- const nanoseconds delta =
- static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
+ const Sample& futureSample = messageToSample(message);
+
+ if (!pointerPropertiesResampleable(pastSample, futureSample)) {
+ return false;
+ }
+
+ const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
return false;
@@ -104,15 +160,20 @@
<< "Not resampled. mLatestSamples must not be empty to interpolate.";
const Sample& pastSample = *(mLatestSamples.end() - 1);
+
const nanoseconds delta =
static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
const float alpha =
std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
- const PointerCoords resampledCoords =
- calculateResampledCoords(pastSample.pointer.coords,
- futureSample.body.motion.pointers[0].coords, alpha);
- return Sample{resampleTime, Pointer{pastSample.pointer.properties, resampledCoords}};
+ std::vector<Pointer> resampledPointers;
+ for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
+ const PointerCoords& resampledCoords =
+ calculateResampledCoords(pastSample.pointers[i].coords,
+ futureSample.body.motion.pointers[i].coords, alpha);
+ resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
+ }
+ return Sample{resampleTime, resampledPointers};
}
bool LegacyResampler::canExtrapolate() const {
@@ -124,6 +185,10 @@
const Sample& pastSample = *(mLatestSamples.end() - 2);
const Sample& presentSample = *(mLatestSamples.end() - 1);
+ if (!pointerPropertiesResampleable(presentSample, pastSample)) {
+ return false;
+ }
+
const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
if (delta < RESAMPLE_MIN_DELTA) {
LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
@@ -160,16 +225,21 @@
const float alpha =
std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
delta;
- const PointerCoords resampledCoords =
- calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
- alpha);
- return Sample{newResampleTime, Pointer{presentSample.pointer.properties, resampledCoords}};
+ std::vector<Pointer> resampledPointers;
+ for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
+ const PointerCoords& resampledCoords =
+ calculateResampledCoords(pastSample.pointers[i].coords,
+ presentSample.pointers[i].coords, alpha);
+ resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
+ }
+ return Sample{newResampleTime, resampledPointers};
}
inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
MotionEvent& motionEvent) {
- motionEvent.addSample(sample.eventTime.count(), &sample.pointer.coords, motionEvent.getId());
+ motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(),
+ motionEvent.getId());
}
void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent,