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

Commit 6d5623e

Browse files
committed
Additional changes for this attempt.
1 parent 8150a3d commit 6d5623e

File tree

10 files changed

+148
-17
lines changed

10 files changed

+148
-17
lines changed

runtime/dart_isolate_unittests.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,10 @@ class FakePlatformConfigurationClient : public PlatformConfigurationClient {
707707
std::string DefaultRouteName() override { return ""; }
708708
void ScheduleFrame() override {}
709709
void EndWarmUpFrame() override {}
710-
void Render(Scene* scene, double width, double height) override {}
710+
void Render(int64_t view_id,
711+
Scene* scene,
712+
double width,
713+
double height) override {}
711714
void UpdateSemantics(SemanticsUpdate* update) override {}
712715
void HandlePlatformMessage(
713716
std::unique_ptr<PlatformMessage> message) override {}

runtime/runtime_controller.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ bool RuntimeController::SetAccessibilityFeatures(int32_t flags) {
226226

227227
bool RuntimeController::BeginFrame(fml::TimePoint frame_time,
228228
uint64_t frame_number) {
229+
MarkAsFrameBorder();
229230
if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) {
230231
platform_configuration->BeginFrame(frame_time, frame_number);
231232
return true;
@@ -341,7 +342,7 @@ void RuntimeController::ScheduleFrame() {
341342
}
342343

343344
void RuntimeController::EndWarmUpFrame() {
344-
client_.EndWarmUpFrame();
345+
client_.OnAllViewsRendered();
345346
}
346347

347348
// |PlatformConfigurationClient|
@@ -356,6 +357,21 @@ void RuntimeController::Render(int64_t view_id,
356357
}
357358
client_.Render(view_id, scene->takeLayerTree(width, height),
358359
view_metrics->device_pixel_ratio);
360+
rendered_views_during_frame_.insert(view_id);
361+
CheckIfAllViewsRendered();
362+
}
363+
364+
void RuntimeController::MarkAsFrameBorder() {
365+
rendered_views_during_frame_.clear();
366+
}
367+
368+
void RuntimeController::CheckIfAllViewsRendered() {
369+
if (rendered_views_during_frame_.size() != 0 &&
370+
rendered_views_during_frame_.size() ==
371+
platform_data_.viewport_metrics_for_views.size()) {
372+
client_.OnAllViewsRendered();
373+
MarkAsFrameBorder();
374+
}
359375
}
360376

361377
// |PlatformConfigurationClient|

runtime/runtime_controller.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,24 @@ class RuntimeController : public PlatformConfigurationClient {
660660
std::shared_ptr<PlatformIsolateManager>(new PlatformIsolateManager());
661661
bool has_flushed_runtime_state_ = false;
662662

663+
// Tracks the views that have been called `Render` during a frame.
664+
//
665+
// If all registered views (through `AddView`) have been rendered, then the
666+
// end of frame will be called immediately, submitting the views to the
667+
// pipeline a bit earlier than having to wait for the end of the vsync.
668+
//
669+
// This mechanism fixes https://github.com/flutter/flutter/issues/144584 with
670+
// option 2 and
671+
// https://github.com/flutter/engine/pull/51186#issuecomment-1977820525 with
672+
// option a in most cases, except if there are multiple views and only part of
673+
// them are rendered.
674+
// TODO(dkwingsmt): Fix these problems for all cases.
675+
std::unordered_set<uint64_t> rendered_views_during_frame_;
676+
677+
void MarkAsFrameBorder();
678+
679+
void CheckIfAllViewsRendered();
680+
663681
PlatformConfiguration* GetPlatformConfigurationIfAvailable();
664682

665683
bool FlushRuntimeStateToIsolate();

runtime/runtime_delegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class RuntimeDelegate {
2525

2626
virtual void ScheduleFrame(bool regenerate_layer_trees = true) = 0;
2727

28-
virtual void EndWarmUpFrame() = 0;
28+
virtual void OnAllViewsRendered() = 0;
2929

3030
virtual void Render(int64_t view_id,
3131
std::unique_ptr<flutter::LayerTree> layer_tree,

shell/common/animator.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,12 @@ void Animator::BeginFrame(
119119
}
120120

121121
void Animator::EndFrame() {
122-
FML_DCHECK(frame_timings_recorder_ != nullptr);
122+
if (frame_timings_recorder_ == nullptr) {
123+
// `EndFrame` has been called in this frame. This happens if the engine has
124+
// called `OnAllViewsRendered` and then the end of the vsync task calls
125+
// `EndFrame` again.
126+
return;
127+
}
123128
if (!layer_trees_tasks_.empty()) {
124129
// The build is completed in OnAnimatorBeginFrame.
125130
frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now());
@@ -274,7 +279,7 @@ void Animator::AwaitVSync() {
274279
}
275280
}
276281

277-
void Animator::EndWarmUpFrame() {
282+
void Animator::OnAllViewsRendered() {
278283
if (!layer_trees_tasks_.empty()) {
279284
EndFrame();
280285
}

shell/common/animator.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,27 @@ class Animator final {
5454
void RequestFrame(bool regenerate_layer_trees = true);
5555

5656
//--------------------------------------------------------------------------
57-
/// @brief Tells the Animator that a warm up frame has ended.
57+
/// @brief Tells the Animator that all views that should render for this
58+
/// frame have been rendered.
5859
///
59-
/// In a warm up frame, `Animator::Render` is called out of vsync
60-
/// tasks, and Animator requires an explicit end-of-frame call to
61-
/// know when to send the layer trees to the pipeline.
60+
/// In regular frames, since all `Render` calls must take place
61+
/// during a vsync task, the Animator knows that all views have
62+
/// been rendered at the end of the vsync task, therefore calling
63+
/// this method is not needed.
6264
///
63-
/// This is different from regular frames, where Animator::Render is
64-
/// always called within a vsync task, and Animator can send
65-
/// the views at the end of the vsync task.
65+
/// However, the engine might decide to start it a bit earlier, for
66+
/// example, if the engine decides that no more views can be
67+
/// rendered, so that the rasterization can start a bit earlier.
68+
///
69+
/// This method is also useful in warm-up frames. In a warm up
70+
/// frame, `Animator::Render` is called out of vsync tasks, and
71+
/// Animator requires an explicit end-of-frame call to know when to
72+
/// send the layer trees to the pipeline.
6673
///
6774
/// For more about warm up frames, see
6875
/// `PlatformDispatcher.scheduleWarmUpFrame`.
6976
///
70-
void EndWarmUpFrame();
77+
void OnAllViewsRendered();
7178

7279
//--------------------------------------------------------------------------
7380
/// @brief Tells the Animator that this frame needs to render another view.

shell/common/engine.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,8 @@ void Engine::ScheduleFrame(bool regenerate_layer_trees) {
464464
animator_->RequestFrame(regenerate_layer_trees);
465465
}
466466

467-
void Engine::EndWarmUpFrame() {
468-
animator_->EndWarmUpFrame();
467+
void Engine::OnAllViewsRendered() {
468+
animator_->OnAllViewsRendered();
469469
}
470470

471471
void Engine::Render(int64_t view_id,

shell/common/engine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
838838
void ScheduleFrame() { ScheduleFrame(true); }
839839

840840
// |RuntimeDelegate|
841-
void EndWarmUpFrame() override;
841+
void OnAllViewsRendered() override;
842842

843843
// |RuntimeDelegate|
844844
FontCollection& GetFontCollection() override;

shell/common/engine_unittests.cc

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
120120
public:
121121
MOCK_METHOD(std::string, DefaultRouteName, (), (override));
122122
MOCK_METHOD(void, ScheduleFrame, (bool), (override));
123-
MOCK_METHOD(void, EndWarmUpFrame, (), (override));
123+
MOCK_METHOD(void, OnAllViewsRendered, (), (override));
124124
MOCK_METHOD(void,
125125
Render,
126126
(int64_t, std::unique_ptr<flutter::LayerTree>, float),
@@ -679,6 +679,64 @@ TEST_F(EngineTest, AnimatorAcceptsMultipleRenders) {
679679
draw_latch.Wait();
680680
}
681681

682+
TEST_F(EngineTest, AnimatorSubmitsSingleViewBeforeDrawFrameEnds) {
683+
MockAnimatorDelegate animator_delegate;
684+
std::unique_ptr<EngineContext> engine_context;
685+
686+
std::shared_ptr<PlatformMessageHandler> platform_message_handler =
687+
std::make_shared<MockPlatformMessageHandler>();
688+
EXPECT_CALL(delegate_, GetPlatformMessageHandler)
689+
.WillOnce(ReturnRef(platform_message_handler));
690+
691+
static bool rasterization_started = false;
692+
EXPECT_CALL(animator_delegate, OnAnimatorDraw)
693+
.WillOnce(Invoke([](const std::shared_ptr<FramePipeline>& pipeline) {
694+
rasterization_started = true;
695+
auto status = pipeline->Consume([&](std::unique_ptr<FrameItem> item) {
696+
EXPECT_EQ(item->layer_tree_tasks.size(), 1u);
697+
EXPECT_EQ(item->layer_tree_tasks[0]->view_id, kFlutterImplicitViewId);
698+
});
699+
EXPECT_EQ(status, PipelineConsumeResult::Done);
700+
}));
701+
EXPECT_CALL(animator_delegate, OnAnimatorBeginFrame)
702+
.WillRepeatedly(Invoke([&engine_context](fml::TimePoint frame_target_time,
703+
uint64_t frame_number) {
704+
engine_context->EngineTaskSync([&](Engine& engine) {
705+
engine.BeginFrame(frame_target_time, frame_number);
706+
});
707+
}));
708+
709+
std::unique_ptr<Animator> animator;
710+
PostSync(task_runners_.GetUITaskRunner(),
711+
[&animator, &animator_delegate, &task_runners = task_runners_] {
712+
animator = std::make_unique<Animator>(
713+
animator_delegate, task_runners,
714+
static_cast<std::unique_ptr<VsyncWaiter>>(
715+
std::make_unique<testing::ConstantFiringVsyncWaiter>(
716+
task_runners)));
717+
});
718+
719+
native_latch.Reset();
720+
// The native_latch is signaled at the end of handleDrawFrame.
721+
AddNativeCallback("NotifyNative", [](auto args) {
722+
EXPECT_EQ(rasterization_started, true);
723+
native_latch.Signal();
724+
});
725+
726+
engine_context = EngineContext::Create(delegate_, settings_, task_runners_,
727+
std::move(animator));
728+
729+
engine_context->EngineTaskSync([](Engine& engine) {
730+
engine.AddView(kFlutterImplicitViewId, ViewportMetrics{1.0, 10, 10, 1, 0});
731+
});
732+
733+
auto configuration = RunConfiguration::InferFromSettings(settings_);
734+
configuration.SetEntrypoint("renderSingleViewAndCallAfterOnDrawFrame");
735+
engine_context->Run(std::move(configuration));
736+
737+
native_latch.Wait();
738+
}
739+
682740
// The animator should submit to the pipeline the implicit view rendered in a
683741
// warm up frame if there's already a continuation (i.e. Animator::BeginFrame
684742
// has been called)

shell/common/fixtures/shell_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ external void sayHiFromFixturesAreFunctionalMain();
142142
@pragma('vm:external-name', 'NotifyNative')
143143
external void notifyNative();
144144

145+
@pragma('vm:entry-point')
146+
@pragma('vm:external-name', 'NotifyNative2')
147+
external void notifyNative2();
148+
145149
@pragma('vm:entry-point')
146150
void thousandCallsToNative() {
147151
for (int i = 0; i < 1000; i++) {
@@ -553,6 +557,26 @@ void onDrawFrameRenderAllViews() {
553557
notifyNative();
554558
}
555559

560+
@pragma('vm:entry-point')
561+
void renderSingleViewAndCallAfterOnDrawFrame() {
562+
PlatformDispatcher.instance.onDrawFrame = () {
563+
final SceneBuilder builder = SceneBuilder();
564+
final PictureRecorder recorder = PictureRecorder();
565+
final Canvas canvas = Canvas(recorder);
566+
canvas.drawPaint(Paint()..color = const Color(0xFFABCDEF));
567+
final Picture picture = recorder.endRecording();
568+
builder.addPicture(Offset.zero, picture);
569+
570+
final Scene scene = builder.build();
571+
PlatformDispatcher.instance.implicitView!.render(scene);
572+
notifyNative();
573+
574+
scene.dispose();
575+
picture.dispose();
576+
};
577+
PlatformDispatcher.instance.scheduleFrame();
578+
}
579+
556580
@pragma('vm:entry-point')
557581
void renderWarmUpImplicitView() {
558582
bool beginFrameCalled = false;

0 commit comments

Comments
 (0)