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/fml/trace_event.h"
8 #include "third_party/dart/runtime/include/dart_tools_api.h"
9 
10 namespace flutter {
11 
12 namespace {
13 
14 // Wait 51 milliseconds (which is 1 more milliseconds than 3 frames at 60hz)
15 // before notifying the engine that we are idle. See comments in |BeginFrame|
16 // for further discussion on why this is necessary.
17 constexpr fml::TimeDelta kNotifyIdleTaskWaitTime =
19 
20 } // namespace
21 
23  TaskRunners task_runners,
24  std::unique_ptr<VsyncWaiter> waiter)
25  : delegate_(delegate),
26  task_runners_(std::move(task_runners)),
27  waiter_(std::move(waiter)),
28  last_frame_begin_time_(),
29  last_vsync_start_time_(),
30  last_frame_target_time_(),
31  dart_frame_deadline_(0),
32 #if FLUTTER_SHELL_ENABLE_METAL
33  layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)),
34 #else // FLUTTER_SHELL_ENABLE_METAL
35  // TODO(dnfield): We should remove this logic and set the pipeline depth
36  // back to 2 in this case. See
37  // https://github.com/flutter/engine/pull/9132 for discussion.
38  layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(
39  task_runners.GetPlatformTaskRunner() ==
40  task_runners.GetRasterTaskRunner()
41  ? 1
42  : 2)),
43 #endif // FLUTTER_SHELL_ENABLE_METAL
44  pending_frame_semaphore_(1),
45  frame_number_(1),
46  paused_(false),
47  regenerate_layer_tree_(false),
48  frame_scheduled_(false),
49  notify_idle_task_id_(0),
50  dimension_change_pending_(false),
51  weak_factory_(this) {
52 }
53 
54 Animator::~Animator() = default;
55 
57  paused_ = true;
58 }
59 
61  if (!paused_) {
62  return;
63  }
64 
65  paused_ = false;
66  RequestFrame();
67 }
68 
69 // Indicate that screen dimensions will be changing in order to force rendering
70 // of an updated frame even if the animator is currently paused.
72  dimension_change_pending_ = true;
73 }
74 
75 void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) {
77  task_runners_.GetUITaskRunner(),
78  [self = weak_factory_.GetWeakPtr(), trace_flow_id] {
79  if (!self) {
80  return;
81  }
82  self->trace_flow_ids_.push_back(trace_flow_id);
83  });
84 }
85 
86 // This Parity is used by the timeline component to correctly align
87 // GPU Workloads events with their respective Framework Workload.
88 const char* Animator::FrameParity() {
89  return (frame_number_ % 2) ? "even" : "odd";
90 }
91 
92 static int64_t FxlToDartOrEarlier(fml::TimePoint time) {
93  int64_t dart_now = Dart_TimelineGetMicros();
95  return (time - fxl_now).ToMicroseconds() + dart_now;
96 }
97 
98 void Animator::BeginFrame(fml::TimePoint vsync_start_time,
99  fml::TimePoint frame_target_time) {
100  TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);
101 
102  TRACE_EVENT0("flutter", "Animator::BeginFrame");
103  while (!trace_flow_ids_.empty()) {
104  uint64_t trace_flow_id = trace_flow_ids_.front();
105  TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
106  trace_flow_ids_.pop_front();
107  }
108 
109  frame_scheduled_ = false;
110  notify_idle_task_id_++;
111  regenerate_layer_tree_ = false;
112  pending_frame_semaphore_.Signal();
113 
114  if (!producer_continuation_) {
115  // We may already have a valid pipeline continuation in case a previous
116  // begin frame did not result in an Animation::Render. Simply reuse that
117  // instead of asking the pipeline for a fresh continuation.
118  producer_continuation_ = layer_tree_pipeline_->Produce();
119 
120  if (!producer_continuation_) {
121  // If we still don't have valid continuation, the pipeline is currently
122  // full because the consumer is being too slow. Try again at the next
123  // frame interval.
124  RequestFrame();
125  return;
126  }
127  }
128 
129  // We have acquired a valid continuation from the pipeline and are ready
130  // to service potential frame.
131  FML_DCHECK(producer_continuation_);
132 
133  last_frame_begin_time_ = fml::TimePoint::Now();
134  last_vsync_start_time_ = vsync_start_time;
135  fml::tracing::TraceEventAsyncComplete("flutter", "VsyncSchedulingOverhead",
136  last_vsync_start_time_,
137  last_frame_begin_time_);
138  last_frame_target_time_ = frame_target_time;
139  dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
140  {
141  TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
142  FrameParity());
143  delegate_.OnAnimatorBeginFrame(frame_target_time);
144  }
145 
146  if (!frame_scheduled_) {
147  // Under certain workloads (such as our parent view resizing us, which is
148  // communicated to us by repeat viewport metrics events), we won't
149  // actually have a frame scheduled yet, despite the fact that we *will* be
150  // producing a frame next vsync (it will be scheduled once we receive the
151  // viewport event). Because of this, we hold off on calling
152  // |OnAnimatorNotifyIdle| for a little bit, as that could cause garbage
153  // collection to trigger at a highly undesirable time.
154  task_runners_.GetUITaskRunner()->PostDelayedTask(
155  [self = weak_factory_.GetWeakPtr(),
156  notify_idle_task_id = notify_idle_task_id_]() {
157  if (!self) {
158  return;
159  }
160  // If our (this task's) task id is the same as the current one
161  // (meaning there were no follow up frames to the |BeginFrame| call
162  // that posted this task) and no frame is currently scheduled, then
163  // assume that we are idle, and notify the engine of this.
164  if (notify_idle_task_id == self->notify_idle_task_id_ &&
165  !self->frame_scheduled_) {
166  TRACE_EVENT0("flutter", "BeginFrame idle callback");
167  self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
168  100000);
169  }
170  },
171  kNotifyIdleTaskWaitTime);
172  }
173 }
174 
175 void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
176  if (dimension_change_pending_ &&
177  layer_tree->frame_size() != last_layer_tree_size_) {
178  dimension_change_pending_ = false;
179  }
180  last_layer_tree_size_ = layer_tree->frame_size();
181 
182  // Note the frame time for instrumentation.
183  layer_tree->RecordBuildTime(last_vsync_start_time_, last_frame_begin_time_,
184  last_frame_target_time_);
185 
186  // Commit the pending continuation.
187  bool result = producer_continuation_.Complete(std::move(layer_tree));
188  if (!result) {
189  FML_DLOG(INFO) << "No pending continuation to commit";
190  }
191 
192  delegate_.OnAnimatorDraw(layer_tree_pipeline_, last_frame_target_time_);
193 }
194 
195 bool Animator::CanReuseLastLayerTree() {
196  return !regenerate_layer_tree_;
197 }
198 
199 void Animator::DrawLastLayerTree() {
200  pending_frame_semaphore_.Signal();
201  delegate_.OnAnimatorDrawLastLayerTree();
202 }
203 
204 void Animator::RequestFrame(bool regenerate_layer_tree) {
205  if (regenerate_layer_tree) {
206  regenerate_layer_tree_ = true;
207  }
208  if (paused_ && !dimension_change_pending_) {
209  return;
210  }
211 
212  if (!pending_frame_semaphore_.TryWait()) {
213  // Multiple calls to Animator::RequestFrame will still result in a
214  // single request to the VsyncWaiter.
215  return;
216  }
217 
218  // The AwaitVSync is going to call us back at the next VSync. However, we want
219  // to be reasonably certain that the UI thread is not in the middle of a
220  // particularly expensive callout. We post the AwaitVSync to run right after
221  // an idle. This does NOT provide a guarantee that the UI thread has not
222  // started an expensive operation right after posting this message however.
223  // To support that, we need edge triggered wakes on VSync.
224 
225  task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
226  frame_number = frame_number_]() {
227  if (!self) {
228  return;
229  }
230  TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
231  self->AwaitVSync();
232  });
233  frame_scheduled_ = true;
234 }
235 
236 void Animator::AwaitVSync() {
237  waiter_->AsyncWaitForVsync(
238  [self = weak_factory_.GetWeakPtr()](fml::TimePoint vsync_start_time,
239  fml::TimePoint frame_target_time) {
240  if (self) {
241  if (self->CanReuseLastLayerTree()) {
242  self->DrawLastLayerTree();
243  } else {
244  self->BeginFrame(vsync_start_time, frame_target_time);
245  }
246  }
247  });
248 
249  delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
250 }
251 
253  waiter_->ScheduleSecondaryCallback(callback);
254 }
255 
256 } // namespace flutter
void SetDimensionChangePending()
Definition: animator.cc:71
virtual void OnAnimatorNotifyIdle(int64_t deadline)=0
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
virtual void OnAnimatorDraw(fml::RefPtr< Pipeline< flutter::LayerTree >> pipeline, fml::TimePoint frame_target_time)=0
#define FML_DCHECK(condition)
Definition: logging.h:86
#define TRACE_FLOW_END(category, name, id)
Definition: trace_event.h:121
bool Complete(ResourcePtr resource)
Definition: pipeline.h:63
void EnqueueTraceFlowId(uint64_t trace_flow_id)
Definition: animator.cc:75
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:22
void RequestFrame(bool regenerate_layer_tree=true)
Definition: animator.cc:204
Definition: ref_ptr.h:252
virtual void PostTask(const fml::closure &task)
Definition: task_runner.cc:24
#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id)
Definition: trace_event.h:89
static int64_t FxlToDartOrEarlier(fml::TimePoint time)
Definition: animator.cc:92
Definition: ascii_trie.cc:9
TaskRunners task_runners_
MockDelegate delegate_
std::function< void()> closure
Definition: closure.h:14
void Render(std::unique_ptr< flutter::LayerTree > layer_tree)
Definition: animator.cc:175
virtual void OnAnimatorDrawLastLayerTree()=0
virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time)=0
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
RefPtr< T > MakeRefCounted(Args &&... args)
Definition: ref_ptr.h:244
#define TRACE_EVENT_ASYNC_END0(category_group, name, id)
Definition: trace_event.h:92
void ScheduleSecondaryVsyncCallback(const fml::closure &callback)
Schedule a secondary callback to be executed right after the main VsyncWaiter::AsyncWaitForVsync call...
Definition: animator.cc:252
if(event->type==GDK_BUTTON_PRESS)
Definition: fl_view.cc:79
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
Definition: trace_event.h:83
bool TryWait()
Definition: semaphore.cc:157
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition: time_delta.h:46
#define FML_DLOG(severity)
Definition: logging.h:85
void TraceEventAsyncComplete(TraceArg category_group, TraceArg name, TimePoint begin, TimePoint end)
Definition: trace_event.cc:326
static TimePoint Now()
Definition: time_point.cc:26
virtual void PostDelayedTask(const fml::closure &task, fml::TimeDelta delay)
Definition: task_runner.cc:33