7#include <lib/async/cpp/task.h>
8#include <lib/async/default.h>
10#include <zircon/rights.h>
11#include <zircon/status.h>
12#include <zircon/types.h>
16#include "flutter/fml/logging.h"
25 return (
time - now).ToNanoseconds();
31 const std::string& debug_label,
32 fuchsia::ui::composition::FlatlandHandle flatland,
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);
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);
58 TRACE_DURATION(
"flutter",
"FlatlandConnection::Present");
59 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
60 if (threadsafe_state_.present_credits_ > 0) {
63 present_waiting_for_credit_ =
true;
68void FlatlandConnection::DoPresent() {
69 TRACE_DURATION(
"flutter",
"FlatlandConnection::DoPresent");
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_;
76 FML_CHECK(threadsafe_state_.present_credits_ > 0);
77 --threadsafe_state_.present_credits_;
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_));
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;
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();
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)++;
103 <<
"status: " << zx_status_get_string(status);
104 if (*fences_completed == fences_size) {
106 const zx_status_t status =
107 overflow->event_.signal(0, ZX_EVENT_SIGNALED);
109 <<
"status: " << zx_status_get_string(status);
114 acquire_overflow_.reset();
118 present_args.set_release_fences(std::move(previous_present_release_fences_));
120 present_args.set_unsquashable(
true);
121 flatland_->Present(std::move(present_args));
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;
133 if (previous_release_overflow_ !=
nullptr) {
134 FML_CHECK(previous_release_overflow_->event_.is_valid());
136 std::shared_ptr<Overflow> fences = previous_release_overflow_;
138 async::PostTask(dispatcher_, [dispatcher = dispatcher_,
139 fences = previous_release_overflow_]() {
143 auto wait = std::make_unique<async::WaitOnce>(fences->event_.get(),
144 ZX_EVENT_SIGNALED, 0u);
145 auto wait_ptr = wait.get();
148 dispatcher, [_wait = std::move(wait), fences](
149 async_dispatcher_t*, async::WaitOnce*,
150 zx_status_t status,
const zx_packet_signal_t*) {
152 <<
"status: " << zx_status_get_string(status);
155 for (
auto&
event : fences->fences_) {
156 const zx_status_t status =
event.signal(0, ZX_EVENT_SIGNALED);
158 <<
"status: " << zx_status_get_string(status);
162 previous_release_overflow_ =
nullptr;
164 FML_CHECK(previous_release_overflow_ ==
nullptr);
166 acquire_fences_.clear();
171 TRACE_DURATION(
"flutter",
"FlatlandConnection::AwaitVsync");
173 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
174 threadsafe_state_.pending_fire_callback_ =
nullptr;
178 if (MaybeRunInitialVsyncCallback(now,
callback)) {
183 if (threadsafe_state_.present_credits_ == 0) {
184 threadsafe_state_.pending_fire_callback_ = std::move(
callback);
195 TRACE_DURATION(
"flutter",
196 "FlatlandConnection::AwaitVsyncForSecondaryCallback");
198 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
202 if (MaybeRunInitialVsyncCallback(now,
callback)) {
211void FlatlandConnection::OnError(
212 fuchsia::ui::composition::FlatlandError
error) {
218void FlatlandConnection::OnNextFrameBegin(
219 fuchsia::ui::composition::OnNextFrameBeginValues
values) {
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_);
230 if (present_waiting_for_credit_ && threadsafe_state_.present_credits_ > 0) {
232 present_waiting_for_credit_ =
false;
238 if (
values.has_future_presentation_infos() &&
239 values.future_presentation_infos().size() > 1) {
241 values.future_presentation_infos().at(1).presentation_time() -
242 values.future_presentation_infos().at(0).presentation_time());
245 <<
"Flatland didn't send enough future_presentation_infos to update "
250 std::queue<fml::TimePoint> new_times;
251 for (
const auto&
info :
values.future_presentation_infos()) {
255 threadsafe_state_.next_presentation_times_.swap(new_times);
262 values.future_presentation_infos().front().presentation_time())) -
264 threadsafe_state_.vsync_interval_;
268 threadsafe_state_.vsync_offset_ = vsync_offset;
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;
280void FlatlandConnection::OnFramePresented(
281 fuchsia::scenic::scheduling::FramePresentedInfo
info) {
282 on_frame_presented_callback_(std::move(
info));
289 now > threadsafe_state_.last_presentation_time_
291 : threadsafe_state_.last_presentation_time_;
295 while (!threadsafe_state_.next_presentation_times_.empty() &&
296 threadsafe_state_.next_presentation_times_.front() <= cutoff) {
297 threadsafe_state_.next_presentation_times_.pop();
304 if (threadsafe_state_.next_presentation_times_.empty()) {
305 auto result = threadsafe_state_.last_presentation_time_;
306 while (
result <= cutoff) {
313 const auto result = threadsafe_state_.next_presentation_times_.front();
314 threadsafe_state_.next_presentation_times_.pop();
319bool FlatlandConnection::MaybeRunInitialVsyncCallback(
322 if (!threadsafe_state_.first_feedback_received_) {
323 TRACE_DURATION(
"flutter",
324 "FlatlandConnection::MaybeRunInitialVsyncCallback");
326 threadsafe_state_.last_presentation_time_ = frame_end;
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",
342 DeltaFromNowInNanoseconds(now,
frame_start),
"frame_end_delta",
343 DeltaFromNowInNanoseconds(now, frame_end));
356 std::vector<zx::event>* fences,
357 std::shared_ptr<Overflow>* overflow) {
359 fuchsia::ui::composition::MAX_ACQUIRE_RELEASE_FENCE_COUNT;
362 const auto num_all_fences =
364 ((*overflow ==
nullptr) ? 0 : (*overflow)->fences_.size());
370 fences->push_back(std::move(fence));
371 }
else if (num_all_fences ==
kMaxFences + 1) {
375 FML_CHECK((*overflow) ==
nullptr) <<
"overflow is still active";
376 *overflow = std::make_shared<Overflow>();
381 zx::event overflow_handle = std::move(fences->back());
386 FML_CHECK(status == ZX_OK) <<
"status: " << zx_status_get_string(status);
389 FML_CHECK(!(*overflow)->event_.is_valid()) <<
"overflow valid";
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));
396 (*overflow)->fences_.push_back(std::move(overflow_handle));
397 (*overflow)->fences_.push_back(std::move(fence));
399 FML_LOG(INFO) <<
"Enqueue using fence overflow, expect a performance hit.";
403 (*overflow)->fences_.push_back(std::move(fence));
409 Enqueue(std::move(fence), &acquire_fences_, &acquire_overflow_);
414 Enqueue(std::move(fence), ¤t_present_release_fences_,
415 ¤t_release_overflow_);
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
void EnqueueReleaseFence(zx::event fence)
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 EnqueueAcquireFence(zx::event fence)
void AwaitVsync(FireCallbackCallback callback)
static constexpr TimeDelta FromNanoseconds(int64_t nanos)
static constexpr TimeDelta Zero()
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
const uint8_t uint32_t uint32_t GError ** error
#define FML_LOG(severity)
#define FML_CHECK(condition)
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
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
static double time(int loops, Benchmark *bench, Target *target)
#define TRACE_FLOW_BEGIN(category, name, id)