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