Flutter Engine
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/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 
12 namespace flutter {
13 
14 namespace {
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.
19 constexpr fml::TimeDelta kNotifyIdleTaskWaitTime =
21 
22 } // namespace
23 
25  TaskRunners task_runners,
26  std::unique_ptr<VsyncWaiter> waiter)
27  : delegate_(delegate),
28  task_runners_(std::move(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  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 
46 Animator::~Animator() = default;
47 
49  paused_ = true;
50 }
51 
53  if (!paused_) {
54  return;
55  }
56 
57  paused_ = false;
58  RequestFrame();
59 }
60 
61 // Indicate that screen dimensions will be changing in order to force rendering
62 // of an updated frame even if the animator is currently paused.
64  dimension_change_pending_ = true;
65 }
66 
67 void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) {
69  task_runners_.GetUITaskRunner(),
70  [self = weak_factory_.GetWeakPtr(), trace_flow_id] {
71  if (!self) {
72  return;
73  }
74  self->trace_flow_ids_.push_back(trace_flow_id);
75  self->ScheduleMaybeClearTraceFlowIds();
76  });
77 }
78 
79 // This Parity is used by the timeline component to correctly align
80 // GPU Workloads events with their respective Framework Workload.
81 const char* Animator::FrameParity() {
82  if (!frame_timings_recorder_) {
83  return "even";
84  }
85  uint64_t frame_number = frame_timings_recorder_->GetFrameNumber();
86  return (frame_number % 2) ? "even" : "odd";
87 }
88 
90  auto dart_now = fml::TimeDelta::FromMicroseconds(Dart_TimelineGetMicros());
92  return fml::TimePoint::FromEpochDelta(time - fxl_now + dart_now);
93 }
94 
95 void Animator::BeginFrame(
96  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
97  TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending",
98  frame_request_number_);
99  frame_request_number_++;
100 
101  frame_timings_recorder_ = std::move(frame_timings_recorder);
102  frame_timings_recorder_->RecordBuildStart(fml::TimePoint::Now());
103 
104  TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter",
105  "Animator::BeginFrame");
106  while (!trace_flow_ids_.empty()) {
107  uint64_t trace_flow_id = trace_flow_ids_.front();
108  TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
109  trace_flow_ids_.pop_front();
110  }
111 
112  frame_scheduled_ = false;
113  notify_idle_task_id_++;
114  regenerate_layer_tree_ = false;
115  pending_frame_semaphore_.Signal();
116 
117  if (!producer_continuation_) {
118  // We may already have a valid pipeline continuation in case a previous
119  // begin frame did not result in an Animation::Render. Simply reuse that
120  // instead of asking the pipeline for a fresh continuation.
121  producer_continuation_ = layer_tree_pipeline_->Produce();
122 
123  if (!producer_continuation_) {
124  // If we still don't have valid continuation, the pipeline is currently
125  // full because the consumer is being too slow. Try again at the next
126  // frame interval.
127  TRACE_EVENT0("flutter", "PipelineFull");
128  RequestFrame();
129  return;
130  }
131  }
132 
133  // We have acquired a valid continuation from the pipeline and are ready
134  // to service potential frame.
135  FML_DCHECK(producer_continuation_);
137  "flutter", "VsyncSchedulingOverhead",
138  frame_timings_recorder_->GetVsyncStartTime(),
139  frame_timings_recorder_->GetBuildStartTime());
140  const fml::TimePoint frame_target_time =
141  frame_timings_recorder_->GetVsyncTargetTime();
142  dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
143  {
144  TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
145  FrameParity());
146  uint64_t frame_number = frame_timings_recorder_->GetFrameNumber();
147  delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
148  }
149 
150  if (!frame_scheduled_ && has_rendered_) {
151  // Under certain workloads (such as our parent view resizing us, which is
152  // communicated to us by repeat viewport metrics events), we won't
153  // actually have a frame scheduled yet, despite the fact that we *will* be
154  // producing a frame next vsync (it will be scheduled once we receive the
155  // viewport event). Because of this, we hold off on calling
156  // |OnAnimatorNotifyIdle| for a little bit, as that could cause garbage
157  // collection to trigger at a highly undesirable time.
158  task_runners_.GetUITaskRunner()->PostDelayedTask(
159  [self = weak_factory_.GetWeakPtr(),
160  notify_idle_task_id = notify_idle_task_id_]() {
161  if (!self) {
162  return;
163  }
164  // If our (this task's) task id is the same as the current one
165  // (meaning there were no follow up frames to the |BeginFrame| call
166  // that posted this task) and no frame is currently scheduled, then
167  // assume that we are idle, and notify the engine of this.
168  if (notify_idle_task_id == self->notify_idle_task_id_ &&
169  !self->frame_scheduled_) {
170  TRACE_EVENT0("flutter", "BeginFrame idle callback");
171  self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
172  100000);
173  }
174  },
175  kNotifyIdleTaskWaitTime);
176  }
177 }
178 
179 void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
180  has_rendered_ = true;
181  if (dimension_change_pending_ &&
182  layer_tree->frame_size() != last_layer_tree_size_) {
183  dimension_change_pending_ = false;
184  }
185  last_layer_tree_size_ = layer_tree->frame_size();
186 
187  if (!frame_timings_recorder_) {
188  // Framework can directly call render with a built scene.
189  frame_timings_recorder_ = std::make_unique<FrameTimingsRecorder>();
190  const fml::TimePoint placeholder_time = fml::TimePoint::Now();
191  frame_timings_recorder_->RecordVsync(placeholder_time, placeholder_time);
192  frame_timings_recorder_->RecordBuildStart(placeholder_time);
193  }
194 
195  TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder_, "flutter",
196  "Animator::Render");
197  frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now());
198 
199  // Commit the pending continuation.
200  bool result = producer_continuation_.Complete(std::move(layer_tree));
201  if (!result) {
202  FML_DLOG(INFO) << "No pending continuation to commit";
203  }
204 
205  delegate_.OnAnimatorDraw(layer_tree_pipeline_,
206  std::move(frame_timings_recorder_));
207 }
208 
209 bool Animator::CanReuseLastLayerTree() {
210  return !regenerate_layer_tree_;
211 }
212 
213 void Animator::DrawLastLayerTree(
214  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
215  pending_frame_semaphore_.Signal();
216  // In this case BeginFrame doesn't get called, we need to
217  // adjust frame timings to update build start and end times,
218  // given that the frame doesn't get built in this case, we
219  // will use Now() for both start and end times as an indication.
220  const auto now = fml::TimePoint::Now();
221  frame_timings_recorder->RecordBuildStart(now);
222  frame_timings_recorder->RecordBuildEnd(now);
223  delegate_.OnAnimatorDrawLastLayerTree(std::move(frame_timings_recorder));
224 }
225 
226 void Animator::RequestFrame(bool regenerate_layer_tree) {
227  if (regenerate_layer_tree) {
228  regenerate_layer_tree_ = true;
229  }
230  if (paused_ && !dimension_change_pending_) {
231  return;
232  }
233 
234  if (!pending_frame_semaphore_.TryWait()) {
235  // Multiple calls to Animator::RequestFrame will still result in a
236  // single request to the VsyncWaiter.
237  return;
238  }
239 
240  // The AwaitVSync is going to call us back at the next VSync. However, we want
241  // to be reasonably certain that the UI thread is not in the middle of a
242  // particularly expensive callout. We post the AwaitVSync to run right after
243  // an idle. This does NOT provide a guarantee that the UI thread has not
244  // started an expensive operation right after posting this message however.
245  // To support that, we need edge triggered wakes on VSync.
246 
247  task_runners_.GetUITaskRunner()->PostTask(
248  [self = weak_factory_.GetWeakPtr(),
249  frame_request_number = frame_request_number_]() {
250  if (!self) {
251  return;
252  }
253  TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending",
254  frame_request_number);
255  self->AwaitVSync();
256  });
257  frame_scheduled_ = true;
258 }
259 
260 void Animator::AwaitVSync() {
261  waiter_->AsyncWaitForVsync(
262  [self = weak_factory_.GetWeakPtr()](
263  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
264  if (self) {
265  if (self->CanReuseLastLayerTree()) {
266  self->DrawLastLayerTree(std::move(frame_timings_recorder));
267  } else {
268  self->BeginFrame(std::move(frame_timings_recorder));
269  }
270  }
271  });
272  if (has_rendered_) {
273  delegate_.OnAnimatorNotifyIdle(
274  dart_frame_deadline_.ToEpochDelta().ToMicroseconds());
275  }
276 }
277 
279  const fml::closure& callback) {
280  waiter_->ScheduleSecondaryCallback(id, callback);
281 }
282 
283 void Animator::ScheduleMaybeClearTraceFlowIds() {
284  waiter_->ScheduleSecondaryCallback(
285  reinterpret_cast<uintptr_t>(this), [self = weak_factory_.GetWeakPtr()] {
286  if (!self) {
287  return;
288  }
289  if (!self->frame_scheduled_ && !self->trace_flow_ids_.empty()) {
290  TRACE_EVENT0("flutter",
291  "Animator::ScheduleMaybeClearTraceFlowIds - callback");
292  while (!self->trace_flow_ids_.empty()) {
293  auto flow_id = self->trace_flow_ids_.front();
294  TRACE_FLOW_END("flutter", "PointerEvent", flow_id);
295  self->trace_flow_ids_.pop_front();
296  }
297  }
298  });
299 }
300 
301 } // namespace flutter
void SetDimensionChangePending()
Definition: animator.cc:63
if(match !=nullptr)
Definition: fl_engine.cc:79
virtual void OnAnimatorNotifyIdle(int64_t deadline)=0
static fml::TimePoint FxlToDartOrEarlier(fml::TimePoint time)
Definition: animator.cc:89
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
MockDelegate delegate_
#define FML_DCHECK(condition)
Definition: logging.h:86
virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, uint64_t frame_number)=0
#define TRACE_FLOW_END(category, name, id)
Definition: trace_event.h:136
bool Complete(ResourcePtr resource)
Definition: pipeline.h:63
void EnqueueTraceFlowId(uint64_t trace_flow_id)
Definition: animator.cc:67
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
Animator(Delegate &delegate, TaskRunners task_runners, std::unique_ptr< VsyncWaiter > waiter)
Definition: animator.cc:24
void RequestFrame(bool regenerate_layer_tree=true)
Definition: animator.cc:226
Definition: ref_ptr.h:252
GAsyncResult * result
TimeDelta ToEpochDelta() const
Definition: time_point.h:47
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition: time_point.h:38
#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id)
Definition: trace_event.h:104
FlKeyEvent FlKeyResponderAsyncCallback callback
else
Definition: fl_engine.cc:82
std::function< void()> closure
Definition: closure.h:14
#define TRACE_EVENT_WITH_FRAME_NUMBER(recorder, category_group, name)
Definition: frame_timings.h:16
constexpr int64_t ToMicroseconds() const
Definition: time_delta.h:62
static constexpr TimeDelta FromMicroseconds(int64_t micros)
Definition: time_delta.h:43
void Render(std::unique_ptr< flutter::LayerTree > layer_tree)
Definition: animator.cc:179
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
#define TRACE_EVENT_ASYNC_END0(category_group, name, id)
Definition: trace_event.h:107
void ScheduleSecondaryVsyncCallback(uintptr_t id, const fml::closure &callback)
Schedule a secondary callback to be executed right after the main VsyncWaiter::AsyncWaitForVsync call...
Definition: animator.cc:278
virtual void OnAnimatorDrawLastLayerTree(std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder)=0
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
Definition: trace_event.h:98
bool TryWait()
Definition: semaphore.cc:157
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
TaskRunners task_runners_
#define FML_DLOG(severity)
Definition: logging.h:85
void TraceEventAsyncComplete(TraceArg category_group, TraceArg name, TimePoint begin, TimePoint end)
Definition: trace_event.cc:333
virtual void OnAnimatorDraw(std::shared_ptr< Pipeline< flutter::LayerTree >> pipeline, std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder)=0
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
static TimePoint Now()
Definition: time_point.cc:39
virtual void PostDelayedTask(const fml::closure &task, fml::TimeDelta delay)
Definition: task_runner.cc:33