Flutter Engine
animator_unittests.cc
Go to the documentation of this file.
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 #define FML_USED_ON_EMBEDDER
6 
7 #include "flutter/shell/common/animator.h"
8 
9 #include <functional>
10 #include <future>
11 #include <memory>
12 
13 #include "flutter/shell/common/shell_test.h"
14 #include "flutter/shell/common/shell_test_platform_view.h"
15 #include "flutter/testing/testing.h"
16 #include "gtest/gtest.h"
17 
18 namespace flutter {
19 namespace testing {
20 
22  public:
23  void OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
24  uint64_t frame_number) override {}
25 
26  void OnAnimatorNotifyIdle(int64_t deadline) override {
27  notify_idle_called_ = true;
28  }
29 
31  std::shared_ptr<Pipeline<flutter::LayerTree>> pipeline,
32  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override {}
33 
35  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override {}
36 
37  bool notify_idle_called_ = false;
38 };
39 
40 TEST_F(ShellTest, VSyncTargetTime) {
41  // Add native callbacks to listen for window.onBeginFrame
42  int64_t target_time;
43  fml::AutoResetWaitableEvent on_target_time_latch;
44  auto nativeOnBeginFrame = [&on_target_time_latch,
45  &target_time](Dart_NativeArguments args) {
46  Dart_Handle exception = nullptr;
47  target_time =
49  on_target_time_latch.Signal();
50  };
51  AddNativeCallback("NativeOnBeginFrame",
52  CREATE_NATIVE_ENTRY(nativeOnBeginFrame));
53 
54  // Create all te prerequisites for a shell.
55  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
56  auto settings = CreateSettingsForFixture();
57 
58  std::unique_ptr<Shell> shell;
59 
60  TaskRunners task_runners = GetTaskRunnersForFixture();
61  // this is not used as we are not using simulated events.
62  const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
63  CreateVsyncWaiter create_vsync_waiter = [&]() {
64  return static_cast<std::unique_ptr<VsyncWaiter>>(
65  std::make_unique<ConstantFiringVsyncWaiter>(task_runners));
66  };
67 
68  // create a shell with a constant firing vsync waiter.
69  auto platform_task = std::async(std::launch::async, [&]() {
71 
72  shell = Shell::Create(
73  flutter::PlatformData(), task_runners, settings,
74  [vsync_clock, &create_vsync_waiter](Shell& shell) {
76  shell, shell.GetTaskRunners(), vsync_clock,
77  std::move(create_vsync_waiter),
79  },
80  [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
81  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
82 
83  auto configuration = RunConfiguration::InferFromSettings(settings);
84  ASSERT_TRUE(configuration.IsValid());
85  configuration.SetEntrypoint("onBeginFrameMain");
86 
87  RunEngine(shell.get(), std::move(configuration));
88  });
89  platform_task.wait();
90 
91  // schedule a frame to trigger window.onBeginFrame
92  fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetUITaskRunner(),
93  [engine = shell->GetEngine()]() {
94  if (engine) {
95  // Engine needs a surface for frames to
96  // be scheduled.
97  engine->OnOutputSurfaceCreated();
98  // this implies we can re-use the last
99  // frame to trigger begin frame rather
100  // than re-generating the layer tree.
101  engine->ScheduleFrame(true);
102  }
103  });
104 
105  on_target_time_latch.Wait();
106  const auto vsync_waiter_target_time =
108  ASSERT_EQ(vsync_waiter_target_time.ToEpochDelta().ToMicroseconds(),
109  target_time);
110 
111  // validate that the latest target time has also been updated.
112  ASSERT_EQ(GetLatestFrameTargetTime(shell.get()), vsync_waiter_target_time);
113 
114  // teardown.
115  DestroyShell(std::move(shell), std::move(task_runners));
116  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
117 }
118 
119 TEST_F(ShellTest, AnimatorStartsPaused) {
120  // Create all te prerequisites for a shell.
121  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
122  auto settings = CreateSettingsForFixture();
123  TaskRunners task_runners = GetTaskRunnersForFixture();
124 
125  auto shell = CreateShell(std::move(settings), task_runners,
126  /* simulate_vsync=*/true);
127  ASSERT_TRUE(DartVMRef::IsInstanceRunning());
128 
129  auto configuration = RunConfiguration::InferFromSettings(settings);
130  ASSERT_TRUE(configuration.IsValid());
131 
132  configuration.SetEntrypoint("emptyMain");
133 
134  RunEngine(shell.get(), std::move(configuration));
135 
136  ASSERT_FALSE(IsAnimatorRunning(shell.get()));
137 
138  // teardown.
139  DestroyShell(std::move(shell), std::move(task_runners));
140  ASSERT_FALSE(DartVMRef::IsInstanceRunning());
141 }
142 
143 TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) {
144  FakeAnimatorDelegate delegate;
145  TaskRunners task_runners = {
146  "test",
147  CreateNewThread(), // platform
148  CreateNewThread(), // raster
149  CreateNewThread(), // ui
150  CreateNewThread() // io
151  };
152 
153  auto clock = std::make_shared<ShellTestVsyncClock>();
155  std::shared_ptr<Animator> animator;
156 
157  auto flush_vsync_task = [&] {
159  task_runners.GetUITaskRunner()->PostTask([&] { ui_latch.Signal(); });
160  do {
161  clock->SimulateVSync();
162  } while (ui_latch.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1)));
163  latch.Signal();
164  };
165 
166  // Create the animator on the UI task runner.
167  task_runners.GetUITaskRunner()->PostTask([&] {
168  auto vsync_waiter = static_cast<std::unique_ptr<VsyncWaiter>>(
169  std::make_unique<ShellTestVsyncWaiter>(task_runners, clock));
170  animator = std::make_unique<Animator>(delegate, task_runners,
171  std::move(vsync_waiter));
172  latch.Signal();
173  });
174  latch.Wait();
175 
176  // Validate it has not notified idle and start it. This will request a frame.
177  task_runners.GetUITaskRunner()->PostTask([&] {
178  ASSERT_FALSE(delegate.notify_idle_called_);
179  animator->Start();
180  // Immediately request a frame saying it can reuse the last layer tree to
181  // avoid more calls to BeginFrame by the animator.
182  animator->RequestFrame(false);
183  task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
184  });
185  latch.Wait();
186  ASSERT_FALSE(delegate.notify_idle_called_);
187 
188  // Validate it has not notified idle and try to render.
189  task_runners.GetUITaskRunner()->PostDelayedTask(
190  [&] {
191  ASSERT_FALSE(delegate.notify_idle_called_);
192  auto layer_tree =
193  std::make_unique<LayerTree>(SkISize::Make(600, 800), 1.0);
194  animator->Render(std::move(layer_tree));
195  task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
196  },
197  // See kNotifyIdleTaskWaitTime in animator.cc.
199  latch.Wait();
200 
201  // Still hasn't notified idle because there has been no frame request.
202  task_runners.GetUITaskRunner()->PostTask([&] {
203  ASSERT_FALSE(delegate.notify_idle_called_);
204  // False to avoid getting cals to BeginFrame that will request more frames
205  // before we are ready.
206  animator->RequestFrame(false);
207  task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
208  });
209  latch.Wait();
210 
211  // Now it should notify idle. Make sure it is destroyed on the UI thread.
212  ASSERT_TRUE(delegate.notify_idle_called_);
213 
214  // Stop and do one more flush so we can safely clean up on the UI thread.
215  animator->Stop();
216  task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
217  latch.Wait();
218 
219  task_runners.GetUITaskRunner()->PostTask([&] {
220  animator.reset();
221  latch.Signal();
222  });
223  latch.Wait();
224 }
225 
226 } // namespace testing
227 } // namespace flutter
G_BEGIN_DECLS FlValue * args
bool WaitWithTimeout(TimeDelta timeout)
const TaskRunners & GetTaskRunners() const override
If callers wish to interact directly with any shell subcomponents, they must (on the platform thread)...
Definition: shell.cc:674
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
fml::RefPtr< fml::TaskRunner > GetPlatformTaskRunner() const
Definition: task_runners.cc:30
static void EnsureInitializedForCurrentThread()
Definition: message_loop.cc:27
void OnAnimatorDrawLastLayerTree(std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder) override
void OnAnimatorDraw(std::shared_ptr< Pipeline< flutter::LayerTree >> pipeline, std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder) override
static bool IsInstanceRunning()
static constexpr fml::TimePoint frame_target_time
std::function< std::unique_ptr< VsyncWaiter >()> CreateVsyncWaiter
static RunConfiguration InferFromSettings(const Settings &settings, fml::RefPtr< fml::TaskRunner > io_worker=nullptr)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
#define CREATE_NATIVE_ENTRY(native_entry)
void OnAnimatorNotifyIdle(int64_t deadline) override
TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies)
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, TaskRunners task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer, bool is_gpu_disabled=false)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition: shell.cc:126
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, uint64_t frame_number) override
static std::unique_ptr< ShellTestPlatformView > Create(PlatformView::Delegate &delegate, TaskRunners task_runners, std::shared_ptr< ShellTestVsyncClock > vsync_clock, CreateVsyncWaiter create_vsync_waiter, BackendType backend, std::shared_ptr< ShellTestExternalViewEmbedder > shell_test_external_view_embedder)
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
virtual void PostDelayedTask(const fml::closure &task, fml::TimeDelta delay)
Definition: task_runner.cc:33