Flutter Engine
The Flutter Engine
flatland_connection.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
6
7#include <lib/async/cpp/task.h>
8#include <lib/async/default.h>
9
10#include <zircon/rights.h>
11#include <zircon/status.h>
12#include <zircon/types.h>
13
14#include <utility>
15
16#include "flutter/fml/logging.h"
17
18namespace flutter_runner {
19
20namespace {
21
22// Helper function for traces.
23double DeltaFromNowInNanoseconds(const fml::TimePoint& now,
24 const fml::TimePoint& time) {
25 return (time - now).ToNanoseconds();
26}
27
28} // namespace
29
31 const std::string& debug_label,
32 fuchsia::ui::composition::FlatlandHandle flatland,
33 fml::closure error_callback,
34 on_frame_presented_event on_frame_presented_callback,
35 async_dispatcher_t* dispatcher)
36 : dispatcher_(dispatcher),
37 flatland_(flatland.Bind()),
38 error_callback_(std::move(error_callback)),
39 on_frame_presented_callback_(std::move(on_frame_presented_callback)) {
40 flatland_.set_error_handler([callback = error_callback_](zx_status_t status) {
41 FML_LOG(ERROR) << "Flatland disconnected: " << zx_status_get_string(status);
42 callback();
43 });
44 debug_label_ = debug_label;
45 flatland_->SetDebugName(debug_label);
46 flatland_.events().OnError =
47 fit::bind_member(this, &FlatlandConnection::OnError);
48 flatland_.events().OnFramePresented =
49 fit::bind_member(this, &FlatlandConnection::OnFramePresented);
50 flatland_.events().OnNextFrameBegin =
51 fit::bind_member(this, &FlatlandConnection::OnNextFrameBegin);
52}
53
55
56// This method is called from the raster thread.
58 TRACE_DURATION("flutter", "FlatlandConnection::Present");
59 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
60 if (threadsafe_state_.present_credits_ > 0) {
61 DoPresent();
62 } else {
63 present_waiting_for_credit_ = true;
64 }
65}
66
67// This method is called from the raster thread.
68void FlatlandConnection::DoPresent() {
69 TRACE_DURATION("flutter", "FlatlandConnection::DoPresent");
70
71 std::string per_app_tracing_name =
72 "Flatland::PerAppPresent[" + debug_label_ + "]";
73 TRACE_FLOW_BEGIN("gfx", per_app_tracing_name.c_str(), next_present_trace_id_);
74 ++next_present_trace_id_;
75
76 FML_CHECK(threadsafe_state_.present_credits_ > 0);
77 --threadsafe_state_.present_credits_;
78
79 fuchsia::ui::composition::PresentArgs present_args;
80 present_args.set_requested_presentation_time(0);
81 present_args.set_acquire_fences(std::move(acquire_fences_));
82
83 // Schedule acquire fence overflow signaling if there is one.
84 if (acquire_overflow_ != nullptr) {
85 FML_CHECK(acquire_overflow_->event_.is_valid());
86 async::PostTask(dispatcher_, [dispatcher = dispatcher_,
87 overflow = acquire_overflow_]() {
88 const size_t fences_size = overflow->fences_.size();
89 std::shared_ptr<size_t> fences_completed = std::make_shared<size_t>(0);
90 std::shared_ptr<std::vector<async::WaitOnce>> closures;
91
92 for (auto i = 0u; i < fences_size; i++) {
93 auto wait = std::make_unique<async::WaitOnce>(
94 overflow->fences_[i].get(), ZX_EVENT_SIGNALED, 0u);
95 auto wait_ptr = wait.get();
96 wait_ptr->Begin(
97 dispatcher,
98 [wait = std::move(wait), overflow, fences_size, fences_completed,
99 closures](async_dispatcher_t*, async::WaitOnce*,
100 zx_status_t status, const zx_packet_signal_t*) {
101 (*fences_completed)++;
102 FML_CHECK(status == ZX_OK)
103 << "status: " << zx_status_get_string(status);
104 if (*fences_completed == fences_size) {
105 // Signal the acquire fence passed on to Flatland.
106 const zx_status_t status =
107 overflow->event_.signal(0, ZX_EVENT_SIGNALED);
108 FML_CHECK(status == ZX_OK)
109 << "status: " << zx_status_get_string(status);
110 }
111 });
112 }
113 });
114 acquire_overflow_.reset();
115 }
116 FML_CHECK(acquire_overflow_ == nullptr);
117
118 present_args.set_release_fences(std::move(previous_present_release_fences_));
119 // Frame rate over latency.
120 present_args.set_unsquashable(true);
121 flatland_->Present(std::move(present_args));
122
123 // In Flatland, release fences apply to the content of the previous present.
124 // Keeping track of the old frame's release fences and swapping ensure we set
125 // the correct ones for VulkanSurface's interpretation.
126 previous_present_release_fences_.clear();
127 previous_present_release_fences_.swap(current_present_release_fences_);
128 previous_release_overflow_ = current_release_overflow_;
129 current_release_overflow_ = nullptr;
130
131 // Similar to the treatment of acquire_fences_overflow_ above. Except in
132 // the other direction.
133 if (previous_release_overflow_ != nullptr) {
134 FML_CHECK(previous_release_overflow_->event_.is_valid());
135
136 std::shared_ptr<Overflow> fences = previous_release_overflow_;
137
138 async::PostTask(dispatcher_, [dispatcher = dispatcher_,
139 fences = previous_release_overflow_]() {
140 FML_CHECK(fences != nullptr);
141 FML_CHECK(fences->event_.is_valid());
142
143 auto wait = std::make_unique<async::WaitOnce>(fences->event_.get(),
144 ZX_EVENT_SIGNALED, 0u);
145 auto wait_ptr = wait.get();
146
147 wait_ptr->Begin(
148 dispatcher, [_wait = std::move(wait), fences](
149 async_dispatcher_t*, async::WaitOnce*,
150 zx_status_t status, const zx_packet_signal_t*) {
151 FML_CHECK(status == ZX_OK)
152 << "status: " << zx_status_get_string(status);
153
154 // Multiplex signaling all events.
155 for (auto& event : fences->fences_) {
156 const zx_status_t status = event.signal(0, ZX_EVENT_SIGNALED);
157 FML_CHECK(status == ZX_OK)
158 << "status: " << zx_status_get_string(status);
159 }
160 });
161 });
162 previous_release_overflow_ = nullptr;
163 }
164 FML_CHECK(previous_release_overflow_ == nullptr); // Moved.
165
166 acquire_fences_.clear();
167}
168
169// This method is called from the UI thread.
171 TRACE_DURATION("flutter", "FlatlandConnection::AwaitVsync");
172
173 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
174 threadsafe_state_.pending_fire_callback_ = nullptr;
175 const auto now = fml::TimePoint::Now();
176
177 // Initial case.
178 if (MaybeRunInitialVsyncCallback(now, callback)) {
179 return;
180 }
181
182 // Throttle case.
183 if (threadsafe_state_.present_credits_ == 0) {
184 threadsafe_state_.pending_fire_callback_ = std::move(callback);
185 return;
186 }
187
188 // Regular case.
189 RunVsyncCallback(now, callback);
190}
191
192// This method is called from the UI thread.
195 TRACE_DURATION("flutter",
196 "FlatlandConnection::AwaitVsyncForSecondaryCallback");
197
198 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
199 const auto now = fml::TimePoint::Now();
200
201 // Initial case.
202 if (MaybeRunInitialVsyncCallback(now, callback)) {
203 return;
204 }
205
206 // Regular case.
207 RunVsyncCallback(now, callback);
208}
209
210// This method is called from the raster thread.
211void FlatlandConnection::OnError(
212 fuchsia::ui::composition::FlatlandError error) {
213 FML_LOG(ERROR) << "Flatland error: " << static_cast<int>(error);
214 error_callback_();
215}
216
217// This method is called from the raster thread.
218void FlatlandConnection::OnNextFrameBegin(
219 fuchsia::ui::composition::OnNextFrameBeginValues values) {
220 // Collect now before locking because this is an important timing information
221 // from Scenic.
222 const auto now = fml::TimePoint::Now();
223
224 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
225 threadsafe_state_.first_feedback_received_ = true;
226 threadsafe_state_.present_credits_ += values.additional_present_credits();
227 TRACE_DURATION("flutter", "FlatlandConnection::OnNextFrameBegin",
228 "present_credits", threadsafe_state_.present_credits_);
229
230 if (present_waiting_for_credit_ && threadsafe_state_.present_credits_ > 0) {
231 DoPresent();
232 present_waiting_for_credit_ = false;
233 }
234
235 // Update vsync_interval_ by calculating the difference between the first two
236 // presentation times. Flatland always returns >1 presentation_infos, so this
237 // check is to guard against any changes to this assumption.
238 if (values.has_future_presentation_infos() &&
239 values.future_presentation_infos().size() > 1) {
240 threadsafe_state_.vsync_interval_ = fml::TimeDelta::FromNanoseconds(
241 values.future_presentation_infos().at(1).presentation_time() -
242 values.future_presentation_infos().at(0).presentation_time());
243 } else {
244 FML_LOG(WARNING)
245 << "Flatland didn't send enough future_presentation_infos to update "
246 "vsync interval.";
247 }
248
249 // Update next_presentation_times_.
250 std::queue<fml::TimePoint> new_times;
251 for (const auto& info : values.future_presentation_infos()) {
252 new_times.emplace(fml::TimePoint::FromEpochDelta(
253 fml::TimeDelta::FromNanoseconds(info.presentation_time())));
254 }
255 threadsafe_state_.next_presentation_times_.swap(new_times);
256
257 // Update vsync_offset_.
258 // We use modulo here because Flatland may point to the following vsync if
259 // OnNextFrameBegin() is called after the current frame's latch point.
260 auto vsync_offset =
262 values.future_presentation_infos().front().presentation_time())) -
263 now) %
264 threadsafe_state_.vsync_interval_;
265 // Thread contention may result in OnNextFrameBegin() being called after the
266 // presentation time. Ignore these outliers.
267 if (vsync_offset > fml::TimeDelta::Zero()) {
268 threadsafe_state_.vsync_offset_ = vsync_offset;
269 }
270
271 // Throttle case.
272 if (threadsafe_state_.pending_fire_callback_ &&
273 threadsafe_state_.present_credits_ > 0) {
274 RunVsyncCallback(now, threadsafe_state_.pending_fire_callback_);
275 threadsafe_state_.pending_fire_callback_ = nullptr;
276 }
277}
278
279// This method is called from the raster thread.
280void FlatlandConnection::OnFramePresented(
281 fuchsia::scenic::scheduling::FramePresentedInfo info) {
282 on_frame_presented_callback_(std::move(info));
283}
284
285// Parses and updates next_presentation_times_.
286fml::TimePoint FlatlandConnection::GetNextPresentationTime(
287 const fml::TimePoint& now) {
288 const fml::TimePoint& cutoff =
289 now > threadsafe_state_.last_presentation_time_
290 ? now
291 : threadsafe_state_.last_presentation_time_;
292
293 // Remove presentation times that may have been passed. This may happen after
294 // a long draw call.
295 while (!threadsafe_state_.next_presentation_times_.empty() &&
296 threadsafe_state_.next_presentation_times_.front() <= cutoff) {
297 threadsafe_state_.next_presentation_times_.pop();
298 }
299
300 // Calculate a presentation time based on
301 // |threadsafe_state_.last_presentation_time_| that is later than cutoff using
302 // |vsync_interval| increments if we don't have any future presentation times
303 // left.
304 if (threadsafe_state_.next_presentation_times_.empty()) {
305 auto result = threadsafe_state_.last_presentation_time_;
306 while (result <= cutoff) {
307 result = result + threadsafe_state_.vsync_interval_;
308 }
309 return result;
310 }
311
312 // Return the next presentation time in the queue for the regular case.
313 const auto result = threadsafe_state_.next_presentation_times_.front();
314 threadsafe_state_.next_presentation_times_.pop();
315 return result;
316}
317
318// This method is called from the UI thread.
319bool FlatlandConnection::MaybeRunInitialVsyncCallback(
320 const fml::TimePoint& now,
322 if (!threadsafe_state_.first_feedback_received_) {
323 TRACE_DURATION("flutter",
324 "FlatlandConnection::MaybeRunInitialVsyncCallback");
325 const auto frame_end = now + kInitialFlatlandVsyncOffset;
326 threadsafe_state_.last_presentation_time_ = frame_end;
327 callback(now, frame_end);
328 return true;
329 }
330 return false;
331}
332
333// This method may be called from the raster or UI thread, but it is safe
334// because VsyncWaiter posts the vsync callback on UI thread.
335void FlatlandConnection::RunVsyncCallback(const fml::TimePoint& now,
337 const auto& frame_end = GetNextPresentationTime(now);
338 const auto& frame_start = frame_end - threadsafe_state_.vsync_offset_;
339 threadsafe_state_.last_presentation_time_ = frame_end;
340 TRACE_DURATION("flutter", "FlatlandConnection::RunVsyncCallback",
341 "frame_start_delta",
342 DeltaFromNowInNanoseconds(now, frame_start), "frame_end_delta",
343 DeltaFromNowInNanoseconds(now, frame_end));
344 callback(frame_start, frame_end);
345}
346
347// Enqueue a single fence into either the "base" vector of fences, or a
348// "special" overflow multiplexer.
349//
350// Args:
351// - fence: the fence to add
352// - fences: the "regular" fences vector to add to.
353// - overflow: the overflow fences vector. Fences added here if there are
354// more than can fit in `fences`.
355static void Enqueue(zx::event fence,
356 std::vector<zx::event>* fences,
357 std::shared_ptr<Overflow>* overflow) {
358 constexpr size_t kMaxFences =
359 fuchsia::ui::composition::MAX_ACQUIRE_RELEASE_FENCE_COUNT;
360
361 // Number of all previously added fences, plus this one.
362 const auto num_all_fences =
363 fences->size() + 1 +
364 ((*overflow == nullptr) ? 0 : (*overflow)->fences_.size());
365
366 // If more than max number of fences come in, schedule any further fences into
367 // an overflow. The overflow fences are scheduled for processing here, but are
368 // processed in DoPresent().
369 if (num_all_fences <= kMaxFences) {
370 fences->push_back(std::move(fence));
371 } else if (num_all_fences == kMaxFences + 1) {
372 // The ownership of the overflow will be handed over to the signaling
373 // closure on DoPresent call. So we always expect that we enter here with
374 // overflow not set.
375 FML_CHECK((*overflow) == nullptr) << "overflow is still active";
376 *overflow = std::make_shared<Overflow>();
377
378 // Set up the overflow fences. Creates an overflow handle, places it
379 // into `fences` instead of the previous fence, and puts the prior fence
380 // and this one into overflow.
381 zx::event overflow_handle = std::move(fences->back());
382 fences->pop_back();
383
384 zx::event overflow_fence;
385 zx_status_t status = zx::event::create(0, &overflow_fence);
386 FML_CHECK(status == ZX_OK) << "status: " << zx_status_get_string(status);
387
388 // Every DoPresent should invalidate this handle. Holler if not.
389 FML_CHECK(!(*overflow)->event_.is_valid()) << "overflow valid";
390 status =
391 overflow_fence.duplicate(ZX_RIGHT_SAME_RIGHTS, &(*overflow)->event_);
392 FML_CHECK(status == ZX_OK) << "status: " << zx_status_get_string(status);
393 fences->push_back(std::move(overflow_fence));
394
395 // Prepare for wait_many call.
396 (*overflow)->fences_.push_back(std::move(overflow_handle));
397 (*overflow)->fences_.push_back(std::move(fence));
398
399 FML_LOG(INFO) << "Enqueue using fence overflow, expect a performance hit.";
400 } else {
401 FML_CHECK((*overflow) != nullptr);
402 // Just add to the overflow fences.
403 (*overflow)->fences_.push_back(std::move(fence));
404 }
405}
406
407// This method is called from the raster thread.
409 Enqueue(std::move(fence), &acquire_fences_, &acquire_overflow_);
410}
411
412// This method is called from the raster thread.
414 Enqueue(std::move(fence), &current_present_release_fences_,
415 &current_release_overflow_);
416}
417
418} // namespace flutter_runner
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
void AwaitVsyncForSecondaryCallback(FireCallbackCallback callback)
FlatlandConnection(const std::string &debug_label, fuchsia::ui::composition::FlatlandHandle flatland, fml::closure error_callback, on_frame_presented_event on_frame_presented_callback, async_dispatcher_t *dispatcher=async_get_default_dispatcher())
void AwaitVsync(FireCallbackCallback callback)
static constexpr TimeDelta FromNanoseconds(int64_t nanos)
Definition: time_delta.h:40
static constexpr TimeDelta Zero()
Definition: time_delta.h:33
static TimePoint Now()
Definition: time_point.cc:49
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition: time_point.h:43
if(end==-1)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
static constexpr size_t kMaxFences
std::function< void(fuchsia::scenic::scheduling::FramePresentedInfo)> on_frame_presented_event
std::function< void(fml::TimePoint, fml::TimePoint)> FireCallbackCallback
Definition: vsync_waiter.h:20
static constexpr fml::TimeDelta kInitialFlatlandVsyncOffset
static void Enqueue(zx::event fence, std::vector< zx::event > *fences, std::shared_ptr< Overflow > *overflow)
std::function< void()> closure
Definition: closure.h:14
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
Definition: ref_ptr.h:256
static double time(int loops, Benchmark *bench, Target *target)
Definition: nanobench.cpp:394
int_closure create
#define ERROR(message)
Definition: elf_loader.cc:260
#define TRACE_FLOW_BEGIN(category, name, id)
Definition: trace_event.h:190