diff options
-rw-r--r-- | include/input/VelocityTracker.h | 68 | ||||
-rw-r--r-- | libs/input/VelocityControl.cpp | 7 | ||||
-rw-r--r-- | libs/input/VelocityTracker.cpp | 351 | ||||
-rw-r--r-- | libs/input/tests/VelocityTracker_test.cpp | 2 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/TouchInputMapper.cpp | 11 |
5 files changed, 207 insertions, 232 deletions
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index d733d3fb65..da97c3e855 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -103,19 +103,13 @@ public: // Resets the velocity tracker state. void clear(); - // Resets the velocity tracker state for specific pointers. + // Resets the velocity tracker state for a specific pointer. // Call this method when some pointers have changed and may be reusing // an id that was assigned to a different pointer earlier. - void clearPointers(BitSet32 idBits); + void clearPointer(int32_t pointerId); - // Adds movement information for a set of pointers. - // The idBits bitfield specifies the pointer ids of the pointers whose data points - // are included in the movement. - // The positions map contains a mapping of an axis to positions array. - // The positions arrays contain information for each pointer in order by increasing id. - // Each array's size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::map<int32_t, std::vector<float>>& positions); + // Adds movement information for a pointer for a specific axis + void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); @@ -173,9 +167,8 @@ protected: public: virtual ~VelocityTrackerStrategy() { } - virtual void clearPointers(BitSet32 idBits) = 0; - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) = 0; + virtual void clearPointer(int32_t pointerId) = 0; + virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0; virtual std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const = 0; }; @@ -204,9 +197,8 @@ public: LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE); ~LeastSquaresVelocityTrackerStrategy() override; - void clearPointers(BitSet32 idBits) override; - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: @@ -220,18 +212,15 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(int32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; - float chooseWeight(uint32_t index) const; + float chooseWeight(int32_t pointerId, uint32_t index) const; const uint32_t mDegree; const Weighting mWeighting; - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; @@ -244,9 +233,8 @@ public: IntegratingVelocityTrackerStrategy(uint32_t degree); ~IntegratingVelocityTrackerStrategy() override; - void clearPointers(BitSet32 idBits) override; - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override; std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: @@ -276,9 +264,8 @@ public: LegacyVelocityTrackerStrategy(); ~LegacyVelocityTrackerStrategy() override; - void clearPointers(BitSet32 idBits) override; - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: @@ -293,14 +280,11 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(int32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { @@ -308,9 +292,8 @@ public: ImpulseVelocityTrackerStrategy(bool deltaValues); ~ImpulseVelocityTrackerStrategy() override; - void clearPointers(BitSet32 idBits) override; - void addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) override; + void clearPointer(int32_t pointerId) override; + void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; std::optional<VelocityTracker::Estimator> getEstimator(int32_t pointerId) const override; private: @@ -324,10 +307,7 @@ private: struct Movement { nsecs_t eventTime; - BitSet32 idBits; - float positions[MAX_POINTERS]; - - inline float getPosition(int32_t id) const { return positions[idBits.getIndexOfBit(id)]; } + float position; }; // Whether or not the input movement values for the strategy come in the form of delta values. @@ -335,8 +315,8 @@ private: // velocity calculation. const bool mDeltaValues; - size_t mIndex; - Movement mMovements[HISTORY_SIZE]; + std::map<int32_t /*pointerId*/, size_t /*positionInArray*/> mIndex; + std::map<int32_t /*pointerId*/, std::array<Movement, HISTORY_SIZE>> mMovements; }; } // namespace android diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index 5c008b1158..5720099033 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -70,9 +70,10 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (deltaY) { mRawPositionY += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), - {{AMOTION_EVENT_AXIS_X, {mRawPositionX}}, - {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}}); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_X, + mRawPositionX); + mVelocityTracker.addMovement(eventTime, /*pointerId=*/0, AMOTION_EVENT_AXIS_Y, + mRawPositionY); std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0); std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0); diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index f02abe0443..1cd782d7a7 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -222,28 +222,26 @@ void VelocityTracker::clear() { mConfiguredStrategies.clear(); } -void VelocityTracker::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); - mCurrentPointerIdBits = remainingIdBits; - - if (mActivePointerId && idBits.hasBit(*mActivePointerId)) { - mActivePointerId = !remainingIdBits.isEmpty() - ? std::make_optional(remainingIdBits.firstMarkedBit()) - : std::nullopt; +void VelocityTracker::clearPointer(int32_t pointerId) { + mCurrentPointerIdBits.clearBit(pointerId); + + if (mActivePointerId && *mActivePointerId == pointerId) { + // The active pointer id is being removed. Mark it invalid and try to find a new one + // from the remaining pointers. + mActivePointerId = std::nullopt; + if (!mCurrentPointerIdBits.isEmpty()) { + mActivePointerId = mCurrentPointerIdBits.firstMarkedBit(); + } } for (const auto& [_, strategy] : mConfiguredStrategies) { - strategy->clearPointers(idBits); + strategy->clearPointer(pointerId); } } -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::map<int32_t /*axis*/, std::vector<float>>& positions) { - while (idBits.count() > MAX_POINTERS) { - idBits.clearLastMarkedBit(); - } - - if ((mCurrentPointerIdBits.value & idBits.value) && +void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, + float position) { + if (mCurrentPointerIdBits.hasBit(pointerId) && std::chrono::nanoseconds(eventTime - mLastEventTime) > ASSUME_POINTER_STOPPED_TIME) { ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state.", toString(std::chrono::nanoseconds(eventTime - mLastEventTime)).c_str()); @@ -254,46 +252,28 @@ void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, } mLastEventTime = eventTime; - mCurrentPointerIdBits = idBits; - if (!mActivePointerId || !idBits.hasBit(*mActivePointerId)) { - mActivePointerId = - idBits.isEmpty() ? std::nullopt : std::make_optional(idBits.firstMarkedBit()); + mCurrentPointerIdBits.markBit(pointerId); + if (!mActivePointerId) { + // Let this be the new active pointer if no active pointer is currently set + mActivePointerId = pointerId; } - for (const auto& [axis, positionValues] : positions) { - LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(), - "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", - idBits.count(), positionValues.size()); - if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { - configureStrategy(axis); - } - mConfiguredStrategies[axis]->addMovement(eventTime, idBits, positionValues); + if (mConfiguredStrategies.find(axis) == mConfiguredStrategies.end()) { + configureStrategy(axis); } + mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position); if (DEBUG_VELOCITY) { - ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 - ", idBits=0x%08x, activePointerId=%s", - eventTime, idBits.value, toString(mActivePointerId).c_str()); - for (const auto& positionsEntry : positions) { - for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) { - uint32_t pointerId = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(pointerId); - iterBits.clearBit(pointerId); - std::optional<Estimator> estimator = getEstimator(positionsEntry.first, pointerId); - if (estimator) { - ALOGD(" %d: axis=%d, position=%0.3f, " - "estimator (degree=%d, coeff=%s, confidence=%f)", - pointerId, positionsEntry.first, positionsEntry.second[index], - int((*estimator).degree), - vectorToString((*estimator).coeff.data(), (*estimator).degree + 1) - .c_str(), - (*estimator).confidence); - } else { - ALOGD(" %d: axis=%d estimated velocity is not valid", pointerId, - positionsEntry.first); - } - } - } + ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32 + ", activePointerId=%s", + eventTime, pointerId, toString(mActivePointerId).c_str()); + + std::optional<Estimator> estimator = getEstimator(axis, pointerId); + ALOGD(" %d: axis=%d, position=%0.3f, " + "estimator (degree=%d, coeff=%s, confidence=%f)", + pointerId, axis, position, int((*estimator).degree), + vectorToString((*estimator).coeff.data(), (*estimator).degree + 1).c_str(), + (*estimator).confidence); } } @@ -313,9 +293,7 @@ void VelocityTracker::addMovement(const MotionEvent* event) { // Start a new movement trace for a pointer that just went down. // We do this on down instead of on up because the client may want to query the // final velocity for a pointer that just went up. - BitSet32 downIdBits; - downIdBits.markBit(event->getPointerId(event->getActionIndex())); - clearPointers(downIdBits); + clearPointer(event->getPointerId(event->getActionIndex())); axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; } @@ -354,35 +332,17 @@ void VelocityTracker::addMovement(const MotionEvent* event) { return; } - size_t pointerCount = event->getPointerCount(); - if (pointerCount > MAX_POINTERS) { - pointerCount = MAX_POINTERS; - } - - BitSet32 idBits; - for (size_t i = 0; i < pointerCount; i++) { - idBits.markBit(event->getPointerId(i)); - } - - uint32_t pointerIndex[MAX_POINTERS]; - for (size_t i = 0; i < pointerCount; i++) { - pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); - } - - std::map<int32_t, std::vector<float>> positions; - for (int32_t axis : axesToProcess) { - positions[axis].resize(pointerCount); - } - size_t historySize = event->getHistorySize(); for (size_t h = 0; h <= historySize; h++) { nsecs_t eventTime = event->getHistoricalEventTime(h); - for (int32_t axis : axesToProcess) { - for (size_t i = 0; i < pointerCount; i++) { - positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h); + for (size_t i = 0; i < event->getPointerCount(); i++) { + // TODO(b/167946721): skip resampled samples + const int32_t pointerId = event->getPointerId(i); + for (int32_t axis : axesToProcess) { + const float position = event->getHistoricalAxisValue(axis, i, h); + addMovement(eventTime, pointerId, axis, position); } } - addMovement(eventTime, idBits, positions); } } @@ -425,38 +385,42 @@ std::optional<VelocityTracker::Estimator> VelocityTracker::getEstimator(int32_t LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting) - : mDegree(degree), mWeighting(weighting), mIndex(0) {} + : mDegree(degree), mWeighting(weighting) {} LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { } -void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void LeastSquaresVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (mMovements[mIndex].eventTime != eventTime) { +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. - mIndex++; + index++; } - if (mIndex == HISTORY_SIZE) { - mIndex = 0; + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } /** @@ -682,26 +646,38 @@ static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::getEstimator( int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data + } // Iterate over movement samples in reverse time order and collect samples. std::vector<float> positions; std::vector<float> w; std::vector<float> time; - uint32_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; + uint32_t index = mIndex.at(pointerId); + const Movement& newestMovement = movementIt->second[index]; do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(pointerId)) { - break; - } + const Movement& movement = movementIt->second[index]; nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } - - positions.push_back(movement.getPosition(pointerId)); - w.push_back(chooseWeight(index)); + if (movement.eventTime == 0 && index != 0) { + // All eventTime's are initialized to 0. In this fixed-width circular buffer, it's + // possible that not all entries are valid. We use a time=0 as a signal for those + // uninitialized values. If we encounter a time of 0 in a position + // that's > 0, it means that we hit the block where the data wasn't initialized. + // We still don't know whether the value at index=0, with eventTime=0 is valid. + // However, that's only possible when the value is by itself. So there's no hard in + // processing it anyways, since the velocity for a single point is zero, and this + // situation will only be encountered in artificial circumstances (in tests). + // In practice, time will never be 0. + break; + } + positions.push_back(movement.position); + w.push_back(chooseWeight(pointerId, index)); time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (positions.size() < HISTORY_SIZE); @@ -758,19 +734,20 @@ std::optional<VelocityTracker::Estimator> LeastSquaresVelocityTrackerStrategy::g return estimator; } -float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { +float LeastSquaresVelocityTrackerStrategy::chooseWeight(int32_t pointerId, uint32_t index) const { + const std::array<Movement, HISTORY_SIZE>& movements = mMovements.at(pointerId); switch (mWeighting) { case Weighting::DELTA: { // Weight points based on how much time elapsed between them and the next // point so that points that "cover" a shorter time span are weighed less. // delta 0ms: 0.5 // delta 10ms: 1.0 - if (index == mIndex) { + if (index == mIndex.at(pointerId)) { return 1.0f; } uint32_t nextIndex = (index + 1) % HISTORY_SIZE; float deltaMillis = - (mMovements[nextIndex].eventTime - mMovements[index].eventTime) * 0.000001f; + (movements[nextIndex].eventTime - movements[index].eventTime) * 0.000001f; if (deltaMillis < 0) { return 0.5f; } @@ -787,7 +764,8 @@ float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { // age 50ms: 1.0 // age 60ms: 0.5 float ageMillis = - (mMovements[mIndex].eventTime - mMovements[index].eventTime) * 0.000001f; + (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * + 0.000001f; if (ageMillis < 0) { return 0.5f; } @@ -809,7 +787,8 @@ float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { // age 50ms: 1.0 // age 100ms: 0.5 float ageMillis = - (mMovements[mIndex].eventTime - mMovements[index].eventTime) * 0.000001f; + (movements[mIndex.at(pointerId)].eventTime - movements[index].eventTime) * + 0.000001f; if (ageMillis < 50) { return 1.0f; } @@ -824,7 +803,6 @@ float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { } } - // --- IntegratingVelocityTrackerStrategy --- IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : @@ -834,25 +812,20 @@ IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { } -void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - mPointerIdBits.value &= ~idBits.value; +void IntegratingVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mPointerIdBits.clearBit(pointerId); } -void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - uint32_t index = 0; - for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { - uint32_t id = iterIdBits.clearFirstMarkedBit(); - State& state = mPointerState[id]; - const float position = positions[index++]; - if (mPointerIdBits.hasBit(id)) { - updateState(state, eventTime, position); - } else { - initState(state, eventTime, position); - } +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + State& state = mPointerState[pointerId]; + if (mPointerIdBits.hasBit(pointerId)) { + updateState(state, eventTime, position); + } else { + initState(state, eventTime, position); } - mPointerIdBits = idBits; + mPointerIdBits.markBit(pointerId); } std::optional<VelocityTracker::Estimator> IntegratingVelocityTrackerStrategy::getEstimator( @@ -924,47 +897,60 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, // --- LegacyVelocityTrackerStrategy --- -LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() : mIndex(0) {} +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {} LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { } -void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void LegacyVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (++mIndex == HISTORY_SIZE) { - mIndex = 0; +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { + // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates + // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include + // the new pointer. If the eventtimes for both events are identical, just update the data + // for this time. + // We only compare against the last value, as it is likely that addMovement is called + // in chronological order as events occur. + index++; + } + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEstimator( int32_t pointerId) const { - const Movement& newestMovement = mMovements[mIndex]; - if (!newestMovement.idBits.hasBit(pointerId)) { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { return std::nullopt; // no data } + const Movement& newestMovement = movementIt->second[mIndex.at(pointerId)]; // Find the oldest sample that contains the pointer and that is not older than HORIZON. nsecs_t minTime = newestMovement.eventTime - HORIZON; - uint32_t oldestIndex = mIndex; + uint32_t oldestIndex = mIndex.at(pointerId); uint32_t numTouches = 1; do { uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; - const Movement& nextOldestMovement = mMovements[nextOldestIndex]; - if (!nextOldestMovement.idBits.hasBit(pointerId) || - nextOldestMovement.eventTime < minTime) { + const Movement& nextOldestMovement = mMovements.at(pointerId)[nextOldestIndex]; + if (nextOldestMovement.eventTime < minTime) { break; } oldestIndex = nextOldestIndex; @@ -984,22 +970,22 @@ std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEsti float accumV = 0; uint32_t index = oldestIndex; uint32_t samplesUsed = 0; - const Movement& oldestMovement = mMovements[oldestIndex]; - float oldestPosition = oldestMovement.getPosition(pointerId); + const Movement& oldestMovement = mMovements.at(pointerId)[oldestIndex]; + float oldestPosition = oldestMovement.position; nsecs_t lastDuration = 0; while (numTouches-- > 1) { if (++index == HISTORY_SIZE) { index = 0; } - const Movement& movement = mMovements[index]; + const Movement& movement = mMovements.at(pointerId)[index]; nsecs_t duration = movement.eventTime - oldestMovement.eventTime; // If the duration between samples is small, we may significantly overestimate // the velocity. Consequently, we impose a minimum duration constraint on the // samples that we include in the calculation. if (duration >= MIN_DURATION) { - float position = movement.getPosition(pointerId); + float position = movement.position; float scale = 1000000000.0f / duration; // one over time delta in seconds float v = (position - oldestPosition) * scale; accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration); @@ -1009,7 +995,7 @@ std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEsti } // Report velocity. - float newestPosition = newestMovement.getPosition(pointerId); + float newestPosition = newestMovement.position; VelocityTracker::Estimator estimator; estimator.time = newestMovement.eventTime; estimator.confidence = 1; @@ -1026,38 +1012,42 @@ std::optional<VelocityTracker::Estimator> LegacyVelocityTrackerStrategy::getEsti // --- ImpulseVelocityTrackerStrategy --- ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues) - : mDeltaValues(deltaValues), mIndex(0) {} + : mDeltaValues(deltaValues) {} ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { } -void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; +void ImpulseVelocityTrackerStrategy::clearPointer(int32_t pointerId) { + mIndex.erase(pointerId); + mMovements.erase(pointerId); } -void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const std::vector<float>& positions) { - if (mMovements[mIndex].eventTime != eventTime) { +void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, int32_t pointerId, + float position) { + // If data for this pointer already exists, we have a valid entry at the position of + // mIndex[pointerId] and mMovements[pointerId]. In that case, we need to advance the index + // to the next position in the circular buffer and write the new Movement there. Otherwise, + // if this is a first movement for this pointer, we initialize the maps mIndex and mMovements + // for this pointer and write to the first position. + auto [movementIt, inserted] = mMovements.insert({pointerId, {}}); + auto [indexIt, _] = mIndex.insert({pointerId, 0}); + size_t& index = indexIt->second; + if (!inserted && movementIt->second[index].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. - mIndex++; + index++; } - if (mIndex == HISTORY_SIZE) { - mIndex = 0; + if (index == HISTORY_SIZE) { + index = 0; } - Movement& movement = mMovements[mIndex]; + Movement& movement = movementIt->second[index]; movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } + movement.position = position; } /** @@ -1187,24 +1177,34 @@ static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t c std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEstimator( int32_t pointerId) const { + const auto movementIt = mMovements.find(pointerId); + if (movementIt == mMovements.end()) { + return std::nullopt; // no data + } + // Iterate over movement samples in reverse time order and collect samples. float positions[HISTORY_SIZE]; nsecs_t time[HISTORY_SIZE]; size_t m = 0; // number of points that will be used for fitting - size_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; + size_t index = mIndex.at(pointerId); + const Movement& newestMovement = movementIt->second[index]; do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(pointerId)) { - break; - } + const Movement& movement = movementIt->second[index]; nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } + if (movement.eventTime == 0 && index != 0) { + // All eventTime's are initialized to 0. If we encounter a time of 0 in a position + // that's >0, it means that we hit the block where the data wasn't initialized. + // It's also possible that the sample at 0 would be invalid, but there's no harm in + // processing it, since it would be just a single point, and will only be encountered + // in artificial circumstances (in tests). + break; + } - positions[m] = movement.getPosition(pointerId); + positions[m] = movement.position; time[m] = movement.eventTime; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); @@ -1228,13 +1228,10 @@ std::optional<VelocityTracker::Estimator> ImpulseVelocityTrackerStrategy::getEst // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons. // X axis chosen arbitrarily for velocity comparisons. VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2); - BitSet32 idBits; - const uint32_t tempPointerId = 0; - idBits.markBit(tempPointerId); for (ssize_t i = m - 1; i >= 0; i--) { - lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}}); + lsq2.addMovement(time[i], pointerId, AMOTION_EVENT_AXIS_X, positions[i]); } - std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, tempPointerId); + std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId); if (v) { ALOGD("lsq2 velocity: %.1f", *v); } else { diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 077645975c..2678f2f2fd 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -431,7 +431,7 @@ TEST_F(VelocityTrackerTest, TestApiInteractionsWithNoMotionEvents) { EXPECT_EQ(-1, vt.getActivePointerId()); // Make sure that the clearing functions execute without an issue. - vt.clearPointers(BitSet32(7U)); + vt.clearPointer(7U); vt.clear(); } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 4d51aeef5d..8cd2cf0255 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -2703,18 +2703,15 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Update the velocity tracker. { - std::vector<float> positionsX; - std::vector<float> positionsY; for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - positionsX.push_back(pointer.x * mPointerXMovementScale); - positionsY.push_back(pointer.y * mPointerYMovementScale); + const float x = pointer.x * mPointerXMovementScale; + const float y = pointer.y * mPointerYMovementScale; + mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_X, x); + mPointerGesture.velocityTracker.addMovement(when, id, AMOTION_EVENT_AXIS_Y, y); } - mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, - {{AMOTION_EVENT_AXIS_X, positionsX}, - {AMOTION_EVENT_AXIS_Y, positionsY}}); } // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning |