diff options
Diffstat (limited to 'gfx/layers/apz/src/SimpleVelocityTracker.cpp')
-rw-r--r-- | gfx/layers/apz/src/SimpleVelocityTracker.cpp | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/SimpleVelocityTracker.cpp b/gfx/layers/apz/src/SimpleVelocityTracker.cpp new file mode 100644 index 0000000000..87cae10d51 --- /dev/null +++ b/gfx/layers/apz/src/SimpleVelocityTracker.cpp @@ -0,0 +1,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 |