Bug 1448439 - Use an accurate pixels per pinch value in AndroidFlingPhysics. r=kats
MozReview-Commit-ID: I0yZKTvlHWV
--- a/gfx/layers/apz/src/AndroidAPZ.cpp
+++ b/gfx/layers/apz/src/AndroidAPZ.cpp
@@ -42,22 +42,24 @@ AndroidSpecificState::AndroidSpecificSta
ANDROID_APZ_LOG("%p Failed to create Android StackScroller\n", this);
return;
}
mOverScroller = scroller;
}
AsyncPanZoomAnimation*
AndroidSpecificState::CreateFlingAnimation(AsyncPanZoomController& aApzc,
- const FlingHandoffState& aHandoffState) {
+ const FlingHandoffState& aHandoffState,
+ float aPLPPI) {
if (gfxPrefs::APZUseChromeFlingPhysics()) {
return new GenericFlingAnimation<AndroidFlingPhysics>(aApzc,
aHandoffState.mChain,
aHandoffState.mIsHandoff,
- aHandoffState.mScrolledApzc);
+ aHandoffState.mScrolledApzc,
+ aPLPPI);
} else {
return new StackScrollerFlingAnimation(aApzc,
this,
aHandoffState.mChain,
aHandoffState.mIsHandoff,
aHandoffState.mScrolledApzc);
}
}
--- a/gfx/layers/apz/src/AndroidAPZ.h
+++ b/gfx/layers/apz/src/AndroidAPZ.h
@@ -18,17 +18,18 @@ class AndroidSpecificState : public Plat
public:
AndroidSpecificState();
virtual AndroidSpecificState* AsAndroidSpecificState() override {
return this;
}
virtual AsyncPanZoomAnimation* CreateFlingAnimation(AsyncPanZoomController& aApzc,
- const FlingHandoffState& aHandoffState) override;
+ const FlingHandoffState& aHandoffState,
+ float aPLPPI) override;
static void InitializeGlobalState();
java::StackScroller::GlobalRef mOverScroller;
TimeStamp mLastFling;
};
class StackScrollerFlingAnimation: public AsyncPanZoomAnimation {
--- a/gfx/layers/apz/src/AndroidFlingPhysics.cpp
+++ b/gfx/layers/apz/src/AndroidFlingPhysics.cpp
@@ -13,23 +13,24 @@
namespace mozilla {
namespace layers {
// The fling physics calculations implemented here are adapted from
// Chrome's implementation of fling physics on Android:
// https://cs.chromium.org/chromium/src/ui/events/android/scroller.cc?rcl=3ae3aaff927038a5c644926842cb0c31dea60c79
-static double ComputeDeceleration(float aFriction)
+static double ComputeDeceleration(float aDPI)
{
+ const float kFriction = 0.84f;
const float kGravityEarth = 9.80665f;
return kGravityEarth // g (m/s^2)
* 39.37f // inch/meter
- * 160.f // pixels/inch
- * aFriction;
+ * aDPI // pixels/inch
+ * kFriction;
}
// == std::log(0.78f) / std::log(0.9f)
const float kDecelerationRate = 2.3582018f;
// Default friction constant in android.view.ViewConfiguration.
static float GetFlingFriction()
{
@@ -54,35 +55,33 @@ static float GetInflexion()
// Fling scroll is stopped when the scroll position is |kThresholdForFlingEnd|
// pixels or closer from the end.
static float GetThresholdForFlingEnd()
{
return gfxPrefs::APZChromeFlingPhysicsStopThreshold();
}
-const float kTuningCoeff = ComputeDeceleration(0.84f);
-
-static double ComputeSplineDeceleration(ParentLayerCoord aVelocity)
+static double ComputeSplineDeceleration(ParentLayerCoord aVelocity, double aTuningCoeff)
{
float velocityPerSec = aVelocity * 1000.0f;
- return std::log(GetInflexion() * velocityPerSec / (GetFlingFriction() * kTuningCoeff));
+ return std::log(GetInflexion() * velocityPerSec / (GetFlingFriction() * aTuningCoeff));
}
-static TimeDuration ComputeFlingDuration(ParentLayerCoord aVelocity)
+static TimeDuration ComputeFlingDuration(ParentLayerCoord aVelocity, double aTuningCoeff)
{
- const double splineDecel = ComputeSplineDeceleration(aVelocity);
+ const double splineDecel = ComputeSplineDeceleration(aVelocity, aTuningCoeff);
const double timeSeconds = std::exp(splineDecel / (kDecelerationRate - 1.0));
return TimeDuration::FromSeconds(timeSeconds);
}
-static ParentLayerCoord ComputeFlingDistance(ParentLayerCoord aVelocity)
+static ParentLayerCoord ComputeFlingDistance(ParentLayerCoord aVelocity, double aTuningCoeff)
{
- const double splineDecel = ComputeSplineDeceleration(aVelocity);
- return GetFlingFriction() * kTuningCoeff *
+ const double splineDecel = ComputeSplineDeceleration(aVelocity, aTuningCoeff);
+ return GetFlingFriction() * aTuningCoeff *
std::exp(kDecelerationRate / (kDecelerationRate - 1.0) * splineDecel);
}
struct SplineConstants {
public:
SplineConstants() {
const float kStartTension = 0.5f;
const float kEndTension = 1.0f;
@@ -143,27 +142,29 @@ private:
StaticAutoPtr<SplineConstants> gSplineConstants;
/* static */ void AndroidFlingPhysics::InitializeGlobalState()
{
gSplineConstants = new SplineConstants();
ClearOnShutdown(&gSplineConstants);
}
-void AndroidFlingPhysics::Init(const ParentLayerPoint& aStartingVelocity)
+void AndroidFlingPhysics::Init(const ParentLayerPoint& aStartingVelocity,
+ float aPLPPI)
{
mVelocity = aStartingVelocity.Length();
- mTargetDuration = ComputeFlingDuration(mVelocity);
+ const double tuningCoeff = ComputeDeceleration(aPLPPI);
+ mTargetDuration = ComputeFlingDuration(mVelocity, tuningCoeff);
MOZ_ASSERT(!mTargetDuration.IsZero());
mDurationSoFar = TimeDuration();
mLastPos = ParentLayerPoint();
mCurrentPos = ParentLayerPoint();
float coeffX = mVelocity == 0 ? 1.0f : aStartingVelocity.x / mVelocity;
float coeffY = mVelocity == 0 ? 1.0f : aStartingVelocity.y / mVelocity;
- mTargetDistance = ComputeFlingDistance(mVelocity);
+ mTargetDistance = ComputeFlingDistance(mVelocity, tuningCoeff);
mTargetPos = ParentLayerPoint(mTargetDistance * coeffX,
mTargetDistance * coeffY);
const float hyp = mTargetPos.Length();
if (FuzzyEqualsAdditive(hyp, 0.0f)) {
mDeltaNorm = ParentLayerPoint(1, 1);
} else {
mDeltaNorm = ParentLayerPoint(mTargetPos.x / hyp, mTargetPos.y / hyp);
}
--- a/gfx/layers/apz/src/AndroidFlingPhysics.h
+++ b/gfx/layers/apz/src/AndroidFlingPhysics.h
@@ -12,17 +12,17 @@
#include "gfxPrefs.h"
#include "mozilla/Assertions.h"
namespace mozilla {
namespace layers {
class AndroidFlingPhysics {
public:
- void Init(const ParentLayerPoint& aVelocity);
+ void Init(const ParentLayerPoint& aVelocity, float aPLPPI);
void Sample(const TimeDuration& aDelta,
ParentLayerPoint* aOutVelocity,
ParentLayerPoint* aOutOffset);
static void InitializeGlobalState();
private:
// Returns false if the animation should end.
bool SampleImpl(const TimeDuration& aDelta, float* aOutVelocity);
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -531,22 +531,24 @@ static bool IsCloseToVertical(float aAng
return (fabs(aAngle - (M_PI / 2)) < aThreshold);
}
// Counter used to give each APZC a unique id
static uint32_t sAsyncPanZoomControllerCount = 0;
AsyncPanZoomAnimation*
PlatformSpecificStateBase::CreateFlingAnimation(AsyncPanZoomController& aApzc,
- const FlingHandoffState& aHandoffState)
+ const FlingHandoffState& aHandoffState,
+ float aPLPPI)
{
return new GenericFlingAnimation<DesktopFlingPhysics>(aApzc,
aHandoffState.mChain,
aHandoffState.mIsHandoff,
- aHandoffState.mScrolledApzc);
+ aHandoffState.mScrolledApzc,
+ aPLPPI);
}
TimeStamp
AsyncPanZoomController::GetFrameTime() const
{
APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
return treeManagerLocal ? treeManagerLocal->GetFrameTime() : TimeStamp::Now();
}
@@ -3010,16 +3012,21 @@ RefPtr<const OverscrollHandoffChain> Asy
// This APZC IsDestroyed(). To avoid callers having to special-case this
// scenario, just build a 1-element chain containing ourselves.
OverscrollHandoffChain* result = new OverscrollHandoffChain;
result->Add(this);
return result;
}
ParentLayerPoint AsyncPanZoomController::AttemptFling(const FlingHandoffState& aHandoffState) {
+ // The PLPPI computation acquires the tree lock, so it needs to be performed
+ // on the controller thread, and before the APZC lock is acquired.
+ APZThreadUtils::AssertOnControllerThread();
+ float PLPPI = ComputePLPPI(PanStart(), aHandoffState.mVelocity);
+
RecursiveMutexAutoLock lock(mRecursiveMutex);
if (!IsPannable()) {
return aHandoffState.mVelocity;
}
// We may have a pre-existing velocity for whatever reason (for example,
// a previously handed off fling). We don't want to clobber that.
@@ -3036,23 +3043,39 @@ ParentLayerPoint AsyncPanZoomController:
}
// If there's a scroll snap point near the predicted fling destination,
// scroll there using a smooth scroll animation. Otherwise, start a
// fling animation.
ScrollSnapToDestination();
if (mState != SMOOTH_SCROLL) {
SetState(FLING);
- AsyncPanZoomAnimation* fling = GetPlatformSpecificState()->CreateFlingAnimation(*this, aHandoffState);
+ AsyncPanZoomAnimation* fling = GetPlatformSpecificState()->CreateFlingAnimation(
+ *this, aHandoffState, PLPPI);
StartAnimation(fling);
}
return residualVelocity;
}
+float AsyncPanZoomController::ComputePLPPI(ParentLayerPoint aPoint, ParentLayerPoint aDirection) const
+{
+ // Convert |aDirection| into a unit vector.
+ aDirection = aDirection / aDirection.Length();
+
+ // Place the vector at |aPoint| and convert to screen coordinates.
+ // The length of the resulting vector is the number of Screen coordinates
+ // that equal 1 ParentLayer coordinate in the given direction.
+ float screenPerParent = ToScreenCoordinates(aDirection, aPoint).Length();
+
+ // Finally, factor in the DPI scale.
+ return GetDPI() / screenPerParent;
+}
+
+
ParentLayerPoint AsyncPanZoomController::AdjustHandoffVelocityForOverscrollBehavior(ParentLayerPoint& aHandoffVelocity) const
{
RecursiveMutexAutoLock lock(mRecursiveMutex);
ParentLayerPoint residualVelocity;
if (!mX.OverscrollBehaviorAllowsHandoff()) {
residualVelocity.x = aHandoffVelocity.x;
aHandoffVelocity.x = 0;
}
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -66,18 +66,20 @@ class GenericOverscrollEffect;
class AndroidSpecificState;
struct KeyboardScrollAction;
// Base class for grouping platform-specific APZC state variables.
class PlatformSpecificStateBase {
public:
virtual ~PlatformSpecificStateBase() = default;
virtual AndroidSpecificState* AsAndroidSpecificState() { return nullptr; }
+ // PLPPI = "ParentLayer pixels per (Screen) inch"
virtual AsyncPanZoomAnimation* CreateFlingAnimation(AsyncPanZoomController& aApzc,
- const FlingHandoffState& aHandoffState);
+ const FlingHandoffState& aHandoffState,
+ float aPLPPI);
static void InitializeGlobalState() {}
};
/*
* Represents a transform from the ParentLayer coordinate space of an APZC
* to the ParentLayer coordinate space of its parent APZC.
* Each layer along the way contributes to the transform. We track
@@ -1202,16 +1204,20 @@ private:
void SmoothScrollTo(const CSSPoint& aDestination);
// Returns whether overscroll is allowed during an event.
bool AllowScrollHandoffInCurrentBlock() const;
// Invoked by the pinch repaint timer.
void DoDelayedRequestContentRepaint();
+ // Compute the number of ParentLayer pixels per (Screen) inch at the given
+ // point and in the given direction.
+ float ComputePLPPI(ParentLayerPoint aPoint, ParentLayerPoint aDirection) const;
+
/* ===================================================================
* The functions and members in this section are used to make ancestor chains
* out of APZC instances. These chains can only be walked or manipulated
* while holding the lock in the associated APZCTreeManager instance.
*/
public:
void SetParent(AsyncPanZoomController* aParent) {
mParent = aParent;
--- a/gfx/layers/apz/src/DesktopFlingPhysics.h
+++ b/gfx/layers/apz/src/DesktopFlingPhysics.h
@@ -15,17 +15,17 @@
#define FLING_PHYS_LOG(...)
// #define FLING_PHYS_LOG(...) printf_stderr("FLING: " __VA_ARGS__)
namespace mozilla {
namespace layers {
class DesktopFlingPhysics {
public:
- void Init(const ParentLayerPoint& aStartingVelocity)
+ void Init(const ParentLayerPoint& aStartingVelocity, float aPLPPI /* unused */)
{
mVelocity = aStartingVelocity;
}
void Sample(const TimeDuration& aDelta,
ParentLayerPoint* aOutVelocity,
ParentLayerPoint* aOutOffset)
{
float friction = gfxPrefs::APZFlingFriction();
--- a/gfx/layers/apz/src/GenericFlingAnimation.h
+++ b/gfx/layers/apz/src/GenericFlingAnimation.h
@@ -28,18 +28,20 @@ namespace mozilla {
namespace layers {
/**
* The FlingPhysics template parameter determines the physics model
* that the fling animation follows. It must have the following methods:
*
* - Default constructor.
*
- * - Init(const ParentLayerPoint& aStartingVelocity).
- * Called at the beginning of the fling, with the fling's starting velocity.
+ * - Init(const ParentLayerPoint& aStartingVelocity, float aPLPPI).
+ * Called at the beginning of the fling, with the fling's starting velocity,
+ * and the number of ParentLayer pixels per (Screen) inch at the point of
+ * the fling's start in the fling's direction.
*
* - Sample(const TimeDuration& aDelta,
* ParentLayerPoint* aOutVelocity,
* ParentLayerPoint* aOutOffset);
* Called on each sample of the fling.
* |aDelta| is the time elapsed since the last sample.
* |aOutVelocity| should be the desired velocity after the current sample,
* in ParentLayer pixels per millisecond.
@@ -49,17 +51,18 @@ namespace layers {
* know the unclamped value to handle handoff/overscroll correctly.
*/
template <typename FlingPhysics>
class GenericFlingAnimation: public AsyncPanZoomAnimation, public FlingPhysics {
public:
GenericFlingAnimation(AsyncPanZoomController& aApzc,
const RefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
bool aFlingIsHandedOff,
- const RefPtr<const AsyncPanZoomController>& aScrolledApzc)
+ const RefPtr<const AsyncPanZoomController>& aScrolledApzc,
+ float aPLPPI)
: mApzc(aApzc)
, mOverscrollHandoffChain(aOverscrollHandoffChain)
, mScrolledApzc(aScrolledApzc)
{
MOZ_ASSERT(mOverscrollHandoffChain);
TimeStamp now = aApzc.GetFrameTime();
// Drop any velocity on axes where we don't have room to scroll anyways
@@ -102,17 +105,17 @@ public:
&mApzc, mApzc.mY.GetVelocity(), velocity.y, mApzc.mLastFlingVelocity.y);
mApzc.mY.SetVelocity(velocity.y);
}
}
mApzc.mLastFlingTime = now;
mApzc.mLastFlingVelocity = velocity;
- FlingPhysics::Init(mApzc.GetVelocityVector());
+ FlingPhysics::Init(mApzc.GetVelocityVector(), aPLPPI);
}
/**
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
* This should be called whenever sampling the content transform for this
* frame. Returns true if the fling animation should be advanced by one frame,
* or false if there is no fling or the fling has ended.
*/