1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "FlingAccelerator.h"
8 
9 #include "mozilla/StaticPrefs_apz.h"
10 
11 #include "GenericFlingAnimation.h"  // for FLING_LOG and FlingHandoffState
12 
13 namespace mozilla {
14 namespace layers {
15 
Reset()16 void FlingAccelerator::Reset() {
17   mPreviousFlingStartingVelocity = ParentLayerPoint{};
18   mPreviousFlingCancelVelocity = ParentLayerPoint{};
19   mIsTracking = false;
20 }
21 
SameDirection(float aVelocity1,float aVelocity2)22 static bool SameDirection(float aVelocity1, float aVelocity2) {
23   return (aVelocity1 == 0.0f) || (aVelocity2 == 0.0f) ||
24          (IsNegative(aVelocity1) == IsNegative(aVelocity2));
25 }
26 
Accelerate(float aBase,float aSupplemental)27 static float Accelerate(float aBase, float aSupplemental) {
28   return (aBase * StaticPrefs::apz_fling_accel_base_mult()) +
29          (aSupplemental * StaticPrefs::apz_fling_accel_supplemental_mult());
30 }
31 
GetFlingStartingVelocity(const SampleTime & aNow,const ParentLayerPoint & aVelocity,const FlingHandoffState & aHandoffState)32 ParentLayerPoint FlingAccelerator::GetFlingStartingVelocity(
33     const SampleTime& aNow, const ParentLayerPoint& aVelocity,
34     const FlingHandoffState& aHandoffState) {
35   // If the fling should be accelerated and is in the same direction as the
36   // previous fling, boost the velocity to be the sum of the two. Check separate
37   // axes separately because we could have two vertical flings with small
38   // horizontal components on the opposite side of zero, and we still want the
39   // y-fling to get accelerated.
40   ParentLayerPoint velocity = aVelocity;
41   if (ShouldAccelerate(aNow, aVelocity, aHandoffState)) {
42     if (velocity.x != 0 &&
43         SameDirection(velocity.x, mPreviousFlingStartingVelocity.x)) {
44       velocity.x = Accelerate(velocity.x, mPreviousFlingStartingVelocity.x);
45       FLING_LOG("%p Applying fling x-acceleration from %f to %f (delta %f)\n",
46                 this, aVelocity.x, velocity.x,
47                 mPreviousFlingStartingVelocity.x);
48     }
49     if (velocity.y != 0 &&
50         SameDirection(velocity.y, mPreviousFlingStartingVelocity.y)) {
51       velocity.y = Accelerate(velocity.y, mPreviousFlingStartingVelocity.y);
52       FLING_LOG("%p Applying fling y-acceleration from %f to %f (delta %f)\n",
53                 this, aVelocity.y, velocity.y,
54                 mPreviousFlingStartingVelocity.y);
55     }
56   }
57 
58   Reset();
59 
60   mPreviousFlingStartingVelocity = velocity;
61   mIsTracking = true;
62 
63   return velocity;
64 }
65 
ShouldAccelerate(const SampleTime & aNow,const ParentLayerPoint & aVelocity,const FlingHandoffState & aHandoffState) const66 bool FlingAccelerator::ShouldAccelerate(
67     const SampleTime& aNow, const ParentLayerPoint& aVelocity,
68     const FlingHandoffState& aHandoffState) const {
69   if (!IsTracking()) {
70     FLING_LOG("%p Fling accelerator was reset, not accelerating.\n", this);
71     return false;
72   }
73 
74   if (!aHandoffState.mTouchStartRestingTime) {
75     FLING_LOG("%p Don't have a touch start resting time, not accelerating.\n",
76               this);
77     return false;
78   }
79 
80   double msBetweenTouchStartAndPanStart =
81       aHandoffState.mTouchStartRestingTime->ToMilliseconds();
82   FLING_LOG(
83       "%p ShouldAccelerate with pan velocity %f pixels/ms, min pan velocity %f "
84       "pixels/ms, previous fling cancel velocity %f pixels/ms, time elapsed "
85       "since starting previous time between touch start and pan "
86       "start %fms.\n",
87       this, float(aVelocity.Length()), float(aHandoffState.mMinPanVelocity),
88       float(mPreviousFlingCancelVelocity.Length()),
89       float(msBetweenTouchStartAndPanStart));
90 
91   if (aVelocity.Length() < StaticPrefs::apz_fling_accel_min_fling_velocity()) {
92     FLING_LOG("%p Fling velocity too low (%f), not accelerating.\n", this,
93               float(aVelocity.Length()));
94     return false;
95   }
96 
97   if (aHandoffState.mMinPanVelocity <
98       StaticPrefs::apz_fling_accel_min_pan_velocity()) {
99     FLING_LOG(
100         "%p Panning velocity was too slow at some point during the pan (%f), "
101         "not accelerating.\n",
102         this, float(aHandoffState.mMinPanVelocity));
103     return false;
104   }
105 
106   if (mPreviousFlingCancelVelocity.Length() <
107       StaticPrefs::apz_fling_accel_min_fling_velocity()) {
108     FLING_LOG(
109         "%p The previous fling animation had slowed down too much when it was "
110         "interrupted (%f), not accelerating.\n",
111         this, float(mPreviousFlingCancelVelocity.Length()));
112     return false;
113   }
114 
115   if (msBetweenTouchStartAndPanStart >=
116       StaticPrefs::apz_fling_accel_max_pause_interval_ms()) {
117     FLING_LOG(
118         "%p Too much time (%fms) elapsed between touch start and pan start, "
119         "not accelerating.\n",
120         this, msBetweenTouchStartAndPanStart);
121     return false;
122   }
123 
124   return true;
125 }
126 
127 }  // namespace layers
128 }  // namespace mozilla
129