diff --git a/shell/common/shell.cc b/shell/common/shell.cc index e919c1b86a2f2..a2f71f6d65788 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1762,10 +1762,16 @@ fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) { "because it is responsible for generating the frame."); } + // Check for overflow. + auto now = std::chrono::steady_clock::now(); + auto max_duration = std::chrono::steady_clock::time_point::max() - now; + auto desired_duration = std::chrono::milliseconds(timeout.ToMilliseconds()); + auto duration = + now + (desired_duration > max_duration ? max_duration : desired_duration); + std::unique_lock lock(waiting_for_first_frame_mutex_); - bool success = waiting_for_first_frame_condition_.wait_for( - lock, std::chrono::milliseconds(timeout.ToMilliseconds()), - [&waiting_for_first_frame = waiting_for_first_frame_] { + bool success = waiting_for_first_frame_condition_.wait_until( + lock, duration, [&waiting_for_first_frame = waiting_for_first_frame_] { return !waiting_for_first_frame.load(); }); if (success) { diff --git a/shell/common/shell.h b/shell/common/shell.h index b830f61c36b16..de24a1915f090 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -298,11 +298,16 @@ class Shell final : public PlatformView::Delegate, bool base64_encode); //---------------------------------------------------------------------------- - /// @brief Pauses the calling thread until the first frame is presented. + /// @brief Pauses the calling thread until the first frame is presented. /// - /// @return 'kOk' when the first frame has been presented before the timeout - /// successfully, 'kFailedPrecondition' if called from the GPU or UI - /// thread, 'kDeadlineExceeded' if there is a timeout. + /// @param[in] timeout The duration to wait before timing out. If this + /// duration would cause an overflow when added to + /// std::chrono::steady_clock::now(), this method will + /// wait indefinitely for the first frame. + /// + /// @return 'kOk' when the first frame has been presented before the + /// timeout successfully, 'kFailedPrecondition' if called from the + /// GPU or UI thread, 'kDeadlineExceeded' if there is a timeout. /// fml::Status WaitForFirstFrame(fml::TimeDelta timeout); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 5a2108544671b..8a1cd95195604 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -672,7 +672,7 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) { SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -727,7 +727,7 @@ TEST_F(ShellTest, SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -779,7 +779,7 @@ TEST_F(ShellTest, SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -788,9 +788,9 @@ TEST_F(ShellTest, PumpOneFrame(shell.get(), 100, 100, builder); - auto result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); - ASSERT_TRUE(result.ok()); + auto result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); + ASSERT_TRUE(result.ok()) << "Result: " << static_cast(result.code()) + << ": " << result.message(); ASSERT_TRUE(raster_thread_merger->IsEnabled()); @@ -800,7 +800,6 @@ TEST_F(ShellTest, // Validate the platform view can be recreated and destroyed again ValidateShell(shell.get()); ASSERT_TRUE(raster_thread_merger->IsEnabled()); - DestroyShell(std::move(shell)); } @@ -853,7 +852,7 @@ TEST_F(ShellTest, SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -928,7 +927,7 @@ TEST_F(ShellTest, SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -1001,7 +1000,7 @@ TEST_F(ShellTest, SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -1049,7 +1048,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) { SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -1120,7 +1119,7 @@ TEST_F(ShellTest, SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -1263,57 +1262,6 @@ TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { } } -#if FLUTTER_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(); - DestroyShell(std::move(shell)); - - fml::TimePoint finish = fml::TimePoint::Now(); - fml::TimeDelta elapsed = finish - start; - -#if FLUTTER_RELEASE - // Our batch time is 1000ms. Hopefully the 800ms limit is relaxed enough to - // make it not too flaky. - ASSERT_TRUE(elapsed >= 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(elapsed <= fml::TimeDelta::FromMilliseconds(500)); -#endif -} - TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { auto settings = CreateSettingsForFixture(); std::unique_ptr shell = CreateShell(settings); @@ -1391,8 +1339,7 @@ TEST_F(ShellTest, WaitForFirstFrame) { RunEngine(shell.get(), std::move(configuration)); PumpOneFrame(shell.get()); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); ASSERT_TRUE(result.ok()); DestroyShell(std::move(shell)); @@ -1410,8 +1357,7 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { RunEngine(shell.get(), std::move(configuration)); PumpOneFrame(shell.get(), {1.0, 0.0, 0.0}); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); @@ -1429,8 +1375,7 @@ TEST_F(ShellTest, WaitForFirstFrameTimeout) { configuration.SetEntrypoint("emptyMain"); RunEngine(shell.get(), std::move(configuration)); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(10)); + fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); @@ -1449,11 +1394,10 @@ TEST_F(ShellTest, WaitForFirstFrameMultiple) { RunEngine(shell.get(), std::move(configuration)); PumpOneFrame(shell.get()); - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); ASSERT_TRUE(result.ok()); for (int i = 0; i < 100; ++i) { - result = shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1)); + result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); ASSERT_TRUE(result.ok()); } @@ -1480,13 +1424,12 @@ TEST_F(ShellTest, WaitForFirstFrameInlined) { PumpOneFrame(shell.get()); fml::AutoResetWaitableEvent event; task_runner->PostTask([&shell, &event] { - fml::Status result = - shell->WaitForFirstFrame(fml::TimeDelta::FromMilliseconds(1000)); + fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Max()); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.code(), fml::StatusCode::kFailedPrecondition); event.Signal(); }); - ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(1000))); + ASSERT_FALSE(event.WaitWithTimeout(fml::TimeDelta::Max())); DestroyShell(std::move(shell), std::move(task_runners)); } @@ -1815,7 +1758,7 @@ TEST_F(ShellTest, Screenshot) { SkPaint(SkColor4f::FromColor(SK_ColorRED))); auto sk_picture = recorder.finishRecordingAsPicture(); fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + this->GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(10, 10), flutter::SkiaGPUObject({sk_picture, queue}), false, false); @@ -2105,7 +2048,7 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { // 1. Construct a picture and a picture layer to be raster cached. sk_sp picture = MakeSizedPicture(10, 10); fml::RefPtr queue = fml::MakeRefCounted( - GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + GetCurrentTaskRunner(), fml::TimeDelta::Zero()); auto picture_layer = std::make_shared( SkPoint::Make(0, 0), flutter::SkiaGPUObject({MakeSizedPicture(100, 100), queue}),