blob: 12bea41da96d379e45c726197789c62f9fbea410 [file] [log] [blame]
// 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/engine.h"
// #include <cstring>
#include "flutter/common/constants.h"
#include "flutter/lib/ui/compositing/scene_builder.h"
#include "flutter/shell/common/shell_test.h"
#include "flutter/testing/fixture_test.h"
#include "gmock/gmock.h"
// CREATE_NATIVE_ENTRY is leaky by design
// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
namespace flutter {
namespace {
using ::testing::Invoke;
using ::testing::ReturnRef;
fml::AutoResetWaitableEvent native_latch;
void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
const fml::closure& task) {
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] {
task();
latch.Signal();
});
latch.Wait();
}
// Sort the argument list of `LayerTreeTask` into a new list that is sorted by
// their view IDs. `FrameItem::layer_tree_tasks` might not come sorted.
std::vector<const LayerTreeTask*> Sorted(
const std::vector<std::unique_ptr<LayerTreeTask>>& layer_tree_tasks) {
std::vector<const LayerTreeTask*> result;
result.reserve(layer_tree_tasks.size());
for (auto& task_ptr : layer_tree_tasks) {
result.push_back(task_ptr.get());
}
std::sort(result.begin(), result.end(),
[](const LayerTreeTask* a, const LayerTreeTask* b) {
return a->view_id < b->view_id;
});
return result;
}
class MockDelegate : public Engine::Delegate {
public:
MOCK_METHOD(void,
OnEngineUpdateSemantics,
(SemanticsNodeUpdates, CustomAccessibilityActionUpdates),
(override));
MOCK_METHOD(void,
OnEngineHandlePlatformMessage,
(std::unique_ptr<PlatformMessage>),
(override));
MOCK_METHOD(void, OnPreEngineRestart, (), (override));
MOCK_METHOD(void, OnRootIsolateCreated, (), (override));
MOCK_METHOD(void,
UpdateIsolateDescription,
(const std::string, int64_t),
(override));
MOCK_METHOD(void, SetNeedsReportTimings, (bool), (override));
MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
ComputePlatformResolvedLocale,
(const std::vector<std::string>&),
(override));
MOCK_METHOD(void, RequestDartDeferredLibrary, (intptr_t), (override));
MOCK_METHOD(fml::TimePoint, GetCurrentTimePoint, (), (override));
MOCK_METHOD(const std::shared_ptr<PlatformMessageHandler>&,
GetPlatformMessageHandler,
(),
(const, override));
MOCK_METHOD(void, OnEngineChannelUpdate, (std::string, bool), (override));
MOCK_METHOD(double,
GetScaledFontSize,
(double font_size, int configuration_id),
(const, override));
};
class MockAnimatorDelegate : public Animator::Delegate {
public:
/* Animator::Delegate */
MOCK_METHOD(void,
OnAnimatorBeginFrame,
(fml::TimePoint frame_target_time, uint64_t frame_number),
(override));
MOCK_METHOD(void,
OnAnimatorNotifyIdle,
(fml::TimeDelta deadline),
(override));
MOCK_METHOD(void,
OnAnimatorUpdateLatestFrameTargetTime,
(fml::TimePoint frame_target_time),
(override));
MOCK_METHOD(void,
OnAnimatorDraw,
(std::shared_ptr<FramePipeline> pipeline),
(override));
MOCK_METHOD(void,
OnAnimatorDrawLastLayerTrees,
(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder),
(override));
};
class MockPlatformMessageHandler : public PlatformMessageHandler {
public:
MOCK_METHOD(void,
HandlePlatformMessage,
(std::unique_ptr<PlatformMessage> message),
(override));
MOCK_METHOD(bool,
DoesHandlePlatformMessageOnPlatformThread,
(),
(const, override));
MOCK_METHOD(void,
InvokePlatformMessageResponseCallback,
(int response_id, std::unique_ptr<fml::Mapping> mapping),
(override));
MOCK_METHOD(void,
InvokePlatformMessageEmptyResponseCallback,
(int response_id),
(override));
};
class EngineAnimatorTest : public testing::FixtureTest {
public:
EngineAnimatorTest()
: thread_host_("EngineAnimatorTest",
ThreadHost::Type::kPlatform | ThreadHost::Type::kIo |
ThreadHost::Type::kUi | ThreadHost::Type::kRaster),
task_runners_({
"EngineAnimatorTest",
thread_host_.platform_thread->GetTaskRunner(), // platform
thread_host_.raster_thread->GetTaskRunner(), // raster
thread_host_.ui_thread->GetTaskRunner(), // ui
thread_host_.io_thread->GetTaskRunner() // io
}) {}
void PostUITaskSync(const std::function<void()>& function) {
fml::AutoResetWaitableEvent latch;
task_runners_.GetUITaskRunner()->PostTask([&] {
function();
latch.Signal();
});
latch.Wait();
}
protected:
void SetUp() override {
settings_ = CreateSettingsForFixture();
dispatcher_maker_ = [](PointerDataDispatcher::Delegate&) {
return nullptr;
};
}
MockDelegate delegate_;
PointerDataDispatcherMaker dispatcher_maker_;
ThreadHost thread_host_;
TaskRunners task_runners_;
Settings settings_;
std::unique_ptr<Animator> animator_;
fml::WeakPtr<IOManager> io_manager_;
std::unique_ptr<RuntimeController> runtime_controller_;
std::shared_ptr<fml::ConcurrentTaskRunner> image_decoder_task_runner_;
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_;
};
// A class that can launch an Engine with the specified Engine::Delegate.
//
// To use this class, contruct this class with Create, call Run, and use the
// engine with EngineTaskSync().
class EngineContext {
public:
using EngineCallback = std::function<void(Engine&)>;
[[nodiscard]] static std::unique_ptr<EngineContext> Create(
Engine::Delegate& delegate, //
Settings settings, //
const TaskRunners& task_runners, //
std::unique_ptr<Animator> animator) {
auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings);
FML_CHECK(vm) << "Must be able to initialize the VM.";
// Construct the class with `new` because `make_unique` has no access to the
// private constructor.
EngineContext* raw_pointer =
new EngineContext(delegate, settings, task_runners, std::move(animator),
vm, isolate_snapshot);
return std::unique_ptr<EngineContext>(raw_pointer);
}
void Run(RunConfiguration configuration) {
PostSync(task_runners_.GetUITaskRunner(), [this, &configuration] {
Engine::RunStatus run_status = engine_->Run(std::move(configuration));
FML_CHECK(run_status == Engine::RunStatus::Success)
<< "Engine failed to run.";
(void)run_status; // Suppress unused-variable warning
});
}
// Run a task that operates the Engine on the UI thread, and wait for the
// task to end.
//
// If called on the UI thread, the task is executed synchronously.
void EngineTaskSync(EngineCallback task) {
ASSERT_TRUE(engine_);
ASSERT_TRUE(task);
auto runner = task_runners_.GetUITaskRunner();
if (runner->RunsTasksOnCurrentThread()) {
task(*engine_);
} else {
PostSync(task_runners_.GetUITaskRunner(), [&]() { task(*engine_); });
}
}
~EngineContext() {
PostSync(task_runners_.GetUITaskRunner(), [this] { engine_.reset(); });
}
private:
EngineContext(Engine::Delegate& delegate, //
Settings settings, //
const TaskRunners& task_runners, //
std::unique_ptr<Animator> animator, //
const DartVMRef& vm, //
fml::RefPtr<const DartSnapshot> isolate_snapshot)
: task_runners_(task_runners), vm_(vm) {
PostSync(task_runners.GetUITaskRunner(), [this, &settings, &animator,
&delegate, &isolate_snapshot] {
auto dispatcher_maker =
[](DefaultPointerDataDispatcher::Delegate& delegate) {
return std::make_unique<DefaultPointerDataDispatcher>(delegate);
};
engine_ = std::make_unique<Engine>(
/*delegate=*/delegate,
/*dispatcher_maker=*/dispatcher_maker,
/*vm=*/*&vm_,
/*isolate_snapshot=*/std::move(isolate_snapshot),
/*task_runners=*/task_runners_,
/*platform_data=*/PlatformData(),
/*settings=*/settings,
/*animator=*/std::move(animator),
/*io_manager=*/io_manager_,
/*unref_queue=*/nullptr,
/*snapshot_delegate=*/snapshot_delegate_,
/*volatile_path_tracker=*/nullptr,
/*gpu_disabled_switch=*/std::make_shared<fml::SyncSwitch>());
});
}
TaskRunners task_runners_;
DartVMRef vm_;
std::unique_ptr<Engine> engine_;
fml::WeakPtr<IOManager> io_manager_;
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate_;
};
} // namespace
TEST_F(EngineAnimatorTest, AnimatorAcceptsMultipleRenders) {
MockAnimatorDelegate animator_delegate;
std::unique_ptr<EngineContext> engine_context;
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
std::make_shared<MockPlatformMessageHandler>();
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
.WillOnce(ReturnRef(platform_message_handler));
fml::AutoResetWaitableEvent draw_latch;
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
.WillOnce(
Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) {
auto status =
pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
auto tasks = Sorted(item->layer_tree_tasks);
EXPECT_EQ(tasks.size(), 2u);
EXPECT_EQ(tasks[0]->view_id, 1);
EXPECT_EQ(tasks[1]->view_id, 2);
});
EXPECT_EQ(status, PipelineConsumeResult::Done);
draw_latch.Signal();
}));
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
.WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time,
uint64_t frame_number) {
engine_context->EngineTaskSync([&](Engine& engine) {
engine.BeginFrame(frame_target_time, frame_number);
});
}));
native_latch.Reset();
AddNativeCallback("NotifyNative", [](auto args) { native_latch.Signal(); });
std::unique_ptr<Animator> animator;
PostSync(task_runners_.GetUITaskRunner(),
[&animator, &animator_delegate, &task_runners = task_runners_] {
animator = std::make_unique<Animator>(
animator_delegate, task_runners,
static_cast<std::unique_ptr<VsyncWaiter>>(
std::make_unique<testing::ConstantFiringVsyncWaiter>(
task_runners)));
});
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
std::move(animator));
auto configuration = RunConfiguration::InferFromSettings(settings_);
configuration.SetEntrypoint("onDrawFrameRenderAllViews");
engine_context->Run(std::move(configuration));
engine_context->EngineTaskSync([](Engine& engine) {
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
});
native_latch.Wait();
engine_context->EngineTaskSync(
[](Engine& engine) { engine.ScheduleFrame(); });
draw_latch.Wait();
}
TEST_F(EngineAnimatorTest, IgnoresOutOfFrameRenders) {
MockAnimatorDelegate animator_delegate;
std::unique_ptr<EngineContext> engine_context;
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
std::make_shared<MockPlatformMessageHandler>();
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
.WillOnce(ReturnRef(platform_message_handler));
fml::AutoResetWaitableEvent draw_latch;
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
.WillOnce(
Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) {
auto status =
pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
// View 1 is rendered before the frame, and is ignored.
// View 2 is rendered within the frame, and is accepted.
EXPECT_EQ(item->layer_tree_tasks.size(), 1u);
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, 2);
});
EXPECT_EQ(status, PipelineConsumeResult::Done);
draw_latch.Signal();
}));
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
.WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time,
uint64_t frame_number) {
engine_context->EngineTaskSync([&](Engine& engine) {
engine.BeginFrame(frame_target_time, frame_number);
});
}));
std::unique_ptr<Animator> animator;
PostSync(task_runners_.GetUITaskRunner(),
[&animator, &animator_delegate, &task_runners = task_runners_] {
animator = std::make_unique<Animator>(
animator_delegate, task_runners,
static_cast<std::unique_ptr<VsyncWaiter>>(
std::make_unique<testing::ConstantFiringVsyncWaiter>(
task_runners)));
});
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
std::move(animator));
engine_context->EngineTaskSync([](Engine& engine) {
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
});
auto configuration = RunConfiguration::InferFromSettings(settings_);
configuration.SetEntrypoint("renderViewsInFrameAndOutOfFrame");
engine_context->Run(std::move(configuration));
draw_latch.Wait();
}
TEST_F(EngineAnimatorTest, IgnoresDuplicateRenders) {
MockAnimatorDelegate animator_delegate;
std::unique_ptr<EngineContext> engine_context;
std::vector<std::shared_ptr<Layer>> benchmark_layers;
auto capture_root_layer = [&benchmark_layers](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
intptr_t peer = 0;
Dart_Handle result = Dart_GetNativeInstanceField(
handle, tonic::DartWrappable::kPeerIndex, &peer);
ASSERT_FALSE(Dart_IsError(result));
SceneBuilder* scene_builder = reinterpret_cast<SceneBuilder*>(peer);
ASSERT_TRUE(scene_builder);
std::shared_ptr<ContainerLayer> root_layer =
scene_builder->layer_stack()[0];
ASSERT_TRUE(root_layer);
benchmark_layers = root_layer->layers();
};
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
std::make_shared<MockPlatformMessageHandler>();
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
.WillOnce(ReturnRef(platform_message_handler));
fml::AutoResetWaitableEvent draw_latch;
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
.WillOnce(Invoke([&draw_latch, &benchmark_layers](
const std::shared_ptr<FramePipeline>& pipeline) {
auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
EXPECT_EQ(item->layer_tree_tasks.size(), 1u);
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId);
ContainerLayer* root_layer = reinterpret_cast<ContainerLayer*>(
item->layer_tree_tasks[0]->layer_tree->root_layer());
std::vector<std::shared_ptr<Layer>> result_layers =
root_layer->layers();
EXPECT_EQ(result_layers.size(), benchmark_layers.size());
EXPECT_EQ(result_layers[0], benchmark_layers[0]);
});
EXPECT_EQ(status, PipelineConsumeResult::Done);
draw_latch.Signal();
}));
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
.WillOnce(Invoke([&engine_context](fml::TimePoint frame_target_time,
uint64_t frame_number) {
engine_context->EngineTaskSync([&](Engine& engine) {
engine.BeginFrame(frame_target_time, frame_number);
});
}));
AddNativeCallback("CaptureRootLayer",
CREATE_NATIVE_ENTRY(capture_root_layer));
std::unique_ptr<Animator> animator;
PostSync(task_runners_.GetUITaskRunner(),
[&animator, &animator_delegate, &task_runners = task_runners_] {
animator = std::make_unique<Animator>(
animator_delegate, task_runners,
static_cast<std::unique_ptr<VsyncWaiter>>(
std::make_unique<testing::ConstantFiringVsyncWaiter>(
task_runners)));
});
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
std::move(animator));
engine_context->EngineTaskSync([](Engine& engine) {
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
});
auto configuration = RunConfiguration::InferFromSettings(settings_);
configuration.SetEntrypoint("renderTwiceForOneView");
engine_context->Run(std::move(configuration));
draw_latch.Wait();
}
TEST_F(EngineAnimatorTest, AnimatorSubmitsImplicitViewBeforeDrawFrameEnds) {
MockAnimatorDelegate animator_delegate;
std::unique_ptr<EngineContext> engine_context;
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
std::make_shared<MockPlatformMessageHandler>();
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
.WillOnce(ReturnRef(platform_message_handler));
bool rasterization_started = false;
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
.WillOnce(Invoke([&rasterization_started](
const std::shared_ptr<FramePipeline>& pipeline) {
rasterization_started = true;
auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
EXPECT_EQ(item->layer_tree_tasks.size(), 1u);
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId);
});
EXPECT_EQ(status, PipelineConsumeResult::Done);
}));
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
.WillRepeatedly(Invoke([&engine_context](fml::TimePoint frame_target_time,
uint64_t frame_number) {
engine_context->EngineTaskSync([&](Engine& engine) {
engine.BeginFrame(frame_target_time, frame_number);
});
}));
std::unique_ptr<Animator> animator;
PostSync(task_runners_.GetUITaskRunner(),
[&animator, &animator_delegate, &task_runners = task_runners_] {
animator = std::make_unique<Animator>(
animator_delegate, task_runners,
static_cast<std::unique_ptr<VsyncWaiter>>(
std::make_unique<testing::ConstantFiringVsyncWaiter>(
task_runners)));
});
native_latch.Reset();
// The native_latch is signaled at the end of handleDrawFrame.
AddNativeCallback("NotifyNative",
CREATE_NATIVE_ENTRY([&rasterization_started](auto args) {
EXPECT_EQ(rasterization_started, true);
native_latch.Signal();
}));
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
std::move(animator));
engine_context->EngineTaskSync([](Engine& engine) {
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0},
[](bool added) { ASSERT_TRUE(added); });
});
auto configuration = RunConfiguration::InferFromSettings(settings_);
configuration.SetEntrypoint("renderSingleViewAndCallAfterOnDrawFrame");
engine_context->Run(std::move(configuration));
native_latch.Wait();
}
// The animator should submit to the pipeline the implicit view rendered in a
// warm up frame if there's already a continuation (i.e. Animator::BeginFrame
// has been called)
TEST_F(EngineAnimatorTest, AnimatorSubmitWarmUpImplicitView) {
MockAnimatorDelegate animator_delegate;
std::unique_ptr<EngineContext> engine_context;
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
std::make_shared<MockPlatformMessageHandler>();
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
.WillOnce(ReturnRef(platform_message_handler));
fml::AutoResetWaitableEvent continuation_ready_latch;
fml::AutoResetWaitableEvent draw_latch;
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
.WillOnce(Invoke([&draw_latch](
const std::shared_ptr<FramePipeline>& pipeline) {
auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
EXPECT_EQ(item->layer_tree_tasks.size(), 1u);
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId);
});
EXPECT_EQ(status, PipelineConsumeResult::Done);
draw_latch.Signal();
}));
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
.WillRepeatedly(
Invoke([&engine_context, &continuation_ready_latch](
fml::TimePoint frame_target_time, uint64_t frame_number) {
continuation_ready_latch.Signal();
engine_context->EngineTaskSync([&](Engine& engine) {
engine.BeginFrame(frame_target_time, frame_number);
});
}));
std::unique_ptr<Animator> animator;
PostSync(task_runners_.GetUITaskRunner(),
[&animator, &animator_delegate, &task_runners = task_runners_] {
animator = std::make_unique<Animator>(
animator_delegate, task_runners,
static_cast<std::unique_ptr<VsyncWaiter>>(
std::make_unique<testing::ConstantFiringVsyncWaiter>(
task_runners)));
});
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
std::move(animator));
engine_context->EngineTaskSync([](Engine& engine) {
// Schedule a frame to trigger Animator::BeginFrame to create a
// continuation. The continuation needs to be available before `Engine::Run`
// since the Dart program immediately schedules a warm up frame.
engine.ScheduleFrame(true);
// Add the implicit view so that the engine recognizes it and that its
// metrics is not empty.
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0},
[](bool added) { ASSERT_TRUE(added); });
});
continuation_ready_latch.Wait();
auto configuration = RunConfiguration::InferFromSettings(settings_);
configuration.SetEntrypoint("renderWarmUpImplicitView");
engine_context->Run(std::move(configuration));
draw_latch.Wait();
}
// The warm up frame should work if only some of the registered views are
// included.
//
// This test also verifies that the warm up frame can render multiple views.
TEST_F(EngineAnimatorTest, AnimatorSubmitPartialViewsForWarmUp) {
MockAnimatorDelegate animator_delegate;
std::unique_ptr<EngineContext> engine_context;
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
std::make_shared<MockPlatformMessageHandler>();
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
.WillOnce(ReturnRef(platform_message_handler));
fml::AutoResetWaitableEvent continuation_ready_latch;
fml::AutoResetWaitableEvent draw_latch;
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
.WillOnce(
Invoke([&draw_latch](const std::shared_ptr<FramePipeline>& pipeline) {
auto status =
pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
auto tasks = Sorted(item->layer_tree_tasks);
EXPECT_EQ(tasks.size(), 2u);
EXPECT_EQ(tasks[0]->view_id, 1);
EXPECT_EQ(tasks[1]->view_id, 2);
});
EXPECT_EQ(status, PipelineConsumeResult::Done);
draw_latch.Signal();
}));
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
.WillRepeatedly(
Invoke([&engine_context, &continuation_ready_latch](
fml::TimePoint frame_target_time, uint64_t frame_number) {
continuation_ready_latch.Signal();
engine_context->EngineTaskSync([&](Engine& engine) {
engine.BeginFrame(frame_target_time, frame_number);
});
}));
std::unique_ptr<Animator> animator;
PostSync(task_runners_.GetUITaskRunner(),
[&animator, &animator_delegate, &task_runners = task_runners_] {
animator = std::make_unique<Animator>(
animator_delegate, task_runners,
static_cast<std::unique_ptr<VsyncWaiter>>(
std::make_unique<testing::ConstantFiringVsyncWaiter>(
task_runners)));
});
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
std::move(animator));
engine_context->EngineTaskSync([](Engine& engine) {
// Schedule a frame to make the animator create a continuation.
engine.ScheduleFrame(true);
// Add multiple views.
engine.AddView(0, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
engine.AddView(1, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
engine.AddView(2, ViewportMetrics{1, 10, 10, 22, 0},
[](bool added) { ASSERT_TRUE(added); });
});
continuation_ready_latch.Wait();
auto configuration = RunConfiguration::InferFromSettings(settings_);
configuration.SetEntrypoint("renderWarmUpView1and2");
engine_context->Run(std::move(configuration));
draw_latch.Wait();
}
} // namespace flutter
// NOLINTEND(clang-analyzer-core.StackAddressEscape)