Bug 1642788 - Use mTimeStamp rather than mTime for APZ velocity computations. r=kats

Differential Revision: https://phabricator.services.mozilla.com/D77979
This commit is contained in:
Botond Ballo 2020-06-03 16:38:38 +00:00
parent 47b44d078e
commit a864440a96
8 changed files with 96 additions and 92 deletions

View file

@ -19,7 +19,8 @@ namespace layers {
// stopped moving. Some input devices do not send move events in the
// case where a pointer has stopped. We need to detect this case so that we can
// accurately predict the velocity after the pointer starts moving again.
static const int kAssumePointerMoveStoppedTimeMs = 40;
static const TimeDuration kAssumePointerMoveStoppedTime =
TimeDuration::FromMilliseconds(40);
// The degree of the approximation.
static const uint8_t kDegree = 2;
@ -31,36 +32,36 @@ static const uint8_t kPolyDegree = kDegree + 1;
// Maximum size of position history.
static const uint8_t kHistorySize = 20;
AndroidVelocityTracker::AndroidVelocityTracker() : mLastEventTime(0) {}
AndroidVelocityTracker::AndroidVelocityTracker() {}
void AndroidVelocityTracker::StartTracking(ParentLayerCoord aPos,
uint32_t aTimestampMs) {
TimeStamp aTimestamp) {
Clear();
mHistory.AppendElement(std::make_pair(aTimestampMs, aPos));
mLastEventTime = aTimestampMs;
mHistory.AppendElement(std::make_pair(aTimestamp, aPos));
mLastEventTime = aTimestamp;
}
Maybe<float> AndroidVelocityTracker::AddPosition(ParentLayerCoord aPos,
uint32_t aTimestampMs) {
if ((aTimestampMs - mLastEventTime) >= kAssumePointerMoveStoppedTimeMs) {
TimeStamp aTimestamp) {
if ((aTimestamp - mLastEventTime) >= kAssumePointerMoveStoppedTime) {
Clear();
}
if (aTimestampMs == mLastEventTime) {
// If we get a sample with the same timestamp as the previous one,
if ((aTimestamp - mLastEventTime).ToMilliseconds() < 1.0) {
// If we get a sample within a millisecond of the previous one,
// just update its position. Two samples in the history with the
// same timestamp can lead to things like infinite velocities.
if (mHistory.Length() > 0) {
mHistory[mHistory.Length() - 1].second = aPos;
}
} else {
mHistory.AppendElement(std::make_pair(aTimestampMs, aPos));
mHistory.AppendElement(std::make_pair(aTimestamp, aPos));
if (mHistory.Length() > kHistorySize) {
mHistory.RemoveElementAt(0);
}
}
mLastEventTime = aTimestampMs;
mLastEventTime = aTimestamp;
if (mHistory.Length() < 2) {
return Nothing();
@ -68,7 +69,8 @@ Maybe<float> AndroidVelocityTracker::AddPosition(ParentLayerCoord aPos,
auto start = mHistory[mHistory.Length() - 2];
auto end = mHistory[mHistory.Length() - 1];
return Some((end.second - start.second) / (end.first - start.first));
return Some((end.second - start.second) /
(end.first - start.first).ToMilliseconds());
}
static float VectorDot(const float* a, const float* b, uint32_t m) {
@ -213,7 +215,7 @@ static bool SolveLeastSquares(const float* x, const float* y, const float* w,
return true;
}
Maybe<float> AndroidVelocityTracker::ComputeVelocity(uint32_t aTimestampMs) {
Maybe<float> AndroidVelocityTracker::ComputeVelocity(TimeStamp aTimestamp) {
if (mHistory.IsEmpty()) {
return Nothing{};
}
@ -230,18 +232,20 @@ Maybe<float> AndroidVelocityTracker::ComputeVelocity(uint32_t aTimestampMs) {
float time[kHistorySize];
uint32_t m = 0;
int index = mHistory.Length() - 1;
const uint32_t horizon = StaticPrefs::apz_velocity_relevance_time_ms();
const TimeDuration horizon = TimeDuration::FromMilliseconds(
StaticPrefs::apz_velocity_relevance_time_ms());
const auto& newest_movement = mHistory[index];
do {
const auto& movement = mHistory[index];
uint32_t age = newest_movement.first - movement.first;
TimeDuration age = newest_movement.first - movement.first;
if (age > horizon) break;
ParentLayerCoord position = movement.second;
pos[m] = position;
w[m] = 1.0f;
time[m] = -static_cast<float>(age) / 1000.0f; // in seconds
time[m] =
-static_cast<float>(age.ToMilliseconds()) / 1000.0f; // in seconds
index--;
m++;
} while (index >= 0);

View file

@ -21,19 +21,19 @@ namespace layers {
class AndroidVelocityTracker : public VelocityTracker {
public:
explicit AndroidVelocityTracker();
void StartTracking(ParentLayerCoord aPos, uint32_t aTimestamp) override;
void StartTracking(ParentLayerCoord aPos, TimeStamp aTimestamp) override;
Maybe<float> AddPosition(ParentLayerCoord aPos,
uint32_t aTimestampMs) override;
Maybe<float> ComputeVelocity(uint32_t aTimestampMs) override;
TimeStamp aTimestamp) override;
Maybe<float> ComputeVelocity(TimeStamp aTimestamp) override;
void Clear() override;
private:
// A queue of (timestamp, position) pairs; these are the historical
// positions at the given timestamps. Timestamps are in milliseconds.
nsTArray<std::pair<uint32_t, ParentLayerCoord>> mHistory;
// The last time an event was added to the tracker (in milliseconds),
// or zero if no events have been added.
uint32_t mLastEventTime;
// positions at the given timestamps.
nsTArray<std::pair<TimeStamp, ParentLayerCoord>> mHistory;
// The last time an event was added to the tracker, or the null moment if no
// events have been added.
TimeStamp mLastEventTime;
};
} // namespace layers

View file

@ -120,7 +120,6 @@ typedef GeckoContentController::APZStateChange APZStateChange;
typedef GeckoContentController::TapType TapType;
typedef mozilla::gfx::Point Point;
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
using mozilla::gfx::PointTyped;
// Choose between platform-specific implementations.
#ifdef MOZ_WIDGET_ANDROID
@ -1353,7 +1352,7 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(
case NOTHING: {
ParentLayerPoint point = GetFirstTouchPoint(aEvent);
mStartTouch = GetFirstExternalTouchPoint(aEvent);
StartTouch(point, aEvent.mTime);
StartTouch(point, aEvent.mTimeStamp);
if (RefPtr<GeckoContentController> controller =
GetGeckoContentController()) {
MOZ_ASSERT(GetCurrentTouchBlock());
@ -1508,7 +1507,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(
case PANNING_LOCKED_Y:
case PAN_MOMENTUM: {
MOZ_ASSERT(GetCurrentTouchBlock());
EndTouch(aEvent.mTime);
EndTouch(aEvent.mTimeStamp);
return HandleEndOfPan();
}
case PINCHING:
@ -1557,7 +1556,7 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(
// If zooming is not allowed, this is a two-finger pan.
// Start tracking panning distance and velocity.
if (!mZoomConstraints.mAllowZoom) {
StartTouch(aEvent.mLocalFocusPoint, aEvent.mTime);
StartTouch(aEvent.mLocalFocusPoint, aEvent.mTimeStamp);
}
// For platforms that don't support APZ zooming, dispatch a message to the
@ -1604,8 +1603,10 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
// UpdateWithTouchAtDevicePoint() acquires the tree lock, so
// it cannot be called while the mRecursiveMutex lock is held.
if (!allowZoom) {
mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.x, aEvent.mTime);
mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.y, aEvent.mTime);
mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.x,
aEvent.mTimeStamp);
mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.y,
aEvent.mTimeStamp);
}
// FIXME: bug 1525793 -- this may need to handle zooming or not on a
@ -1765,7 +1766,7 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(
// One finger is still down, so transition to a TOUCHING state
if (mZoomConstraints.mAllowZoom) {
mPanDirRestricted = false;
StartTouch(aEvent.mLocalFocusPoint, aEvent.mTime);
StartTouch(aEvent.mLocalFocusPoint, aEvent.mTimeStamp);
SetState(TOUCHING);
} else {
// If zooming isn't allowed, StartTouch() was already called
@ -1804,7 +1805,7 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(
ScrollSnap();
} else {
// when zoom is not allowed
EndTouch(aEvent.mTime);
EndTouch(aEvent.mTimeStamp);
if (stateWasPinching) {
// still pinching
if (HasReadyTouchBlock()) {
@ -2466,7 +2467,7 @@ nsEventStatus AsyncPanZoomController::OnPanMayBegin(
const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
StartTouch(aEvent.mLocalPanStartPoint, aEvent.mTime);
StartTouch(aEvent.mLocalPanStartPoint, aEvent.mTimeStamp);
MOZ_ASSERT(GetCurrentPanGestureBlock());
GetCurrentPanGestureBlock()->GetOverscrollHandoffChain()->CancelAnimations();
@ -2492,7 +2493,7 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(
CancelAnimation();
}
StartTouch(aEvent.mLocalPanStartPoint, aEvent.mTime);
StartTouch(aEvent.mLocalPanStartPoint, aEvent.mTimeStamp);
if (GetAxisLockMode() == FREE) {
SetState(PANNING);
@ -2620,9 +2621,9 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent,
// the only caller of UpdateWithTouchAtDevicePoint() for pan events, so
// there is no risk of other calls resetting the position.)
mX.UpdateWithTouchAtDevicePoint(mX.GetPos() - logicalPanDisplacement.x,
aEvent.mTime);
aEvent.mTimeStamp);
mY.UpdateWithTouchAtDevicePoint(mY.GetPos() - logicalPanDisplacement.y,
aEvent.mTime);
aEvent.mTimeStamp);
HandlePanningUpdate(physicalPanDisplacement);
@ -2663,7 +2664,7 @@ nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
// Call into OnPan in order to process any delta included in this event.
OnPan(aEvent, true);
EndTouch(aEvent.mTime);
EndTouch(aEvent.mTimeStamp);
// Use HandleEndOfPan for fling on platforms that don't
// emit momentum events (Gtk).
@ -3161,8 +3162,8 @@ nsEventStatus AsyncPanZoomController::StartPanning(
void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(
const MultiTouchInput& aEvent) {
ParentLayerPoint point = GetFirstTouchPoint(aEvent);
mX.UpdateWithTouchAtDevicePoint(point.x, aEvent.mTime);
mY.UpdateWithTouchAtDevicePoint(point.y, aEvent.mTime);
mX.UpdateWithTouchAtDevicePoint(point.x, aEvent.mTimeStamp);
mY.UpdateWithTouchAtDevicePoint(point.y, aEvent.mTimeStamp);
}
Maybe<CompositionPayload> AsyncPanZoomController::NotifyScrollSampling() {
@ -3540,16 +3541,16 @@ void AsyncPanZoomController::RecordScrollPayload(const TimeStamp& aTimeStamp) {
}
void AsyncPanZoomController::StartTouch(const ParentLayerPoint& aPoint,
uint32_t aTimestampMs) {
TimeStamp aTimestamp) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
mX.StartTouch(aPoint.x, aTimestampMs);
mY.StartTouch(aPoint.y, aTimestampMs);
mX.StartTouch(aPoint.x, aTimestamp);
mY.StartTouch(aPoint.y, aTimestamp);
}
void AsyncPanZoomController::EndTouch(uint32_t aTimestampMs) {
void AsyncPanZoomController::EndTouch(TimeStamp aTimestamp) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
mX.EndTouch(aTimestampMs);
mY.EndTouch(aTimestampMs);
mX.EndTouch(aTimestamp);
mY.EndTouch(aTimestamp);
}
void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {

View file

@ -805,12 +805,12 @@ class AsyncPanZoomController {
* Register the start of a touch or pan gesture at the given position and
* time.
*/
void StartTouch(const ParentLayerPoint& aPoint, uint32_t aTimestampMs);
void StartTouch(const ParentLayerPoint& aPoint, TimeStamp aTimestamp);
/**
* Register the end of a touch or pan gesture at the given time.
*/
void EndTouch(uint32_t aTimestampMs);
void EndTouch(TimeStamp aTimestamp);
/**
* Utility function to send updated FrameMetrics to Gecko so that it can paint

View file

@ -58,7 +58,7 @@ float Axis::ToLocalVelocity(float aVelocityInchesPerMs) const {
}
void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos,
uint32_t aTimestampMs) {
TimeStamp aTimestamp) {
// mVelocityTracker is controller-thread only
APZThreadUtils::AssertOnControllerThread();
@ -67,17 +67,17 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos,
AXIS_LOG("%p|%s got position %f\n", mAsyncPanZoomController, Name(),
mPos.value);
if (Maybe<float> newVelocity =
mVelocityTracker->AddPosition(aPos, aTimestampMs)) {
mVelocityTracker->AddPosition(aPos, aTimestamp)) {
mVelocity = mAxisLocked ? 0 : *newVelocity;
AXIS_LOG("%p|%s velocity from tracker is %f\n", mAsyncPanZoomController,
Name(), mVelocity);
}
}
void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
void Axis::StartTouch(ParentLayerCoord aPos, TimeStamp aTimestamp) {
mStartPos = aPos;
mPos = aPos;
mVelocityTracker->StartTracking(aPos, aTimestampMs);
mVelocityTracker->StartTracking(aPos, aTimestamp);
mAxisLocked = false;
}
@ -226,7 +226,7 @@ ParentLayerCoord Axis::PanDistance(ParentLayerCoord aPos) const {
return fabs(aPos - mStartPos);
}
void Axis::EndTouch(uint32_t aTimestampMs) {
void Axis::EndTouch(TimeStamp aTimestamp) {
// mVelocityQueue is controller-thread only
APZThreadUtils::AssertOnControllerThread();
@ -238,7 +238,7 @@ void Axis::EndTouch(uint32_t aTimestampMs) {
if (mAxisLocked) {
mVelocity = 0;
} else if (Maybe<float> velocity =
mVelocityTracker->ComputeVelocity(aTimestampMs)) {
mVelocityTracker->ComputeVelocity(aTimestamp)) {
mVelocity = *velocity;
} else {
mVelocity = 0;

View file

@ -46,14 +46,14 @@ class VelocityTracker {
* Start tracking velocity along this axis, starting with the given
* initial position and corresponding timestamp.
*/
virtual void StartTracking(ParentLayerCoord aPos, uint32_t aTimestamp) = 0;
virtual void StartTracking(ParentLayerCoord aPos, TimeStamp aTimestamp) = 0;
/**
* Record a new position along this axis, at the given timestamp.
* Returns the average velocity between the last sample and this one, or
* or Nothing() if a reasonable average cannot be computed.
*/
virtual Maybe<float> AddPosition(ParentLayerCoord aPos,
uint32_t aTimestampMs) = 0;
TimeStamp aTimestamp) = 0;
/**
* Compute an estimate of the axis's current velocity, based on recent
* position samples. It's up to implementation how many samples to consider
@ -61,7 +61,7 @@ class VelocityTracker {
* If the tracker doesn't have enough samples to compute a result, it
* may return Nothing{}.
*/
virtual Maybe<float> ComputeVelocity(uint32_t aTimestampMs) = 0;
virtual Maybe<float> ComputeVelocity(TimeStamp aTimestamp) = 0;
/**
* Clear all state in the velocity tracker.
*/
@ -85,20 +85,20 @@ class Axis {
* accumulated displacements over the course of the pan gesture.
*/
void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos,
uint32_t aTimestampMs);
TimeStamp aTimestamp);
public:
/**
* Notify this Axis that a touch has begun, i.e. the user has put their finger
* on the screen but has not yet tried to pan.
*/
void StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs);
void StartTouch(ParentLayerCoord aPos, TimeStamp aTimestamp);
/**
* Notify this Axis that a touch has ended gracefully. This may perform
* recalculations of the axis velocity.
*/
void EndTouch(uint32_t aTimestampMs);
void EndTouch(TimeStamp aTimestamp);
/**
* Notify this Axis that the gesture has ended forcefully. Useful for stopping

View file

@ -21,56 +21,56 @@ namespace layers {
// delta can be really small, which can make the velocity computation very
// volatile. To avoid this we impose a minimum time delta below which we do
// not recompute the velocity.
const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5;
const TimeDuration MIN_VELOCITY_SAMPLE_TIME = TimeDuration::FromMilliseconds(5);
extern StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
SimpleVelocityTracker::SimpleVelocityTracker(Axis* aAxis)
: mAxis(aAxis), mVelocitySampleTimeMs(0), mVelocitySamplePos(0) {}
: mAxis(aAxis), mVelocitySamplePos(0) {}
void SimpleVelocityTracker::StartTracking(ParentLayerCoord aPos,
uint32_t aTimestampMs) {
TimeStamp aTimestamp) {
Clear();
mVelocitySampleTimeMs = aTimestampMs;
mVelocitySampleTime = aTimestamp;
mVelocitySamplePos = aPos;
}
Maybe<float> SimpleVelocityTracker::AddPosition(ParentLayerCoord aPos,
uint32_t aTimestampMs) {
if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) {
// See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS.
// We still update mPos so that the positioning is correct (and we don't run
// into problems like bug 1042734) but the velocity will remain where it
// was. In particular we don't update either mVelocitySampleTimeMs or
// mVelocitySamplePos so that eventually when we do get an event with the
// required time delta we use the corresponding distance delta as well.
SVT_LOG("%p|%s skipping velocity computation for small time delta %dms\n",
TimeStamp aTimestamp) {
if (aTimestamp <= mVelocitySampleTime + MIN_VELOCITY_SAMPLE_TIME) {
// See also the comment on MIN_VELOCITY_SAMPLE_TIME.
// We don't update either mVelocitySampleTime or mVelocitySamplePos so that
// eventually when we do get an event with the required time delta we use
// the corresponding distance delta as well.
SVT_LOG("%p|%s skipping velocity computation for small time delta %f ms\n",
mAxis->OpaqueApzcPointer(), mAxis->Name(),
(aTimestampMs - mVelocitySampleTimeMs));
(aTimestamp - mVelocitySampleTime).ToMilliseconds());
return Nothing();
}
float newVelocity = (float)(mVelocitySamplePos - aPos) /
(float)(aTimestampMs - mVelocitySampleTimeMs);
float newVelocity =
(float)(mVelocitySamplePos - aPos) /
(float)(aTimestamp - mVelocitySampleTime).ToMilliseconds();
newVelocity = ApplyFlingCurveToVelocity(newVelocity);
SVT_LOG("%p|%s updating velocity to %f with touch\n",
mAxis->OpaqueApzcPointer(), mAxis->Name(), newVelocity);
mVelocitySampleTimeMs = aTimestampMs;
mVelocitySampleTime = aTimestamp;
mVelocitySamplePos = aPos;
AddVelocityToQueue(aTimestampMs, newVelocity);
AddVelocityToQueue(aTimestamp, newVelocity);
return Some(newVelocity);
}
Maybe<float> SimpleVelocityTracker::ComputeVelocity(uint32_t aTimestampMs) {
Maybe<float> SimpleVelocityTracker::ComputeVelocity(TimeStamp aTimestamp) {
float velocity = 0;
int count = 0;
for (const auto& e : mVelocityQueue) {
uint32_t timeDelta = (aTimestampMs - e.first);
if (timeDelta < StaticPrefs::apz_velocity_relevance_time_ms()) {
TimeDuration timeDelta = (aTimestamp - e.first);
if (timeDelta < TimeDuration::FromMilliseconds(
StaticPrefs::apz_velocity_relevance_time_ms())) {
count++;
velocity += e.second;
}
@ -84,9 +84,9 @@ Maybe<float> SimpleVelocityTracker::ComputeVelocity(uint32_t aTimestampMs) {
void SimpleVelocityTracker::Clear() { mVelocityQueue.Clear(); }
void SimpleVelocityTracker::AddVelocityToQueue(uint32_t aTimestampMs,
void SimpleVelocityTracker::AddVelocityToQueue(TimeStamp aTimestamp,
float aVelocity) {
mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, aVelocity));
mVelocityQueue.AppendElement(std::make_pair(aTimestamp, aVelocity));
if (mVelocityQueue.Length() >
StaticPrefs::apz_max_velocity_queue_size_AtStartup()) {
mVelocityQueue.RemoveElementAt(0);

View file

@ -20,14 +20,14 @@ namespace layers {
class SimpleVelocityTracker : public VelocityTracker {
public:
explicit SimpleVelocityTracker(Axis* aAxis);
void StartTracking(ParentLayerCoord aPos, uint32_t aTimestamp) override;
void StartTracking(ParentLayerCoord aPos, TimeStamp aTimestamp) override;
Maybe<float> AddPosition(ParentLayerCoord aPos,
uint32_t aTimestampMs) override;
Maybe<float> ComputeVelocity(uint32_t aTimestampMs) override;
TimeStamp aTimestamp) override;
Maybe<float> ComputeVelocity(TimeStamp aTimestamp) override;
void Clear() override;
private:
void AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity);
void AddVelocityToQueue(TimeStamp aTimestamp, float aVelocity);
float ApplyFlingCurveToVelocity(float aVelocity) const;
// The Axis that uses this velocity tracker.
@ -36,16 +36,15 @@ class SimpleVelocityTracker : public VelocityTracker {
Axis* MOZ_NON_OWNING_REF mAxis;
// A queue of (timestamp, velocity) pairs; these are the historical
// velocities at the given timestamps. Timestamps are in milliseconds,
// velocities are in screen pixels per ms. This member can only be
// accessed on the controller/UI thread.
nsTArray<std::pair<uint32_t, float>> mVelocityQueue;
// velocities at the given timestamps. Velocities are in screen pixels per ms.
// This member can only be accessed on the controller/UI thread.
nsTArray<std::pair<TimeStamp, float>> mVelocityQueue;
// mVelocitySampleTimeMs and mVelocitySamplePos are the time and position
// mVelocitySampleTime and mVelocitySamplePos are the time and position
// used in the last velocity sampling. They get updated when a new sample is
// taken (which may not happen on every input event, if the time delta is too
// small).
uint32_t mVelocitySampleTimeMs;
TimeStamp mVelocitySampleTime;
ParentLayerCoord mVelocitySamplePos;
};