summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/SimpleVelocityTracker.cpp
blob: 87cae10d51f9fb2e07f9346eeab030d7c596e2f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "SimpleVelocityTracker.h"

#include "mozilla/ServoStyleConsts.h"  // for StyleComputedTimingFunction
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPtr.h"  // for StaticAutoPtr

static mozilla::LazyLogModule sApzSvtLog("apz.simplevelocitytracker");
#define SVT_LOG(...) MOZ_LOG(sApzSvtLog, LogLevel::Debug, (__VA_ARGS__))

namespace mozilla {
namespace layers {

// When we compute the velocity we do so by taking two input events and
// dividing the distance delta over the time delta. In some cases the time
// 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 TimeDuration MIN_VELOCITY_SAMPLE_TIME = TimeDuration::FromMilliseconds(5);

extern StaticAutoPtr<StyleComputedTimingFunction> gVelocityCurveFunction;

SimpleVelocityTracker::SimpleVelocityTracker(Axis* aAxis)
    : mAxis(aAxis), mVelocitySamplePos(0) {}

void SimpleVelocityTracker::StartTracking(ParentLayerCoord aPos,
                                          TimeStamp aTimestamp) {
  Clear();
  mVelocitySampleTime = aTimestamp;
  mVelocitySamplePos = aPos;
}

Maybe<float> SimpleVelocityTracker::AddPosition(ParentLayerCoord aPos,
                                                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(),
            (aTimestamp - mVelocitySampleTime).ToMilliseconds());
    return Nothing();
  }

  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);
  mVelocitySampleTime = aTimestamp;
  mVelocitySamplePos = aPos;

  AddVelocityToQueue(aTimestamp, newVelocity);

  return Some(newVelocity);
}

Maybe<float> SimpleVelocityTracker::ComputeVelocity(TimeStamp aTimestamp) {
  float velocity = 0;
  int count = 0;
  for (const auto& e : mVelocityQueue) {
    TimeDuration timeDelta = (aTimestamp - e.first);
    if (timeDelta < TimeDuration::FromMilliseconds(
                        StaticPrefs::apz_velocity_relevance_time_ms())) {
      count++;
      velocity += e.second;
    }
  }
  mVelocityQueue.Clear();
  if (count > 1) {
    velocity /= count;
  }
  return Some(velocity);
}

void SimpleVelocityTracker::Clear() { mVelocityQueue.Clear(); }

void SimpleVelocityTracker::AddVelocityToQueue(TimeStamp aTimestamp,
                                               float aVelocity) {
  mVelocityQueue.AppendElement(std::make_pair(aTimestamp, aVelocity));
  if (mVelocityQueue.Length() >
      StaticPrefs::apz_max_velocity_queue_size_AtStartup()) {
    mVelocityQueue.RemoveElementAt(0);
  }
}

float SimpleVelocityTracker::ApplyFlingCurveToVelocity(float aVelocity) const {
  float newVelocity = aVelocity;
  if (StaticPrefs::apz_max_velocity_inches_per_ms() > 0.0f) {
    bool velocityIsNegative = (newVelocity < 0);
    newVelocity = fabs(newVelocity);

    float maxVelocity =
        mAxis->ToLocalVelocity(StaticPrefs::apz_max_velocity_inches_per_ms());
    newVelocity = std::min(newVelocity, maxVelocity);

    if (StaticPrefs::apz_fling_curve_threshold_inches_per_ms() > 0.0f &&
        StaticPrefs::apz_fling_curve_threshold_inches_per_ms() <
            StaticPrefs::apz_max_velocity_inches_per_ms()) {
      float curveThreshold = mAxis->ToLocalVelocity(
          StaticPrefs::apz_fling_curve_threshold_inches_per_ms());
      if (newVelocity > curveThreshold) {
        // here, 0 < curveThreshold < newVelocity <= maxVelocity, so we apply
        // the curve
        float scale = maxVelocity - curveThreshold;
        float funcInput = (newVelocity - curveThreshold) / scale;
        float funcOutput =
            gVelocityCurveFunction->At(funcInput, /* aBeforeFlag = */ false);
        float curvedVelocity = (funcOutput * scale) + curveThreshold;
        SVT_LOG("%p|%s curving up velocity from %f to %f\n",
                mAxis->OpaqueApzcPointer(), mAxis->Name(), newVelocity,
                curvedVelocity);
        newVelocity = curvedVelocity;
      }
    }

    if (velocityIsNegative) {
      newVelocity = -newVelocity;
    }
  }

  return newVelocity;
}

}  // namespace layers
}  // namespace mozilla