diff options
author | 2024-08-12 14:37:02 +0000 | |
---|---|---|
committer | 2024-08-15 17:43:15 +0000 | |
commit | 486ca6d598f09e97962740cabc73cf48a6cfafa6 (patch) | |
tree | 2ae0b909d044f7df6ae89f2c3a39e23e1c087552 | |
parent | d7f52be5cecc1c70faa3e1b944f8b922c5402856 (diff) |
Refactor resampler logic to constrain MotionEvent mutation
Refactored resampler logic to constrain the functions where MotionEvent
can mutate.
Bug: 297226446
Flag: EXEMPT refactor
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="ResamplerTest*"
Change-Id: I4b9c9140b73cd45f866dc51931b03cdf2883e5a3
-rw-r--r-- | include/input/Resampler.h | 41 | ||||
-rw-r--r-- | libs/input/Resampler.cpp | 104 |
2 files changed, 99 insertions, 46 deletions
diff --git a/include/input/Resampler.h b/include/input/Resampler.h index ff9c4b0868..5980d5d4c4 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -44,7 +44,7 @@ struct Resampler { * samples by the end of the resampling. No other field of motionEvent should be modified. * - If resampling does not occur, then motionEvent must not be modified in any way. */ - virtual void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, + virtual void resampleMotionEvent(std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, const InputMessage* futureSample) = 0; }; @@ -60,7 +60,7 @@ public: * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and * `motionEvent` is unmodified. */ - void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, + void resampleMotionEvent(std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, const InputMessage* futureSample) override; private: @@ -72,10 +72,6 @@ private: struct Sample { std::chrono::nanoseconds eventTime; Pointer pointer; - - Sample(const std::chrono::nanoseconds eventTime, const PointerProperties& properties, - const PointerCoords& coords) - : eventTime{eventTime}, pointer{properties, coords} {} }; /** @@ -99,17 +95,34 @@ private: void updateLatestSamples(const MotionEvent& motionEvent); /** - * May add a sample at the end of motionEvent with eventTime equal to resampleTime, and - * interpolated coordinates between the latest motionEvent sample and futureSample. + * Checks if there are necessary conditions to interpolate. For example, interpolation cannot + * take place if samples are too far apart in time. mLatestSamples must have at least one sample + * when canInterpolate is invoked. + */ + bool canInterpolate(const InputMessage& futureSample) const; + + /** + * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample, + * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt. + * mLatestSamples must have at least one sample when attemptInterpolation is called. */ - void interpolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, - const InputMessage& futureSample) const; + std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime, + const InputMessage& futureSample) const; /** - * May add a sample at the end of motionEvent by extrapolating from the latest two samples. The - * added sample either has eventTime equal to resampleTime, or an earlier time if resampleTime - * is too far in the future. + * Checks if there are necessary conditions to extrapolate. That is, there are at least two + * samples in mLatestSamples, and delta is bounded within a time interval. */ - void extrapolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent) const; + bool canExtrapolate() const; + + /** + * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from + * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime, + * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false, + * this function returns nullopt. + */ + std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const; + + inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; } // namespace android
\ No newline at end of file diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 836a97af53..342f7f557f 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -60,8 +60,8 @@ inline float lerp(float a, float b, float alpha) { return a + alpha * (b - a); } -const PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b, - const float alpha) { +PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b, + float alpha) { // We use the value of alpha to initialize resampledCoords with the latest sample information. PointerCoords resampledCoords = (alpha < 1.0f) ? a : b; resampledCoords.isResampled = true; @@ -72,52 +72,85 @@ const PointerCoords calculateResampledCoords(const PointerCoords& a, const Point } // namespace void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { - const size_t motionEventSampleSize = motionEvent.getHistorySize() + 1; - for (size_t i = 0; i < motionEventSampleSize; ++i) { - Sample sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)), - *motionEvent.getPointerProperties(0), - motionEvent.getSamplePointerCoords()[i]}; - mLatestSamples.pushBack(sample); + const size_t numSamples = motionEvent.getHistorySize() + 1; + for (size_t i = 0; i < numSamples; ++i) { + mLatestSamples.pushBack( + Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)), + Pointer{*motionEvent.getPointerProperties(0), + motionEvent.getSamplePointerCoords()[i]}}); } } -void LegacyResampler::interpolate(const nanoseconds resampleTime, MotionEvent& motionEvent, - const InputMessage& futureSample) const { - const Sample pastSample = mLatestSamples.back(); +bool LegacyResampler::canInterpolate(const InputMessage& futureSample) 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; if (delta < RESAMPLE_MIN_DELTA) { LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; - return; + return false; + } + return true; +} + +std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation( + nanoseconds resampleTime, const InputMessage& futureSample) const { + if (!canInterpolate(futureSample)) { + return std::nullopt; } + 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 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); - motionEvent.addSample(resampleTime.count(), &resampledCoords, motionEvent.getId()); + + return Sample{resampleTime, Pointer{pastSample.pointer.properties, resampledCoords}}; } -void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& motionEvent) const { +bool LegacyResampler::canExtrapolate() const { if (mLatestSamples.size() < 2) { - return; + LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data."; + return false; } - const Sample pastSample = *(mLatestSamples.end() - 2); - const Sample presentSample = *(mLatestSamples.end() - 1); - const nanoseconds delta = - static_cast<nanoseconds>(presentSample.eventTime - pastSample.eventTime); + + const Sample& pastSample = *(mLatestSamples.end() - 2); + const Sample& presentSample = *(mLatestSamples.end() - 1); + + const nanoseconds delta = presentSample.eventTime - pastSample.eventTime; if (delta < RESAMPLE_MIN_DELTA) { LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; - return; + return false; } else if (delta > RESAMPLE_MAX_DELTA) { LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns."; - return; + return false; } + return true; +} + +std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation( + nanoseconds resampleTime) const { + if (!canExtrapolate()) { + return std::nullopt; + } + LOG_IF(FATAL, mLatestSamples.size() < 2) + << "Not resampled. mLatestSamples must have at least two samples to extrapolate."; + + const Sample& pastSample = *(mLatestSamples.end() - 2); + const Sample& presentSample = *(mLatestSamples.end() - 1); + + const nanoseconds delta = presentSample.eventTime - pastSample.eventTime; // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this, // we use this value as the resample time target. - const nanoseconds farthestPrediction = static_cast<nanoseconds>(presentSample.eventTime) + - std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION); + const nanoseconds farthestPrediction = + presentSample.eventTime + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION); const nanoseconds newResampleTime = (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime); LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction) @@ -127,25 +160,32 @@ void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& m const float alpha = std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) / delta; - const PointerCoords resampledCoords = calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords, alpha); - motionEvent.addSample(newResampleTime.count(), &resampledCoords, motionEvent.getId()); + + return Sample{newResampleTime, Pointer{presentSample.pointer.properties, resampledCoords}}; +} + +inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample, + MotionEvent& motionEvent) { + motionEvent.addSample(sample.eventTime.count(), &sample.pointer.coords, motionEvent.getId()); } -void LegacyResampler::resampleMotionEvent(const nanoseconds resampleTime, MotionEvent& motionEvent, +void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent, const InputMessage* futureSample) { if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { mLatestSamples.clear(); } mPreviousDeviceId = motionEvent.getDeviceId(); + updateLatestSamples(motionEvent); - if (futureSample) { - interpolate(resampleTime, motionEvent, *futureSample); - } else { - extrapolate(resampleTime, motionEvent); + + const std::optional<Sample> sample = (futureSample != nullptr) + ? (attemptInterpolation(resampleTime, *futureSample)) + : (attemptExtrapolation(resampleTime)); + if (sample.has_value()) { + addSampleToMotionEvent(*sample, motionEvent); } - LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data."; } } // namespace android |