diff --git a/DEPS b/DEPS index 9856fa7552539..297af528b902e 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '325d9b628caa8ca043181f92bf6e050b85a7c2f9', + 'dart_revision': '52130c19ca593b185ea9cf72b26b1d02455551ef', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 51552e649bffb..f5805269c9196 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 14693ed982fa820c5dbce3c385be0a60 +Signature: a1bbcd05a2657658be7c5f38e0d366f4 UNUSED LICENSES: diff --git a/flow/compositor_context.cc b/flow/compositor_context.cc index ddf9f1c698893..b300d68f10d0f 100644 --- a/flow/compositor_context.cc +++ b/flow/compositor_context.cc @@ -82,6 +82,9 @@ RasterStatus CompositorContext::ScopedFrame::Raster( if (post_preroll_result == PostPrerollResult::kResubmitFrame) { return RasterStatus::kResubmit; } + if (post_preroll_result == PostPrerollResult::kSkipAndRetryFrame) { + return RasterStatus::kSkipAndRetry; + } // Clearing canvas after preroll reduces one render target switch when preroll // paints some raster cache. if (canvas()) { diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 47992abda6028..afe19271795d3 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -24,12 +24,25 @@ class LayerTree; enum class RasterStatus { // Frame has successfully rasterized. kSuccess, - // Frame needs to be resubmitted for rasterization. This is - // currently only called when thread configuration change occurs. + // Frame is submitted twice. This is only used on Android when + // switching the background surface to FlutterImageView. + // + // On Android, the first frame doesn't make the image available + // to the ImageReader right away. The second frame does. + // + // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 kResubmit, + // Frame is dropped and a new frame with the same layer tree is + // attempted. + // + // This is currently used to wait for the thread merger to merge + // the raster and platform threads. + // + // Since the thread merger may be disabled, + kSkipAndRetry, // Frame has been successfully rasterized, but "there are additional items in // the pipeline waiting to be consumed. This is currently - // only called when thread configuration change occurs. + // only used when thread configuration change occurs. kEnqueuePipeline, // Failed to rasterize the frame. kFailed diff --git a/flow/embedded_views.h b/flow/embedded_views.h index cbfde228786a3..c2e116010dbea 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -242,7 +242,17 @@ class EmbeddedViewParams { SkRect final_bounding_rect_; }; -enum class PostPrerollResult { kResubmitFrame, kSuccess }; +enum class PostPrerollResult { + // Frame has successfully rasterized. + kSuccess, + // Frame is submitted twice. This is currently only used when + // thread configuration change occurs. + kResubmitFrame, + // Frame is dropped and a new frame with the same layer tree is + // attempted. This is currently only used when thread configuration + // change occurs. + kSkipAndRetryFrame +}; // Facilitates embedding of platform views within the flow layer tree. // diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index a402197cedc1f..c3f8001ee24e4 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -172,7 +172,10 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { PipelineConsumeResult consume_result = pipeline->Consume(consumer); // if the raster status is to resubmit the frame, we push the frame to the // front of the queue and also change the consume status to more available. - if (raster_status == RasterStatus::kResubmit) { + + auto should_resubmit_frame = raster_status == RasterStatus::kResubmit || + raster_status == RasterStatus::kSkipAndRetry; + if (should_resubmit_frame) { auto front_continuation = pipeline->ProduceIfEmpty(); bool result = front_continuation.Complete(std::move(resubmitted_layer_tree_)); @@ -186,7 +189,6 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { // Merging the thread as we know the next `Draw` should be run on the platform // thread. if (surface_ != nullptr && surface_->GetExternalViewEmbedder() != nullptr) { - auto should_resubmit_frame = raster_status == RasterStatus::kResubmit; surface_->GetExternalViewEmbedder()->EndFrame(should_resubmit_frame, raster_thread_merger_); } @@ -332,7 +334,8 @@ RasterStatus Rasterizer::DoDraw( RasterStatus raster_status = DrawToSurface(*layer_tree); if (raster_status == RasterStatus::kSuccess) { last_layer_tree_ = std::move(layer_tree); - } else if (raster_status == RasterStatus::kResubmit) { + } else if (raster_status == RasterStatus::kResubmit || + raster_status == RasterStatus::kSkipAndRetry) { resubmitted_layer_tree_ = std::move(layer_tree); return raster_status; } @@ -457,7 +460,8 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (compositor_frame) { RasterStatus raster_status = compositor_frame->Raster(layer_tree, false); - if (raster_status == RasterStatus::kFailed) { + if (raster_status == RasterStatus::kFailed || + raster_status == RasterStatus::kSkipAndRetry) { return raster_status; } if (external_view_embedder != nullptr) { diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc index 07557768a46b7..a3dc8daada298 100644 --- a/shell/common/shell_test_external_view_embedder.cc +++ b/shell/common/shell_test_external_view_embedder.cc @@ -8,17 +8,16 @@ ShellTestExternalViewEmbedder::ShellTestExternalViewEmbedder( bool support_thread_merging) : end_frame_call_back_(end_frame_call_back), post_preroll_result_(post_preroll_result), - support_thread_merging_(support_thread_merging) { - resubmit_once_ = false; -} + support_thread_merging_(support_thread_merging), + submitted_frame_count_(0) {} void ShellTestExternalViewEmbedder::UpdatePostPrerollResult( PostPrerollResult post_preroll_result) { post_preroll_result_ = post_preroll_result; } -void ShellTestExternalViewEmbedder::SetResubmitOnce() { - resubmit_once_ = true; +int ShellTestExternalViewEmbedder::GetSubmittedFrameCount() { + return submitted_frame_count_; } // |ExternalViewEmbedder| @@ -40,10 +39,6 @@ void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView( PostPrerollResult ShellTestExternalViewEmbedder::PostPrerollAction( fml::RefPtr raster_thread_merger) { FML_DCHECK(raster_thread_merger); - if (resubmit_once_) { - resubmit_once_ = false; - return PostPrerollResult::kResubmitFrame; - } return post_preroll_result_; } @@ -62,6 +57,7 @@ void ShellTestExternalViewEmbedder::SubmitFrame( GrDirectContext* context, std::unique_ptr frame) { frame->Submit(); + submitted_frame_count_++; } // |ExternalViewEmbedder| diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h index 6d90879c74e17..7220b175a8a84 100644 --- a/shell/common/shell_test_external_view_embedder.h +++ b/shell/common/shell_test_external_view_embedder.h @@ -28,9 +28,9 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { // returns the new `post_preroll_result`. void UpdatePostPrerollResult(PostPrerollResult post_preroll_result); - // Updates the post preroll result to `PostPrerollResult::kResubmitFrame` for - // only the next frame. - void SetResubmitOnce(); + // Gets the number of times the SubmitFrame method has been called in + // the external view embedder. + int GetSubmittedFrameCount(); private: // |ExternalViewEmbedder| @@ -74,11 +74,13 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { bool SupportsDynamicThreadMerging() override; const EndFrameCallBack end_frame_call_back_; + PostPrerollResult post_preroll_result_; - bool resubmit_once_; bool support_thread_merging_; + std::atomic submitted_frame_count_; + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestExternalViewEmbedder); }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 29118836faf24..c348b0e071bcf 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -617,8 +617,7 @@ TEST_F(ShellTest, }; auto external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSuccess, true); - // Set resubmit once to trigger thread merging. - external_view_embedder->SetResubmitOnce(); + auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), false, external_view_embedder); @@ -674,18 +673,25 @@ TEST_F(ShellTest, const size_t ThreadMergingLease = 10; auto settings = CreateSettingsForFixture(); fml::AutoResetWaitableEvent end_frame_latch; + std::shared_ptr external_view_embedder; + auto end_frame_callback = [&](bool should_resubmit_frame, fml::RefPtr raster_thread_merger) { if (should_resubmit_frame && !raster_thread_merger->IsMerged()) { raster_thread_merger->MergeWithLease(ThreadMergingLease); + + ASSERT_TRUE(raster_thread_merger->IsMerged()); + external_view_embedder->UpdatePostPrerollResult( + PostPrerollResult::kSuccess); } end_frame_latch.Signal(); }; - auto external_view_embedder = std::make_shared( + external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSuccess, true); // Set resubmit once to trigger thread merging. - external_view_embedder->SetResubmitOnce(); + external_view_embedder->UpdatePostPrerollResult( + PostPrerollResult::kResubmitFrame); auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), false, external_view_embedder); @@ -796,7 +802,8 @@ TEST_F(ShellTest, // Pump a frame with `PostPrerollResult::kResubmitFrame` to start merging // threads - external_view_embedder->SetResubmitOnce(); + external_view_embedder->UpdatePostPrerollResult( + PostPrerollResult::kResubmitFrame); PumpOneFrame(shell.get(), 100, 100, builder); // Now destroy the platform view immediately. @@ -988,6 +995,96 @@ TEST_F(ShellTest, DestroyShell(std::move(shell), std::move(task_runners)); } +// TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia. +TEST_F(ShellTest, +#if defined(OS_FUCHSIA) + DISABLED_SkipAndSubmitFrame +#else + SkipAndSubmitFrame +#endif +) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent end_frame_latch; + std::shared_ptr external_view_embedder; + + auto end_frame_callback = + [&](bool should_resubmit_frame, + fml::RefPtr raster_thread_merger) { + external_view_embedder->UpdatePostPrerollResult( + PostPrerollResult::kSuccess); + end_frame_latch.Signal(); + }; + external_view_embedder = std::make_shared( + end_frame_callback, PostPrerollResult::kSkipAndRetryFrame, true); + + auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), + false, external_view_embedder); + + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + RunEngine(shell.get(), std::move(configuration)); + + ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount()); + + PumpOneFrame(shell.get()); + + // `EndFrame` changed the post preroll result to `kSuccess`. + end_frame_latch.Wait(); + ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount()); + + PumpOneFrame(shell.get()); + end_frame_latch.Wait(); + ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount()); + + DestroyShell(std::move(shell)); +} + +// TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia. +TEST_F(ShellTest, +#if defined(OS_FUCHSIA) + DISABLED_ResubmitFrame +#else + ResubmitFrame +#endif +) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent end_frame_latch; + std::shared_ptr external_view_embedder; + + auto end_frame_callback = + [&](bool should_resubmit_frame, + fml::RefPtr raster_thread_merger) { + external_view_embedder->UpdatePostPrerollResult( + PostPrerollResult::kSuccess); + end_frame_latch.Signal(); + }; + external_view_embedder = std::make_shared( + end_frame_callback, PostPrerollResult::kResubmitFrame, true); + + auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), + false, external_view_embedder); + + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + RunEngine(shell.get(), std::move(configuration)); + + ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount()); + + PumpOneFrame(shell.get()); + // `EndFrame` changed the post preroll result to `kSuccess`. + end_frame_latch.Wait(); + ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount()); + + end_frame_latch.Wait(); + ASSERT_EQ(2, external_view_embedder->GetSubmittedFrameCount()); + + DestroyShell(std::move(shell)); +} + TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { // Ensure that all phases are in kPhases. ASSERT_EQ(sizeof(FrameTiming::kPhases), diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index a78ad935044f1..5ccd649abab69 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -77,10 +77,6 @@ void AndroidExternalViewEmbedder::SubmitFrame( std::unique_ptr frame) { TRACE_EVENT0("flutter", "AndroidExternalViewEmbedder::SubmitFrame"); - if (should_run_rasterizer_on_platform_thread_) { - // Don't submit the current frame if the frame will be resubmitted. - return; - } if (!FrameHasPlatformLayers()) { frame->Submit(); return; @@ -217,27 +213,25 @@ AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, // |ExternalViewEmbedder| PostPrerollResult AndroidExternalViewEmbedder::PostPrerollAction( fml::RefPtr raster_thread_merger) { - // This frame may remove existing platform views that aren't contained - // in `composition_order_`. - // - // If this frame doesn't have platform views, it's still required to keep - // the rasterizer running on the platform thread for at least one more - // frame. - // - // To keep the rasterizer running on the platform thread one more frame, - // `kDefaultMergedLeaseDuration` must be at least `1`. if (!FrameHasPlatformLayers()) { return PostPrerollResult::kSuccess; } - if (raster_thread_merger->IsMerged()) { - raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); - } else { - // Merge the raster and platform threads in `EndFrame`. - should_run_rasterizer_on_platform_thread_ = true; + if (!raster_thread_merger->IsMerged()) { + // The raster thread merger may be disabled if the rasterizer is being + // created or teared down. + // + // In such cases, the current frame is dropped, and a new frame is attempted + // with the same layer tree. + // + // Eventually, the frame is submitted once this method returns `kSuccess`. + // At that point, the raster tasks are handled on the platform thread. + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); CancelFrame(); - return PostPrerollResult::kResubmitFrame; + return PostPrerollResult::kSkipAndRetryFrame; } + raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); // Surface switch requires to resubmit the frame. + // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 if (previous_frame_view_count_ == 0) { return PostPrerollResult::kResubmitFrame; } @@ -291,17 +285,6 @@ void AndroidExternalViewEmbedder::CancelFrame() { void AndroidExternalViewEmbedder::EndFrame( bool should_resubmit_frame, fml::RefPtr raster_thread_merger) { - if (should_resubmit_frame && should_run_rasterizer_on_platform_thread_) { - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); - if (raster_thread_merger->IsMerged()) { - // The raster thread merger may be disabled if the rasterizer is being - // teared down. - // - // Therefore, set `should_run_rasterizer_on_platform_thread_` to `false` - // only if the thread merger was able to set the lease. - should_run_rasterizer_on_platform_thread_ = false; - } - } surface_pool_->RecycleLayers(); // JNI method must be called on the platform thread. if (raster_thread_merger->IsOnPlatformThread()) { diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index 349142d097bc9..adb5137e2edb1 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -98,10 +98,6 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // Holds surfaces. Allows to recycle surfaces or allocate new ones. const std::unique_ptr surface_pool_; - // Whether the rasterizer task runner should run on the platform thread. - // When this is true, the current frame is cancelled and resubmitted. - bool should_run_rasterizer_on_platform_thread_ = false; - // The size of the root canvas. SkISize frame_size_; diff --git a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc index e84801dca0a5a..7099124ba783d 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc @@ -160,7 +160,7 @@ TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) { 0, std::make_unique()); auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger); - ASSERT_EQ(PostPrerollResult::kResubmitFrame, postpreroll_result); + ASSERT_EQ(PostPrerollResult::kSkipAndRetryFrame, postpreroll_result); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger); @@ -584,7 +584,7 @@ TEST(AndroidExternalViewEmbedder, DisableThreadMerger) { 0, std::make_unique()); auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger); - ASSERT_EQ(PostPrerollResult::kResubmitFrame, postpreroll_result); + ASSERT_EQ(PostPrerollResult::kSkipAndRetryFrame, postpreroll_result); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()).Times(0); embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java b/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java index 7482c1f398e64..85f3ce4c2737c 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterImageView.java @@ -195,7 +195,11 @@ public boolean acquireLatestImage() { // While the engine will also stop producing frames, there is a race condition. // // To avoid exceptions, check if a new image can be acquired. - if (imageQueue.size() < imageReader.getMaxImages()) { + int imageOpenedCount = imageQueue.size(); + if (currentImage != null) { + imageOpenedCount++; + } + if (imageOpenedCount < imageReader.getMaxImages()) { final Image image = imageReader.acquireLatestImage(); if (image != null) { imageQueue.add(image); diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index ccafbc1c16039..204799d361867 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -590,11 +590,13 @@ public void flutterImageView_acquireLatestImageReturnsFalse() { } @Test + @SuppressLint("WrongCall") /*View#onDraw*/ public void flutterImageView_acquiresMaxImagesAtMost() { final ImageReader mockReader = mock(ImageReader.class); - when(mockReader.getMaxImages()).thenReturn(2); + when(mockReader.getMaxImages()).thenReturn(3); final Image mockImage = mock(Image.class); + when(mockImage.getPlanes()).thenReturn(new Plane[0]); when(mockReader.acquireLatestImage()).thenReturn(mockImage); final FlutterImageView imageView = @@ -606,13 +608,31 @@ public void flutterImageView_acquiresMaxImagesAtMost() { final FlutterJNI jni = mock(FlutterJNI.class); imageView.attachToRenderer(new FlutterRenderer(jni)); - doNothing().when(imageView).invalidate(); - assertTrue(imageView.acquireLatestImage()); - assertTrue(imageView.acquireLatestImage()); - assertTrue(imageView.acquireLatestImage()); - verify(mockReader, times(2)).acquireLatestImage(); + assertTrue(imageView.acquireLatestImage()); // 1 image + assertTrue(imageView.acquireLatestImage()); // 2 images + assertTrue(imageView.acquireLatestImage()); // 3 images + assertTrue(imageView.acquireLatestImage()); // 3 images + verify(mockReader, times(3)).acquireLatestImage(); + + imageView.onDraw(mock(Canvas.class)); // 3 images + assertTrue(imageView.acquireLatestImage()); // 3 images + verify(mockReader, times(3)).acquireLatestImage(); + + imageView.onDraw(mock(Canvas.class)); // 2 images + assertTrue(imageView.acquireLatestImage()); // 3 images + verify(mockReader, times(4)).acquireLatestImage(); + + imageView.onDraw(mock(Canvas.class)); // 2 images + imageView.onDraw(mock(Canvas.class)); // 1 image + imageView.onDraw(mock(Canvas.class)); // 1 image + + assertTrue(imageView.acquireLatestImage()); // 2 images + assertTrue(imageView.acquireLatestImage()); // 3 images + assertTrue(imageView.acquireLatestImage()); // 3 images + assertTrue(imageView.acquireLatestImage()); // 3 images + verify(mockReader, times(6)).acquireLatestImage(); } @Test diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 8826c7c74edb1..2056880aea384 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -247,7 +247,8 @@ } void FlutterPlatformViewsController::CancelFrame() { - composition_order_ = active_composition_order_; + picture_recorders_.clear(); + composition_order_.clear(); } // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 @@ -263,17 +264,23 @@ fml::RefPtr raster_thread_merger) { // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 // Rename `has_platform_view` to `view_mutated` when the above issue is resolved. - const bool has_platform_view = HasPlatformViewThisOrNextFrame(); - if (has_platform_view) { - if (raster_thread_merger->IsMerged()) { - raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); - } else { - // Wait until |EndFrame| to merge the threads. - merge_threads_ = true; - CancelFrame(); - return PostPrerollResult::kResubmitFrame; - } + if (!HasPlatformViewThisOrNextFrame()) { + return PostPrerollResult::kSuccess; } + if (!raster_thread_merger->IsMerged()) { + // The raster thread merger may be disabled if the rasterizer is being + // created or teared down. + // + // In such cases, the current frame is dropped, and a new frame is attempted + // with the same layer tree. + // + // Eventually, the frame is submitted once this method returns `kSuccess`. + // At that point, the raster tasks are handled on the platform thread. + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + CancelFrame(); + return PostPrerollResult::kSkipAndRetryFrame; + } + raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); return PostPrerollResult::kSuccess; } @@ -449,15 +456,6 @@ std::shared_ptr ios_context, std::unique_ptr frame) { FML_DCHECK(flutter_view_); - if (merge_threads_) { - // Threads are about to be merged, we drop everything from this frame - // and possibly resubmit the same layer tree in the next frame. - // Before merging thread, we know the code is not running on the main thread. Assert that - FML_DCHECK(![[NSThread currentThread] isMainThread]); - picture_recorders_.clear(); - composition_order_.clear(); - return true; - } // Any UIKit related code has to run on main thread. // When on a non-main thread, we only allow the rest of the method to run if there is no @@ -583,12 +581,7 @@ void FlutterPlatformViewsController::EndFrame( bool should_resubmit_frame, - fml::RefPtr raster_thread_merger) { - if (should_resubmit_frame && merge_threads_) { - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); - merge_threads_ = false; - } -} + fml::RefPtr raster_thread_merger) {} std::shared_ptr FlutterPlatformViewsController::GetLayer( GrDirectContext* gr_context, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 92d043a7357e8..f8c01083f4634 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -279,10 +279,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - // Default to `false`. - // If `true`, gpu thread and platform thread should be merged during |EndFrame|. - // Always resets to `false` right after the threads are merged. - bool merge_threads_ = false; // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from // the picture on the layer's canvas. std::shared_ptr GetLayer(GrDirectContext* gr_context, diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index 65fccb052e78b..bcdb233ce33ef 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -101,7 +101,12 @@ fml::RefPtr raster_thread_merger) { TRACE_EVENT0("flutter", "IOSSurface::PostPrerollAction"); FML_CHECK(platform_views_controller_ != nullptr); - return platform_views_controller_->PostPrerollAction(raster_thread_merger); + PostPrerollResult result = platform_views_controller_->PostPrerollAction(raster_thread_merger); + if (result == PostPrerollResult::kSkipAndRetryFrame) { + // Commit the current transaction if the frame is dropped. + [CATransaction commit]; + } + return result; } // |ExternalViewEmbedder|