Skip to content

Commit

Permalink
Revert "Revert "Smooth out iOS irregular input events delivery (flutt…
Browse files Browse the repository at this point in the history
…er#11817)" (flutter#12251)"

This reverts commit 3c6383f.
  • Loading branch information
liyuqian committed Sep 13, 2019
1 parent 07d4cd9 commit 3cb5ce3
Show file tree
Hide file tree
Showing 16 changed files with 625 additions and 49 deletions.
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ FILE: ../../../flutter/shell/common/canvas_spy_unittests.cc
FILE: ../../../flutter/shell/common/engine.cc
FILE: ../../../flutter/shell/common/engine.h
FILE: ../../../flutter/shell/common/fixtures/shell_test.dart
FILE: ../../../flutter/shell/common/input_events_unittests.cc
FILE: ../../../flutter/shell/common/isolate_configuration.cc
FILE: ../../../flutter/shell/common/isolate_configuration.h
FILE: ../../../flutter/shell/common/persistent_cache.cc
Expand All @@ -494,6 +495,8 @@ FILE: ../../../flutter/shell/common/pipeline.h
FILE: ../../../flutter/shell/common/pipeline_unittests.cc
FILE: ../../../flutter/shell/common/platform_view.cc
FILE: ../../../flutter/shell/common/platform_view.h
FILE: ../../../flutter/shell/common/pointer_data_dispatcher.cc
FILE: ../../../flutter/shell/common/pointer_data_dispatcher.h
FILE: ../../../flutter/shell/common/rasterizer.cc
FILE: ../../../flutter/shell/common/rasterizer.h
FILE: ../../../flutter/shell/common/run_configuration.cc
Expand Down
3 changes: 3 additions & 0 deletions shell/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ source_set("common") {
"pipeline.h",
"platform_view.cc",
"platform_view.h",
"pointer_data_dispatcher.cc",
"pointer_data_dispatcher.h",
"rasterizer.cc",
"rasterizer.h",
"run_configuration.cc",
Expand Down Expand Up @@ -156,6 +158,7 @@ if (current_toolchain == host_toolchain) {
shell_host_executable("shell_unittests") {
sources = [
"canvas_spy_unittests.cc",
"input_events_unittests.cc",
"pipeline_unittests.cc",
"shell_test.cc",
"shell_test.h",
Expand Down
16 changes: 11 additions & 5 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static constexpr char kSettingsChannel[] = "flutter/settings";
static constexpr char kIsolateChannel[] = "flutter/isolate";

Engine::Engine(Delegate& delegate,
PointerDataDispatcherMaker& dispatcher_maker,
DartVM& vm,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
fml::RefPtr<const DartSnapshot> shared_snapshot,
Expand All @@ -60,7 +61,7 @@ Engine::Engine(Delegate& delegate,
&vm, // VM
std::move(isolate_snapshot), // isolate snapshot
std::move(shared_snapshot), // shared snapshot
std::move(task_runners), // task runners
task_runners, // task runners
std::move(io_manager), // io manager
image_decoder_.GetWeakPtr(), // image decoder
settings_.advisory_script_uri, // advisory script uri
Expand All @@ -69,6 +70,9 @@ Engine::Engine(Delegate& delegate,
settings_.isolate_create_callback, // isolate create callback
settings_.isolate_shutdown_callback // isolate shutdown callback
);

pointer_data_dispatcher_ = dispatcher_maker(*animator_, *runtime_controller_,
std::move(task_runners));
}

Engine::~Engine() = default;
Expand Down Expand Up @@ -381,12 +385,12 @@ void Engine::HandleSettingsPlatformMessage(PlatformMessage* message) {
}
}

void Engine::DispatchPointerDataPacket(const PointerDataPacket& packet,
uint64_t trace_flow_id) {
void Engine::DispatchPointerDataPacket(
std::unique_ptr<PointerDataPacket> packet,
uint64_t trace_flow_id) {
TRACE_EVENT0("flutter", "Engine::DispatchPointerDataPacket");
TRACE_FLOW_STEP("flutter", "PointerEvent", trace_flow_id);
animator_->EnqueueTraceFlowId(trace_flow_id);
runtime_controller_->DispatchPointerDataPacket(packet);
pointer_data_dispatcher_->DispatchPacket(std::move(packet), trace_flow_id);
}

void Engine::DispatchSemanticsAction(int id,
Expand Down Expand Up @@ -434,6 +438,8 @@ void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {

layer_tree->set_frame_size(frame_size);
animator_->Render(std::move(layer_tree));

pointer_data_dispatcher_->OnFrameLayerTreeReceived();
}

void Engine::UpdateSemantics(SemanticsNodeUpdates update,
Expand Down
12 changes: 11 additions & 1 deletion shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include "flutter/runtime/runtime_controller.h"
#include "flutter/runtime/runtime_delegate.h"
#include "flutter/shell/common/animator.h"
#include "flutter/shell/common/platform_view.h"
#include "flutter/shell/common/pointer_data_dispatcher.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
#include "flutter/shell/common/shell_io_manager.h"
Expand Down Expand Up @@ -234,6 +236,12 @@ class Engine final : public RuntimeDelegate {
/// tasks that require access to components
/// that cannot be safely accessed by the
/// engine. This is the shell.
/// @param dispatcher_maker The `std::function` provided by
/// `PlatformView` for engine to create the
/// pointer data dispatcher. Similar to other
/// engine resources, this dispatcher_maker and
/// its returned dispatcher is only safe to be
/// called from the UI thread.
/// @param vm An instance of the running Dart VM.
/// @param[in] isolate_snapshot The snapshot used to create the root
/// isolate. Even though the isolate is not
Expand Down Expand Up @@ -265,6 +273,7 @@ class Engine final : public RuntimeDelegate {
/// GPU.
///
Engine(Delegate& delegate,
PointerDataDispatcherMaker& dispatcher_maker,
DartVM& vm,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
fml::RefPtr<const DartSnapshot> shared_snapshot,
Expand Down Expand Up @@ -649,7 +658,7 @@ class Engine final : public RuntimeDelegate {
/// timeline and allow grouping frames and input
/// events into logical chunks.
///
void DispatchPointerDataPacket(const PointerDataPacket& packet,
void DispatchPointerDataPacket(std::unique_ptr<PointerDataPacket> packet,
uint64_t trace_flow_id);

//----------------------------------------------------------------------------
Expand Down Expand Up @@ -705,6 +714,7 @@ class Engine final : public RuntimeDelegate {
const Settings settings_;
std::unique_ptr<Animator> animator_;
std::unique_ptr<RuntimeController> runtime_controller_;
std::unique_ptr<PointerDataDispatcher> pointer_data_dispatcher_;
std::string initial_route_;
ViewportMetrics viewport_metrics_;
std::shared_ptr<AssetManager> asset_manager_;
Expand Down
11 changes: 11 additions & 0 deletions shell/common/fixtures/shell_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ void main() {}

void nativeReportTimingsCallback(List<int> timings) native 'NativeReportTimingsCallback';
void nativeOnBeginFrame(int microseconds) native 'NativeOnBeginFrame';
void nativeOnPointerDataPacket() native 'NativeOnPointerDataPacket';

@pragma('vm:entry-point')
void reportTimingsMain() {
Expand All @@ -32,6 +33,16 @@ void onBeginFrameMain() {
};
}

@pragma('vm:entry-point')
void onPointerDataPacketMain() {
window.onPointerDataPacket = (PointerDataPacket packet) {
nativeOnPointerDataPacket();
};
window.onBeginFrame = (Duration beginTime) {
nativeOnBeginFrame(beginTime.inMicroseconds);
};
}

@pragma('vm:entry-point')
void emptyMain() {}

Expand Down
236 changes: 236 additions & 0 deletions shell/common/input_events_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// 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/shell_test.h"
#include "flutter/testing/testing.h"

namespace flutter {
namespace testing {

// Throughout these tests, the choice of time unit is irrelevant as long as all
// times have the same units.
using UnitlessTime = int;

// Signature of a generator function that takes the frame index as input and
// returns the time of that frame.
using Generator = std::function<UnitlessTime(int)>;

//----------------------------------------------------------------------------
/// Simulate n input events where the i-th one is delivered at delivery_time(i).
///
/// Simulation results will be written into events_consumed_at_frame whose
/// length will be equal to the number of frames drawn. Each element in the
/// vector is the number of input events consumed up to that frame. (We can't
/// return such vector because ASSERT_TRUE requires return type of void.)
///
/// We assume (and check) that the delivery latency is some base latency plus a
/// random latency where the random latency must be within one frame:
///
/// 1. latency = delivery_time(i) - j * frame_time = base_latency +
/// random_latency
/// 2. 0 <= base_latency, 0 <= random_latency < frame_time
///
/// We also assume that there will be at least one input event per frame if
/// there were no latency. Let j = floor( (delivery_time(i) - base_latency) /
/// frame_time ) be the frame index if there were no latency. Then the set of j
/// should be all integers from 0 to continuous_frame_count - 1 for some
/// integer continuous_frame_count.
///
/// (Note that there coulds be multiple input events within one frame.)
///
/// The test here is insensitive to the choice of time unit as long as
/// delivery_time and frame_time are in the same unit.
static void TestSimulatedInputEvents(
ShellTest* fixture,
int num_events,
UnitlessTime base_latency,
Generator delivery_time,
UnitlessTime frame_time,
std::vector<UnitlessTime>& events_consumed_at_frame) {
///// Begin constructing shell ///////////////////////////////////////////////
auto settings = fixture->CreateSettingsForFixture();
std::unique_ptr<Shell> shell = fixture->CreateShell(settings);

auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("onPointerDataPacketMain");

// The following 4 variables are only accessed in the UI thread by
// nativeOnPointerDataPacket and nativeOnBeginFrame between their
// initializations and `shell.reset()`.
events_consumed_at_frame.clear();
bool will_draw_new_frame = true;
int events_consumed = 0;
int frame_drawn = 0;
auto nativeOnPointerDataPacket = [&events_consumed_at_frame,
&will_draw_new_frame, &events_consumed,
&frame_drawn](Dart_NativeArguments args) {
events_consumed += 1;
if (will_draw_new_frame) {
frame_drawn += 1;
will_draw_new_frame = false;
events_consumed_at_frame.push_back(events_consumed);
} else {
events_consumed_at_frame.back() = events_consumed;
}
};
fixture->AddNativeCallback("NativeOnPointerDataPacket",
CREATE_NATIVE_ENTRY(nativeOnPointerDataPacket));

auto nativeOnBeginFrame = [&will_draw_new_frame](Dart_NativeArguments args) {
will_draw_new_frame = true;
};
fixture->AddNativeCallback("NativeOnBeginFrame",
CREATE_NATIVE_ENTRY(nativeOnBeginFrame));

ASSERT_TRUE(configuration.IsValid());
fixture->RunEngine(shell.get(), std::move(configuration));
///// End constructing shell /////////////////////////////////////////////////

ASSERT_GE(base_latency, 0);

// Check that delivery_time satisfies our assumptions.
int continuous_frame_count = 0;
for (int i = 0; i < num_events; i += 1) {
// j is the frame index of event i if there were no latency.
int j = static_cast<int>((delivery_time(i) - base_latency) / frame_time);
if (j == continuous_frame_count) {
continuous_frame_count += 1;
}
double random_latency = delivery_time(i) - j * frame_time - base_latency;
ASSERT_GE(random_latency, 0);
ASSERT_LT(random_latency, frame_time);

// If there were no latency, there should be at least one event per frame.
// Hence j should never skip any integer less than continuous_frame_count.
ASSERT_LT(j, continuous_frame_count);
}

// i is the input event's index.
// j is the frame's index.
for (int i = 0, j = 0; i < num_events; j += 1) {
double t = j * frame_time;
while (i < num_events && delivery_time(i) <= t) {
ShellTest::DispatchFakePointerData(shell.get());
i += 1;
}
ShellTest::PumpOneFrame(shell.get());
}

shell.reset();
}

TEST_F(ShellTest, MissAtMostOneFrameForIrregularInputEvents) {
// We don't use `constexpr int frame_time` here because MSVC doesn't handle
// it well with lambda capture.
UnitlessTime frame_time = 10;
UnitlessTime base_latency = 0.5 * frame_time;
Generator extreme = [frame_time, base_latency](int i) {
return static_cast<UnitlessTime>(
i * frame_time + base_latency +
(i % 2 == 0 ? 0.1 * frame_time : 0.9 * frame_time));
};
constexpr int n = 40;
std::vector<int> events_consumed_at_frame;
TestSimulatedInputEvents(this, n, base_latency, extreme, frame_time,
events_consumed_at_frame);
int frame_drawn = events_consumed_at_frame.size();
ASSERT_GE(frame_drawn, n - 1);
}

TEST_F(ShellTest, DelayAtMostOneEventForFasterThanVSyncInputEvents) {
// We don't use `constexpr int frame_time` here because MSVC doesn't handle
// it well with lambda capture.
UnitlessTime frame_time = 10;
UnitlessTime base_latency = 0.2 * frame_time;
Generator double_sampling = [frame_time, base_latency](int i) {
return static_cast<UnitlessTime>(i * 0.5 * frame_time + base_latency);
};
constexpr int n = 40;
std::vector<int> events_consumed_at_frame;
TestSimulatedInputEvents(this, n, base_latency, double_sampling, frame_time,
events_consumed_at_frame);

// Draw one extra frame due to delaying a pending packet for the next frame.
int frame_drawn = events_consumed_at_frame.size();
ASSERT_EQ(frame_drawn, n / 2 + 1);

for (int i = 0; i < n / 2; i += 1) {
ASSERT_GE(events_consumed_at_frame[i], 2 * i - 1);
}
}

TEST_F(ShellTest, HandlesActualIphoneXsInputEvents) {
// Actual delivery times measured on iPhone Xs, in the unit of frame_time
// (16.67ms for 60Hz).
constexpr double iphone_xs_times[] = {0.15,
1.0773046874999999,
2.1738720703124996,
3.0579052734374996,
4.0890087890624995,
5.0952685546875,
6.1251708984375,
7.1253076171875,
8.125927734374999,
9.37248046875,
10.133950195312499,
11.161201171875,
12.226992187499999,
13.1443798828125,
14.440327148437499,
15.091684570312498,
16.138681640625,
17.126469726562497,
18.1592431640625,
19.371372070312496,
20.033774414062496,
21.021782226562497,
22.070053710937497,
23.325541992187496,
24.119648437499997,
25.084262695312496,
26.077866210937497,
27.036547851562496,
28.035073242187497,
29.081411132812498,
30.066064453124998,
31.089360351562497,
32.086142578125,
33.4618798828125,
34.14697265624999,
35.0513525390625,
36.136025390624994,
37.1618408203125,
38.144472656249995,
39.201123046875,
40.4339501953125,
41.1552099609375,
42.102128906249995,
43.0426318359375,
44.070131835937495,
45.08862304687499,
46.091469726562494};
constexpr int n = sizeof(iphone_xs_times) / sizeof(iphone_xs_times[0]);
// We don't use `constexpr int frame_time` here because MSVC doesn't handle
// it well with lambda capture.
UnitlessTime frame_time = 10000;
for (double base_latency_f = 0; base_latency_f < 1; base_latency_f += 0.1) {
// Everything is converted to int to avoid floating point error in
// TestSimulatedInputEvents.
UnitlessTime base_latency =
static_cast<UnitlessTime>(base_latency_f * frame_time);
Generator iphone_xs_generator = [frame_time, iphone_xs_times,
base_latency](int i) {
return base_latency +
static_cast<UnitlessTime>(iphone_xs_times[i] * frame_time);
};
std::vector<int> events_consumed_at_frame;
TestSimulatedInputEvents(this, n, base_latency, iphone_xs_generator,
frame_time, events_consumed_at_frame);
int frame_drawn = events_consumed_at_frame.size();
ASSERT_GE(frame_drawn, n - 1);
}
}

} // namespace testing
} // namespace flutter
Loading

0 comments on commit 3cb5ce3

Please sign in to comment.