From c82f8d04c86d41f1430e6249f2bb2cc78c837170 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 1 Aug 2019 14:56:10 -0700 Subject: [PATCH 01/12] Reland Skia cache size reduction --- shell/common/rasterizer.cc | 21 ++++++++++++++++++++- shell/common/rasterizer.h | 23 ++++++++++++++++++++++- shell/common/shell.cc | 13 ++++++++++++- shell/gpu/gpu_surface_gl.cc | 5 ++++- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 0d3cbcd0a11e8..9361014645838 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -43,6 +43,7 @@ Rasterizer::Rasterizer( : delegate_(delegate), task_runners_(std::move(task_runners)), compositor_context_(std::move(compositor_context)), + user_override_resource_cache_bytes_(false), weak_factory_(this) { FML_DCHECK(compositor_context_); } @@ -355,7 +356,15 @@ void Rasterizer::FireNextFrameCallbackIfPresent() { callback(); } -void Rasterizer::SetResourceCacheMaxBytes(int max_bytes) { +void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) { + user_override_resource_cache_bytes_ |= from_user; + + if (!from_user && user_override_resource_cache_bytes_) { + // We should not update the setting here if a user has explicitly set a + // value for this over the flutter/skia channel. + return; + } + GrContext* context = surface_->GetContext(); if (context) { int max_resources; @@ -364,6 +373,16 @@ void Rasterizer::SetResourceCacheMaxBytes(int max_bytes) { } } +size_t Rasterizer::GetResourceCacheMaxBytes() const { + GrContext* context = surface_->GetContext(); + if (context) { + size_t max_bytes; + context->getResourceCacheLimits(nullptr, &max_bytes); + return max_bytes; + } + return 0; +} + Rasterizer::Screenshot::Screenshot() {} Rasterizer::Screenshot::Screenshot(sk_sp p_data, SkISize p_size) diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index daa6f0fe19e44..c5df96a98c252 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -374,8 +374,28 @@ class Rasterizer final { /// /// @param[in] max_bytes The maximum byte size of resource that may be /// cached for GPU rendering. + /// @param[in] from_user Whether this request was from user code, e.g. via + /// the flutter/skia message channel, in which case + /// it should not be overridden by the platform. /// - void SetResourceCacheMaxBytes(int max_bytes); + void SetResourceCacheMaxBytes(size_t max_bytes, bool from_user); + + //---------------------------------------------------------------------------- + /// @brief The current value of Skia's resource cache size. + /// + /// @attention This cache setting will be invalidated when the surface is + /// torn down via `Rasterizer::Teardown`. This call must be made + /// again with new limits after surface re-acquisition. + /// + /// @attention This cache does not describe the entirety of GPU resources + /// that may be cached. The `RasterCache` also holds very large + /// GPU resources. + /// + /// @see `RasterCache` + /// + /// @return The size of Skia's resource cache. + /// + size_t GetResourceCacheMaxBytes() const; private: Delegate& delegate_; @@ -384,6 +404,7 @@ class Rasterizer final { std::unique_ptr compositor_context_; std::unique_ptr last_layer_tree_; fml::closure next_frame_callback_; + bool user_override_resource_cache_bytes_; fml::WeakPtrFactory weak_factory_; void DoDraw(std::unique_ptr layer_tree); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 7bba32b761a83..43a537f20292b 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -682,6 +682,16 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + // This is the formula Android uses. + // https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41 + int max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4; + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), max_bytes] { + if (rasterizer) { + rasterizer->SetResourceCacheMaxBytes(max_bytes, false); + } + }); + task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), metrics]() { if (engine) { @@ -942,7 +952,8 @@ void Shell::HandleEngineSkiaMessage(fml::RefPtr message) { [rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt()] { if (rasterizer) { - rasterizer->SetResourceCacheMaxBytes(max_bytes); + rasterizer->SetResourceCacheMaxBytes(static_cast(max_bytes), + true); } }); } diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index d344e7225a595..6ddbcde23b3c3 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -28,7 +28,10 @@ static const int kGrCacheMaxCount = 8192; // Default maximum number of bytes of GPU memory of budgeted resources in the // cache. -static const size_t kGrCacheMaxByteSize = 512 * (1 << 20); +// The shell will dynamically increase or decrease this cache based on the +// viewport size, unless a user has specifically requested a size on the Skia +// system channel. +static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate) : delegate_(delegate), weak_factory_(this) { From 871ead750e477b603e99a5058b28d7fd92ccd803 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 1 Aug 2019 16:48:29 -0700 Subject: [PATCH 02/12] test --- shell/common/shell.cc | 2 +- shell/common/shell_test.cc | 16 + shell/common/shell_test.h | 4 + shell/common/shell_unittests.cc | 1160 ++++++++++++++++--------------- 4 files changed, 630 insertions(+), 552 deletions(-) diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 43a537f20292b..aba283955c040 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -684,7 +684,7 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { // This is the formula Android uses. // https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41 - int max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4; + size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4; task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), max_bytes] { if (rasterizer) { diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 491263fccf8a4..df33e0b671e1a 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -22,6 +22,22 @@ ShellTest::ShellTest() ShellTest::~ShellTest() = default; +void ShellTest::SendEnginePlatformMessage( + Shell* shell, + fml::RefPtr message) { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), + [shell, &latch, message = std::move(message)]() { + if (!shell->weak_engine_) { + return; + } + shell->weak_engine_->HandlePlatformMessage(std::move(message)); + latch.Signal(); + }); + latch.Wait(); +} + void ShellTest::SetSnapshotsAndAssets(Settings& settings) { if (!assets_dir_.is_valid()) { return; diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 4a69e7e516e3a..662453c0d0c44 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -9,6 +9,7 @@ #include "flutter/common/settings.h" #include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" @@ -32,6 +33,9 @@ class ShellTest : public ThreadTest { TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); + void SendEnginePlatformMessage(Shell* shell, + fml::RefPtr message); + void AddNativeCallback(std::string name, Dart_NativeFunction callback); static void PlatformViewNotifyCreated( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 45d2a658e2707..a97d7fbdc57a5 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -26,232 +26,597 @@ namespace flutter { namespace testing { -static bool ValidateShell(Shell* shell) { - if (!shell) { - return false; - } - - if (!shell->IsSetup()) { - return false; - } - - ShellTest::PlatformViewNotifyCreated(shell); - - { - fml::AutoResetWaitableEvent latch; - fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() { - shell->GetPlatformView()->NotifyDestroyed(); - latch.Signal(); - }); - latch.Wait(); - } - - return true; -} - -TEST_F(ShellTest, InitializeWithInvalidThreads) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - Settings settings = CreateSettingsForFixture(); - TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); - auto shell = CreateShell(std::move(settings), std::move(task_runners)); - ASSERT_FALSE(shell); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, InitializeWithDifferentThreads) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// static bool ValidateShell(Shell* shell) { +// if (!shell) { +// return false; +// } + +// if (!shell->IsSetup()) { +// return false; +// } + +// ShellTest::PlatformViewNotifyCreated(shell); + +// { +// fml::AutoResetWaitableEvent latch; +// fml::TaskRunner::RunNowOrPostTask( +// shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() { +// shell->GetPlatformView()->NotifyDestroyed(); +// latch.Signal(); +// }); +// latch.Wait(); +// } + +// return true; +// } + +// TEST_F(ShellTest, InitializeWithInvalidThreads) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// Settings settings = CreateSettingsForFixture(); +// TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); +// auto shell = CreateShell(std::move(settings), std::move(task_runners)); +// ASSERT_FALSE(shell); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, InitializeWithDifferentThreads) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// Settings settings = CreateSettingsForFixture(); +// ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", +// ThreadHost::Type::Platform | ThreadHost::Type::GPU | +// ThreadHost::Type::IO | ThreadHost::Type::UI); +// TaskRunners task_runners("test", +// thread_host.platform_thread->GetTaskRunner(), +// thread_host.gpu_thread->GetTaskRunner(), +// thread_host.ui_thread->GetTaskRunner(), +// thread_host.io_thread->GetTaskRunner()); +// auto shell = CreateShell(std::move(settings), std::move(task_runners)); +// ASSERT_TRUE(ValidateShell(shell.get())); +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, InitializeWithSingleThread) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// Settings settings = CreateSettingsForFixture(); +// ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", +// ThreadHost::Type::Platform); +// auto task_runner = thread_host.platform_thread->GetTaskRunner(); +// TaskRunners task_runners("test", task_runner, task_runner, task_runner, +// task_runner); +// auto shell = CreateShell(std::move(settings), std::move(task_runners)); +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// ASSERT_TRUE(ValidateShell(shell.get())); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// Settings settings = CreateSettingsForFixture(); +// fml::MessageLoop::EnsureInitializedForCurrentThread(); +// auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); +// TaskRunners task_runners("test", task_runner, task_runner, task_runner, +// task_runner); +// auto shell = CreateShell(std::move(settings), std::move(task_runners)); +// ASSERT_TRUE(ValidateShell(shell.get())); +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, +// InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// Settings settings = CreateSettingsForFixture(); +// ThreadHost thread_host( +// "io.flutter.test." + GetCurrentTestName() + ".", +// ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); +// fml::MessageLoop::EnsureInitializedForCurrentThread(); +// TaskRunners task_runners("test", +// fml::MessageLoop::GetCurrent().GetTaskRunner(), +// thread_host.gpu_thread->GetTaskRunner(), +// thread_host.ui_thread->GetTaskRunner(), +// thread_host.io_thread->GetTaskRunner()); +// auto shell = Shell::Create( +// std::move(task_runners), settings, +// [](Shell& shell) { +// return std::make_unique(shell, +// shell.GetTaskRunners()); +// }, +// [](Shell& shell) { +// return std::make_unique(shell, shell.GetTaskRunners()); +// }); +// ASSERT_TRUE(ValidateShell(shell.get())); +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// Settings settings = CreateSettingsForFixture(); +// ThreadHost thread_host( +// "io.flutter.test." + GetCurrentTestName() + ".", +// ThreadHost::Type::Platform | ThreadHost::Type::IO | +// ThreadHost::Type::UI); +// TaskRunners task_runners( +// "test", +// thread_host.platform_thread->GetTaskRunner(), // platform +// thread_host.platform_thread->GetTaskRunner(), // gpu +// thread_host.ui_thread->GetTaskRunner(), // ui +// thread_host.io_thread->GetTaskRunner() // io +// ); +// auto shell = CreateShell(std::move(settings), std::move(task_runners)); +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// ASSERT_TRUE(ValidateShell(shell.get())); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, FixturesAreFunctional) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// auto settings = CreateSettingsForFixture(); +// auto shell = CreateShell(settings); +// ASSERT_TRUE(ValidateShell(shell.get())); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// ASSERT_TRUE(configuration.IsValid()); +// configuration.SetEntrypoint("fixturesAreFunctionalMain"); + +// fml::AutoResetWaitableEvent main_latch; +// AddNativeCallback( +// "SayHiFromFixturesAreFunctionalMain", +// CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); +// })); + +// RunEngine(shell.get(), std::move(configuration)); +// main_latch.Wait(); +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) { +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// auto settings = CreateSettingsForFixture(); +// auto shell = CreateShell(settings); +// ASSERT_TRUE(ValidateShell(shell.get())); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// ASSERT_TRUE(configuration.IsValid()); +// configuration.SetEntrypoint("testCanLaunchSecondaryIsolate"); + +// fml::CountDownLatch latch(2); +// AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { +// latch.CountDown(); +// })); + +// RunEngine(shell.get(), std::move(configuration)); + +// latch.Wait(); + +// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); +// shell.reset(); +// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +// } + +// TEST(ShellTestNoFixture, EnableMirrorsIsWhitelisted) { +// if (DartVM::IsRunningPrecompiledCode()) { +// // This covers profile and release modes which use AOT (where this flag +// does +// // not make sense anyway). +// GTEST_SKIP(); +// return; +// } + +// const std::vector options = { +// fml::CommandLine::Option("dart-flags", "--enable_mirrors")}; +// fml::CommandLine command_line("", options, std::vector()); +// flutter::Settings settings = +// flutter::SettingsFromCommandLine(command_line); +// EXPECT_EQ(settings.dart_flags.size(), 1u); +// } + +// TEST_F(ShellTest, BlacklistedDartVMFlag) { +// // Run this test in a thread-safe manner, otherwise gtest will complain. +// ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + +// const std::vector options = { +// fml::CommandLine::Option("dart-flags", "--verify_after_gc")}; +// fml::CommandLine command_line("", options, std::vector()); + +// #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE +// // Upon encountering a non-whitelisted Dart flag the process terminates. +// const char* expected = +// "Encountered blacklisted Dart VM flag: --verify_after_gc"; +// ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected); +// #else +// flutter::Settings settings = +// flutter::SettingsFromCommandLine(command_line); +// EXPECT_EQ(settings.dart_flags.size(), 0u); +// #endif +// } + +// TEST_F(ShellTest, WhitelistedDartVMFlag) { +// const std::vector options = { +// fml::CommandLine::Option("dart-flags", +// "--max_profile_depth 1,--random_seed 42")}; +// fml::CommandLine command_line("", options, std::vector()); +// flutter::Settings settings = +// flutter::SettingsFromCommandLine(command_line); + +// #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE +// EXPECT_EQ(settings.dart_flags.size(), 2u); +// EXPECT_EQ(settings.dart_flags[0], "--max_profile_depth 1"); +// EXPECT_EQ(settings.dart_flags[1], "--random_seed 42"); +// #else +// EXPECT_EQ(settings.dart_flags.size(), 0u); +// #endif +// } + +// TEST_F(ShellTest, NoNeedToReportTimingsByDefault) { +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("emptyMain"); + +// RunEngine(shell.get(), std::move(configuration)); +// PumpOneFrame(shell.get()); +// ASSERT_FALSE(GetNeedsReportTimings(shell.get())); + +// // This assertion may or may not be the direct result of +// needs_report_timings_ +// // being false. The count could be 0 simply because we just cleared +// unreported +// // timings by reporting them. Hence this can't replace the +// // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added this +// // assertion for an additional confidence that we're not pushing back to +// // unreported timings unnecessarily. +// // +// // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be +// // positive in any tests. Otherwise those tests will be flaky as the +// clearing +// // of unreported timings is unpredictive. +// ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0); +// } + +// TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) { +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("dummyReportTimingsMain"); + +// RunEngine(shell.get(), std::move(configuration)); +// PumpOneFrame(shell.get()); +// ASSERT_TRUE(GetNeedsReportTimings(shell.get())); +// } + +// static void CheckFrameTimings(const std::vector& timings, +// fml::TimePoint start, +// fml::TimePoint finish) { +// fml::TimePoint last_frame_start; +// for (size_t i = 0; i < timings.size(); i += 1) { +// // Ensure that timings are sorted. +// ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start); +// last_frame_start = timings[i].Get(FrameTiming::kPhases[0]); + +// fml::TimePoint last_phase_time; +// for (auto phase : FrameTiming::kPhases) { +// ASSERT_TRUE(timings[i].Get(phase) >= start); +// ASSERT_TRUE(timings[i].Get(phase) <= finish); + +// // phases should have weakly increasing time points +// ASSERT_TRUE(last_phase_time <= timings[i].Get(phase)); +// last_phase_time = timings[i].Get(phase); +// } +// } +// } + +// TEST_F(ShellTest, ReportTimingsIsCalled) { +// fml::TimePoint start = fml::TimePoint::Now(); +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// ASSERT_TRUE(configuration.IsValid()); +// configuration.SetEntrypoint("reportTimingsMain"); +// fml::AutoResetWaitableEvent reportLatch; +// std::vector timestamps; +// auto nativeTimingCallback = [&reportLatch, +// ×tamps](Dart_NativeArguments args) { +// Dart_Handle exception = nullptr; +// timestamps = tonic::DartConverter>::FromArguments( +// args, 0, exception); +// reportLatch.Signal(); +// }; +// AddNativeCallback("NativeReportTimingsCallback", +// CREATE_NATIVE_ENTRY(nativeTimingCallback)); +// RunEngine(shell.get(), std::move(configuration)); + +// // Pump many frames so we can trigger the report quickly instead of waiting +// // for the 1 second threshold. +// for (int i = 0; i < 200; i += 1) { +// PumpOneFrame(shell.get()); +// } + +// reportLatch.Wait(); +// shell.reset(); + +// fml::TimePoint finish = fml::TimePoint::Now(); +// ASSERT_TRUE(timestamps.size() > 0); +// ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0); +// std::vector timings(timestamps.size() / FrameTiming::kCount); + +// for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) { +// for (auto phase : FrameTiming::kPhases) { +// timings[i].Set( +// phase, +// fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds( +// timestamps[i * FrameTiming::kCount + phase]))); +// } +// } +// CheckFrameTimings(timings, start, finish); +// } + +// TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { +// fml::TimePoint start = fml::TimePoint::Now(); + +// auto settings = CreateSettingsForFixture(); +// fml::AutoResetWaitableEvent timingLatch; +// FrameTiming timing; + +// for (auto phase : FrameTiming::kPhases) { +// timing.Set(phase, fml::TimePoint()); +// // Check that the time points are initially smaller than start, so +// // CheckFrameTimings will fail if they're not properly set later. +// ASSERT_TRUE(timing.Get(phase) < start); +// } + +// settings.frame_rasterized_callback = [&timing, +// &timingLatch](const FrameTiming& t) { +// timing = t; +// timingLatch.Signal(); +// }; + +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("onBeginFrameMain"); + +// int64_t begin_frame; +// auto nativeOnBeginFrame = [&begin_frame](Dart_NativeArguments args) { +// Dart_Handle exception = nullptr; +// begin_frame = +// tonic::DartConverter::FromArguments(args, 0, exception); +// }; +// AddNativeCallback("NativeOnBeginFrame", +// CREATE_NATIVE_ENTRY(nativeOnBeginFrame)); + +// RunEngine(shell.get(), std::move(configuration)); + +// PumpOneFrame(shell.get()); + +// // Check that timing is properly set. This implies that +// // settings.frame_rasterized_callback is called. +// timingLatch.Wait(); +// fml::TimePoint finish = fml::TimePoint::Now(); +// std::vector timings = {timing}; +// CheckFrameTimings(timings, start, finish); + +// // Check that onBeginFrame has the same timestamp as FrameTiming's build +// start int64_t build_start = +// timing.Get(FrameTiming::kBuildStart).ToEpochDelta().ToMicroseconds(); +// ASSERT_EQ(build_start, begin_frame); +// } + +// TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { +// // Ensure that all phases are in kPhases. +// ASSERT_EQ(sizeof(FrameTiming::kPhases), +// FrameTiming::kCount * sizeof(FrameTiming::Phase)); + +// int lastPhaseIndex = -1; +// FrameTiming timing; +// for (auto phase : FrameTiming::kPhases) { +// ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in +// order. lastPhaseIndex = phase; auto fake_time = +// fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds(phase)); +// timing.Set(phase, fake_time); +// ASSERT_TRUE(timing.Get(phase) == fake_time); +// } +// } + +// #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE +// TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) { +// #else +// TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) { +// #endif +// fml::TimePoint start = fml::TimePoint::Now(); +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// ASSERT_TRUE(configuration.IsValid()); +// configuration.SetEntrypoint("reportTimingsMain"); + +// // Wait for 2 reports: the first one is the immediate callback of the first +// // frame; the second one will exercise the batching logic. +// fml::CountDownLatch reportLatch(2); +// std::vector timestamps; +// auto nativeTimingCallback = [&reportLatch, +// ×tamps](Dart_NativeArguments args) { +// Dart_Handle exception = nullptr; +// timestamps = tonic::DartConverter>::FromArguments( +// args, 0, exception); +// reportLatch.CountDown(); +// }; +// AddNativeCallback("NativeReportTimingsCallback", +// CREATE_NATIVE_ENTRY(nativeTimingCallback)); +// RunEngine(shell.get(), std::move(configuration)); + +// PumpOneFrame(shell.get()); +// PumpOneFrame(shell.get()); + +// reportLatch.Wait(); +// shell.reset(); + +// fml::TimePoint finish = fml::TimePoint::Now(); +// fml::TimeDelta ellapsed = finish - start; + +// #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE +// // Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to +// // make it not too flaky. +// ASSERT_TRUE(ellapsed >= fml::TimeDelta::FromMilliseconds(800)); +// #else +// // Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to +// // make it not too flaky. +// ASSERT_TRUE(ellapsed <= fml::TimeDelta::FromMilliseconds(500)); +// #endif +// } + +// TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// ASSERT_TRUE(configuration.IsValid()); +// configuration.SetEntrypoint("reportTimingsMain"); +// fml::AutoResetWaitableEvent reportLatch; +// std::vector timestamps; +// auto nativeTimingCallback = [&reportLatch, +// ×tamps](Dart_NativeArguments args) { +// Dart_Handle exception = nullptr; +// timestamps = tonic::DartConverter>::FromArguments( +// args, 0, exception); +// reportLatch.Signal(); +// }; +// AddNativeCallback("NativeReportTimingsCallback", +// CREATE_NATIVE_ENTRY(nativeTimingCallback)); +// RunEngine(shell.get(), std::move(configuration)); + +// for (int i = 0; i < 10; i += 1) { +// PumpOneFrame(shell.get()); +// } + +// reportLatch.Wait(); +// shell.reset(); + +// // Check for the immediate callback of the first frame that doesn't wait +// for +// // the other 9 frames to be rasterized. +// ASSERT_EQ(timestamps.size(), FrameTiming::kCount); +// } + +// TEST_F(ShellTest, WaitForFirstFrame) { +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("emptyMain"); + +// RunEngine(shell.get(), std::move(configuration)); +// PumpOneFrame(shell.get()); +// fml::Status result = +// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); +// ASSERT_TRUE(result.ok()); +// } + +// TEST_F(ShellTest, WaitForFirstFrameTimeout) { +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("emptyMain"); + +// RunEngine(shell.get(), std::move(configuration)); +// fml::Status result = +// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); +// ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); +// } + +// TEST_F(ShellTest, WaitForFirstFrameMultiple) { +// auto settings = CreateSettingsForFixture(); +// std::unique_ptr shell = CreateShell(settings); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("emptyMain"); + +// RunEngine(shell.get(), std::move(configuration)); +// PumpOneFrame(shell.get()); +// fml::Status result = +// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); +// ASSERT_TRUE(result.ok()); +// for (int i = 0; i < 100; ++i) { +// result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); +// ASSERT_TRUE(result.ok()); +// } +// } + +// /// Makes sure that WaitForFirstFrame works if we rendered a frame with the +// /// single-thread setup. +// TEST_F(ShellTest, WaitForFirstFrameInlined) { +// Settings settings = CreateSettingsForFixture(); +// auto task_runner = GetThreadTaskRunner(); +// TaskRunners task_runners("test", task_runner, task_runner, task_runner, +// task_runner); +// std::unique_ptr shell = +// CreateShell(std::move(settings), std::move(task_runners)); + +// // Create the surface needed by rasterizer +// PlatformViewNotifyCreated(shell.get()); + +// auto configuration = RunConfiguration::InferFromSettings(settings); +// configuration.SetEntrypoint("emptyMain"); + +// RunEngine(shell.get(), std::move(configuration)); +// PumpOneFrame(shell.get()); +// fml::AutoResetWaitableEvent event; +// task_runner->PostTask([&shell, &event] { +// fml::Status result = +// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); +// ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); +// event.Signal(); +// }); +// ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); +// } + +TEST_F(ShellTest, SetResourceCacheSize) { Settings settings = CreateSettingsForFixture(); - ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform | ThreadHost::Type::GPU | - ThreadHost::Type::IO | ThreadHost::Type::UI); - TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), - thread_host.gpu_thread->GetTaskRunner(), - thread_host.ui_thread->GetTaskRunner(), - thread_host.io_thread->GetTaskRunner()); - auto shell = CreateShell(std::move(settings), std::move(task_runners)); - ASSERT_TRUE(ValidateShell(shell.get())); - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, InitializeWithSingleThread) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - Settings settings = CreateSettingsForFixture(); - ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform); - auto task_runner = thread_host.platform_thread->GetTaskRunner(); - TaskRunners task_runners("test", task_runner, task_runner, task_runner, - task_runner); - auto shell = CreateShell(std::move(settings), std::move(task_runners)); - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - ASSERT_TRUE(ValidateShell(shell.get())); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - Settings settings = CreateSettingsForFixture(); - fml::MessageLoop::EnsureInitializedForCurrentThread(); - auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + auto task_runner = GetThreadTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(std::move(settings), std::move(task_runners)); - ASSERT_TRUE(ValidateShell(shell.get())); - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, - InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - Settings settings = CreateSettingsForFixture(); - ThreadHost thread_host( - "io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); - fml::MessageLoop::EnsureInitializedForCurrentThread(); - TaskRunners task_runners("test", - fml::MessageLoop::GetCurrent().GetTaskRunner(), - thread_host.gpu_thread->GetTaskRunner(), - thread_host.ui_thread->GetTaskRunner(), - thread_host.io_thread->GetTaskRunner()); - auto shell = Shell::Create( - std::move(task_runners), settings, - [](Shell& shell) { - return std::make_unique(shell, - shell.GetTaskRunners()); - }, - [](Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); - }); - ASSERT_TRUE(ValidateShell(shell.get())); - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - Settings settings = CreateSettingsForFixture(); - ThreadHost thread_host( - "io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI); - TaskRunners task_runners( - "test", - thread_host.platform_thread->GetTaskRunner(), // platform - thread_host.platform_thread->GetTaskRunner(), // gpu - thread_host.ui_thread->GetTaskRunner(), // ui - thread_host.io_thread->GetTaskRunner() // io - ); - auto shell = CreateShell(std::move(settings), std::move(task_runners)); - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - ASSERT_TRUE(ValidateShell(shell.get())); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, FixturesAreFunctional) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - auto settings = CreateSettingsForFixture(); - auto shell = CreateShell(settings); - ASSERT_TRUE(ValidateShell(shell.get())); - - auto configuration = RunConfiguration::InferFromSettings(settings); - ASSERT_TRUE(configuration.IsValid()); - configuration.SetEntrypoint("fixturesAreFunctionalMain"); - - fml::AutoResetWaitableEvent main_latch; - AddNativeCallback( - "SayHiFromFixturesAreFunctionalMain", - CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); })); - - RunEngine(shell.get(), std::move(configuration)); - main_latch.Wait(); - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) { - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); - auto settings = CreateSettingsForFixture(); - auto shell = CreateShell(settings); - ASSERT_TRUE(ValidateShell(shell.get())); - - auto configuration = RunConfiguration::InferFromSettings(settings); - ASSERT_TRUE(configuration.IsValid()); - configuration.SetEntrypoint("testCanLaunchSecondaryIsolate"); - - fml::CountDownLatch latch(2); - AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { - latch.CountDown(); - })); - - RunEngine(shell.get(), std::move(configuration)); - - latch.Wait(); - - ASSERT_TRUE(DartVMRef::IsInstanceRunning()); - shell.reset(); - ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -} - -TEST(ShellTestNoFixture, EnableMirrorsIsWhitelisted) { - if (DartVM::IsRunningPrecompiledCode()) { - // This covers profile and release modes which use AOT (where this flag does - // not make sense anyway). - GTEST_SKIP(); - return; - } - - const std::vector options = { - fml::CommandLine::Option("dart-flags", "--enable_mirrors")}; - fml::CommandLine command_line("", options, std::vector()); - flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); - EXPECT_EQ(settings.dart_flags.size(), 1u); -} - -TEST_F(ShellTest, BlacklistedDartVMFlag) { - // Run this test in a thread-safe manner, otherwise gtest will complain. - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - const std::vector options = { - fml::CommandLine::Option("dart-flags", "--verify_after_gc")}; - fml::CommandLine command_line("", options, std::vector()); - -#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - // Upon encountering a non-whitelisted Dart flag the process terminates. - const char* expected = - "Encountered blacklisted Dart VM flag: --verify_after_gc"; - ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected); -#else - flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); - EXPECT_EQ(settings.dart_flags.size(), 0u); -#endif -} - -TEST_F(ShellTest, WhitelistedDartVMFlag) { - const std::vector options = { - fml::CommandLine::Option("dart-flags", - "--max_profile_depth 1,--random_seed 42")}; - fml::CommandLine command_line("", options, std::vector()); - flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); - -#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - EXPECT_EQ(settings.dart_flags.size(), 2u); - EXPECT_EQ(settings.dart_flags[0], "--max_profile_depth 1"); - EXPECT_EQ(settings.dart_flags[1], "--random_seed 42"); -#else - EXPECT_EQ(settings.dart_flags.size(), 0u); -#endif -} - -TEST_F(ShellTest, NoNeedToReportTimingsByDefault) { - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -260,344 +625,37 @@ TEST_F(ShellTest, NoNeedToReportTimingsByDefault) { configuration.SetEntrypoint("emptyMain"); RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get()); - ASSERT_FALSE(GetNeedsReportTimings(shell.get())); - - // This assertion may or may not be the direct result of needs_report_timings_ - // being false. The count could be 0 simply because we just cleared unreported - // timings by reporting them. Hence this can't replace the - // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added this - // assertion for an additional confidence that we're not pushing back to - // unreported timings unnecessarily. - // - // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be - // positive in any tests. Otherwise those tests will be flaky as the clearing - // of unreported timings is unpredictive. - ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0); -} - -TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) { - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("dummyReportTimingsMain"); - - RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get()); - ASSERT_TRUE(GetNeedsReportTimings(shell.get())); -} - -static void CheckFrameTimings(const std::vector& timings, - fml::TimePoint start, - fml::TimePoint finish) { - fml::TimePoint last_frame_start; - for (size_t i = 0; i < timings.size(); i += 1) { - // Ensure that timings are sorted. - ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start); - last_frame_start = timings[i].Get(FrameTiming::kPhases[0]); - - fml::TimePoint last_phase_time; - for (auto phase : FrameTiming::kPhases) { - ASSERT_TRUE(timings[i].Get(phase) >= start); - ASSERT_TRUE(timings[i].Get(phase) <= finish); - - // phases should have weakly increasing time points - ASSERT_TRUE(last_phase_time <= timings[i].Get(phase)); - last_phase_time = timings[i].Get(phase); - } - } -} - -TEST_F(ShellTest, ReportTimingsIsCalled) { - fml::TimePoint start = fml::TimePoint::Now(); - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - ASSERT_TRUE(configuration.IsValid()); - configuration.SetEntrypoint("reportTimingsMain"); - fml::AutoResetWaitableEvent reportLatch; - std::vector timestamps; - auto nativeTimingCallback = [&reportLatch, - ×tamps](Dart_NativeArguments args) { - Dart_Handle exception = nullptr; - timestamps = tonic::DartConverter>::FromArguments( - args, 0, exception); - reportLatch.Signal(); - }; - AddNativeCallback("NativeReportTimingsCallback", - CREATE_NATIVE_ENTRY(nativeTimingCallback)); - RunEngine(shell.get(), std::move(configuration)); - - // Pump many frames so we can trigger the report quickly instead of waiting - // for the 1 second threshold. - for (int i = 0; i < 200; i += 1) { - PumpOneFrame(shell.get()); - } - - reportLatch.Wait(); - shell.reset(); - - fml::TimePoint finish = fml::TimePoint::Now(); - ASSERT_TRUE(timestamps.size() > 0); - ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0); - std::vector timings(timestamps.size() / FrameTiming::kCount); - - for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) { - for (auto phase : FrameTiming::kPhases) { - timings[i].Set( - phase, - fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds( - timestamps[i * FrameTiming::kCount + phase]))); - } - } - CheckFrameTimings(timings, start, finish); -} - -TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { - fml::TimePoint start = fml::TimePoint::Now(); - - auto settings = CreateSettingsForFixture(); - fml::AutoResetWaitableEvent timingLatch; - FrameTiming timing; - - for (auto phase : FrameTiming::kPhases) { - timing.Set(phase, fml::TimePoint()); - // Check that the time points are initially smaller than start, so - // CheckFrameTimings will fail if they're not properly set later. - ASSERT_TRUE(timing.Get(phase) < start); - } - - settings.frame_rasterized_callback = [&timing, - &timingLatch](const FrameTiming& t) { - timing = t; - timingLatch.Signal(); - }; - - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("onBeginFrameMain"); - - int64_t begin_frame; - auto nativeOnBeginFrame = [&begin_frame](Dart_NativeArguments args) { - Dart_Handle exception = nullptr; - begin_frame = - tonic::DartConverter::FromArguments(args, 0, exception); - }; - AddNativeCallback("NativeOnBeginFrame", - CREATE_NATIVE_ENTRY(nativeOnBeginFrame)); - - RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get()); - // Check that timing is properly set. This implies that - // settings.frame_rasterized_callback is called. - timingLatch.Wait(); - fml::TimePoint finish = fml::TimePoint::Now(); - std::vector timings = {timing}; - CheckFrameTimings(timings, start, finish); - - // Check that onBeginFrame has the same timestamp as FrameTiming's build start - int64_t build_start = - timing.Get(FrameTiming::kBuildStart).ToEpochDelta().ToMicroseconds(); - ASSERT_EQ(build_start, begin_frame); -} + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), + static_cast(24 * (1 << 20))); -TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { - // Ensure that all phases are in kPhases. - ASSERT_EQ(sizeof(FrameTiming::kPhases), - FrameTiming::kCount * sizeof(FrameTiming::Phase)); - - int lastPhaseIndex = -1; - FrameTiming timing; - for (auto phase : FrameTiming::kPhases) { - ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in order. - lastPhaseIndex = phase; - auto fake_time = - fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds(phase)); - timing.Set(phase, fake_time); - ASSERT_TRUE(timing.Get(phase) == fake_time); - } -} - -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE -TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) { -#else -TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) { -#endif - fml::TimePoint start = fml::TimePoint::Now(); - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - ASSERT_TRUE(configuration.IsValid()); - configuration.SetEntrypoint("reportTimingsMain"); - - // Wait for 2 reports: the first one is the immediate callback of the first - // frame; the second one will exercise the batching logic. - fml::CountDownLatch reportLatch(2); - std::vector timestamps; - auto nativeTimingCallback = [&reportLatch, - ×tamps](Dart_NativeArguments args) { - Dart_Handle exception = nullptr; - timestamps = tonic::DartConverter>::FromArguments( - args, 0, exception); - reportLatch.CountDown(); - }; - AddNativeCallback("NativeReportTimingsCallback", - CREATE_NATIVE_ENTRY(nativeTimingCallback)); - RunEngine(shell.get(), std::move(configuration)); - - PumpOneFrame(shell.get()); + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { + shell->GetPlatformView()->SetViewportMetrics( + {1.0, 400, 200, 0, 0, 0, 0, 0, 0, 0, 0}); + }); PumpOneFrame(shell.get()); - reportLatch.Wait(); - shell.reset(); - - fml::TimePoint finish = fml::TimePoint::Now(); - fml::TimeDelta ellapsed = finish - start; - -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - // Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to - // make it not too flaky. - ASSERT_TRUE(ellapsed >= fml::TimeDelta::FromMilliseconds(800)); -#else - // Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to - // make it not too flaky. - ASSERT_TRUE(ellapsed <= fml::TimeDelta::FromMilliseconds(500)); -#endif -} - -TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - ASSERT_TRUE(configuration.IsValid()); - configuration.SetEntrypoint("reportTimingsMain"); - fml::AutoResetWaitableEvent reportLatch; - std::vector timestamps; - auto nativeTimingCallback = [&reportLatch, - ×tamps](Dart_NativeArguments args) { - Dart_Handle exception = nullptr; - timestamps = tonic::DartConverter>::FromArguments( - args, 0, exception); - reportLatch.Signal(); - }; - AddNativeCallback("NativeReportTimingsCallback", - CREATE_NATIVE_ENTRY(nativeTimingCallback)); - RunEngine(shell.get(), std::move(configuration)); - - for (int i = 0; i < 10; i += 1) { - PumpOneFrame(shell.get()); - } - - reportLatch.Wait(); - shell.reset(); - - // Check for the immediate callback of the first frame that doesn't wait for - // the other 9 frames to be rasterized. - ASSERT_EQ(timestamps.size(), FrameTiming::kCount); -} - -TEST_F(ShellTest, WaitForFirstFrame) { - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("emptyMain"); + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 3840000U); - RunEngine(shell.get(), std::move(configuration)); + std::string request_json = + "{\"method\": \"Skia.setResourceCacheMaxBytes\", \"args\": 10000}"; + std::vector data(request_json.begin(), request_json.end()); + auto platform_message = fml::MakeRefCounted( + "flutter/skia", std::move(data), nullptr); + SendEnginePlatformMessage(shell.get(), std::move(platform_message)); PumpOneFrame(shell.get()); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); - ASSERT_TRUE(result.ok()); -} - -TEST_F(ShellTest, WaitForFirstFrameTimeout) { - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("emptyMain"); - - RunEngine(shell.get(), std::move(configuration)); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); - ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); -} - -TEST_F(ShellTest, WaitForFirstFrameMultiple) { - auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("emptyMain"); + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 10000U); - RunEngine(shell.get(), std::move(configuration)); + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { + shell->GetPlatformView()->SetViewportMetrics( + {1.0, 800, 400, 0, 0, 0, 0, 0, 0, 0, 0}); + }); PumpOneFrame(shell.get()); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); - ASSERT_TRUE(result.ok()); - for (int i = 0; i < 100; ++i) { - result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); - ASSERT_TRUE(result.ok()); - } -} -/// Makes sure that WaitForFirstFrame works if we rendered a frame with the -/// single-thread setup. -TEST_F(ShellTest, WaitForFirstFrameInlined) { - Settings settings = CreateSettingsForFixture(); - auto task_runner = GetThreadTaskRunner(); - TaskRunners task_runners("test", task_runner, task_runner, task_runner, - task_runner); - std::unique_ptr shell = - CreateShell(std::move(settings), std::move(task_runners)); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("emptyMain"); - - RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get()); - fml::AutoResetWaitableEvent event; - task_runner->PostTask([&shell, &event] { - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); - ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); - event.Signal(); - }); - ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 10000U); } } // namespace testing From a831de6d26c2398d1f007f8108e97fc3213173af Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 1 Aug 2019 16:55:20 -0700 Subject: [PATCH 03/12] uncomment tests --- shell/common/shell_unittests.cc | 1166 +++++++++++++++---------------- 1 file changed, 583 insertions(+), 583 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index a97d7fbdc57a5..db36783f7a638 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -26,589 +26,589 @@ namespace flutter { namespace testing { -// static bool ValidateShell(Shell* shell) { -// if (!shell) { -// return false; -// } - -// if (!shell->IsSetup()) { -// return false; -// } - -// ShellTest::PlatformViewNotifyCreated(shell); - -// { -// fml::AutoResetWaitableEvent latch; -// fml::TaskRunner::RunNowOrPostTask( -// shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() { -// shell->GetPlatformView()->NotifyDestroyed(); -// latch.Signal(); -// }); -// latch.Wait(); -// } - -// return true; -// } - -// TEST_F(ShellTest, InitializeWithInvalidThreads) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// Settings settings = CreateSettingsForFixture(); -// TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); -// auto shell = CreateShell(std::move(settings), std::move(task_runners)); -// ASSERT_FALSE(shell); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, InitializeWithDifferentThreads) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// Settings settings = CreateSettingsForFixture(); -// ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", -// ThreadHost::Type::Platform | ThreadHost::Type::GPU | -// ThreadHost::Type::IO | ThreadHost::Type::UI); -// TaskRunners task_runners("test", -// thread_host.platform_thread->GetTaskRunner(), -// thread_host.gpu_thread->GetTaskRunner(), -// thread_host.ui_thread->GetTaskRunner(), -// thread_host.io_thread->GetTaskRunner()); -// auto shell = CreateShell(std::move(settings), std::move(task_runners)); -// ASSERT_TRUE(ValidateShell(shell.get())); -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, InitializeWithSingleThread) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// Settings settings = CreateSettingsForFixture(); -// ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", -// ThreadHost::Type::Platform); -// auto task_runner = thread_host.platform_thread->GetTaskRunner(); -// TaskRunners task_runners("test", task_runner, task_runner, task_runner, -// task_runner); -// auto shell = CreateShell(std::move(settings), std::move(task_runners)); -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// ASSERT_TRUE(ValidateShell(shell.get())); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// Settings settings = CreateSettingsForFixture(); -// fml::MessageLoop::EnsureInitializedForCurrentThread(); -// auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); -// TaskRunners task_runners("test", task_runner, task_runner, task_runner, -// task_runner); -// auto shell = CreateShell(std::move(settings), std::move(task_runners)); -// ASSERT_TRUE(ValidateShell(shell.get())); -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, -// InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// Settings settings = CreateSettingsForFixture(); -// ThreadHost thread_host( -// "io.flutter.test." + GetCurrentTestName() + ".", -// ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); -// fml::MessageLoop::EnsureInitializedForCurrentThread(); -// TaskRunners task_runners("test", -// fml::MessageLoop::GetCurrent().GetTaskRunner(), -// thread_host.gpu_thread->GetTaskRunner(), -// thread_host.ui_thread->GetTaskRunner(), -// thread_host.io_thread->GetTaskRunner()); -// auto shell = Shell::Create( -// std::move(task_runners), settings, -// [](Shell& shell) { -// return std::make_unique(shell, -// shell.GetTaskRunners()); -// }, -// [](Shell& shell) { -// return std::make_unique(shell, shell.GetTaskRunners()); -// }); -// ASSERT_TRUE(ValidateShell(shell.get())); -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// Settings settings = CreateSettingsForFixture(); -// ThreadHost thread_host( -// "io.flutter.test." + GetCurrentTestName() + ".", -// ThreadHost::Type::Platform | ThreadHost::Type::IO | -// ThreadHost::Type::UI); -// TaskRunners task_runners( -// "test", -// thread_host.platform_thread->GetTaskRunner(), // platform -// thread_host.platform_thread->GetTaskRunner(), // gpu -// thread_host.ui_thread->GetTaskRunner(), // ui -// thread_host.io_thread->GetTaskRunner() // io -// ); -// auto shell = CreateShell(std::move(settings), std::move(task_runners)); -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// ASSERT_TRUE(ValidateShell(shell.get())); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, FixturesAreFunctional) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// auto settings = CreateSettingsForFixture(); -// auto shell = CreateShell(settings); -// ASSERT_TRUE(ValidateShell(shell.get())); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// ASSERT_TRUE(configuration.IsValid()); -// configuration.SetEntrypoint("fixturesAreFunctionalMain"); - -// fml::AutoResetWaitableEvent main_latch; -// AddNativeCallback( -// "SayHiFromFixturesAreFunctionalMain", -// CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); -// })); - -// RunEngine(shell.get(), std::move(configuration)); -// main_latch.Wait(); -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) { -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// auto settings = CreateSettingsForFixture(); -// auto shell = CreateShell(settings); -// ASSERT_TRUE(ValidateShell(shell.get())); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// ASSERT_TRUE(configuration.IsValid()); -// configuration.SetEntrypoint("testCanLaunchSecondaryIsolate"); - -// fml::CountDownLatch latch(2); -// AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { -// latch.CountDown(); -// })); - -// RunEngine(shell.get(), std::move(configuration)); - -// latch.Wait(); - -// ASSERT_TRUE(DartVMRef::IsInstanceRunning()); -// shell.reset(); -// ASSERT_FALSE(DartVMRef::IsInstanceRunning()); -// } - -// TEST(ShellTestNoFixture, EnableMirrorsIsWhitelisted) { -// if (DartVM::IsRunningPrecompiledCode()) { -// // This covers profile and release modes which use AOT (where this flag -// does -// // not make sense anyway). -// GTEST_SKIP(); -// return; -// } - -// const std::vector options = { -// fml::CommandLine::Option("dart-flags", "--enable_mirrors")}; -// fml::CommandLine command_line("", options, std::vector()); -// flutter::Settings settings = -// flutter::SettingsFromCommandLine(command_line); -// EXPECT_EQ(settings.dart_flags.size(), 1u); -// } - -// TEST_F(ShellTest, BlacklistedDartVMFlag) { -// // Run this test in a thread-safe manner, otherwise gtest will complain. -// ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - -// const std::vector options = { -// fml::CommandLine::Option("dart-flags", "--verify_after_gc")}; -// fml::CommandLine command_line("", options, std::vector()); - -// #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE -// // Upon encountering a non-whitelisted Dart flag the process terminates. -// const char* expected = -// "Encountered blacklisted Dart VM flag: --verify_after_gc"; -// ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected); -// #else -// flutter::Settings settings = -// flutter::SettingsFromCommandLine(command_line); -// EXPECT_EQ(settings.dart_flags.size(), 0u); -// #endif -// } - -// TEST_F(ShellTest, WhitelistedDartVMFlag) { -// const std::vector options = { -// fml::CommandLine::Option("dart-flags", -// "--max_profile_depth 1,--random_seed 42")}; -// fml::CommandLine command_line("", options, std::vector()); -// flutter::Settings settings = -// flutter::SettingsFromCommandLine(command_line); - -// #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE -// EXPECT_EQ(settings.dart_flags.size(), 2u); -// EXPECT_EQ(settings.dart_flags[0], "--max_profile_depth 1"); -// EXPECT_EQ(settings.dart_flags[1], "--random_seed 42"); -// #else -// EXPECT_EQ(settings.dart_flags.size(), 0u); -// #endif -// } - -// TEST_F(ShellTest, NoNeedToReportTimingsByDefault) { -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("emptyMain"); - -// RunEngine(shell.get(), std::move(configuration)); -// PumpOneFrame(shell.get()); -// ASSERT_FALSE(GetNeedsReportTimings(shell.get())); - -// // This assertion may or may not be the direct result of -// needs_report_timings_ -// // being false. The count could be 0 simply because we just cleared -// unreported -// // timings by reporting them. Hence this can't replace the -// // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added this -// // assertion for an additional confidence that we're not pushing back to -// // unreported timings unnecessarily. -// // -// // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be -// // positive in any tests. Otherwise those tests will be flaky as the -// clearing -// // of unreported timings is unpredictive. -// ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0); -// } - -// TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) { -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("dummyReportTimingsMain"); - -// RunEngine(shell.get(), std::move(configuration)); -// PumpOneFrame(shell.get()); -// ASSERT_TRUE(GetNeedsReportTimings(shell.get())); -// } - -// static void CheckFrameTimings(const std::vector& timings, -// fml::TimePoint start, -// fml::TimePoint finish) { -// fml::TimePoint last_frame_start; -// for (size_t i = 0; i < timings.size(); i += 1) { -// // Ensure that timings are sorted. -// ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start); -// last_frame_start = timings[i].Get(FrameTiming::kPhases[0]); - -// fml::TimePoint last_phase_time; -// for (auto phase : FrameTiming::kPhases) { -// ASSERT_TRUE(timings[i].Get(phase) >= start); -// ASSERT_TRUE(timings[i].Get(phase) <= finish); - -// // phases should have weakly increasing time points -// ASSERT_TRUE(last_phase_time <= timings[i].Get(phase)); -// last_phase_time = timings[i].Get(phase); -// } -// } -// } - -// TEST_F(ShellTest, ReportTimingsIsCalled) { -// fml::TimePoint start = fml::TimePoint::Now(); -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// ASSERT_TRUE(configuration.IsValid()); -// configuration.SetEntrypoint("reportTimingsMain"); -// fml::AutoResetWaitableEvent reportLatch; -// std::vector timestamps; -// auto nativeTimingCallback = [&reportLatch, -// ×tamps](Dart_NativeArguments args) { -// Dart_Handle exception = nullptr; -// timestamps = tonic::DartConverter>::FromArguments( -// args, 0, exception); -// reportLatch.Signal(); -// }; -// AddNativeCallback("NativeReportTimingsCallback", -// CREATE_NATIVE_ENTRY(nativeTimingCallback)); -// RunEngine(shell.get(), std::move(configuration)); - -// // Pump many frames so we can trigger the report quickly instead of waiting -// // for the 1 second threshold. -// for (int i = 0; i < 200; i += 1) { -// PumpOneFrame(shell.get()); -// } - -// reportLatch.Wait(); -// shell.reset(); - -// fml::TimePoint finish = fml::TimePoint::Now(); -// ASSERT_TRUE(timestamps.size() > 0); -// ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0); -// std::vector timings(timestamps.size() / FrameTiming::kCount); - -// for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) { -// for (auto phase : FrameTiming::kPhases) { -// timings[i].Set( -// phase, -// fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds( -// timestamps[i * FrameTiming::kCount + phase]))); -// } -// } -// CheckFrameTimings(timings, start, finish); -// } - -// TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { -// fml::TimePoint start = fml::TimePoint::Now(); - -// auto settings = CreateSettingsForFixture(); -// fml::AutoResetWaitableEvent timingLatch; -// FrameTiming timing; - -// for (auto phase : FrameTiming::kPhases) { -// timing.Set(phase, fml::TimePoint()); -// // Check that the time points are initially smaller than start, so -// // CheckFrameTimings will fail if they're not properly set later. -// ASSERT_TRUE(timing.Get(phase) < start); -// } - -// settings.frame_rasterized_callback = [&timing, -// &timingLatch](const FrameTiming& t) { -// timing = t; -// timingLatch.Signal(); -// }; - -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("onBeginFrameMain"); - -// int64_t begin_frame; -// auto nativeOnBeginFrame = [&begin_frame](Dart_NativeArguments args) { -// Dart_Handle exception = nullptr; -// begin_frame = -// tonic::DartConverter::FromArguments(args, 0, exception); -// }; -// AddNativeCallback("NativeOnBeginFrame", -// CREATE_NATIVE_ENTRY(nativeOnBeginFrame)); - -// RunEngine(shell.get(), std::move(configuration)); - -// PumpOneFrame(shell.get()); - -// // Check that timing is properly set. This implies that -// // settings.frame_rasterized_callback is called. -// timingLatch.Wait(); -// fml::TimePoint finish = fml::TimePoint::Now(); -// std::vector timings = {timing}; -// CheckFrameTimings(timings, start, finish); - -// // Check that onBeginFrame has the same timestamp as FrameTiming's build -// start int64_t build_start = -// timing.Get(FrameTiming::kBuildStart).ToEpochDelta().ToMicroseconds(); -// ASSERT_EQ(build_start, begin_frame); -// } - -// TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { -// // Ensure that all phases are in kPhases. -// ASSERT_EQ(sizeof(FrameTiming::kPhases), -// FrameTiming::kCount * sizeof(FrameTiming::Phase)); - -// int lastPhaseIndex = -1; -// FrameTiming timing; -// for (auto phase : FrameTiming::kPhases) { -// ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in -// order. lastPhaseIndex = phase; auto fake_time = -// fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds(phase)); -// timing.Set(phase, fake_time); -// ASSERT_TRUE(timing.Get(phase) == fake_time); -// } -// } - -// #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE -// TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) { -// #else -// TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) { -// #endif -// fml::TimePoint start = fml::TimePoint::Now(); -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// ASSERT_TRUE(configuration.IsValid()); -// configuration.SetEntrypoint("reportTimingsMain"); - -// // Wait for 2 reports: the first one is the immediate callback of the first -// // frame; the second one will exercise the batching logic. -// fml::CountDownLatch reportLatch(2); -// std::vector timestamps; -// auto nativeTimingCallback = [&reportLatch, -// ×tamps](Dart_NativeArguments args) { -// Dart_Handle exception = nullptr; -// timestamps = tonic::DartConverter>::FromArguments( -// args, 0, exception); -// reportLatch.CountDown(); -// }; -// AddNativeCallback("NativeReportTimingsCallback", -// CREATE_NATIVE_ENTRY(nativeTimingCallback)); -// RunEngine(shell.get(), std::move(configuration)); - -// PumpOneFrame(shell.get()); -// PumpOneFrame(shell.get()); - -// reportLatch.Wait(); -// shell.reset(); - -// fml::TimePoint finish = fml::TimePoint::Now(); -// fml::TimeDelta ellapsed = finish - start; - -// #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE -// // Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to -// // make it not too flaky. -// ASSERT_TRUE(ellapsed >= fml::TimeDelta::FromMilliseconds(800)); -// #else -// // Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to -// // make it not too flaky. -// ASSERT_TRUE(ellapsed <= fml::TimeDelta::FromMilliseconds(500)); -// #endif -// } - -// TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// ASSERT_TRUE(configuration.IsValid()); -// configuration.SetEntrypoint("reportTimingsMain"); -// fml::AutoResetWaitableEvent reportLatch; -// std::vector timestamps; -// auto nativeTimingCallback = [&reportLatch, -// ×tamps](Dart_NativeArguments args) { -// Dart_Handle exception = nullptr; -// timestamps = tonic::DartConverter>::FromArguments( -// args, 0, exception); -// reportLatch.Signal(); -// }; -// AddNativeCallback("NativeReportTimingsCallback", -// CREATE_NATIVE_ENTRY(nativeTimingCallback)); -// RunEngine(shell.get(), std::move(configuration)); - -// for (int i = 0; i < 10; i += 1) { -// PumpOneFrame(shell.get()); -// } - -// reportLatch.Wait(); -// shell.reset(); - -// // Check for the immediate callback of the first frame that doesn't wait -// for -// // the other 9 frames to be rasterized. -// ASSERT_EQ(timestamps.size(), FrameTiming::kCount); -// } - -// TEST_F(ShellTest, WaitForFirstFrame) { -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("emptyMain"); - -// RunEngine(shell.get(), std::move(configuration)); -// PumpOneFrame(shell.get()); -// fml::Status result = -// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); -// ASSERT_TRUE(result.ok()); -// } - -// TEST_F(ShellTest, WaitForFirstFrameTimeout) { -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("emptyMain"); - -// RunEngine(shell.get(), std::move(configuration)); -// fml::Status result = -// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); -// ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); -// } - -// TEST_F(ShellTest, WaitForFirstFrameMultiple) { -// auto settings = CreateSettingsForFixture(); -// std::unique_ptr shell = CreateShell(settings); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("emptyMain"); - -// RunEngine(shell.get(), std::move(configuration)); -// PumpOneFrame(shell.get()); -// fml::Status result = -// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); -// ASSERT_TRUE(result.ok()); -// for (int i = 0; i < 100; ++i) { -// result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); -// ASSERT_TRUE(result.ok()); -// } -// } - -// /// Makes sure that WaitForFirstFrame works if we rendered a frame with the -// /// single-thread setup. -// TEST_F(ShellTest, WaitForFirstFrameInlined) { -// Settings settings = CreateSettingsForFixture(); -// auto task_runner = GetThreadTaskRunner(); -// TaskRunners task_runners("test", task_runner, task_runner, task_runner, -// task_runner); -// std::unique_ptr shell = -// CreateShell(std::move(settings), std::move(task_runners)); - -// // Create the surface needed by rasterizer -// PlatformViewNotifyCreated(shell.get()); - -// auto configuration = RunConfiguration::InferFromSettings(settings); -// configuration.SetEntrypoint("emptyMain"); - -// RunEngine(shell.get(), std::move(configuration)); -// PumpOneFrame(shell.get()); -// fml::AutoResetWaitableEvent event; -// task_runner->PostTask([&shell, &event] { -// fml::Status result = -// shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); -// ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); -// event.Signal(); -// }); -// ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); -// } +static bool ValidateShell(Shell* shell) { + if (!shell) { + return false; + } + + if (!shell->IsSetup()) { + return false; + } + + ShellTest::PlatformViewNotifyCreated(shell); + + { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() { + shell->GetPlatformView()->NotifyDestroyed(); + latch.Signal(); + }); + latch.Wait(); + } + + return true; +} + +TEST_F(ShellTest, InitializeWithInvalidThreads) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); + auto shell = CreateShell(std::move(settings), std::move(task_runners)); + ASSERT_FALSE(shell); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, InitializeWithDifferentThreads) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::Platform | ThreadHost::Type::GPU | + ThreadHost::Type::IO | ThreadHost::Type::UI); + TaskRunners task_runners("test", + thread_host.platform_thread->GetTaskRunner(), + thread_host.gpu_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + auto shell = CreateShell(std::move(settings), std::move(task_runners)); + ASSERT_TRUE(ValidateShell(shell.get())); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, InitializeWithSingleThread) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::Platform); + auto task_runner = thread_host.platform_thread->GetTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = CreateShell(std::move(settings), std::move(task_runners)); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + ASSERT_TRUE(ValidateShell(shell.get())); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = CreateShell(std::move(settings), std::move(task_runners)); + ASSERT_TRUE(ValidateShell(shell.get())); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, + InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + ThreadHost thread_host( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + TaskRunners task_runners("test", + fml::MessageLoop::GetCurrent().GetTaskRunner(), + thread_host.gpu_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, + shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }); + ASSERT_TRUE(ValidateShell(shell.get())); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + ThreadHost thread_host( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::Platform | ThreadHost::Type::IO | + ThreadHost::Type::UI); + TaskRunners task_runners( + "test", + thread_host.platform_thread->GetTaskRunner(), // platform + thread_host.platform_thread->GetTaskRunner(), // gpu + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io + ); + auto shell = CreateShell(std::move(settings), std::move(task_runners)); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + ASSERT_TRUE(ValidateShell(shell.get())); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, FixturesAreFunctional) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + auto settings = CreateSettingsForFixture(); + auto shell = CreateShell(settings); + ASSERT_TRUE(ValidateShell(shell.get())); + + auto configuration = RunConfiguration::InferFromSettings(settings); + ASSERT_TRUE(configuration.IsValid()); + configuration.SetEntrypoint("fixturesAreFunctionalMain"); + + fml::AutoResetWaitableEvent main_latch; + AddNativeCallback( + "SayHiFromFixturesAreFunctionalMain", + CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); + })); + + RunEngine(shell.get(), std::move(configuration)); + main_latch.Wait(); + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + auto settings = CreateSettingsForFixture(); + auto shell = CreateShell(settings); + ASSERT_TRUE(ValidateShell(shell.get())); + + auto configuration = RunConfiguration::InferFromSettings(settings); + ASSERT_TRUE(configuration.IsValid()); + configuration.SetEntrypoint("testCanLaunchSecondaryIsolate"); + + fml::CountDownLatch latch(2); + AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { + latch.CountDown(); + })); + + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + shell.reset(); + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); +} + +TEST(ShellTestNoFixture, EnableMirrorsIsWhitelisted) { + if (DartVM::IsRunningPrecompiledCode()) { + // This covers profile and release modes which use AOT (where this flag + does + // not make sense anyway). + GTEST_SKIP(); + return; + } + + const std::vector options = { + fml::CommandLine::Option("dart-flags", "--enable_mirrors")}; + fml::CommandLine command_line("", options, std::vector()); + flutter::Settings settings = + flutter::SettingsFromCommandLine(command_line); + EXPECT_EQ(settings.dart_flags.size(), 1u); +} + +TEST_F(ShellTest, BlacklistedDartVMFlag) { + // Run this test in a thread-safe manner, otherwise gtest will complain. + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + const std::vector options = { + fml::CommandLine::Option("dart-flags", "--verify_after_gc")}; + fml::CommandLine command_line("", options, std::vector()); + +#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE + // Upon encountering a non-whitelisted Dart flag the process terminates. + const char* expected = + "Encountered blacklisted Dart VM flag: --verify_after_gc"; + ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected); +#else + flutter::Settings settings = + flutter::SettingsFromCommandLine(command_line); + EXPECT_EQ(settings.dart_flags.size(), 0u); +#endif +} + +TEST_F(ShellTest, WhitelistedDartVMFlag) { + const std::vector options = { + fml::CommandLine::Option("dart-flags", + "--max_profile_depth 1,--random_seed 42")}; + fml::CommandLine command_line("", options, std::vector()); + flutter::Settings settings = + flutter::SettingsFromCommandLine(command_line); + +#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE + EXPECT_EQ(settings.dart_flags.size(), 2u); + EXPECT_EQ(settings.dart_flags[0], "--max_profile_depth 1"); + EXPECT_EQ(settings.dart_flags[1], "--random_seed 42"); +#else + EXPECT_EQ(settings.dart_flags.size(), 0u); +#endif +} + +TEST_F(ShellTest, NoNeedToReportTimingsByDefault) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + ASSERT_FALSE(GetNeedsReportTimings(shell.get())); + + // This assertion may or may not be the direct result of + needs_report_timings_ + // being false. The count could be 0 simply because we just cleared + unreported + // timings by reporting them. Hence this can't replace the + // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added this + // assertion for an additional confidence that we're not pushing back to + // unreported timings unnecessarily. + // + // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be + // positive in any tests. Otherwise those tests will be flaky as the + clearing + // of unreported timings is unpredictive. + ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0); +} + +TEST_F(ShellTest, NeedsReportTimingsIsSetWithCallback) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("dummyReportTimingsMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + ASSERT_TRUE(GetNeedsReportTimings(shell.get())); +} + +static void CheckFrameTimings(const std::vector& timings, + fml::TimePoint start, + fml::TimePoint finish) { + fml::TimePoint last_frame_start; + for (size_t i = 0; i < timings.size(); i += 1) { + // Ensure that timings are sorted. + ASSERT_TRUE(timings[i].Get(FrameTiming::kPhases[0]) >= last_frame_start); + last_frame_start = timings[i].Get(FrameTiming::kPhases[0]); + + fml::TimePoint last_phase_time; + for (auto phase : FrameTiming::kPhases) { + ASSERT_TRUE(timings[i].Get(phase) >= start); + ASSERT_TRUE(timings[i].Get(phase) <= finish); + + // phases should have weakly increasing time points + ASSERT_TRUE(last_phase_time <= timings[i].Get(phase)); + last_phase_time = timings[i].Get(phase); + } + } +} + +TEST_F(ShellTest, ReportTimingsIsCalled) { + fml::TimePoint start = fml::TimePoint::Now(); + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + ASSERT_TRUE(configuration.IsValid()); + configuration.SetEntrypoint("reportTimingsMain"); + fml::AutoResetWaitableEvent reportLatch; + std::vector timestamps; + auto nativeTimingCallback = [&reportLatch, + ×tamps](Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + timestamps = tonic::DartConverter>::FromArguments( + args, 0, exception); + reportLatch.Signal(); + }; + AddNativeCallback("NativeReportTimingsCallback", + CREATE_NATIVE_ENTRY(nativeTimingCallback)); + RunEngine(shell.get(), std::move(configuration)); + + // Pump many frames so we can trigger the report quickly instead of waiting + // for the 1 second threshold. + for (int i = 0; i < 200; i += 1) { + PumpOneFrame(shell.get()); + } + + reportLatch.Wait(); + shell.reset(); + + fml::TimePoint finish = fml::TimePoint::Now(); + ASSERT_TRUE(timestamps.size() > 0); + ASSERT_TRUE(timestamps.size() % FrameTiming::kCount == 0); + std::vector timings(timestamps.size() / FrameTiming::kCount); + + for (size_t i = 0; i * FrameTiming::kCount < timestamps.size(); i += 1) { + for (auto phase : FrameTiming::kPhases) { + timings[i].Set( + phase, + fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds( + timestamps[i * FrameTiming::kCount + phase]))); + } + } + CheckFrameTimings(timings, start, finish); +} + +TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { + fml::TimePoint start = fml::TimePoint::Now(); + + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent timingLatch; + FrameTiming timing; + + for (auto phase : FrameTiming::kPhases) { + timing.Set(phase, fml::TimePoint()); + // Check that the time points are initially smaller than start, so + // CheckFrameTimings will fail if they're not properly set later. + ASSERT_TRUE(timing.Get(phase) < start); + } + + settings.frame_rasterized_callback = [&timing, + &timingLatch](const FrameTiming& t) { + timing = t; + timingLatch.Signal(); + }; + + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("onBeginFrameMain"); + + int64_t begin_frame; + auto nativeOnBeginFrame = [&begin_frame](Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + begin_frame = + tonic::DartConverter::FromArguments(args, 0, exception); + }; + AddNativeCallback("NativeOnBeginFrame", + CREATE_NATIVE_ENTRY(nativeOnBeginFrame)); + + RunEngine(shell.get(), std::move(configuration)); + + PumpOneFrame(shell.get()); + + // Check that timing is properly set. This implies that + // settings.frame_rasterized_callback is called. + timingLatch.Wait(); + fml::TimePoint finish = fml::TimePoint::Now(); + std::vector timings = {timing}; + CheckFrameTimings(timings, start, finish); + + // Check that onBeginFrame has the same timestamp as FrameTiming's build + start int64_t build_start = + timing.Get(FrameTiming::kBuildStart).ToEpochDelta().ToMicroseconds(); + ASSERT_EQ(build_start, begin_frame); +} + +TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { + // Ensure that all phases are in kPhases. + ASSERT_EQ(sizeof(FrameTiming::kPhases), + FrameTiming::kCount * sizeof(FrameTiming::Phase)); + + int lastPhaseIndex = -1; + FrameTiming timing; + for (auto phase : FrameTiming::kPhases) { + ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in + order. lastPhaseIndex = phase; auto fake_time = + fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds(phase)); + timing.Set(phase, fake_time); + ASSERT_TRUE(timing.Get(phase) == fake_time); + } +} + +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE +TEST_F(ShellTest, ReportTimingsIsCalledLaterInReleaseMode) { +#else +TEST_F(ShellTest, ReportTimingsIsCalledSoonerInNonReleaseMode) { +#endif + fml::TimePoint start = fml::TimePoint::Now(); + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + ASSERT_TRUE(configuration.IsValid()); + configuration.SetEntrypoint("reportTimingsMain"); + + // Wait for 2 reports: the first one is the immediate callback of the first + // frame; the second one will exercise the batching logic. + fml::CountDownLatch reportLatch(2); + std::vector timestamps; + auto nativeTimingCallback = [&reportLatch, + ×tamps](Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + timestamps = tonic::DartConverter>::FromArguments( + args, 0, exception); + reportLatch.CountDown(); + }; + AddNativeCallback("NativeReportTimingsCallback", + CREATE_NATIVE_ENTRY(nativeTimingCallback)); + RunEngine(shell.get(), std::move(configuration)); + + PumpOneFrame(shell.get()); + PumpOneFrame(shell.get()); + + reportLatch.Wait(); + shell.reset(); + + fml::TimePoint finish = fml::TimePoint::Now(); + fml::TimeDelta ellapsed = finish - start; + +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE + // Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to + // make it not too flaky. + ASSERT_TRUE(ellapsed >= fml::TimeDelta::FromMilliseconds(800)); +#else + // Our batch time is 100ms. Hopefully the 500ms limit is relaxed enough to + // make it not too flaky. + ASSERT_TRUE(ellapsed <= fml::TimeDelta::FromMilliseconds(500)); +#endif +} + +TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + ASSERT_TRUE(configuration.IsValid()); + configuration.SetEntrypoint("reportTimingsMain"); + fml::AutoResetWaitableEvent reportLatch; + std::vector timestamps; + auto nativeTimingCallback = [&reportLatch, + ×tamps](Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + timestamps = tonic::DartConverter>::FromArguments( + args, 0, exception); + reportLatch.Signal(); + }; + AddNativeCallback("NativeReportTimingsCallback", + CREATE_NATIVE_ENTRY(nativeTimingCallback)); + RunEngine(shell.get(), std::move(configuration)); + + for (int i = 0; i < 10; i += 1) { + PumpOneFrame(shell.get()); + } + + reportLatch.Wait(); + shell.reset(); + + // Check for the immediate callback of the first frame that doesn't wait + for + // the other 9 frames to be rasterized. + ASSERT_EQ(timestamps.size(), FrameTiming::kCount); +} + +TEST_F(ShellTest, WaitForFirstFrame) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + fml::Status result = + shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + ASSERT_TRUE(result.ok()); +} + +TEST_F(ShellTest, WaitForFirstFrameTimeout) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + fml::Status result = + shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); + ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); +} + +TEST_F(ShellTest, WaitForFirstFrameMultiple) { + auto settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + fml::Status result = + shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + ASSERT_TRUE(result.ok()); + for (int i = 0; i < 100; ++i) { + result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); + ASSERT_TRUE(result.ok()); + } +} + +/// Makes sure that WaitForFirstFrame works if we rendered a frame with the +/// single-thread setup. +TEST_F(ShellTest, WaitForFirstFrameInlined) { + Settings settings = CreateSettingsForFixture(); + auto task_runner = GetThreadTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + fml::AutoResetWaitableEvent event; + task_runner->PostTask([&shell, &event] { + fml::Status result = + shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); + event.Signal(); + }); + ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); +} TEST_F(ShellTest, SetResourceCacheSize) { Settings settings = CreateSettingsForFixture(); From 612c38fe96192f47f47cf99a015cbb0c58b04b89 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 1 Aug 2019 20:02:17 -0700 Subject: [PATCH 04/12] fix bad format --- shell/common/rasterizer.cc | 8 ++++++ shell/common/rasterizer.h | 1 + shell/common/shell_unittests.cc | 48 +++++++++++++-------------------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 9361014645838..b4e2311d8fe33 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -56,6 +56,9 @@ fml::WeakPtr Rasterizer::GetWeakPtr() const { void Rasterizer::Setup(std::unique_ptr surface) { surface_ = std::move(surface); + if (max_cache_bytes_.has_value()) { + SetResourceCacheMaxBytes(max_cache_bytes_.value(), false); + } compositor_context_->OnGrContextCreated(); } @@ -365,6 +368,11 @@ void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) { return; } + max_cache_bytes_ = max_bytes; + if (!surface_) { + return; + } + GrContext* context = surface_->GetContext(); if (context) { int max_resources; diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index c5df96a98c252..96873514d3ecd 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -405,6 +405,7 @@ class Rasterizer final { std::unique_ptr last_layer_tree_; fml::closure next_frame_callback_; bool user_override_resource_cache_bytes_; + std::optional max_cache_bytes_; fml::WeakPtrFactory weak_factory_; void DoDraw(std::unique_ptr layer_tree); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index db36783f7a638..9e37f45b95b5b 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -65,8 +65,7 @@ TEST_F(ShellTest, InitializeWithDifferentThreads) { ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".", ThreadHost::Type::Platform | ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); - TaskRunners task_runners("test", - thread_host.platform_thread->GetTaskRunner(), + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), thread_host.gpu_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); @@ -139,8 +138,7 @@ TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { Settings settings = CreateSettingsForFixture(); ThreadHost thread_host( "io.flutter.test." + GetCurrentTestName() + ".", - ThreadHost::Type::Platform | ThreadHost::Type::IO | - ThreadHost::Type::UI); + ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI); TaskRunners task_runners( "test", thread_host.platform_thread->GetTaskRunner(), // platform @@ -168,8 +166,7 @@ TEST_F(ShellTest, FixturesAreFunctional) { fml::AutoResetWaitableEvent main_latch; AddNativeCallback( "SayHiFromFixturesAreFunctionalMain", - CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); - })); + CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); })); RunEngine(shell.get(), std::move(configuration)); main_latch.Wait(); @@ -204,8 +201,7 @@ TEST_F(ShellTest, SecondaryIsolateBindingsAreSetupViaShellSettings) { TEST(ShellTestNoFixture, EnableMirrorsIsWhitelisted) { if (DartVM::IsRunningPrecompiledCode()) { - // This covers profile and release modes which use AOT (where this flag - does + // This covers profile and release modes which use AOT (where this flag does // not make sense anyway). GTEST_SKIP(); return; @@ -214,8 +210,7 @@ TEST(ShellTestNoFixture, EnableMirrorsIsWhitelisted) { const std::vector options = { fml::CommandLine::Option("dart-flags", "--enable_mirrors")}; fml::CommandLine command_line("", options, std::vector()); - flutter::Settings settings = - flutter::SettingsFromCommandLine(command_line); + flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); EXPECT_EQ(settings.dart_flags.size(), 1u); } @@ -233,8 +228,7 @@ TEST_F(ShellTest, BlacklistedDartVMFlag) { "Encountered blacklisted Dart VM flag: --verify_after_gc"; ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected); #else - flutter::Settings settings = - flutter::SettingsFromCommandLine(command_line); + flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); EXPECT_EQ(settings.dart_flags.size(), 0u); #endif } @@ -244,8 +238,7 @@ TEST_F(ShellTest, WhitelistedDartVMFlag) { fml::CommandLine::Option("dart-flags", "--max_profile_depth 1,--random_seed 42")}; fml::CommandLine command_line("", options, std::vector()); - flutter::Settings settings = - flutter::SettingsFromCommandLine(command_line); + flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE EXPECT_EQ(settings.dart_flags.size(), 2u); @@ -270,18 +263,15 @@ TEST_F(ShellTest, NoNeedToReportTimingsByDefault) { PumpOneFrame(shell.get()); ASSERT_FALSE(GetNeedsReportTimings(shell.get())); - // This assertion may or may not be the direct result of - needs_report_timings_ + // This assertion may or may not be the direct result of needs_report_timings_ // being false. The count could be 0 simply because we just cleared - unreported - // timings by reporting them. Hence this can't replace the - // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added this - // assertion for an additional confidence that we're not pushing back to - // unreported timings unnecessarily. + // unreported timings by reporting them. Hence this can't replace the + // ASSERT_FALSE(GetNeedsReportTimings(shell.get())) check. We added + // this assertion for an additional confidence that we're not pushing + // back to unreported timings unnecessarily. // // Conversely, do not assert UnreportedTimingsCount(shell.get()) to be - // positive in any tests. Otherwise those tests will be flaky as the - clearing + // positive in any tests. Otherwise those tests will be flaky as the clearing // of unreported timings is unpredictive. ASSERT_EQ(UnreportedTimingsCount(shell.get()), 0); } @@ -419,8 +409,8 @@ TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { std::vector timings = {timing}; CheckFrameTimings(timings, start, finish); - // Check that onBeginFrame has the same timestamp as FrameTiming's build - start int64_t build_start = + // Check that onBeginFrame has the same timestamp as FrameTiming's build start + int64_t build_start = timing.Get(FrameTiming::kBuildStart).ToEpochDelta().ToMicroseconds(); ASSERT_EQ(build_start, begin_frame); } @@ -433,8 +423,9 @@ TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { int lastPhaseIndex = -1; FrameTiming timing; for (auto phase : FrameTiming::kPhases) { - ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in - order. lastPhaseIndex = phase; auto fake_time = + ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in ƒorder. + lastPhaseIndex = phase; + auto fake_time = fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds(phase)); timing.Set(phase, fake_time); ASSERT_TRUE(timing.Get(phase) == fake_time); @@ -522,8 +513,7 @@ TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { reportLatch.Wait(); shell.reset(); - // Check for the immediate callback of the first frame that doesn't wait - for + // Check for the immediate callback of the first frame that doesn't wait for // the other 9 frames to be rasterized. ASSERT_EQ(timestamps.size(), FrameTiming::kCount); } From 9456798b7c07ebd8db525a65a99f750ed641a7c2 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 1 Aug 2019 20:22:16 -0700 Subject: [PATCH 05/12] test --- shell/common/rasterizer.cc | 3 ++- shell/common/shell_unittests.cc | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index b4e2311d8fe33..39602612d87b6 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -57,7 +57,8 @@ fml::WeakPtr Rasterizer::GetWeakPtr() const { void Rasterizer::Setup(std::unique_ptr surface) { surface_ = std::move(surface); if (max_cache_bytes_.has_value()) { - SetResourceCacheMaxBytes(max_cache_bytes_.value(), false); + SetResourceCacheMaxBytes(max_cache_bytes_.value(), + user_override_resource_cache_bytes_); } compositor_context_->OnGrContextCreated(); } diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 9e37f45b95b5b..6ca48c19321ce 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -648,5 +648,34 @@ TEST_F(ShellTest, SetResourceCacheSize) { EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 10000U); } +TEST_F(ShellTest, SetResourceCacheSizeEarly) { + Settings settings = CreateSettingsForFixture(); + auto task_runner = GetThreadTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { + shell->GetPlatformView()->SetViewportMetrics( + {1.0, 400, 200, 0, 0, 0, 0, 0, 0, 0, 0}); + }); + PumpOneFrame(shell.get()); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), + static_cast(3840000U)); +} + } // namespace testing } // namespace flutter From 43038a26291ca9cda4f3f5501c3b90a60d4b2e88 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 1 Aug 2019 20:24:04 -0700 Subject: [PATCH 06/12] fix test --- shell/common/shell_test.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index df33e0b671e1a..414275a3dcb49 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -29,10 +29,9 @@ void ShellTest::SendEnginePlatformMessage( fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch, message = std::move(message)]() { - if (!shell->weak_engine_) { - return; + if (auto engine = shell->weak_engine_) { + engine->HandlePlatformMessage(std::move(message)); } - shell->weak_engine_->HandlePlatformMessage(std::move(message)); latch.Signal(); }); latch.Wait(); From e1307274588082056d397e26c6e40c3d823f3cd5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Aug 2019 13:16:33 -0700 Subject: [PATCH 07/12] optional for get bytes --- shell/common/rasterizer.cc | 7 +++++-- shell/common/rasterizer.h | 7 ++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 39602612d87b6..35b4b29af7e8a 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -382,14 +382,17 @@ void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) { } } -size_t Rasterizer::GetResourceCacheMaxBytes() const { +std::optional Rasterizer::GetResourceCacheMaxBytes() const { + if (!surface_) { + return std::nullopt; + } GrContext* context = surface_->GetContext(); if (context) { size_t max_bytes; context->getResourceCacheLimits(nullptr, &max_bytes); return max_bytes; } - return 0; + return std::nullopt; } Rasterizer::Screenshot::Screenshot() {} diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 96873514d3ecd..565e893589646 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -381,7 +381,8 @@ class Rasterizer final { void SetResourceCacheMaxBytes(size_t max_bytes, bool from_user); //---------------------------------------------------------------------------- - /// @brief The current value of Skia's resource cache size. + /// @brief The current value of Skia's resource cache size, if a surface + /// is present. /// /// @attention This cache setting will be invalidated when the surface is /// torn down via `Rasterizer::Teardown`. This call must be made @@ -393,9 +394,9 @@ class Rasterizer final { /// /// @see `RasterCache` /// - /// @return The size of Skia's resource cache. + /// @return The size of Skia's resource cache, if available. /// - size_t GetResourceCacheMaxBytes() const; + std::optional GetResourceCacheMaxBytes() const; private: Delegate& delegate_; From 910508261bacacc0e6abbefb4c6c7cf97a5488f0 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Aug 2019 13:52:35 -0700 Subject: [PATCH 08/12] doc fix, test fix --- shell/common/rasterizer.h | 4 ---- shell/common/shell_unittests.cc | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 565e893589646..adb0433b4fa9c 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -384,10 +384,6 @@ class Rasterizer final { /// @brief The current value of Skia's resource cache size, if a surface /// is present. /// - /// @attention This cache setting will be invalidated when the surface is - /// torn down via `Rasterizer::Teardown`. This call must be made - /// again with new limits after surface re-acquisition. - /// /// @attention This cache does not describe the entirety of GPU resources /// that may be cached. The `RasterCache` also holds very large /// GPU resources. diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 6ca48c19321ce..f87f8c0fd7c6c 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -423,7 +423,7 @@ TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { int lastPhaseIndex = -1; FrameTiming timing; for (auto phase : FrameTiming::kPhases) { - ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in ƒorder. + ASSERT_TRUE(phase > lastPhaseIndex); // Ensure that kPhases are in order. lastPhaseIndex = phase; auto fake_time = fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMicroseconds(phase)); @@ -629,8 +629,10 @@ TEST_F(ShellTest, SetResourceCacheSize) { EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 3840000U); - std::string request_json = - "{\"method\": \"Skia.setResourceCacheMaxBytes\", \"args\": 10000}"; + std::string request_json = R"json({ + "mesthod": "Skia.setResourceCacheMaxBytes", + "args": 10000 + })json"; std::vector data(request_json.begin(), request_json.end()); auto platform_message = fml::MakeRefCounted( "flutter/skia", std::move(data), nullptr); @@ -656,7 +658,6 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { std::unique_ptr shell = CreateShell(std::move(settings), std::move(task_runners)); - fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { shell->GetPlatformView()->SetViewportMetrics( From f3892af8c4b5bbfad69d3cc231390f1ea214246c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Aug 2019 14:21:19 -0700 Subject: [PATCH 09/12] fix buildfile --- shell/common/BUILD.gn | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 135bf43ae1799..bd4f7cae5ec52 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -164,6 +164,7 @@ if (current_toolchain == host_toolchain) { ":shell_unittests_gpu_configuration", "$flutter_root/common", "$flutter_root/flow", + "$flutter_root/lib/ui:ui", "$flutter_root/shell", "$flutter_root/testing:dart", "$flutter_root/testing:opengl", From cd687f8c800040e3c405eb88e2b31199a43d4c4f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Aug 2019 14:42:47 -0700 Subject: [PATCH 10/12] fix tests --- shell/common/shell_unittests.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index f87f8c0fd7c6c..09b340ba24666 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -617,7 +617,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { RunEngine(shell.get(), std::move(configuration)); PumpOneFrame(shell.get()); - EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0U), static_cast(24 * (1 << 20))); fml::TaskRunner::RunNowOrPostTask( @@ -627,10 +627,11 @@ TEST_F(ShellTest, SetResourceCacheSize) { }); PumpOneFrame(shell.get()); - EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 3840000U); + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0U), + 3840000U); std::string request_json = R"json({ - "mesthod": "Skia.setResourceCacheMaxBytes", + "method": "Skia.setResourceCacheMaxBytes", "args": 10000 })json"; std::vector data(request_json.begin(), request_json.end()); @@ -638,7 +639,8 @@ TEST_F(ShellTest, SetResourceCacheSize) { "flutter/skia", std::move(data), nullptr); SendEnginePlatformMessage(shell.get(), std::move(platform_message)); PumpOneFrame(shell.get()); - EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 10000U); + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0U), + 10000U); fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { @@ -647,7 +649,8 @@ TEST_F(ShellTest, SetResourceCacheSize) { }); PumpOneFrame(shell.get()); - EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), 10000U); + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0U), + 10000U); } TEST_F(ShellTest, SetResourceCacheSizeEarly) { @@ -674,7 +677,7 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { RunEngine(shell.get(), std::move(configuration)); PumpOneFrame(shell.get()); - EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes(), + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0), static_cast(3840000U)); } From b50cab1ba9f4fa4686798007898e89c9383f06d0 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Aug 2019 15:21:46 -0700 Subject: [PATCH 11/12] add empty callback and test --- shell/common/fixtures/shell_test.dart | 18 +++++++++++++ shell/common/shell.cc | 7 +++-- shell/common/shell_unittests.cc | 38 +++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 9dae97c1f1a37..dc7abff12381c 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert' show utf8; import 'dart:isolate'; +import 'dart:typed_data'; import 'dart:ui'; void main() {} @@ -57,3 +59,19 @@ void testCanLaunchSecondaryIsolate() { Isolate.spawn(secondaryIsolateMain, 'Hello from root isolate.'); notifyNative(); } + +@pragma('vm:entry-point') +void testSkiaResourceCacheSendsResponse() { + final PlatformMessageResponseCallback callback = (ByteData data) { + notifyNative(); + }; + const String json = '''{ + "method": "Skia.setResourceCacheMaxBytes", + "args": 10000 + }'''; + window.sendPlatformMessage( + 'flutter/skia', + Uint8List.fromList(utf8.encode(json)).buffer.asByteData(), + callback, + ); +} diff --git a/shell/common/shell.cc b/shell/common/shell.cc index aba283955c040..29f3b2dd286bb 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -949,12 +949,15 @@ void Shell::HandleEngineSkiaMessage(fml::RefPtr message) { return; task_runners_.GetGPUTaskRunner()->PostTask( - [rasterizer = rasterizer_->GetWeakPtr(), - max_bytes = args->value.GetInt()] { + [rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt(), + response = std::move(message->response())] { if (rasterizer) { rasterizer->SetResourceCacheMaxBytes(static_cast(max_bytes), true); } + if (response) { + response->CompleteEmpty(); + } }); } diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 09b340ba24666..6d8545e999eda 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -681,5 +681,43 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { static_cast(3840000U)); } +TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) { + Settings settings = CreateSettingsForFixture(); + auto task_runner = GetThreadTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + std::unique_ptr shell = + CreateShell(std::move(settings), std::move(task_runners)); + + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { + shell->GetPlatformView()->SetViewportMetrics( + {1.0, 400, 200, 0, 0, 0, 0, 0, 0, 0, 0}); + }); + PumpOneFrame(shell.get()); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("testSkiaResourceCacheSendsResponse"); + + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0), + static_cast(3840000U)); + + fml::AutoResetWaitableEvent latch; + AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { + latch.Signal(); + })); + + RunEngine(shell.get(), std::move(configuration)); + PumpOneFrame(shell.get()); + + latch.Wait(); + + EXPECT_EQ(shell->GetRasterizer()->GetResourceCacheMaxBytes().value_or(0), + static_cast(10000U)); +} + } // namespace testing } // namespace flutter From aa93b164b1f3f98d85ffe673d1c30dd5b8449c16 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Aug 2019 15:45:14 -0700 Subject: [PATCH 12/12] include optional --- shell/common/rasterizer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index adb0433b4fa9c..0271a441dfa2e 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -6,6 +6,7 @@ #define SHELL_COMMON_RASTERIZER_H_ #include +#include #include "flutter/common/settings.h" #include "flutter/common/task_runners.h"