flutter_flutter/shell/common/vsync_waiter.cc
liyuqian 9675ca2f6b
Reland "Smooth out iOS irregular input events delivery (#12280)" (#12385)
This reverts commit c2879cae2ee3707ad07af1118bf4862dc1d82bb7.

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.
2019-09-30 11:25:50 -07:00

139 lines
4.5 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.
#include "flutter/shell/common/vsync_waiter.h"
#include "flutter/fml/task_runner.h"
#include "flutter/fml/trace_event.h"
namespace flutter {
#if defined(OS_FUCHSIA)
// In general, traces on Fuchsia are recorded across the whole system.
// Because of this, emitting a "VSYNC" event per flutter process is
// undesirable, as the events will collide with each other. We
// instead let another area of the system emit them.
static constexpr const char* kVsyncTraceName = "vsync callback";
#else // defined(OS_FUCHSIA)
// Note: The tag name must be "VSYNC" (it is special) so that the
// "Highlight Vsync" checkbox in the timeline can be enabled.
static constexpr const char* kVsyncTraceName = "VSYNC";
#endif // defined(OS_FUCHSIA)
static constexpr const char* kVsyncFlowName = "VsyncFlow";
VsyncWaiter::VsyncWaiter(TaskRunners task_runners)
: task_runners_(std::move(task_runners)) {}
VsyncWaiter::~VsyncWaiter() = default;
// Public method invoked by the animator.
void VsyncWaiter::AsyncWaitForVsync(Callback callback) {
if (!callback) {
return;
}
TRACE_EVENT0("flutter", "AsyncWaitForVsync");
{
std::scoped_lock lock(callback_mutex_);
if (callback_) {
// The animator may request a frame more than once within a frame
// interval. Multiple calls to request frame must result in a single
// callback per frame interval.
TRACE_EVENT_INSTANT0("flutter", "MultipleCallsToVsyncInFrameInterval");
return;
}
callback_ = std::move(callback);
if (secondary_callback_) {
// Return directly as `AwaitVSync` is already called by
// `ScheduleSecondaryCallback`.
return;
}
}
AwaitVSync();
}
void VsyncWaiter::ScheduleSecondaryCallback(fml::closure callback) {
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
if (!callback) {
return;
}
TRACE_EVENT0("flutter", "ScheduleSecondaryCallback");
{
std::scoped_lock lock(callback_mutex_);
if (secondary_callback_) {
// Multiple schedules must result in a single callback per frame interval.
TRACE_EVENT_INSTANT0("flutter",
"MultipleCallsToSecondaryVsyncInFrameInterval");
return;
}
secondary_callback_ = std::move(callback);
if (callback_) {
// Return directly as `AwaitVSync` is already called by
// `AsyncWaitForVsync`.
return;
}
}
AwaitVSync();
}
void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
Callback callback;
fml::closure secondary_callback;
{
std::scoped_lock lock(callback_mutex_);
callback = std::move(callback_);
secondary_callback = std::move(secondary_callback_);
}
if (!callback && !secondary_callback) {
// This means that the vsync waiter implementation fired a callback for a
// request we did not make. This is a paranoid check but we still want to
// make sure we catch misbehaving vsync implementations.
TRACE_EVENT_INSTANT0("flutter", "MismatchedFrameCallback");
return;
}
if (callback) {
auto flow_identifier = fml::tracing::TraceNonce();
// The base trace ensures that flows have a root to begin from if one does
// not exist. The trace viewer will ignore traces that have no base event
// trace. While all our message loops insert a base trace trace
// (MessageLoop::RunExpiredTasks), embedders may not.
TRACE_EVENT0("flutter", "VsyncFireCallback");
TRACE_FLOW_BEGIN("flutter", kVsyncFlowName, flow_identifier);
task_runners_.GetUITaskRunner()->PostTaskForTime(
[callback, flow_identifier, frame_start_time, frame_target_time]() {
FML_TRACE_EVENT("flutter", kVsyncTraceName, "StartTime",
frame_start_time, "TargetTime", frame_target_time);
fml::tracing::TraceEventAsyncComplete(
"flutter", "VsyncSchedulingOverhead", fml::TimePoint::Now(),
frame_start_time);
callback(frame_start_time, frame_target_time);
TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier);
},
frame_start_time);
}
if (secondary_callback) {
task_runners_.GetUITaskRunner()->PostTaskForTime(
std::move(secondary_callback), frame_start_time);
}
}
float VsyncWaiter::GetDisplayRefreshRate() const {
return kUnknownRefreshRateFPS;
}
} // namespace flutter