Flutter Engine
The 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 <zircon/status.h>
8
9#include "flutter/fml/logging.h"
10#include "flutter/fml/trace_event.h"
11
12namespace flutter_runner {
13
14namespace {
15
16// Helper function for traces.
17double DeltaFromNowInNanoseconds(const fml::TimePoint& now,
18 const fml::TimePoint& time) {
19 return (time - now).ToNanoseconds();
20}
21
22} // namespace
23
25 std::string debug_label,
26 fuchsia::ui::composition::FlatlandHandle flatland,
27 fml::closure error_callback,
28 on_frame_presented_event on_frame_presented_callback)
29 : flatland_(flatland.Bind()),
30 error_callback_(error_callback),
31 on_frame_presented_callback_(std::move(on_frame_presented_callback)) {
32 flatland_.set_error_handler([callback = error_callback_](zx_status_t status) {
33 FML_LOG(ERROR) << "Flatland disconnected: " << zx_status_get_string(status);
34 callback();
35 });
36 debug_label_ = debug_label;
37 flatland_->SetDebugName(debug_label);
38 flatland_.events().OnError =
39 fit::bind_member(this, &FlatlandConnection::OnError);
40 flatland_.events().OnFramePresented =
41 fit::bind_member(this, &FlatlandConnection::OnFramePresented);
42 flatland_.events().OnNextFrameBegin =
43 fit::bind_member(this, &FlatlandConnection::OnNextFrameBegin);
44}
45
47
48// This method is called from the raster thread.
50 TRACE_DURATION("flutter", "FlatlandConnection::Present");
51 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
52 if (threadsafe_state_.present_credits_ > 0) {
53 DoPresent();
54 } else {
55 present_waiting_for_credit_ = true;
56 }
57}
58
59// This method is called from the raster thread.
60void FlatlandConnection::DoPresent() {
61 TRACE_DURATION("flutter", "FlatlandConnection::DoPresent");
62
63 std::string per_app_tracing_name =
64 "Flatland::PerAppPresent[" + debug_label_ + "]";
65 TRACE_FLOW_BEGIN("gfx", per_app_tracing_name.c_str(), next_present_trace_id_);
66 ++next_present_trace_id_;
67
68 FML_CHECK(threadsafe_state_.present_credits_ > 0);
69 --threadsafe_state_.present_credits_;
70
71 fuchsia::ui::composition::PresentArgs present_args;
72 present_args.set_requested_presentation_time(0);
73 present_args.set_acquire_fences(std::move(acquire_fences_));
74 present_args.set_release_fences(std::move(previous_present_release_fences_));
75 // Frame rate over latency.
76 present_args.set_unsquashable(true);
77 flatland_->Present(std::move(present_args));
78
79 // In Flatland, release fences apply to the content of the previous present.
80 // Keeping track of the old frame's release fences and swapping ensure we set
81 // the correct ones for VulkanSurface's interpretation.
82 previous_present_release_fences_.clear();
83 previous_present_release_fences_.swap(current_present_release_fences_);
84 acquire_fences_.clear();
85}
86
87// This method is called from the UI thread.
89 TRACE_DURATION("flutter", "FlatlandConnection::AwaitVsync");
90
91 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
92 threadsafe_state_.pending_fire_callback_ = nullptr;
93 const auto now = fml::TimePoint::Now();
94
95 // Initial case.
96 if (MaybeRunInitialVsyncCallback(now, callback))
97 return;
98
99 // Throttle case.
100 if (threadsafe_state_.present_credits_ == 0) {
101 threadsafe_state_.pending_fire_callback_ = callback;
102 return;
103 }
104
105 // Regular case.
106 RunVsyncCallback(now, callback);
107}
108
109// This method is called from the UI thread.
112 TRACE_DURATION("flutter",
113 "FlatlandConnection::AwaitVsyncForSecondaryCallback");
114
115 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
116 const auto now = fml::TimePoint::Now();
117
118 // Initial case.
119 if (MaybeRunInitialVsyncCallback(now, callback))
120 return;
121
122 // Regular case.
123 RunVsyncCallback(now, callback);
124}
125
126// This method is called from the raster thread.
127void FlatlandConnection::OnError(
128 fuchsia::ui::composition::FlatlandError error) {
129 FML_LOG(ERROR) << "Flatland error: " << static_cast<int>(error);
130 error_callback_();
131}
132
133// This method is called from the raster thread.
134void FlatlandConnection::OnNextFrameBegin(
135 fuchsia::ui::composition::OnNextFrameBeginValues values) {
136 // Collect now before locking because this is an important timing information
137 // from Scenic.
138 const auto now = fml::TimePoint::Now();
139
140 std::scoped_lock<std::mutex> lock(threadsafe_state_.mutex_);
141 threadsafe_state_.first_feedback_received_ = true;
142 threadsafe_state_.present_credits_ += values.additional_present_credits();
143 TRACE_DURATION("flutter", "FlatlandConnection::OnNextFrameBegin",
144 "present_credits", threadsafe_state_.present_credits_);
145
146 if (present_waiting_for_credit_ && threadsafe_state_.present_credits_ > 0) {
147 DoPresent();
148 present_waiting_for_credit_ = false;
149 }
150
151 // Update vsync_interval_ by calculating the difference between the first two
152 // presentation times. Flatland always returns >1 presentation_infos, so this
153 // check is to guard against any changes to this assumption.
154 if (values.has_future_presentation_infos() &&
155 values.future_presentation_infos().size() > 1) {
156 threadsafe_state_.vsync_interval_ = fml::TimeDelta::FromNanoseconds(
157 values.future_presentation_infos().at(1).presentation_time() -
158 values.future_presentation_infos().at(0).presentation_time());
159 } else {
160 FML_LOG(WARNING)
161 << "Flatland didn't send enough future_presentation_infos to update "
162 "vsync interval.";
163 }
164
165 // Update next_presentation_times_.
166 std::queue<fml::TimePoint> new_times;
167 for (const auto& info : values.future_presentation_infos()) {
168 new_times.emplace(fml::TimePoint::FromEpochDelta(
169 fml::TimeDelta::FromNanoseconds(info.presentation_time())));
170 }
171 threadsafe_state_.next_presentation_times_.swap(new_times);
172
173 // Update vsync_offset_.
174 // We use modulo here because Flatland may point to the following vsync if
175 // OnNextFrameBegin() is called after the current frame's latch point.
176 auto vsync_offset =
178 values.future_presentation_infos().front().presentation_time())) -
179 now) %
180 threadsafe_state_.vsync_interval_;
181 // Thread contention may result in OnNextFrameBegin() being called after the
182 // presentation time. Ignore these outliers.
183 if (vsync_offset > fml::TimeDelta::Zero()) {
184 threadsafe_state_.vsync_offset_ = vsync_offset;
185 }
186
187 // Throttle case.
188 if (threadsafe_state_.pending_fire_callback_ &&
189 threadsafe_state_.present_credits_ > 0) {
190 RunVsyncCallback(now, threadsafe_state_.pending_fire_callback_);
191 threadsafe_state_.pending_fire_callback_ = nullptr;
192 }
193}
194
195// This method is called from the raster thread.
196void FlatlandConnection::OnFramePresented(
197 fuchsia::scenic::scheduling::FramePresentedInfo info) {
198 on_frame_presented_callback_(std::move(info));
199}
200
201// Parses and updates next_presentation_times_.
202fml::TimePoint FlatlandConnection::GetNextPresentationTime(
203 const fml::TimePoint& now) {
204 const fml::TimePoint& cutoff =
205 now > threadsafe_state_.last_presentation_time_
206 ? now
207 : threadsafe_state_.last_presentation_time_;
208
209 // Remove presentation times that may have been passed. This may happen after
210 // a long draw call.
211 while (!threadsafe_state_.next_presentation_times_.empty() &&
212 threadsafe_state_.next_presentation_times_.front() <= cutoff) {
213 threadsafe_state_.next_presentation_times_.pop();
214 }
215
216 // Calculate a presentation time based on
217 // |threadsafe_state_.last_presentation_time_| that is later than cutoff using
218 // |vsync_interval| increments if we don't have any future presentation times
219 // left.
220 if (threadsafe_state_.next_presentation_times_.empty()) {
221 auto result = threadsafe_state_.last_presentation_time_;
222 while (result <= cutoff) {
223 result = result + threadsafe_state_.vsync_interval_;
224 }
225 return result;
226 }
227
228 // Return the next presentation time in the queue for the regular case.
229 const auto result = threadsafe_state_.next_presentation_times_.front();
230 threadsafe_state_.next_presentation_times_.pop();
231 return result;
232}
233
234// This method is called from the UI thread.
235bool FlatlandConnection::MaybeRunInitialVsyncCallback(
236 const fml::TimePoint& now,
238 if (!threadsafe_state_.first_feedback_received_) {
239 TRACE_DURATION("flutter",
240 "FlatlandConnection::MaybeRunInitialVsyncCallback");
241 const auto frame_end = now + kInitialFlatlandVsyncOffset;
242 threadsafe_state_.last_presentation_time_ = frame_end;
243 callback(now, frame_end);
244 return true;
245 }
246 return false;
247}
248
249// This method may be called from the raster or UI thread, but it is safe
250// because VsyncWaiter posts the vsync callback on UI thread.
251void FlatlandConnection::RunVsyncCallback(const fml::TimePoint& now,
253 const auto& frame_end = GetNextPresentationTime(now);
254 const auto& frame_start = frame_end - threadsafe_state_.vsync_offset_;
255 threadsafe_state_.last_presentation_time_ = frame_end;
256 TRACE_DURATION("flutter", "FlatlandConnection::RunVsyncCallback",
257 "frame_start_delta",
258 DeltaFromNowInNanoseconds(now, frame_start), "frame_end_delta",
259 DeltaFromNowInNanoseconds(now, frame_end));
260 callback(frame_start, frame_end);
261}
262
263// This method is called from the raster thread.
265 acquire_fences_.push_back(std::move(fence));
266}
267
268// This method is called from the raster thread.
270 current_present_release_fences_.push_back(std::move(fence));
271}
272
273} // namespace flutter_runner
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
FlatlandConnection(std::string debug_label, fuchsia::ui::composition::FlatlandHandle flatland, fml::closure error_callback, on_frame_presented_event on_frame_presented_callback)
void AwaitVsyncForSecondaryCallback(FireCallbackCallback callback)
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
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
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
std::function< void(fuchsia::scenic::scheduling::FramePresentedInfo)> on_frame_presented_event
std::function< void(fml::TimePoint, fml::TimePoint)> FireCallbackCallback
static constexpr fml::TimeDelta kInitialFlatlandVsyncOffset
std::function< void()> closure
Definition closure.h:14
Definition ref_ptr.h:256
#define ERROR(message)
#define TRACE_FLOW_BEGIN(category, name, id)