diff options
Diffstat (limited to 'gfx/layers/apz/src/GenericFlingAnimation.h')
-rw-r--r-- | gfx/layers/apz/src/GenericFlingAnimation.h | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/GenericFlingAnimation.h b/gfx/layers/apz/src/GenericFlingAnimation.h new file mode 100644 index 0000000000..82f981ae0a --- /dev/null +++ b/gfx/layers/apz/src/GenericFlingAnimation.h @@ -0,0 +1,207 @@ +/* -*- 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/. */ + +#ifndef mozilla_layers_GenericFlingAnimation_h_ +#define mozilla_layers_GenericFlingAnimation_h_ + +#include "APZUtils.h" +#include "AsyncPanZoomAnimation.h" +#include "AsyncPanZoomController.h" +#include "FrameMetrics.h" +#include "Units.h" +#include "OverscrollHandoffState.h" +#include "mozilla/Assertions.h" +#include "mozilla/Monitor.h" +#include "mozilla/RefPtr.h" +#include "mozilla/StaticPrefs_apz.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/ToString.h" +#include "nsThreadUtils.h" + +static mozilla::LazyLogModule sApzFlgLog("apz.fling"); +#define FLING_LOG(...) MOZ_LOG(sApzFlgLog, LogLevel::Debug, (__VA_ARGS__)) + +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, 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. + * |aOutOffset| should be the desired _delta_ to the scroll offset after + * the current sample. |aOutOffset| should _not_ be clamped to the APZC's + * scrollable bounds; the caller will do the clamping, and it needs to + * know the unclamped value to handle handoff/overscroll correctly. + */ +template <typename FlingPhysics> +class GenericFlingAnimation : public AsyncPanZoomAnimation, + public FlingPhysics { + public: + GenericFlingAnimation(AsyncPanZoomController& aApzc, + const FlingHandoffState& aHandoffState, float aPLPPI) + : mApzc(aApzc), + mOverscrollHandoffChain(aHandoffState.mChain), + mScrolledApzc(aHandoffState.mScrolledApzc) { + MOZ_ASSERT(mOverscrollHandoffChain); + + // Drop any velocity on axes where we don't have room to scroll anyways + // (in this APZC, or an APZC further in the handoff chain). + // This ensures that we don't take the 'overscroll' path in Sample() + // on account of one axis which can't scroll having a velocity. + if (!mOverscrollHandoffChain->CanScrollInDirection( + &mApzc, ScrollDirection::eHorizontal)) { + RecursiveMutexAutoLock lock(mApzc.mRecursiveMutex); + mApzc.mX.SetVelocity(0); + } + if (!mOverscrollHandoffChain->CanScrollInDirection( + &mApzc, ScrollDirection::eVertical)) { + RecursiveMutexAutoLock lock(mApzc.mRecursiveMutex); + mApzc.mY.SetVelocity(0); + } + + if (aHandoffState.mIsHandoff) { + // Only apply acceleration in the APZC that originated the fling, not in + // APZCs further down the handoff chain during handoff. + mApzc.mFlingAccelerator.Reset(); + } + + ParentLayerPoint velocity = + mApzc.mFlingAccelerator.GetFlingStartingVelocity( + aApzc.GetFrameTime(), mApzc.GetVelocityVector(), aHandoffState); + + mApzc.SetVelocityVector(velocity); + + 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. + */ + virtual bool DoSample(FrameMetrics& aFrameMetrics, + const TimeDuration& aDelta) override { + CSSToParentLayerScale zoom(aFrameMetrics.GetZoom()); + if (zoom == CSSToParentLayerScale(0)) { + return false; + } + + ParentLayerPoint velocity; + ParentLayerPoint offset; + FlingPhysics::Sample(aDelta, &velocity, &offset); + + mApzc.SetVelocityVector(velocity); + + // If we shouldn't continue the fling, let's just stop and repaint. + if (IsZero(velocity / zoom)) { + FLING_LOG("%p ending fling animation. overscrolled=%d\n", &mApzc, + mApzc.IsOverscrolled()); + // This APZC or an APZC further down the handoff chain may be be + // overscrolled. Start a snap-back animation on the overscrolled APZC. + // Note: + // This needs to be a deferred task even though it can safely run + // while holding mRecursiveMutex, because otherwise, if the overscrolled + // APZC is this one, then the SetState(NOTHING) in UpdateAnimation will + // stomp on the SetState(SNAP_BACK) it does. + mDeferredTasks.AppendElement(NewRunnableMethod<AsyncPanZoomController*>( + "layers::OverscrollHandoffChain::SnapBackOverscrolledApzc", + mOverscrollHandoffChain.get(), + &OverscrollHandoffChain::SnapBackOverscrolledApzc, &mApzc)); + return false; + } + + // Ordinarily we might need to do a ScheduleComposite if either of + // the following AdjustDisplacement calls returns true, but this + // is already running as part of a FlingAnimation, so we'll be compositing + // per frame of animation anyway. + ParentLayerPoint overscroll; + ParentLayerPoint adjustedOffset; + mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x); + mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y); + if (aFrameMetrics.GetZoom() != CSSToParentLayerScale(0)) { + mApzc.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom()); + } + + // The fling may have caused us to reach the end of our scroll range. + if (!IsZero(overscroll / zoom)) { + // Hand off the fling to the next APZC in the overscroll handoff chain. + + // We may have reached the end of the scroll range along one axis but + // not the other. In such a case we only want to hand off the relevant + // component of the fling. + if (mApzc.IsZero(overscroll.x)) { + velocity.x = 0; + } else if (mApzc.IsZero(overscroll.y)) { + velocity.y = 0; + } + + // To hand off the fling, we attempt to find a target APZC and start a new + // fling with the same velocity on that APZC. For simplicity, the actual + // overscroll of the current sample is discarded rather than being handed + // off. The compositor should sample animations sufficiently frequently + // that this is not noticeable. The target APZC is chosen by seeing if + // there is an APZC further in the handoff chain which is pannable; if + // there isn't, we take the new fling ourselves, entering an overscrolled + // state. + // Note: APZC is holding mRecursiveMutex, so directly calling + // HandleFlingOverscroll() (which acquires the tree lock) would violate + // the lock ordering. Instead we schedule HandleFlingOverscroll() to be + // called after mRecursiveMutex is released. + FLING_LOG("%p fling went into overscroll, handing off with velocity %s\n", + &mApzc, ToString(velocity).c_str()); + mDeferredTasks.AppendElement( + NewRunnableMethod<ParentLayerPoint, SideBits, + RefPtr<const OverscrollHandoffChain>, + RefPtr<const AsyncPanZoomController>>( + "layers::AsyncPanZoomController::HandleFlingOverscroll", &mApzc, + &AsyncPanZoomController::HandleFlingOverscroll, velocity, + apz::GetOverscrollSideBits(overscroll), mOverscrollHandoffChain, + mScrolledApzc)); + + // If there is a remaining velocity on this APZC, continue this fling + // as well. (This fling and the handed-off fling will run concurrently.) + // Note that AdjustDisplacement() will have zeroed out the velocity + // along the axes where we're overscrolled. + return !IsZero(mApzc.GetVelocityVector() / zoom); + } + + return true; + } + + void Cancel(CancelAnimationFlags aFlags) override { + mApzc.mFlingAccelerator.ObserveFlingCanceled(mApzc.GetVelocityVector()); + } + + virtual bool HandleScrollOffsetUpdate( + const Maybe<CSSPoint>& aRelativeDelta) override { + return true; + } + + private: + AsyncPanZoomController& mApzc; + RefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain; + RefPtr<const AsyncPanZoomController> mScrolledApzc; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_GenericFlingAnimation_h_ |