// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef POINTER_DATA_DISPATCHER_H_ #define POINTER_DATA_DISPATCHER_H_ #include "flutter/runtime/runtime_controller.h" #include "flutter/shell/common/animator.h" namespace flutter { class PointerDataDispatcher; //------------------------------------------------------------------------------ /// The `Engine` pointer data dispatcher that forwards the packet received from /// `PlatformView::DispatchPointerDataPacket` on the platform thread, to /// `Window::DispatchPointerDataPacket` on the UI thread. /// /// This class is used to filter the packets so the Flutter framework on the UI /// thread will receive packets with some desired properties. See /// `SmoothPointerDataDispatcher` for an example which filters irregularly /// delivered packets, and dispatches them in sync with the VSYNC signal. /// /// This object will be owned by the engine because it relies on the engine's /// `Animator` (which owns `VsyncWaiter`) and `RuntimeController` to do the /// filtering. This object is currently designed to be only called from the UI /// thread (no thread safety is guaranteed). /// /// The `PlatformView` decides which subclass of `PointerDataDispatcher` is /// constructed by sending a `PointerDataDispatcherMaker` to the engine's /// constructor in `Shell::CreateShellOnPlatformThread`. This is needed because: /// (1) Different platforms (e.g., Android, iOS) have different dispatchers /// so the decision has to be made per `PlatformView`. /// (2) The `PlatformView` can only be accessed from the PlatformThread while /// this class (as owned by engine) can only be accessed in the UI thread. /// Hence `PlatformView` creates a `PointerDataDispatchMaker` on the /// platform thread, and sends it to the UI thread for the final /// construction of the `PointerDataDispatcher`. class PointerDataDispatcher { public: /// The interface for Engine to implement. class Delegate { public: /// Actually dispatch the packet using Engine's `animator_` and /// `runtime_controller_`. virtual void DoDispatchPacket(std::unique_ptr packet, uint64_t trace_flow_id) = 0; //-------------------------------------------------------------------------- /// @brief Schedule a secondary callback to be executed right after the /// main `VsyncWaiter::AsyncWaitForVsync` callback (which is added /// by `Animator::RequestFrame`). /// /// Like the callback in `AsyncWaitForVsync`, this callback is /// only scheduled to be called once per |id|, and it will be /// called in the UI thread. If there is no AsyncWaitForVsync /// callback (`Animator::RequestFrame` is not called), this /// secondary callback will still be executed at vsync. /// /// This callback is used to provide the vsync signal needed by /// `SmoothPointerDataDispatcher`, and for `Animator` input flow /// events. virtual void ScheduleSecondaryVsyncCallback( uintptr_t id, const fml::closure& callback) = 0; }; //---------------------------------------------------------------------------- /// @brief Signal that `PlatformView` has a packet to be dispatched. /// /// @param[in] packet The `PointerDataPacket` to be dispatched. /// @param[in] trace_flow_id The id for `Animator::EnqueueTraceFlowId`. virtual void DispatchPacket(std::unique_ptr packet, uint64_t trace_flow_id) = 0; //---------------------------------------------------------------------------- /// @brief Default destructor. virtual ~PointerDataDispatcher(); }; //------------------------------------------------------------------------------ /// The default dispatcher that forwards the packet without any modification. /// class DefaultPointerDataDispatcher : public PointerDataDispatcher { public: explicit DefaultPointerDataDispatcher(Delegate& delegate) : delegate_(delegate) {} // |PointerDataDispatcer| void DispatchPacket(std::unique_ptr packet, uint64_t trace_flow_id) override; virtual ~DefaultPointerDataDispatcher(); protected: Delegate& delegate_; FML_DISALLOW_COPY_AND_ASSIGN(DefaultPointerDataDispatcher); }; //------------------------------------------------------------------------------ /// A dispatcher that may temporarily store and defer the last received /// PointerDataPacket if multiple packets are received in one VSYNC. The /// deferred packet will be sent in the next vsync in order to smooth out the /// events. This filters out irregular input events delivery to provide a smooth /// scroll on iPhone X/Xs. /// /// It works as follows: /// /// When `DispatchPacket` is called while a previous pointer data dispatch is /// still in progress (its frame isn't finished yet), it means that an input /// event is delivered to us too fast. That potentially means a later event will /// be too late which could cause the missing of a frame. Hence we'll cache it /// in `pending_packet_` for the next frame to smooth it out. /// /// If the input event is sent to us regularly at the same rate of VSYNC (say /// at 60Hz), this would be identical to `DefaultPointerDataDispatcher` where /// `runtime_controller_->DispatchPointerDataPacket` is always called right /// away. That's because `is_pointer_data_in_progress_` will always be false /// when `DispatchPacket` is called since it will be cleared by the end of a /// frame through `ScheduleSecondaryVsyncCallback`. This is the case for all /// Android/iOS devices before iPhone X/XS. /// /// If the input event is irregular, but with a random latency of no more than /// one frame, this would guarantee that we'll miss at most 1 frame. Without /// this, we could miss half of the frames. /// /// If the input event is delivered at a higher rate than that of VSYNC, this /// would at most add a latency of one event delivery. For example, if the /// input event is delivered at 120Hz (this is only true for iPad pro, not even /// iPhone X), this may delay the handling of an input event by 8ms. /// /// The assumption of this solution is that the sampling itself is still /// regular. Only the event delivery is allowed to be irregular. So far this /// assumption seems to hold on all devices. If it's changed in the future, /// we'll need a different solution. /// /// See also input_events_unittests.cc where we test all our claims above. class SmoothPointerDataDispatcher : public DefaultPointerDataDispatcher { public: explicit SmoothPointerDataDispatcher(Delegate& delegate); // |PointerDataDispatcer| void DispatchPacket(std::unique_ptr packet, uint64_t trace_flow_id) override; virtual ~SmoothPointerDataDispatcher(); private: void DispatchPendingPacket(); void ScheduleSecondaryVsyncCallback(); // If non-null, this will be a pending pointer data packet for the next frame // to consume. This is used to smooth out the irregular drag events delivery. // See also `DispatchPointerDataPacket` and input_events_unittests.cc. std::unique_ptr pending_packet_; int pending_trace_flow_id_ = -1; bool is_pointer_data_in_progress_ = false; // WeakPtrFactory must be the last member. fml::WeakPtrFactory weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(SmoothPointerDataDispatcher); }; //-------------------------------------------------------------------------- /// @brief Signature for constructing PointerDataDispatcher. /// /// @param[in] delegate the `Flutter::Engine` /// using PointerDataDispatcherMaker = std::function( PointerDataDispatcher::Delegate&)>; } // namespace flutter #endif // POINTER_DATA_DISPATCHER_H_