Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 016fbde

Browse files
author
Chris Yang
authored
Do not involve external_view_embedder in submit frame process if threads are not merged. (#22275)
1 parent b0a2ed6 commit 016fbde

File tree

5 files changed

+260
-38
lines changed

5 files changed

+260
-38
lines changed

shell/common/rasterizer.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,7 @@ void Rasterizer::Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline,
190190
consume_result = PipelineConsumeResult::MoreAvailable;
191191
}
192192

193-
// Merging the thread as we know the next `Draw` should be run on the platform
194-
// thread.
193+
// EndFrame should perform cleanups for the external_view_embedder.
195194
if (external_view_embedder_) {
196195
external_view_embedder_->EndFrame(should_resubmit_frame,
197196
raster_thread_merger_);
@@ -467,7 +466,8 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
467466
raster_status == RasterStatus::kSkipAndRetry) {
468467
return raster_status;
469468
}
470-
if (external_view_embedder_) {
469+
if (external_view_embedder_ != nullptr &&
470+
(!raster_thread_merger_ || raster_thread_merger_->IsMerged())) {
471471
FML_DCHECK(!frame->IsSubmitted());
472472
external_view_embedder_->SubmitFrame(surface_->GetContext(),
473473
std::move(frame));

shell/common/rasterizer_unittests.cc

Lines changed: 135 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#define FML_USED_ON_EMBEDDER
6+
57
#include "flutter/shell/common/rasterizer.h"
68

79
#include "flutter/shell/common/thread_host.h"
810
#include "flutter/testing/testing.h"
911
#include "gmock/gmock.h"
1012

1113
using testing::_;
14+
using testing::ByMove;
1215
using testing::Return;
1316
using testing::ReturnRef;
1417

@@ -92,7 +95,8 @@ TEST(RasterizerTest, drawEmptyPipeline) {
9295
latch.Wait();
9396
}
9497

95-
TEST(RasterizerTest, drawWithExternalViewEmbedder) {
98+
TEST(RasterizerTest,
99+
drawWithExternalViewEmbedder_ExternalViewEmbedderSubmitFrameCalled) {
96100
std::string test_name =
97101
::testing::UnitTest::GetInstance()->current_test_info()->name();
98102
ThreadHost thread_host("io.flutter.test." + test_name + ".",
@@ -111,11 +115,81 @@ TEST(RasterizerTest, drawWithExternalViewEmbedder) {
111115
std::shared_ptr<MockExternalViewEmbedder> external_view_embedder =
112116
std::make_shared<MockExternalViewEmbedder>();
113117
rasterizer->SetExternalViewEmbedder(external_view_embedder);
118+
119+
auto surface_frame = std::make_unique<SurfaceFrame>(
120+
/*surface=*/nullptr, /*supports_readback=*/true,
121+
/*submit_callback=*/[](const SurfaceFrame&, SkCanvas*) { return true; });
122+
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
123+
.WillOnce(Return(ByMove(std::move(surface_frame))));
124+
114125
EXPECT_CALL(*external_view_embedder,
115-
BeginFrame(SkISize(), nullptr, 2.0,
116-
fml::RefPtr<fml::RasterThreadMerger>(nullptr)));
126+
BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr,
127+
/*device_pixel_ratio=*/2.0,
128+
/*raster_thread_merger=*/
129+
fml::RefPtr<fml::RasterThreadMerger>(nullptr)))
130+
.Times(1);
131+
EXPECT_CALL(*external_view_embedder, SubmitFrame).Times(1);
132+
EXPECT_CALL(
133+
*external_view_embedder,
134+
EndFrame(/*should_resubmit_frame=*/false,
135+
/*raster_thread_merger=*/fml::RefPtr<fml::RasterThreadMerger>(
136+
nullptr)))
137+
.Times(1);
138+
139+
rasterizer->Setup(std::move(surface));
140+
fml::AutoResetWaitableEvent latch;
141+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
142+
auto pipeline = fml::AdoptRef(new Pipeline<LayerTree>(/*depth=*/10));
143+
auto layer_tree = std::make_unique<LayerTree>(/*frame_size=*/SkISize(),
144+
/*device_pixel_ratio=*/2.0f);
145+
bool result = pipeline->Produce().Complete(std::move(layer_tree));
146+
EXPECT_TRUE(result);
147+
auto no_discard = [](LayerTree&) { return false; };
148+
rasterizer->Draw(pipeline, no_discard);
149+
latch.Signal();
150+
});
151+
latch.Wait();
152+
}
153+
154+
TEST(
155+
RasterizerTest,
156+
drawWithExternalViewEmbedderAndThreadMergerNotMerged_ExternalViewEmbedderSubmitFrameNotCalled) {
157+
std::string test_name =
158+
::testing::UnitTest::GetInstance()->current_test_info()->name();
159+
ThreadHost thread_host("io.flutter.test." + test_name + ".",
160+
ThreadHost::Type::Platform | ThreadHost::Type::GPU |
161+
ThreadHost::Type::IO | ThreadHost::Type::UI);
162+
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
163+
thread_host.raster_thread->GetTaskRunner(),
164+
thread_host.ui_thread->GetTaskRunner(),
165+
thread_host.io_thread->GetTaskRunner());
166+
MockDelegate delegate;
167+
EXPECT_CALL(delegate, GetTaskRunners())
168+
.WillRepeatedly(ReturnRef(task_runners));
169+
EXPECT_CALL(delegate, OnFrameRasterized(_));
170+
auto rasterizer = std::make_unique<Rasterizer>(delegate);
171+
auto surface = std::make_unique<MockSurface>();
172+
std::shared_ptr<MockExternalViewEmbedder> external_view_embedder =
173+
std::make_shared<MockExternalViewEmbedder>();
174+
rasterizer->SetExternalViewEmbedder(external_view_embedder);
175+
EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging)
176+
.WillRepeatedly(Return(true));
177+
auto surface_frame = std::make_unique<SurfaceFrame>(
178+
/*surface=*/nullptr, /*supports_readback=*/true,
179+
/*submit_callback=*/[](const SurfaceFrame&, SkCanvas*) { return true; });
180+
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
181+
.WillOnce(Return(ByMove(std::move(surface_frame))));
182+
117183
EXPECT_CALL(*external_view_embedder,
118-
EndFrame(false, fml::RefPtr<fml::RasterThreadMerger>(nullptr)));
184+
BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr,
185+
/*device_pixel_ratio=*/2.0,
186+
/*raster_thread_merger=*/_))
187+
.Times(1);
188+
EXPECT_CALL(*external_view_embedder, SubmitFrame).Times(0);
189+
EXPECT_CALL(*external_view_embedder, EndFrame(/*should_resubmit_frame=*/false,
190+
/*raster_thread_merger=*/_))
191+
.Times(1);
192+
119193
rasterizer->Setup(std::move(surface));
120194
fml::AutoResetWaitableEvent latch;
121195
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
@@ -124,12 +198,66 @@ TEST(RasterizerTest, drawWithExternalViewEmbedder) {
124198
/*device_pixel_ratio=*/2.0f);
125199
bool result = pipeline->Produce().Complete(std::move(layer_tree));
126200
EXPECT_TRUE(result);
127-
std::function<bool(LayerTree&)> no_discard = [](LayerTree&) {
128-
return false;
129-
};
201+
auto no_discard = [](LayerTree&) { return false; };
130202
rasterizer->Draw(pipeline, no_discard);
131203
latch.Signal();
132204
});
133205
latch.Wait();
134206
}
207+
208+
TEST(
209+
RasterizerTest,
210+
drawWithExternalViewEmbedderAndThreadsMerged_ExternalViewEmbedderSubmitFrameCalled) {
211+
std::string test_name =
212+
::testing::UnitTest::GetInstance()->current_test_info()->name();
213+
ThreadHost thread_host("io.flutter.test." + test_name + ".",
214+
ThreadHost::Type::Platform | ThreadHost::Type::GPU |
215+
ThreadHost::Type::IO | ThreadHost::Type::UI);
216+
fml::MessageLoop::EnsureInitializedForCurrentThread();
217+
TaskRunners task_runners("test",
218+
fml::MessageLoop::GetCurrent().GetTaskRunner(),
219+
fml::MessageLoop::GetCurrent().GetTaskRunner(),
220+
thread_host.ui_thread->GetTaskRunner(),
221+
thread_host.io_thread->GetTaskRunner());
222+
223+
MockDelegate delegate;
224+
EXPECT_CALL(delegate, GetTaskRunners())
225+
.WillRepeatedly(ReturnRef(task_runners));
226+
EXPECT_CALL(delegate, OnFrameRasterized(_));
227+
228+
auto rasterizer = std::make_unique<Rasterizer>(delegate);
229+
auto surface = std::make_unique<MockSurface>();
230+
231+
std::shared_ptr<MockExternalViewEmbedder> external_view_embedder =
232+
std::make_shared<MockExternalViewEmbedder>();
233+
rasterizer->SetExternalViewEmbedder(external_view_embedder);
234+
235+
auto surface_frame = std::make_unique<SurfaceFrame>(
236+
/*surface=*/nullptr, /*supports_readback=*/true,
237+
/*submit_callback=*/[](const SurfaceFrame&, SkCanvas*) { return true; });
238+
EXPECT_CALL(*surface, AcquireFrame(SkISize()))
239+
.WillOnce(Return(ByMove(std::move(surface_frame))));
240+
EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging)
241+
.WillRepeatedly(Return(true));
242+
243+
EXPECT_CALL(*external_view_embedder,
244+
BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr,
245+
/*device_pixel_ratio=*/2.0,
246+
/*raster_thread_merger=*/_))
247+
.Times(1);
248+
EXPECT_CALL(*external_view_embedder, SubmitFrame).Times(1);
249+
EXPECT_CALL(*external_view_embedder, EndFrame(/*should_resubmit_frame=*/false,
250+
/*raster_thread_merger=*/_))
251+
.Times(1);
252+
253+
rasterizer->Setup(std::move(surface));
254+
255+
auto pipeline = fml::AdoptRef(new Pipeline<LayerTree>(/*depth=*/10));
256+
auto layer_tree = std::make_unique<LayerTree>(/*frame_size=*/SkISize(),
257+
/*device_pixel_ratio=*/2.0f);
258+
bool result = pipeline->Produce().Complete(std::move(layer_tree));
259+
EXPECT_TRUE(result);
260+
auto no_discard = [](LayerTree&) { return false; };
261+
rasterizer->Draw(pipeline, no_discard);
262+
}
135263
} // namespace flutter

shell/common/shell_unittests.cc

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,20 +1051,25 @@ TEST_F(ShellTest,
10511051
auto settings = CreateSettingsForFixture();
10521052
fml::AutoResetWaitableEvent end_frame_latch;
10531053
std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
1054-
1054+
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
10551055
auto end_frame_callback =
10561056
[&](bool should_resubmit_frame,
10571057
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
1058-
external_view_embedder->UpdatePostPrerollResult(
1059-
PostPrerollResult::kSuccess);
1058+
if (!raster_thread_merger_ref) {
1059+
raster_thread_merger_ref = raster_thread_merger;
1060+
}
1061+
if (should_resubmit_frame && !raster_thread_merger->IsMerged()) {
1062+
raster_thread_merger->MergeWithLease(10);
1063+
external_view_embedder->UpdatePostPrerollResult(
1064+
PostPrerollResult::kSuccess);
1065+
}
10601066
end_frame_latch.Signal();
10611067
};
10621068
external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
10631069
end_frame_callback, PostPrerollResult::kResubmitFrame, true);
10641070

10651071
auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
10661072
false, external_view_embedder);
1067-
10681073
PlatformViewNotifyCreated(shell.get());
10691074

10701075
auto configuration = RunConfiguration::InferFromSettings(settings);
@@ -1074,13 +1079,18 @@ TEST_F(ShellTest,
10741079
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
10751080

10761081
PumpOneFrame(shell.get());
1077-
// `EndFrame` changed the post preroll result to `kSuccess`.
1082+
// `EndFrame` changed the post preroll result to `kSuccess` and merged the
1083+
// threads. During the frame, the threads are not merged, So no
1084+
// `external_view_embedder->GetSubmittedFrameCount()` is called.
10781085
end_frame_latch.Wait();
1079-
ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
1086+
ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
1087+
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
10801088

1089+
// This is the resubmitted frame, which threads are also merged.
10811090
end_frame_latch.Wait();
1082-
ASSERT_EQ(2, external_view_embedder->GetSubmittedFrameCount());
1091+
ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
10831092

1093+
PlatformViewNotifyDestroyed(shell.get());
10841094
DestroyShell(std::move(shell));
10851095
}
10861096

@@ -2020,14 +2030,29 @@ TEST_F(ShellTest, DiscardLayerTreeOnResize) {
20202030
SkISize expected_size = SkISize::Make(400, 200);
20212031

20222032
fml::AutoResetWaitableEvent end_frame_latch;
2033+
std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder;
2034+
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger_ref;
2035+
auto end_frame_callback =
2036+
[&](bool should_merge_thread,
2037+
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
2038+
if (!raster_thread_merger_ref) {
2039+
raster_thread_merger_ref = raster_thread_merger;
2040+
}
2041+
if (should_merge_thread) {
2042+
// TODO(cyanglaz): This test used external_view_embedder so we need to
2043+
// merge the threads here. However, the scenario it is testing is
2044+
// unrelated to platform views. We should consider to update this test
2045+
// so it doesn't require external_view_embedder.
2046+
// https://github.com/flutter/flutter/issues/69895
2047+
raster_thread_merger->MergeWithLease(10);
2048+
external_view_embedder->UpdatePostPrerollResult(
2049+
PostPrerollResult::kSuccess);
2050+
}
2051+
end_frame_latch.Signal();
2052+
};
20232053

2024-
auto end_frame_callback = [&](bool, fml::RefPtr<fml::RasterThreadMerger>) {
2025-
end_frame_latch.Signal();
2026-
};
2027-
2028-
std::shared_ptr<ShellTestExternalViewEmbedder> external_view_embedder =
2029-
std::make_shared<ShellTestExternalViewEmbedder>(
2030-
std::move(end_frame_callback), PostPrerollResult::kSuccess, true);
2054+
external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
2055+
std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true);
20312056

20322057
std::unique_ptr<Shell> shell = CreateShell(
20332058
settings, GetTaskRunnersForFixture(), false, external_view_embedder);
@@ -2048,23 +2073,29 @@ TEST_F(ShellTest, DiscardLayerTreeOnResize) {
20482073

20492074
RunEngine(shell.get(), std::move(configuration));
20502075

2051-
fml::WeakPtr<RuntimeDelegate> runtime_delegate = shell->GetEngine();
2052-
20532076
PumpOneFrame(shell.get(), static_cast<double>(wrong_size.width()),
20542077
static_cast<double>(wrong_size.height()));
20552078

20562079
end_frame_latch.Wait();
20572080

20582081
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
20592082

2083+
// Threads will be merged at the end of this frame.
20602084
PumpOneFrame(shell.get(), static_cast<double>(expected_size.width()),
20612085
static_cast<double>(expected_size.height()));
20622086

20632087
end_frame_latch.Wait();
2088+
// Even the threads are merged at the end of the frame,
2089+
// during the frame, the threads are not merged,
2090+
// So no `external_view_embedder->GetSubmittedFrameCount()` is called.
2091+
ASSERT_TRUE(raster_thread_merger_ref->IsMerged());
2092+
ASSERT_EQ(0, external_view_embedder->GetSubmittedFrameCount());
20642093

2094+
end_frame_latch.Wait();
20652095
ASSERT_EQ(1, external_view_embedder->GetSubmittedFrameCount());
20662096
ASSERT_EQ(expected_size, external_view_embedder->GetLastSubmittedFrameSize());
20672097

2098+
PlatformViewNotifyDestroyed(shell.get());
20682099
DestroyShell(std::move(shell));
20692100
}
20702101

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@
342342

343343
void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack,
344344
UIView* embedded_view) {
345+
if (flutter_view_ == nullptr) {
346+
return;
347+
}
345348
FML_DCHECK(CATransform3DEqualToTransform(embedded_view.layer.transform, CATransform3DIdentity));
346349
ResetAnchor(embedded_view.layer);
347350
ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview;
@@ -398,7 +401,6 @@
398401

399402
void FlutterPlatformViewsController::CompositeWithParams(int view_id,
400403
const EmbeddedViewParams& params) {
401-
FML_DCHECK(flutter_view_);
402404
CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height());
403405
UIView* touchInterceptor = touch_interceptors_[view_id].get();
404406
touchInterceptor.layer.transform = CATransform3DIdentity;
@@ -419,9 +421,8 @@
419421
}
420422

421423
SkCanvas* FlutterPlatformViewsController::CompositeEmbeddedView(int view_id) {
422-
FML_DCHECK(flutter_view_);
423-
// TODO(amirh): assert that this is running on the platform thread once we support the iOS
424-
// embedded views thread configuration.
424+
// Any UIKit related code has to run on main thread.
425+
FML_DCHECK([[NSThread currentThread] isMainThread]);
425426

426427
// Do nothing if the view doesn't need to be composited.
427428
if (views_to_recomposite_.count(view_id) == 0) {
@@ -469,12 +470,11 @@
469470
bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context,
470471
std::shared_ptr<IOSContext> ios_context,
471472
std::unique_ptr<SurfaceFrame> frame) {
472-
FML_DCHECK(flutter_view_);
473-
474473
// Any UIKit related code has to run on main thread.
475-
// When on a non-main thread, we only allow the rest of the method to run if there is no
476-
// Pending UIView operations.
477-
FML_DCHECK([[NSThread currentThread] isMainThread] || !HasPlatformViewThisOrNextFrame());
474+
FML_DCHECK([[NSThread currentThread] isMainThread]);
475+
if (flutter_view_ == nullptr) {
476+
return frame->Submit();
477+
}
478478

479479
DisposeViews();
480480

@@ -558,8 +558,6 @@
558558
BringLayersIntoView(platform_view_layers);
559559
// Mark all layers as available, so they can be used in the next frame.
560560
layer_pool_->RecycleLayers();
561-
// Reset the composition order, so next frame starts empty.
562-
composition_order_.clear();
563561

564562
did_submit &= frame->Submit();
565563

@@ -599,7 +597,10 @@
599597

600598
void FlutterPlatformViewsController::EndFrame(
601599
bool should_resubmit_frame,
602-
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {}
600+
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
601+
// Reset the composition order, so next frame starts empty.
602+
composition_order_.clear();
603+
}
603604

604605
std::shared_ptr<FlutterPlatformViewLayer> FlutterPlatformViewsController::GetLayer(
605606
GrDirectContext* gr_context,

0 commit comments

Comments
 (0)