Skip to content

Commit 81035b7

Browse files
authored
Reland 4: Multiview pipeline (flutter#50931)
This relands flutter#49950. Nothing is changed. The error turns out a flake (it passes once), although this PR might have made the flake more flaky. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 74447b6 commit 81035b7

21 files changed

+488
-153
lines changed

flow/frame_timings.cc

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,31 @@
1313

1414
namespace flutter {
1515

16+
namespace {
17+
18+
const char* StateToString(FrameTimingsRecorder::State state) {
19+
#ifndef NDEBUG
20+
switch (state) {
21+
case FrameTimingsRecorder::State::kUninitialized:
22+
return "kUninitialized";
23+
case FrameTimingsRecorder::State::kVsync:
24+
return "kVsync";
25+
case FrameTimingsRecorder::State::kBuildStart:
26+
return "kBuildStart";
27+
case FrameTimingsRecorder::State::kBuildEnd:
28+
return "kBuildEnd";
29+
case FrameTimingsRecorder::State::kRasterStart:
30+
return "kRasterStart";
31+
case FrameTimingsRecorder::State::kRasterEnd:
32+
return "kRasterEnd";
33+
};
34+
FML_UNREACHABLE();
35+
#endif
36+
return "";
37+
}
38+
39+
} // namespace
40+
1641
std::atomic<uint64_t> FrameTimingsRecorder::frame_number_gen_ = {1};
1742

1843
FrameTimingsRecorder::FrameTimingsRecorder()
@@ -255,7 +280,8 @@ const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const {
255280
}
256281

257282
void FrameTimingsRecorder::AssertInState(State state) const {
258-
FML_DCHECK(state_ == state);
283+
FML_DCHECK(state_ == state) << "Expected state " << StateToString(state)
284+
<< ", actual state " << StateToString(state_);
259285
}
260286

261287
} // namespace flutter

flow/frame_timings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class FrameTimingsRecorder {
3131
public:
3232
/// Various states that the recorder can be in. When created the recorder is
3333
/// in an unitialized state and transtions in sequential order of the states.
34+
// After adding an item to this enum, modify StateToString accordingly.
3435
enum class State : uint32_t {
3536
kUninitialized,
3637
kVsync,
@@ -121,6 +122,8 @@ class FrameTimingsRecorder {
121122
///
122123
/// Instead of adding a `GetState` method and asserting on the result, this
123124
/// method prevents other logic from relying on the state.
125+
///
126+
/// In release builds, this call is a no-op.
124127
void AssertInState(State state) const;
125128

126129
private:

lib/ui/painting/image_dispose_unittests.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define FML_USED_ON_EMBEDDER
66

77
#include "flutter/common/task_runners.h"
8+
#include "flutter/fml/synchronization/count_down_latch.h"
89
#include "flutter/fml/synchronization/waitable_event.h"
910
#include "flutter/lib/ui/painting/canvas.h"
1011
#include "flutter/lib/ui/painting/image.h"
@@ -57,6 +58,10 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
5758
};
5859

5960
Settings settings = CreateSettingsForFixture();
61+
fml::CountDownLatch frame_latch{2};
62+
settings.frame_rasterized_callback = [&frame_latch](const FrameTiming& t) {
63+
frame_latch.CountDown();
64+
};
6065
auto task_runner = CreateNewThread();
6166
TaskRunners task_runners("test", // label
6267
GetCurrentTaskRunner(), // platform
@@ -83,12 +88,15 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
8388
shell->RunEngine(std::move(configuration), [&](auto result) {
8489
ASSERT_EQ(result, Engine::RunStatus::Success);
8590
});
86-
8791
message_latch_.Wait();
8892

8993
ASSERT_TRUE(current_display_list_);
9094
ASSERT_TRUE(current_image_);
9195

96+
// Wait for 2 frames to be rasterized. The 2nd frame releases resources of the
97+
// 1st frame.
98+
frame_latch.Wait();
99+
92100
// Force a drain the SkiaUnrefQueue. The engine does this normally as frames
93101
// pump, but we force it here to make the test more deterministic.
94102
message_latch_.Reset();

lib/ui/window/platform_configuration.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,12 +453,9 @@ void PlatformConfigurationNativeApi::Render(int64_t view_id,
453453
Scene* scene,
454454
double width,
455455
double height) {
456-
// TODO(dkwingsmt): Currently only supports a single window.
457-
// See https://github.com/flutter/flutter/issues/135530, item 2.
458-
FML_DCHECK(view_id == kFlutterImplicitViewId);
459456
UIDartState::ThrowIfUIOperationsProhibited();
460457
UIDartState::Current()->platform_configuration()->client()->Render(
461-
scene, width, height);
458+
view_id, scene, width, height);
462459
}
463460

464461
void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) {

lib/ui/window/platform_configuration.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ class PlatformConfigurationClient {
7676
/// @brief Updates the client's rendering on the GPU with the newly
7777
/// provided Scene.
7878
///
79-
virtual void Render(Scene* scene, double width, double height) = 0;
79+
virtual void Render(int64_t view_id,
80+
Scene* scene,
81+
double width,
82+
double height) = 0;
8083

8184
//--------------------------------------------------------------------------
8285
/// @brief Receives an updated semantics tree from the Framework.

runtime/runtime_controller.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -340,21 +340,21 @@ void RuntimeController::ScheduleFrame() {
340340
client_.ScheduleFrame();
341341
}
342342

343-
// |PlatformConfigurationClient|
344343
void RuntimeController::EndWarmUpFrame() {
345344
client_.EndWarmUpFrame();
346345
}
347346

348347
// |PlatformConfigurationClient|
349-
void RuntimeController::Render(Scene* scene, double width, double height) {
350-
// TODO(dkwingsmt): Currently only supports a single window.
351-
int64_t view_id = kFlutterImplicitViewId;
348+
void RuntimeController::Render(int64_t view_id,
349+
Scene* scene,
350+
double width,
351+
double height) {
352352
const ViewportMetrics* view_metrics =
353353
UIDartState::Current()->platform_configuration()->GetMetrics(view_id);
354354
if (view_metrics == nullptr) {
355355
return;
356356
}
357-
client_.Render(scene->takeLayerTree(width, height),
357+
client_.Render(view_id, scene->takeLayerTree(width, height),
358358
view_metrics->device_pixel_ratio);
359359
}
360360

runtime/runtime_controller.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,10 @@ class RuntimeController : public PlatformConfigurationClient {
661661
void EndWarmUpFrame() override;
662662

663663
// |PlatformConfigurationClient|
664-
void Render(Scene* scene, double width, double height) override;
664+
void Render(int64_t view_id,
665+
Scene* scene,
666+
double width,
667+
double height) override;
665668

666669
// |PlatformConfigurationClient|
667670
void UpdateSemantics(SemanticsUpdate* update) override;

runtime/runtime_delegate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class RuntimeDelegate {
2727

2828
virtual void EndWarmUpFrame() = 0;
2929

30-
virtual void Render(std::unique_ptr<flutter::LayerTree> layer_tree,
30+
virtual void Render(int64_t view_id,
31+
std::unique_ptr<flutter::LayerTree> layer_tree,
3132
float device_pixel_ratio) = 0;
3233

3334
virtual void UpdateSemantics(SemanticsNodeUpdates update,

shell/common/animator.cc

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ void Animator::BeginFrame(
6262
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
6363
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending",
6464
frame_request_number_);
65+
// Clear layer trees rendered out of a frame. Only Animator::Render called
66+
// within a frame is used.
67+
layer_trees_tasks_.clear();
68+
6569
frame_request_number_++;
6670

6771
frame_timings_recorder_ = std::move(frame_timings_recorder);
@@ -112,6 +116,33 @@ void Animator::BeginFrame(
112116
dart_frame_deadline_ = frame_target_time.ToEpochDelta();
113117
uint64_t frame_number = frame_timings_recorder_->GetFrameNumber();
114118
delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
119+
}
120+
121+
void Animator::EndFrame() {
122+
FML_DCHECK(frame_timings_recorder_ != nullptr);
123+
if (!layer_trees_tasks_.empty()) {
124+
// The build is completed in OnAnimatorBeginFrame.
125+
frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now());
126+
127+
delegate_.OnAnimatorUpdateLatestFrameTargetTime(
128+
frame_timings_recorder_->GetVsyncTargetTime());
129+
130+
// Commit the pending continuation.
131+
PipelineProduceResult result =
132+
producer_continuation_.Complete(std::make_unique<FrameItem>(
133+
std::move(layer_trees_tasks_), std::move(frame_timings_recorder_)));
134+
135+
if (!result.success) {
136+
FML_DLOG(INFO) << "Failed to commit to the pipeline";
137+
} else if (!result.is_first_item) {
138+
// Do nothing. It has been successfully pushed to the pipeline but not as
139+
// the first item. Eventually the 'Rasterizer' will consume it, so we
140+
// don't need to notify the delegate.
141+
} else {
142+
delegate_.OnAnimatorDraw(layer_tree_pipeline_);
143+
}
144+
}
145+
frame_timings_recorder_ = nullptr;
115146

116147
if (!frame_scheduled_ && has_rendered_) {
117148
// Wait a tad more than 3 60hz frames before reporting a big idle period.
@@ -139,14 +170,18 @@ void Animator::BeginFrame(
139170
},
140171
kNotifyIdleTaskWaitTime);
141172
}
173+
FML_DCHECK(layer_trees_tasks_.empty());
174+
FML_DCHECK(frame_timings_recorder_ == nullptr);
142175
}
143176

144-
void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree,
177+
void Animator::Render(int64_t view_id,
178+
std::unique_ptr<flutter::LayerTree> layer_tree,
145179
float device_pixel_ratio) {
146180
has_rendered_ = true;
147181

148182
if (!frame_timings_recorder_) {
149-
// Framework can directly call render with a built scene.
183+
// Framework can directly call render with a built scene. A major reason is
184+
// to render warm up frames.
150185
frame_timings_recorder_ = std::make_unique<FrameTimingsRecorder>();
151186
const fml::TimePoint placeholder_time = fml::TimePoint::Now();
152187
frame_timings_recorder_->RecordVsync(placeholder_time, placeholder_time);
@@ -156,35 +191,9 @@ void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree,
156191
TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter",
157192
"Animator::Render", /*flow_id_count=*/0,
158193
/*flow_ids=*/nullptr);
159-
frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now());
160-
161-
delegate_.OnAnimatorUpdateLatestFrameTargetTime(
162-
frame_timings_recorder_->GetVsyncTargetTime());
163194

164-
// TODO(dkwingsmt): Currently only supports a single window.
165-
// See https://github.com/flutter/flutter/issues/135530, item 2.
166-
int64_t view_id = kFlutterImplicitViewId;
167-
std::vector<std::unique_ptr<LayerTreeTask>> layer_trees_tasks;
168-
layer_trees_tasks.push_back(std::make_unique<LayerTreeTask>(
195+
layer_trees_tasks_.push_back(std::make_unique<LayerTreeTask>(
169196
view_id, std::move(layer_tree), device_pixel_ratio));
170-
// Commit the pending continuation.
171-
PipelineProduceResult result =
172-
producer_continuation_.Complete(std::make_unique<FrameItem>(
173-
std::move(layer_trees_tasks), std::move(frame_timings_recorder_)));
174-
175-
if (!result.success) {
176-
FML_DLOG(INFO) << "No pending continuation to commit";
177-
return;
178-
}
179-
180-
if (!result.is_first_item) {
181-
// It has been successfully pushed to the pipeline but not as the first
182-
// item. Eventually the 'Rasterizer' will consume it, so we don't need to
183-
// notify the delegate.
184-
return;
185-
}
186-
187-
delegate_.OnAnimatorDraw(layer_tree_pipeline_);
188197
}
189198

190199
const std::weak_ptr<VsyncWaiter> Animator::GetVsyncWaiter() const {
@@ -256,6 +265,7 @@ void Animator::AwaitVSync() {
256265
self->DrawLastLayerTrees(std::move(frame_timings_recorder));
257266
} else {
258267
self->BeginFrame(std::move(frame_timings_recorder));
268+
self->EndFrame();
259269
}
260270
}
261271
});
@@ -265,9 +275,9 @@ void Animator::AwaitVSync() {
265275
}
266276

267277
void Animator::EndWarmUpFrame() {
268-
// Do nothing. The warm up frame does not need any additional work to end the
269-
// frame for now. This will change once the pipeline supports multi-view.
270-
// https://github.com/flutter/flutter/issues/142851
278+
if (!layer_trees_tasks_.empty()) {
279+
EndFrame();
280+
}
271281
}
272282

273283
void Animator::ScheduleSecondaryVsyncCallback(uintptr_t id,

shell/common/animator.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ class Animator final {
7676
/// technically, between Animator::BeginFrame and Animator::EndFrame
7777
/// (both private methods). Otherwise, this call will be ignored.
7878
///
79-
void Render(std::unique_ptr<flutter::LayerTree> layer_tree,
79+
void Render(int64_t view_id,
80+
std::unique_ptr<flutter::LayerTree> layer_tree,
8081
float device_pixel_ratio);
8182

8283
const std::weak_ptr<VsyncWaiter> GetVsyncWaiter() const;
@@ -105,7 +106,13 @@ class Animator final {
105106
void EnqueueTraceFlowId(uint64_t trace_flow_id);
106107

107108
private:
109+
// Animator's work during a vsync is split into two methods, BeginFrame and
110+
// EndFrame. The two methods should be called synchronously back-to-back to
111+
// avoid being interrupted by a regular vsync. The reason to split them is to
112+
// allow ShellTest::PumpOneFrame to insert a Render in between.
113+
108114
void BeginFrame(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder);
115+
void EndFrame();
109116

110117
bool CanReuseLastLayerTrees();
111118

@@ -122,6 +129,7 @@ class Animator final {
122129
std::shared_ptr<VsyncWaiter> waiter_;
123130

124131
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder_;
132+
std::vector<std::unique_ptr<LayerTreeTask>> layer_trees_tasks_;
125133
uint64_t frame_request_number_ = 1;
126134
fml::TimeDelta dart_frame_deadline_;
127135
std::shared_ptr<FramePipeline> layer_tree_pipeline_;

0 commit comments

Comments
 (0)