Flutter Engine
vsync_waiter.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 "vsync_waiter.h"
6 
7 #include <cstdint>
8 
9 #include <lib/async/default.h>
10 
11 #include "flutter/fml/logging.h"
12 #include "flutter/fml/make_copyable.h"
13 #include "flutter/fml/synchronization/waitable_event.h"
14 #include "flutter/fml/time/time_delta.h"
15 #include "flutter/fml/trace_event.h"
16 
18 #include "vsync_recorder.h"
19 
20 namespace flutter_runner {
21 
22 VsyncWaiter::VsyncWaiter(std::string debug_label,
23  zx_handle_t session_present_handle,
24  flutter::TaskRunners task_runners,
25  fml::TimeDelta vsync_offset)
26  : flutter::VsyncWaiter(task_runners),
27  debug_label_(std::move(debug_label)),
28  session_wait_(session_present_handle, SessionPresentSignal),
29  vsync_offset_(vsync_offset),
30  weak_factory_ui_(nullptr),
31  weak_factory_(this) {
32  auto wait_handler = [&](async_dispatcher_t* dispatcher, //
33  async::Wait* wait, //
34  zx_status_t status, //
35  const zx_packet_signal_t* signal //
36  ) {
37  if (status != ZX_OK) {
38  FML_LOG(ERROR) << "Vsync wait failed.";
39  return;
40  }
41 
42  wait->Cancel();
43 
44  FireCallbackNow();
45  };
46 
47  // Generate a WeakPtrFactory for use with the UI thread. This does not need
48  // to wait on a latch because we only ever use the WeakPtrFactory on the UI
49  // thread so we have ordering guarantees (see ::AwaitVSync())
51  task_runners_.GetUITaskRunner(), fml::MakeCopyable([this]() mutable {
52  this->weak_factory_ui_ =
53  std::make_unique<fml::WeakPtrFactory<VsyncWaiter>>(this);
54  }));
55  session_wait_.set_handler(wait_handler);
56 
57  if (vsync_offset_ >= fml::TimeDelta::FromSeconds(1)) {
58  FML_LOG(WARNING) << "Given vsync_offset is extremely high: "
59  << vsync_offset_.ToMilliseconds() << "ms";
60  } else {
61  FML_LOG(INFO) << "Set vsync_offset to " << vsync_offset_.ToMicroseconds()
62  << "us";
63  }
64 }
65 
67  session_wait_.Cancel();
68 
73  [weak_factory_ui = std::move(weak_factory_ui_), &ui_latch]() mutable {
74  weak_factory_ui.reset();
75  ui_latch.Signal();
76  }));
77  ui_latch.Wait();
78 }
79 
80 /// Returns the system time at which the next frame is likely to be presented.
81 ///
82 /// Consider the following scenarios, where in both the
83 /// scenarious the result will be the same.
84 ///
85 /// Scenario 1:
86 /// presentation_interval is 2
87 /// ^ ^ ^ ^ ^
88 /// + + + + +
89 /// 0--1--2--3--4--5--6--7--8--9--
90 /// + + +
91 /// | | +---------> result: next_presentation_time
92 /// | v
93 /// v now
94 /// last_presentation_time
95 ///
96 /// Scenario 2:
97 /// presentation_interval is 2
98 /// ^ ^ ^ ^ ^
99 /// + + + + +
100 /// 0--1--2--3--4--5--6--7--8--9--
101 /// + + +
102 /// | | +--------->result: next_presentation_time
103 /// | |
104 /// | +>now
105 /// |
106 /// +->last_presentation_time
108  const fml::TimePoint now,
109  const fml::TimePoint last_frame_presentation_time,
110  const fml::TimeDelta presentation_interval) {
111  if (presentation_interval <= fml::TimeDelta::Zero()) {
112  FML_LOG(ERROR) << "Presentation interval must be positive. The value was: "
113  << presentation_interval.ToMilliseconds() << "ms.";
114  return now;
115  }
116 
117  if (last_frame_presentation_time >= now) {
118  FML_LOG(ERROR)
119  << "Last frame was presented in the future. Clamping to now.";
120  return now + presentation_interval;
121  }
122 
123  const fml::TimeDelta time_since_last_presentation =
124  now - last_frame_presentation_time;
125  // this will be the most likely scenario if we are rendering at a good
126  // frame rate, short circuiting the other checks in this case.
127  if (time_since_last_presentation < presentation_interval) {
128  return last_frame_presentation_time + presentation_interval;
129  } else {
130  const int64_t num_phases_passed =
131  (time_since_last_presentation / presentation_interval);
132  return last_frame_presentation_time +
133  (presentation_interval * (num_phases_passed + 1));
134  }
135 }
136 
137 void VsyncWaiter::AwaitVSync() {
139 
141  fml::TimePoint last_presentation_time =
143 
144  fml::TimePoint next_vsync =
146 
147  if (next_vsync <= now) {
148  next_vsync = SnapToNextPhase(now, last_presentation_time,
149  vsync_info.presentation_interval);
150  }
151 
152  auto next_vsync_start_time = next_vsync - vsync_offset_;
153 
154  if (now >= next_vsync_start_time)
155  next_vsync_start_time =
156  next_vsync_start_time + vsync_info.presentation_interval;
157 
158  fml::TimeDelta delta = next_vsync_start_time - now;
159 
161  [&weak_factory_ui = this->weak_factory_ui_] {
162  if (!weak_factory_ui) {
163  FML_LOG(WARNING) << "WeakPtrFactory for VsyncWaiter is null, likely "
164  "due to the VsyncWaiter being destroyed.";
165  return;
166  }
167  auto self = weak_factory_ui->GetWeakPtr();
168  if (self) {
169  self->FireCallbackWhenSessionAvailable();
170  }
171  },
172  delta);
173 }
174 
175 void VsyncWaiter::FireCallbackWhenSessionAvailable() {
176  TRACE_EVENT0("flutter", "VsyncWaiter::FireCallbackWhenSessionAvailable");
178  if (session_wait_.Begin(async_get_default_dispatcher()) != ZX_OK) {
179  FML_LOG(ERROR) << "Could not begin wait for Vsync.";
180  }
181 }
182 
183 void VsyncWaiter::FireCallbackNow() {
185 
187 
189  fml::TimePoint last_presentation_time =
191  fml::TimePoint next_vsync =
193 
194  if (next_vsync <= now) {
195  next_vsync = SnapToNextPhase(now, last_presentation_time,
196  vsync_info.presentation_interval);
197  }
198  fml::TimePoint previous_vsync = next_vsync - vsync_info.presentation_interval;
199 
200  FireCallback(previous_vsync, next_vsync);
201 }
202 
203 } // namespace flutter_runner
static constexpr TimeDelta FromSeconds(int64_t seconds)
Definition: time_delta.h:49
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
#define FML_DCHECK(condition)
Definition: logging.h:86
static constexpr TimeDelta Zero()
Definition: time_delta.h:33
constexpr int64_t ToMilliseconds() const
Definition: time_delta.h:63
fml::TimeDelta presentation_interval
static fml::TimePoint SnapToNextPhase(const fml::TimePoint now, const fml::TimePoint last_frame_presentation_time, const fml::TimeDelta presentation_interval)
static void RunNowOrPostTask(fml::RefPtr< fml::TaskRunner > runner, const fml::closure &task)
Definition: task_runner.cc:55
Definition: ref_ptr.h:252
VsyncWaiter(std::string debug_label, zx_handle_t session_present_handle, flutter::TaskRunners task_runners, fml::TimeDelta vsync_offset)
Definition: vsync_waiter.cc:22
#define FML_LOG(severity)
Definition: logging.h:65
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
fml::TimePoint GetLastPresentationTime() const
constexpr int64_t ToMicroseconds() const
Definition: time_delta.h:62
VsyncInfo GetCurrentVsyncInfo() const
fml::TimePoint presentation_time
fml::RefPtr< fml::TaskRunner > GetUITaskRunner() const
Definition: task_runners.cc:34
static VsyncRecorder & GetInstance()
void FireCallback(fml::TimePoint frame_start_time, fml::TimePoint frame_target_time)
Definition: vsync_waiter.cc:91
const TaskRunners task_runners_
Definition: vsync_waiter.h:39
virtual bool RunsTasksOnCurrentThread()
Definition: task_runner.cc:43
static TimePoint Now()
Definition: time_point.cc:26
virtual void PostDelayedTask(const fml::closure &task, fml::TimeDelta delay)
Definition: task_runner.cc:33