Flutter Engine
session_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 
5 #include "session_connection.h"
6 
7 #include "flutter/fml/make_copyable.h"
8 #include "flutter/fml/trace_event.h"
9 
10 #include "vsync_recorder.h"
11 #include "vsync_waiter.h"
12 
13 namespace flutter_runner {
14 
16  std::string debug_label,
17  fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session,
18  fml::closure session_error_callback,
19  on_frame_presented_event on_frame_presented_callback,
20  zx_handle_t vsync_event_handle,
21  uint64_t max_frames_in_flight)
22  : session_wrapper_(session.Bind(), nullptr),
23  on_frame_presented_callback_(std::move(on_frame_presented_callback)),
24  vsync_event_handle_(vsync_event_handle),
25  kMaxFramesInFlight(max_frames_in_flight) {
26  session_wrapper_.set_error_handler(
27  [callback = session_error_callback](zx_status_t status) { callback(); });
28 
29  // Set the |fuchsia::ui::scenic::OnFramePresented()| event handler that will
30  // fire every time a set of one or more frames is presented.
31  session_wrapper_.set_on_frame_presented_handler(
32  [this, handle = vsync_event_handle_](
33  fuchsia::scenic::scheduling::FramePresentedInfo info) {
34  // Update Scenic's limit for our remaining frames in flight allowed.
35  size_t num_presents_handled = info.presentation_infos.size();
36  frames_in_flight_allowed_ = info.num_presents_allowed;
37 
38  // A frame was presented: Update our |frames_in_flight| to match the
39  // updated unfinalized present requests.
40  frames_in_flight_ -= num_presents_handled;
41  TRACE_EVENT2("gfx", "OnFramePresented", "frames_in_flight",
42  frames_in_flight_, "max_frames_in_flight",
43  kMaxFramesInFlight);
44  FML_DCHECK(frames_in_flight_ >= 0);
45 
47  zx::time(info.actual_presentation_time));
48 
49  // Call the client-provided callback once we are done using |info|.
50  on_frame_presented_callback_(std::move(info));
51 
52  if (present_session_pending_) {
53  PresentSession();
54  }
55  ToggleSignal(handle, true);
56  } // callback
57  );
58 
59  session_wrapper_.SetDebugName(debug_label);
60 
61  // Get information to finish initialization and only then allow Present()s.
62  session_wrapper_.RequestPresentationTimes(
63  /*requested_prediction_span=*/0,
64  [this](fuchsia::scenic::scheduling::FuturePresentationTimes info) {
65  frames_in_flight_allowed_ = info.remaining_presents_in_flight_allowed;
66 
67  // If Scenic alloted us 0 frames to begin with, we should fail here.
68  FML_CHECK(frames_in_flight_allowed_ > 0);
69 
71  std::move(info));
72 
73  // Signal is initially high indicating availability of the session.
74  ToggleSignal(vsync_event_handle_, true);
75  initialized_ = true;
76 
77  PresentSession();
78  });
79 }
80 
82 
84  TRACE_EVENT2("gfx", "SessionConnection::Present", "frames_in_flight",
85  frames_in_flight_, "max_frames_in_flight", kMaxFramesInFlight);
86 
87  TRACE_FLOW_BEGIN("gfx", "SessionConnection::PresentSession",
88  next_present_session_trace_id_);
89  next_present_session_trace_id_++;
90 
91  present_requested_time_ = fml::TimePoint::Now();
92 
93  // Throttle frame submission to Scenic if we already have the maximum amount
94  // of frames in flight. This allows the paint tasks for this frame to execute
95  // in parallel with the presentation of previous frame but still provides
96  // back-pressure to prevent us from enqueuing even more work.
97  if (initialized_ && frames_in_flight_ < kMaxFramesInFlight) {
98  PresentSession();
99  } else {
100  // We should never exceed the max frames in flight.
101  FML_CHECK(frames_in_flight_ <= kMaxFramesInFlight);
102 
103  present_session_pending_ = true;
104  ToggleSignal(vsync_event_handle_, false);
105  }
106 }
107 
109  fml::TimePoint present_requested_time,
110  fml::TimePoint now,
111  fml::TimePoint last_latch_point_targeted,
112  fml::TimeDelta flutter_frame_build_time,
113  fml::TimeDelta vsync_interval,
114  std::deque<std::pair<fml::TimePoint, fml::TimePoint>>&
115  future_presentation_infos) {
116  // The minimum latch point is the largest of:
117  // Now
118  // When we expect the Flutter work for the frame to be completed
119  // The last latch point targeted
120  fml::TimePoint minimum_latch_point_to_target =
121  std::max(std::max(now, present_requested_time + flutter_frame_build_time),
122  last_latch_point_targeted);
123 
124  for (auto& info : future_presentation_infos) {
125  fml::TimePoint latch_point = info.first;
126 
127  if (latch_point >= minimum_latch_point_to_target) {
128  return latch_point;
129  }
130  }
131 
132  // We could not find a suitable latch point in the list given to us from
133  // Scenic, so aim for the smallest safe value.
134  return minimum_latch_point_to_target;
135 }
136 
137 void SessionConnection::PresentSession() {
138  TRACE_EVENT0("gfx", "SessionConnection::PresentSession");
139 
140  // If we cannot call Present2() because we have no more Scenic frame budget,
141  // then we must wait until the OnFramePresented() event fires so we can
142  // continue our work.
143  if (frames_in_flight_allowed_ == 0) {
144  FML_CHECK(!initialized_ || present_session_pending_);
145  return;
146  }
147 
148  present_session_pending_ = false;
149 
150  while (processed_present_session_trace_id_ < next_present_session_trace_id_) {
151  TRACE_FLOW_END("gfx", "SessionConnection::PresentSession",
152  processed_present_session_trace_id_);
153  processed_present_session_trace_id_++;
154  }
155  TRACE_FLOW_BEGIN("gfx", "Session::Present", next_present_trace_id_);
156  next_present_trace_id_++;
157 
158  ++frames_in_flight_;
159 
160  // Flush all session ops. Paint tasks may not yet have executed but those are
161  // fenced. The compositor can start processing ops while we finalize paint
162  // tasks.
163 
164  fml::TimeDelta presentation_interval =
166 
167  fml::TimePoint next_latch_point = CalculateNextLatchPoint(
168  fml::TimePoint::Now(), present_requested_time_,
169  last_latch_point_targeted_,
170  fml::TimeDelta::FromMicroseconds(0), // flutter_frame_build_time
171  presentation_interval, future_presentation_infos_);
172 
173  last_latch_point_targeted_ = next_latch_point;
174 
175  session_wrapper_.Present2(
176  /*requested_presentation_time=*/next_latch_point.ToEpochDelta()
177  .ToNanoseconds(),
178  /*requested_prediction_span=*/presentation_interval.ToNanoseconds() * 10,
179  [this](fuchsia::scenic::scheduling::FuturePresentationTimes info) {
180  // Clear |future_presentation_infos_| and replace it with the updated
181  // information.
182  std::deque<std::pair<fml::TimePoint, fml::TimePoint>>().swap(
183  future_presentation_infos_);
184 
185  for (fuchsia::scenic::scheduling::PresentationInfo& presentation_info :
186  info.future_presentations) {
187  future_presentation_infos_.push_back(
189  presentation_info.latch_point())),
191  presentation_info.presentation_time()))});
192  }
193 
194  frames_in_flight_allowed_ = info.remaining_presents_in_flight_allowed;
196  std::move(info));
197  });
198 }
199 
200 void SessionConnection::ToggleSignal(zx_handle_t handle, bool set) {
201  const auto signal = VsyncWaiter::SessionPresentSignal;
202  auto status = zx_object_signal(handle, // handle
203  set ? 0 : signal, // clear mask
204  set ? signal : 0 // set mask
205  );
206  if (status != ZX_OK) {
207  FML_LOG(ERROR) << "Could not toggle vsync signal: " << set;
208  }
209 }
210 
211 } // namespace flutter_runner
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
static constexpr TimeDelta FromNanoseconds(int64_t nanos)
Definition: time_delta.h:40
#define FML_DCHECK(condition)
Definition: logging.h:86
fml::TimeDelta presentation_interval
#define TRACE_FLOW_END(category, name, id)
Definition: trace_event.h:121
void UpdateNextPresentationInfo(fuchsia::scenic::scheduling::FuturePresentationTimes info)
Definition: ref_ptr.h:252
TimeDelta ToEpochDelta() const
Definition: time_point.h:40
#define FML_LOG(severity)
Definition: logging.h:65
static constexpr TimePoint FromEpochDelta(TimeDelta ticks)
Definition: time_point.h:36
void swap(scoped_nsprotocol< C > &p1, scoped_nsprotocol< C > &p2)
std::function< void()> closure
Definition: closure.h:14
#define TRACE_FLOW_BEGIN(category, name, id)
Definition: trace_event.h:115
void UpdateFramePresentedInfo(zx::time presentation_time)
static fml::TimePoint CalculateNextLatchPoint(fml::TimePoint present_requested_time, fml::TimePoint now, fml::TimePoint last_latch_point_targeted, fml::TimeDelta flutter_frame_build_time, fml::TimeDelta vsync_interval, std::deque< std::pair< fml::TimePoint, fml::TimePoint >> &future_presentation_infos)
VsyncInfo GetCurrentVsyncInfo() const
static constexpr TimeDelta FromMicroseconds(int64_t micros)
Definition: time_delta.h:43
constexpr int64_t ToNanoseconds() const
Definition: time_delta.h:61
SessionConnection(std::string debug_label, fidl::InterfaceHandle< fuchsia::ui::scenic::Session > session, fml::closure session_error_callback, on_frame_presented_event on_frame_presented_callback, zx_handle_t vsync_event_handle, uint64_t max_frames_in_flight)
static VsyncRecorder & GetInstance()
#define FML_CHECK(condition)
Definition: logging.h:68
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
Definition: trace_event.h:83
static constexpr zx_signals_t SessionPresentSignal
Definition: vsync_waiter.h:21
std::function< void(fuchsia::scenic::scheduling::FramePresentedInfo)> on_frame_presented_event
static TimePoint Now()
Definition: time_point.cc:26