Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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/post_task_sync.h"
16#include "flutter/testing/testing.h"
17#include "gmock/gmock.h"
18#include "gtest/gtest.h"
19
20// CREATE_NATIVE_ENTRY is leaky by design
21// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
22
23namespace flutter {
24namespace testing {
25
26constexpr int64_t kImplicitViewId = 0;
27
29 public:
32 (fml::TimePoint frame_target_time, uint64_t frame_number),
33 (override));
34
35 void OnAnimatorNotifyIdle(fml::TimeDelta deadline) override {
37 }
38
41 (fml::TimePoint frame_target_time),
42 (override));
43
46 (std::shared_ptr<FramePipeline> pipeline),
47 (override));
48
50 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override {}
51
52 bool notify_idle_called_ = false;
53};
54
55TEST_F(ShellTest, VSyncTargetTime) {
56 // Add native callbacks to listen for window.onBeginFrame
57 int64_t target_time;
58 fml::AutoResetWaitableEvent on_target_time_latch;
59 auto nativeOnBeginFrame = [&on_target_time_latch,
60 &target_time](Dart_NativeArguments args) {
61 Dart_Handle exception = nullptr;
62 target_time =
64 on_target_time_latch.Signal();
65 };
66 AddNativeCallback("NativeOnBeginFrame",
67 CREATE_NATIVE_ENTRY(nativeOnBeginFrame));
68
69 // Create all te prerequisites for a shell.
70 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
71 auto settings = CreateSettingsForFixture();
72
73 std::unique_ptr<Shell> shell;
74
76 // this is not used as we are not using simulated events.
77 const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
78 CreateVsyncWaiter create_vsync_waiter = [&]() {
79 return static_cast<std::unique_ptr<VsyncWaiter>>(
80 std::make_unique<ConstantFiringVsyncWaiter>(task_runners));
81 };
82
83 // create a shell with a constant firing vsync waiter.
84 auto platform_task = std::async(std::launch::async, [&]() {
86
87 shell = Shell::Create(
88 flutter::PlatformData(), task_runners, settings,
89 [vsync_clock, &create_vsync_waiter](Shell& shell) {
91 shell, shell.GetTaskRunners(), vsync_clock, create_vsync_waiter,
92 ShellTestPlatformView::BackendType::kDefaultBackend, nullptr,
93 shell.GetIsGpuDisabledSyncSwitch());
94 },
95 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
96 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
97
98 auto configuration = RunConfiguration::InferFromSettings(settings);
99 ASSERT_TRUE(configuration.IsValid());
100 configuration.SetEntrypoint("onBeginFrameMain");
101
102 RunEngine(shell.get(), std::move(configuration));
103 });
104 platform_task.wait();
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), task_runners);
116 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
117}
118
119TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) {
120 FakeAnimatorDelegate delegate;
121 TaskRunners task_runners = {
122 "test",
123 CreateNewThread(), // platform
124 CreateNewThread(), // raster
125 CreateNewThread(), // ui
126 CreateNewThread() // io
127 };
128
129 auto clock = std::make_shared<ShellTestVsyncClock>();
131 std::shared_ptr<Animator> animator;
132
133 auto flush_vsync_task = [&] {
135 task_runners.GetUITaskRunner()->PostTask([&] { ui_latch.Signal(); });
136 do {
137 clock->SimulateVSync();
139 latch.Signal();
140 };
141
142 // Create the animator on the UI task runner.
143 task_runners.GetUITaskRunner()->PostTask([&] {
144 auto vsync_waiter = static_cast<std::unique_ptr<VsyncWaiter>>(
145 std::make_unique<ShellTestVsyncWaiter>(task_runners, clock));
146 animator = std::make_unique<Animator>(delegate, task_runners,
147 std::move(vsync_waiter));
148 latch.Signal();
149 });
150 latch.Wait();
151
152 // Validate it has not notified idle and start it. This will request a frame.
153 task_runners.GetUITaskRunner()->PostTask([&] {
154 ASSERT_FALSE(delegate.notify_idle_called_);
155 // Immediately request a frame saying it can reuse the last layer tree to
156 // avoid more calls to BeginFrame by the animator.
157 animator->RequestFrame(false);
158 task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
159 });
160 latch.Wait();
161 ASSERT_FALSE(delegate.notify_idle_called_);
162
163 fml::AutoResetWaitableEvent render_latch;
164 // Validate it has not notified idle and try to render.
165 task_runners.GetUITaskRunner()->PostDelayedTask(
166 [&] {
167 ASSERT_FALSE(delegate.notify_idle_called_);
168 EXPECT_CALL(delegate, OnAnimatorBeginFrame).WillOnce([&] {
169 auto layer_tree = std::make_unique<LayerTree>(
170 LayerTree::Config(), SkISize::Make(600, 800));
171 animator->Render(kImplicitViewId, std::move(layer_tree), 1.0);
172 render_latch.Signal();
173 });
174 // Request a frame that builds a layer tree and renders a frame.
175 // When the frame is rendered, render_latch will be signaled.
176 animator->RequestFrame(true);
177 task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
178 },
179 // See kNotifyIdleTaskWaitTime in animator.cc.
181 latch.Wait();
182 render_latch.Wait();
183
184 // A frame has been rendered, and the next frame request will notify idle.
185 // But at the moment there isn't another frame request, therefore it still
186 // hasn't notified idle.
187 task_runners.GetUITaskRunner()->PostTask([&] {
188 ASSERT_FALSE(delegate.notify_idle_called_);
189 // False to avoid getting cals to BeginFrame that will request more frames
190 // before we are ready.
191 animator->RequestFrame(false);
192 task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
193 });
194 latch.Wait();
195
196 // Now it should notify idle. Make sure it is destroyed on the UI thread.
197 ASSERT_TRUE(delegate.notify_idle_called_);
198
199 task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
200 latch.Wait();
201
202 task_runners.GetUITaskRunner()->PostTask([&] {
203 animator.reset();
204 latch.Signal();
205 });
206 latch.Wait();
207}
208
209TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) {
210 FakeAnimatorDelegate delegate;
211 TaskRunners task_runners = {
212 "test",
213 CreateNewThread(), // platform
214 CreateNewThread(), // raster
215 CreateNewThread(), // ui
216 CreateNewThread() // io
217 };
218
219 auto clock = std::make_shared<ShellTestVsyncClock>();
220 std::shared_ptr<Animator> animator;
221
222 auto flush_vsync_task = [&] {
224 task_runners.GetUITaskRunner()->PostTask([&] { ui_latch.Signal(); });
225 do {
226 clock->SimulateVSync();
228 };
229
230 // Create the animator on the UI task runner.
231 PostTaskSync(task_runners.GetUITaskRunner(), [&] {
232 auto vsync_waiter = static_cast<std::unique_ptr<VsyncWaiter>>(
233 std::make_unique<ShellTestVsyncWaiter>(task_runners, clock));
234 animator = std::make_unique<Animator>(delegate, task_runners,
235 std::move(vsync_waiter));
236 });
237
238 fml::AutoResetWaitableEvent begin_frame_latch;
239 // It must always be called when the method 'Animator::Render' is called,
240 // regardless of whether the pipeline is empty or not.
241 EXPECT_CALL(delegate, OnAnimatorUpdateLatestFrameTargetTime).Times(2);
242 // It will only be called once even though we call the method
243 // 'Animator::Render' twice. because it will only be called when the pipeline
244 // is empty.
245 EXPECT_CALL(delegate, OnAnimatorDraw).Times(1);
246
247 for (int i = 0; i < 2; i++) {
248 task_runners.GetUITaskRunner()->PostTask([&] {
249 EXPECT_CALL(delegate, OnAnimatorBeginFrame).WillOnce([&] {
250 auto layer_tree = std::make_unique<LayerTree>(LayerTree::Config(),
251 SkISize::Make(600, 800));
252 animator->Render(kImplicitViewId, std::move(layer_tree), 1.0);
253 begin_frame_latch.Signal();
254 });
255 animator->RequestFrame();
256 task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task);
257 });
258 begin_frame_latch.Wait();
259 }
260
261 PostTaskSync(task_runners.GetUITaskRunner(), [&] { animator.reset(); });
262}
263
264} // namespace testing
265} // namespace flutter
266
267// NOLINTEND(clang-analyzer-core.StackAddressEscape)
virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, uint64_t frame_number)=0
virtual void OnAnimatorUpdateLatestFrameTargetTime(fml::TimePoint frame_target_time)=0
virtual void OnAnimatorDraw(std::shared_ptr< FramePipeline > pipeline)=0
static bool IsInstanceRunning()
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, const 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:167
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
fml::RefPtr< fml::TaskRunner > GetPlatformTaskRunner() const
static constexpr fml::TimePoint kFrameTargetTime
MOCK_METHOD(void, OnAnimatorUpdateLatestFrameTargetTime,(fml::TimePoint frame_target_time),(override))
MOCK_METHOD(void, OnAnimatorDraw,(std::shared_ptr< FramePipeline > pipeline),(override))
MOCK_METHOD(void, OnAnimatorBeginFrame,(fml::TimePoint frame_target_time, uint64_t frame_number),(override))
void OnAnimatorDrawLastLayerTrees(std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder) override
void OnAnimatorNotifyIdle(fml::TimeDelta deadline) override
static std::unique_ptr< ShellTestPlatformView > Create(PlatformView::Delegate &delegate, const TaskRunners &task_runners, const std::shared_ptr< ShellTestVsyncClock > &vsync_clock, const CreateVsyncWaiter &create_vsync_waiter, BackendType backend, const std::shared_ptr< ShellTestExternalViewEmbedder > &shell_test_external_view_embedder, const std::shared_ptr< const fml::SyncSwitch > &is_gpu_disabled_sync_switch)
bool WaitWithTimeout(TimeDelta timeout)
static void EnsureInitializedForCurrentThread()
virtual void PostTask(const fml::closure &task) override
virtual void PostDelayedTask(const fml::closure &task, fml::TimeDelta delay)
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition time_delta.h:46
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
struct _Dart_NativeArguments * Dart_NativeArguments
Definition dart_api.h:3010
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
TEST_F(DisplayListTest, Defaults)
void PostTaskSync(const fml::RefPtr< fml::TaskRunner > &task_runner, const std::function< void()> &function)
std::function< std::unique_ptr< VsyncWaiter >()> CreateVsyncWaiter
constexpr int64_t kImplicitViewId
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
#define CREATE_NATIVE_ENTRY(native_entry)