|  | // 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 FLUTTER_SHELL_COMMON_POINTER_DATA_DISPATCHER_H_ | 
|  | #define FLUTTER_SHELL_COMMON_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<PointerDataPacket> 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<PointerDataPacket> 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<PointerDataPacket> 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<PointerDataPacket> 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<PointerDataPacket> pending_packet_; | 
|  | int pending_trace_flow_id_ = -1; | 
|  | bool is_pointer_data_in_progress_ = false; | 
|  |  | 
|  | // WeakPtrFactory must be the last member. | 
|  | fml::WeakPtrFactory<SmoothPointerDataDispatcher> weak_factory_; | 
|  | FML_DISALLOW_COPY_AND_ASSIGN(SmoothPointerDataDispatcher); | 
|  | }; | 
|  |  | 
|  | //-------------------------------------------------------------------------- | 
|  | /// @brief      Signature for constructing PointerDataDispatcher. | 
|  | /// | 
|  | /// @param[in]  delegate      the `Flutter::Engine` | 
|  | /// | 
|  | using PointerDataDispatcherMaker = | 
|  | std::function<std::unique_ptr<PointerDataDispatcher>( | 
|  | PointerDataDispatcher::Delegate&)>; | 
|  |  | 
|  | }  // namespace flutter | 
|  |  | 
|  | #endif  // FLUTTER_SHELL_COMMON_POINTER_DATA_DISPATCHER_H_ |