1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/shell/common/animator.h"
6
7#include "flutter/flow/frame_timings.h"
8#include "flutter/fml/time/time_point.h"
9#include "flutter/fml/trace_event.h"
10#include "third_party/dart/runtime/include/dart_tools_api.h"
11
12namespace flutter {
13
14namespace {
15
16// Wait 51 milliseconds (which is 1 more milliseconds than 3 frames at 60hz)
17// before notifying the engine that we are idle. See comments in |BeginFrame|
18// for further discussion on why this is necessary.
19constexpr fml::TimeDelta kNotifyIdleTaskWaitTime =
20 fml::TimeDelta::FromMilliseconds(millis: 51);
21
22} // namespace
23
24Animator::Animator(Delegate& delegate,
25 const TaskRunners& task_runners,
26 std::unique_ptr<VsyncWaiter> waiter)
27 : delegate_(delegate),
28 task_runners_(task_runners),
29 waiter_(std::move(waiter)),
30#if SHELL_ENABLE_METAL
31 layer_tree_pipeline_(std::make_shared<LayerTreePipeline>(2)),
32#else // SHELL_ENABLE_METAL
33 // TODO(dnfield): We should remove this logic and set the pipeline depth
34 // back to 2 in this case. See
35 // https://github.com/flutter/engine/pull/9132 for discussion.
36 layer_tree_pipeline_(std::make_shared<LayerTreePipeline>(
37 args: task_runners.GetPlatformTaskRunner() ==
38 task_runners.GetRasterTaskRunner()
39 ? 1
40 : 2)),
41#endif // SHELL_ENABLE_METAL
42 pending_frame_semaphore_(1),
43 weak_factory_(this) {
44}
45
46Animator::~Animator() = default;
47
48void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) {
49 fml::TaskRunner::RunNowOrPostTask(
50 runner: task_runners_.GetUITaskRunner(),
51 task: [self = weak_factory_.GetWeakPtr(), trace_flow_id] {
52 if (!self) {
53 return;
54 }
55 self->trace_flow_ids_.push_back(v: trace_flow_id);
56 self->ScheduleMaybeClearTraceFlowIds();
57 });
58}
59
60void Animator::BeginFrame(
61 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
62 TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending",
63 frame_request_number_);
64 frame_request_number_++;
65
66 frame_timings_recorder_ = std::move(frame_timings_recorder);
67 frame_timings_recorder_->RecordBuildStart(build_start: fml::TimePoint::Now());
68
69 size_t flow_id_count = trace_flow_ids_.size();
70 std::unique_ptr<uint64_t[]> flow_ids =
71 std::make_unique<uint64_t[]>(n: flow_id_count);
72 for (size_t i = 0; i < flow_id_count; ++i) {
73 flow_ids.get()[i] = trace_flow_ids_.at(i: i);
74 }
75
76 TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter",
77 "Animator::BeginFrame", flow_id_count,
78 flow_ids.get());
79
80 while (!trace_flow_ids_.empty()) {
81 uint64_t trace_flow_id = trace_flow_ids_.front();
82 TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
83 trace_flow_ids_.pop_front();
84 }
85
86 frame_scheduled_ = false;
87 regenerate_layer_tree_ = false;
88 pending_frame_semaphore_.Signal();
89
90 if (!producer_continuation_) {
91 // We may already have a valid pipeline continuation in case a previous
92 // begin frame did not result in an Animator::Render. Simply reuse that
93 // instead of asking the pipeline for a fresh continuation.
94 producer_continuation_ = layer_tree_pipeline_->Produce();
95
96 if (!producer_continuation_) {
97 // If we still don't have valid continuation, the pipeline is currently
98 // full because the consumer is being too slow. Try again at the next
99 // frame interval.
100 TRACE_EVENT0("flutter", "PipelineFull");
101 RequestFrame();
102 return;
103 }
104 }
105
106 // We have acquired a valid continuation from the pipeline and are ready
107 // to service potential frame.
108 FML_DCHECK(producer_continuation_);
109 const fml::TimePoint frame_target_time =
110 frame_timings_recorder_->GetVsyncTargetTime();
111 dart_frame_deadline_ = frame_target_time.ToEpochDelta();
112 uint64_t frame_number = frame_timings_recorder_->GetFrameNumber();
113 delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
114
115 if (!frame_scheduled_ && has_rendered_) {
116 // Wait a tad more than 3 60hz frames before reporting a big idle period.
117 // This is a heuristic that is meant to avoid giving false positives to the
118 // VM when we are about to schedule a frame in the next vsync, the idea
119 // being that if there have been three vsyncs with no frames it's a good
120 // time to start doing GC work.
121 task_runners_.GetUITaskRunner()->PostDelayedTask(
122 task: [self = weak_factory_.GetWeakPtr()]() {
123 if (!self) {
124 return;
125 }
126 auto now = fml::TimeDelta::FromMicroseconds(micros: Dart_TimelineGetMicros());
127 // If there's a frame scheduled, bail.
128 // If there's no frame scheduled, but we're not yet past the last
129 // vsync deadline, bail.
130 if (!self->frame_scheduled_ && now > self->dart_frame_deadline_) {
131 TRACE_EVENT0("flutter", "BeginFrame idle callback");
132 self->delegate_.OnAnimatorNotifyIdle(
133 deadline: now + fml::TimeDelta::FromMilliseconds(millis: 100));
134 }
135 },
136 delay: kNotifyIdleTaskWaitTime);
137 }
138}
139
140void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree,
141 float device_pixel_ratio) {
142 has_rendered_ = true;
143 last_layer_tree_size_ = layer_tree->frame_size();
144
145 if (!frame_timings_recorder_) {
146 // Framework can directly call render with a built scene.
147 frame_timings_recorder_ = std::make_unique<FrameTimingsRecorder>();
148 const fml::TimePoint placeholder_time = fml::TimePoint::Now();
149 frame_timings_recorder_->RecordVsync(vsync_start: placeholder_time, vsync_target: placeholder_time);
150 frame_timings_recorder_->RecordBuildStart(build_start: placeholder_time);
151 }
152
153 TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter",
154 "Animator::Render", /*flow_id_count=*/0,
155 /*flow_ids=*/nullptr);
156 frame_timings_recorder_->RecordBuildEnd(build_end: fml::TimePoint::Now());
157
158 delegate_.OnAnimatorUpdateLatestFrameTargetTime(
159 frame_target_time: frame_timings_recorder_->GetVsyncTargetTime());
160
161 auto layer_tree_item = std::make_unique<LayerTreeItem>(
162 args: std::move(layer_tree), args: std::move(frame_timings_recorder_),
163 args&: device_pixel_ratio);
164 // Commit the pending continuation.
165 PipelineProduceResult result =
166 producer_continuation_.Complete(resource: std::move(layer_tree_item));
167
168 if (!result.success) {
169 FML_DLOG(INFO) << "No pending continuation to commit";
170 return;
171 }
172
173 if (!result.is_first_item) {
174 // It has been successfully pushed to the pipeline but not as the first
175 // item. Eventually the 'Rasterizer' will consume it, so we don't need to
176 // notify the delegate.
177 return;
178 }
179
180 delegate_.OnAnimatorDraw(pipeline: layer_tree_pipeline_);
181}
182
183const std::weak_ptr<VsyncWaiter> Animator::GetVsyncWaiter() const {
184 std::weak_ptr<VsyncWaiter> weak = waiter_;
185 return weak;
186}
187
188bool Animator::CanReuseLastLayerTree() {
189 return !regenerate_layer_tree_;
190}
191
192void Animator::DrawLastLayerTree(
193 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
194 // This method is very cheap, but this makes it explicitly clear in trace
195 // files.
196 TRACE_EVENT0("flutter", "Animator::DrawLastLayerTree");
197
198 pending_frame_semaphore_.Signal();
199 // In this case BeginFrame doesn't get called, we need to
200 // adjust frame timings to update build start and end times,
201 // given that the frame doesn't get built in this case, we
202 // will use Now() for both start and end times as an indication.
203 const auto now = fml::TimePoint::Now();
204 frame_timings_recorder->RecordBuildStart(build_start: now);
205 frame_timings_recorder->RecordBuildEnd(build_end: now);
206 delegate_.OnAnimatorDrawLastLayerTree(frame_timings_recorder: std::move(frame_timings_recorder));
207}
208
209void Animator::RequestFrame(bool regenerate_layer_tree) {
210 if (regenerate_layer_tree) {
211 // This event will be closed by BeginFrame. BeginFrame will only be called
212 // if regenerating the layer tree. If a frame has been requested to update
213 // an external texture, this will be false and no BeginFrame call will
214 // happen.
215 TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending",
216 frame_request_number_);
217 regenerate_layer_tree_ = true;
218 }
219
220 if (!pending_frame_semaphore_.TryWait()) {
221 // Multiple calls to Animator::RequestFrame will still result in a
222 // single request to the VsyncWaiter.
223 return;
224 }
225
226 // The AwaitVSync is going to call us back at the next VSync. However, we want
227 // to be reasonably certain that the UI thread is not in the middle of a
228 // particularly expensive callout. We post the AwaitVSync to run right after
229 // an idle. This does NOT provide a guarantee that the UI thread has not
230 // started an expensive operation right after posting this message however.
231 // To support that, we need edge triggered wakes on VSync.
232
233 task_runners_.GetUITaskRunner()->PostTask(
234 task: [self = weak_factory_.GetWeakPtr()]() {
235 if (!self) {
236 return;
237 }
238 self->AwaitVSync();
239 });
240 frame_scheduled_ = true;
241}
242
243void Animator::AwaitVSync() {
244 waiter_->AsyncWaitForVsync(
245 callback: [self = weak_factory_.GetWeakPtr()](
246 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
247 if (self) {
248 if (self->CanReuseLastLayerTree()) {
249 self->DrawLastLayerTree(frame_timings_recorder: std::move(frame_timings_recorder));
250 } else {
251 self->BeginFrame(frame_timings_recorder: std::move(frame_timings_recorder));
252 }
253 }
254 });
255 if (has_rendered_) {
256 delegate_.OnAnimatorNotifyIdle(deadline: dart_frame_deadline_);
257 }
258}
259
260void Animator::ScheduleSecondaryVsyncCallback(uintptr_t id,
261 const fml::closure& callback) {
262 waiter_->ScheduleSecondaryCallback(id, callback);
263}
264
265void Animator::ScheduleMaybeClearTraceFlowIds() {
266 waiter_->ScheduleSecondaryCallback(
267 id: reinterpret_cast<uintptr_t>(this), callback: [self = weak_factory_.GetWeakPtr()] {
268 if (!self) {
269 return;
270 }
271 if (!self->frame_scheduled_ && !self->trace_flow_ids_.empty()) {
272 size_t flow_id_count = self->trace_flow_ids_.size();
273 std::unique_ptr<uint64_t[]> flow_ids =
274 std::make_unique<uint64_t[]>(n: flow_id_count);
275 for (size_t i = 0; i < flow_id_count; ++i) {
276 flow_ids.get()[i] = self->trace_flow_ids_.at(i: i);
277 }
278
279 TRACE_EVENT0_WITH_FLOW_IDS(
280 "flutter", "Animator::ScheduleMaybeClearTraceFlowIds - callback",
281 flow_id_count, flow_ids.get());
282
283 while (!self->trace_flow_ids_.empty()) {
284 auto flow_id = self->trace_flow_ids_.front();
285 TRACE_FLOW_END("flutter", "PointerEvent", flow_id);
286 self->trace_flow_ids_.pop_front();
287 }
288 }
289 });
290}
291
292} // namespace flutter
293

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com

source code of flutter_engine/flutter/shell/common/animator.cc