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