mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This reverts commit 56bb40c0179628e37ba3614534552441642c0492. Additionally, we fix https://github.com/flutter/flutter/issues/40863 by adding a secondary VSYNC callback. Unit tests are updated to provide VSYNC mocking and check the fix of https://github.com/flutter/flutter/issues/40863. The root cause of having https://github.com/flutter/flutter/issues/40863 is the false assumption that each input event must trigger a new frame. That was true in the framework PR https://github.com/flutter/flutter/pull/36616 because the input events there are all scrolling move events. When the PR was ported to the engine, we can no longer distinguish different types of events, and tap events may no longer trigger a new frame. Therefore, this PR directly hooks into the `VsyncWaiter` and uses its (newly added) secondary callback to dispatch the pending input event.
176 lines
7.8 KiB
C++
176 lines
7.8 KiB
C++
// 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 `RuntomeController` 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, 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`.
|
|
virtual void ScheduleSecondaryVsyncCallback(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:
|
|
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 preivous 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:
|
|
SmoothPointerDataDispatcher(Delegate& delegate);
|
|
|
|
// |PointerDataDispatcer|
|
|
void DispatchPacket(std::unique_ptr<PointerDataPacket> packet,
|
|
uint64_t trace_flow_id) override;
|
|
|
|
virtual ~SmoothPointerDataDispatcher();
|
|
|
|
private:
|
|
// 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;
|
|
|
|
fml::WeakPtrFactory<SmoothPointerDataDispatcher> weak_factory_;
|
|
|
|
void DispatchPendingPacket();
|
|
|
|
void ScheduleSecondaryVsyncCallback();
|
|
|
|
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 // POINTER_DATA_DISPATCHER_H_
|