Flutter Engine
 
Loading...
Searching...
No Matches
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_.present_credits_ += values.additional_present_credits();
226 TRACE_DURATION("flutter", "FlatlandConnection::OnNextFrameBegin",
227 "present_credits", threadsafe_state_.present_credits_);
228
229 if (present_waiting_for_credit_ && threadsafe_state_.present_credits_ > 0) {
230 DoPresent();
231 present_waiting_for_credit_ = false;
232 }
233
234 // Update vsync_interval_ by calculating the difference between the first two
235 // presentation times. Flatland always returns >1 presentation_infos, so this
236 // check is to guard against any changes to this assumption.
237 if (values.has_future_presentation_infos() &&
238 values.future_presentation_infos().size() > 1) {
239 threadsafe_state_.vsync_interval_ = fml::TimeDelta::FromNanoseconds(
240 values.future_presentation_infos().at(1).presentation_time() -
241 values.future_presentation_infos().at(0).presentation_time());
242 } else {
243 FML_LOG(WARNING)
244 << "Flatland didn't send enough future_presentation_infos to update "
245 "vsync interval.";
246 }
247
248 // Update next_presentation_times_.
249 std::queue<fml::TimePoint> new_times;
250 for (const auto& info : values.future_presentation_infos()) {
251 new_times.emplace(fml::TimePoint::FromEpochDelta(
252 fml::TimeDelta::FromNanoseconds(info.presentation_time())));
253 }
254 threadsafe_state_.next_presentation_times_.swap(new_times);
255
256 // Update vsync_offset_.
257 // We use modulo here because Flatland may point to the following vsync if
258 // OnNextFrameBegin() is called after the current frame's latch point.
259 auto vsync_offset =
261 values.future_presentation_infos().front().presentation_time())) -
262 now) %
263 threadsafe_state_.vsync_interval_;
264 // Thread contention may result in OnNextFrameBegin() being called after the
265 // presentation time. Ignore these outliers.
266 if (vsync_offset > fml::TimeDelta::Zero()) {
267 threadsafe_state_.vsync_offset_ = vsync_offset;
268 }
269
270 // Throttle case.
271 if (threadsafe_state_.pending_fire_callback_ &&
272 threadsafe_state_.present_credits_ > 0) {
273 RunVsyncCallback(now, threadsafe_state_.pending_fire_callback_);
274 threadsafe_state_.pending_fire_callback_ = nullptr;
275 }
276}
277
278// This method is called from the raster thread.
279void FlatlandConnection::OnFramePresented(
280 fuchsia::scenic::scheduling::FramePresentedInfo info) {
281 on_frame_presented_callback_(std::move(info));
282}
283
284// Parses and updates next_presentation_times_.
285fml::TimePoint FlatlandConnection::GetNextPresentationTime(
286 const fml::TimePoint& now) {
287 const fml::TimePoint& cutoff =
288 now > threadsafe_state_.last_presentation_time_
289 ? now
290 : threadsafe_state_.last_presentation_time_;
291
292 // Remove presentation times that may have been passed. This may happen after
293 // a long draw call.
294 while (!threadsafe_state_.next_presentation_times_.empty() &&
295 threadsafe_state_.next_presentation_times_.front() <= cutoff) {
296 threadsafe_state_.next_presentation_times_.pop();
297 }
298
299 // Calculate a presentation time based on
300 // |threadsafe_state_.last_presentation_time_| that is later than cutoff using
301 // |vsync_interval| increments if we don't have any future presentation times
302 // left.
303 if (threadsafe_state_.next_presentation_times_.empty()) {
304 auto result = threadsafe_state_.last_presentation_time_;
305 while (result <= cutoff) {
306 result = result + threadsafe_state_.vsync_interval_;
307 }
308 return result;
309 }
310
311 // Return the next presentation time in the queue for the regular case.
312 const auto result = threadsafe_state_.next_presentation_times_.front();
313 threadsafe_state_.next_presentation_times_.pop();
314 return result;
315}
316
317// This method is called from the UI thread.
318bool FlatlandConnection::MaybeRunInitialVsyncCallback(
319 const fml::TimePoint& now,
321 // Only sent maybe_run_initial_vsync once.
322 if (threadsafe_state_.initial_vsync_callback_ran_) {
323 return false;
324 }
325 TRACE_DURATION("flutter", "FlatlandConnection::MaybeRunInitialVsyncCallback");
326 const auto frame_end = now + kInitialFlatlandVsyncOffset;
327 threadsafe_state_.last_presentation_time_ = frame_end;
328 threadsafe_state_.initial_vsync_callback_ran_ = true;
329 callback(now, frame_end);
330 return true;
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
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
const uint8_t uint32_t uint32_t GError ** error
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
static constexpr size_t kMaxFences
std::function< void(fuchsia::scenic::scheduling::FramePresentedInfo)> on_frame_presented_event
std::function< void(fml::TimePoint, fml::TimePoint)> FireCallbackCallback
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
Definition ref_ptr.h:261
#define TRACE_FLOW_BEGIN(category, name, id)