1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef POINTER_DATA_DISPATCHER_H_ |
6 | #define POINTER_DATA_DISPATCHER_H_ |
7 | |
8 | #include "flutter/runtime/runtime_controller.h" |
9 | #include "flutter/shell/common/animator.h" |
10 | |
11 | namespace flutter { |
12 | |
13 | class PointerDataDispatcher; |
14 | |
15 | //------------------------------------------------------------------------------ |
16 | /// The `Engine` pointer data dispatcher that forwards the packet received from |
17 | /// `PlatformView::DispatchPointerDataPacket` on the platform thread, to |
18 | /// `Window::DispatchPointerDataPacket` on the UI thread. |
19 | /// |
20 | /// This class is used to filter the packets so the Flutter framework on the UI |
21 | /// thread will receive packets with some desired properties. See |
22 | /// `SmoothPointerDataDispatcher` for an example which filters irregularly |
23 | /// delivered packets, and dispatches them in sync with the VSYNC signal. |
24 | /// |
25 | /// This object will be owned by the engine because it relies on the engine's |
26 | /// `Animator` (which owns `VsyncWaiter`) and `RuntimeController` to do the |
27 | /// filtering. This object is currently designed to be only called from the UI |
28 | /// thread (no thread safety is guaranteed). |
29 | /// |
30 | /// The `PlatformView` decides which subclass of `PointerDataDispatcher` is |
31 | /// constructed by sending a `PointerDataDispatcherMaker` to the engine's |
32 | /// constructor in `Shell::CreateShellOnPlatformThread`. This is needed because: |
33 | /// (1) Different platforms (e.g., Android, iOS) have different dispatchers |
34 | /// so the decision has to be made per `PlatformView`. |
35 | /// (2) The `PlatformView` can only be accessed from the PlatformThread while |
36 | /// this class (as owned by engine) can only be accessed in the UI thread. |
37 | /// Hence `PlatformView` creates a `PointerDataDispatchMaker` on the |
38 | /// platform thread, and sends it to the UI thread for the final |
39 | /// construction of the `PointerDataDispatcher`. |
40 | class PointerDataDispatcher { |
41 | public: |
42 | /// The interface for Engine to implement. |
43 | class Delegate { |
44 | public: |
45 | /// Actually dispatch the packet using Engine's `animator_` and |
46 | /// `runtime_controller_`. |
47 | virtual void DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet, |
48 | uint64_t trace_flow_id) = 0; |
49 | |
50 | //-------------------------------------------------------------------------- |
51 | /// @brief Schedule a secondary callback to be executed right after the |
52 | /// main `VsyncWaiter::AsyncWaitForVsync` callback (which is added |
53 | /// by `Animator::RequestFrame`). |
54 | /// |
55 | /// Like the callback in `AsyncWaitForVsync`, this callback is |
56 | /// only scheduled to be called once per |id|, and it will be |
57 | /// called in the UI thread. If there is no AsyncWaitForVsync |
58 | /// callback (`Animator::RequestFrame` is not called), this |
59 | /// secondary callback will still be executed at vsync. |
60 | /// |
61 | /// This callback is used to provide the vsync signal needed by |
62 | /// `SmoothPointerDataDispatcher`, and for `Animator` input flow |
63 | /// events. |
64 | virtual void ScheduleSecondaryVsyncCallback( |
65 | uintptr_t id, |
66 | const fml::closure& callback) = 0; |
67 | }; |
68 | |
69 | //---------------------------------------------------------------------------- |
70 | /// @brief Signal that `PlatformView` has a packet to be dispatched. |
71 | /// |
72 | /// @param[in] packet The `PointerDataPacket` to be dispatched. |
73 | /// @param[in] trace_flow_id The id for `Animator::EnqueueTraceFlowId`. |
74 | virtual void DispatchPacket(std::unique_ptr<PointerDataPacket> packet, |
75 | uint64_t trace_flow_id) = 0; |
76 | |
77 | //---------------------------------------------------------------------------- |
78 | /// @brief Default destructor. |
79 | virtual ~PointerDataDispatcher(); |
80 | }; |
81 | |
82 | //------------------------------------------------------------------------------ |
83 | /// The default dispatcher that forwards the packet without any modification. |
84 | /// |
85 | class DefaultPointerDataDispatcher : public PointerDataDispatcher { |
86 | public: |
87 | explicit DefaultPointerDataDispatcher(Delegate& delegate) |
88 | : delegate_(delegate) {} |
89 | |
90 | // |PointerDataDispatcer| |
91 | void DispatchPacket(std::unique_ptr<PointerDataPacket> packet, |
92 | uint64_t trace_flow_id) override; |
93 | |
94 | virtual ~DefaultPointerDataDispatcher(); |
95 | |
96 | protected: |
97 | Delegate& delegate_; |
98 | |
99 | FML_DISALLOW_COPY_AND_ASSIGN(DefaultPointerDataDispatcher); |
100 | }; |
101 | |
102 | //------------------------------------------------------------------------------ |
103 | /// A dispatcher that may temporarily store and defer the last received |
104 | /// PointerDataPacket if multiple packets are received in one VSYNC. The |
105 | /// deferred packet will be sent in the next vsync in order to smooth out the |
106 | /// events. This filters out irregular input events delivery to provide a smooth |
107 | /// scroll on iPhone X/Xs. |
108 | /// |
109 | /// It works as follows: |
110 | /// |
111 | /// When `DispatchPacket` is called while a previous pointer data dispatch is |
112 | /// still in progress (its frame isn't finished yet), it means that an input |
113 | /// event is delivered to us too fast. That potentially means a later event will |
114 | /// be too late which could cause the missing of a frame. Hence we'll cache it |
115 | /// in `pending_packet_` for the next frame to smooth it out. |
116 | /// |
117 | /// If the input event is sent to us regularly at the same rate of VSYNC (say |
118 | /// at 60Hz), this would be identical to `DefaultPointerDataDispatcher` where |
119 | /// `runtime_controller_->DispatchPointerDataPacket` is always called right |
120 | /// away. That's because `is_pointer_data_in_progress_` will always be false |
121 | /// when `DispatchPacket` is called since it will be cleared by the end of a |
122 | /// frame through `ScheduleSecondaryVsyncCallback`. This is the case for all |
123 | /// Android/iOS devices before iPhone X/XS. |
124 | /// |
125 | /// If the input event is irregular, but with a random latency of no more than |
126 | /// one frame, this would guarantee that we'll miss at most 1 frame. Without |
127 | /// this, we could miss half of the frames. |
128 | /// |
129 | /// If the input event is delivered at a higher rate than that of VSYNC, this |
130 | /// would at most add a latency of one event delivery. For example, if the |
131 | /// input event is delivered at 120Hz (this is only true for iPad pro, not even |
132 | /// iPhone X), this may delay the handling of an input event by 8ms. |
133 | /// |
134 | /// The assumption of this solution is that the sampling itself is still |
135 | /// regular. Only the event delivery is allowed to be irregular. So far this |
136 | /// assumption seems to hold on all devices. If it's changed in the future, |
137 | /// we'll need a different solution. |
138 | /// |
139 | /// See also input_events_unittests.cc where we test all our claims above. |
140 | class SmoothPointerDataDispatcher : public DefaultPointerDataDispatcher { |
141 | public: |
142 | explicit SmoothPointerDataDispatcher(Delegate& delegate); |
143 | |
144 | // |PointerDataDispatcer| |
145 | void DispatchPacket(std::unique_ptr<PointerDataPacket> packet, |
146 | uint64_t trace_flow_id) override; |
147 | |
148 | virtual ~SmoothPointerDataDispatcher(); |
149 | |
150 | private: |
151 | void DispatchPendingPacket(); |
152 | void ScheduleSecondaryVsyncCallback(); |
153 | |
154 | // If non-null, this will be a pending pointer data packet for the next frame |
155 | // to consume. This is used to smooth out the irregular drag events delivery. |
156 | // See also `DispatchPointerDataPacket` and input_events_unittests.cc. |
157 | std::unique_ptr<PointerDataPacket> pending_packet_; |
158 | int pending_trace_flow_id_ = -1; |
159 | bool is_pointer_data_in_progress_ = false; |
160 | |
161 | // WeakPtrFactory must be the last member. |
162 | fml::WeakPtrFactory<SmoothPointerDataDispatcher> weak_factory_; |
163 | FML_DISALLOW_COPY_AND_ASSIGN(SmoothPointerDataDispatcher); |
164 | }; |
165 | |
166 | //-------------------------------------------------------------------------- |
167 | /// @brief Signature for constructing PointerDataDispatcher. |
168 | /// |
169 | /// @param[in] delegate the `Flutter::Engine` |
170 | /// |
171 | using PointerDataDispatcherMaker = |
172 | std::function<std::unique_ptr<PointerDataDispatcher>( |
173 | PointerDataDispatcher::Delegate&)>; |
174 | |
175 | } // namespace flutter |
176 | |
177 | #endif // POINTER_DATA_DISPATCHER_H_ |
178 | |