Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
flatland_connection_unittests.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 "flutter/shell/platform/fuchsia/flutter/flatland_connection.h"
6
7#include <fuchsia/scenic/scheduling/cpp/fidl.h>
8#include <fuchsia/ui/composition/cpp/fidl.h>
9#include <lib/async-testing/test_loop.h>
10#include <lib/async/cpp/task.h>
11
12#include <string>
13#include <vector>
14
15#include "flutter/fml/logging.h"
16#include "flutter/fml/time/time_delta.h"
17#include "flutter/fml/time/time_point.h"
18#include "gtest/gtest.h"
19
21
23
24namespace {
25
26std::string GetCurrentTestName() {
27 return ::testing::UnitTest::GetInstance()->current_test_info()->name();
28}
29
30void AwaitVsyncChecked(FlatlandConnection& flatland_connection,
31 bool& condition_variable,
32 fml::TimeDelta expected_frame_delta) {
33 flatland_connection.AwaitVsync(
34 [&condition_variable,
35 expected_frame_delta = std::move(expected_frame_delta)](
36 fml::TimePoint frame_start, fml::TimePoint frame_end) {
37 EXPECT_EQ(frame_end.ToEpochDelta() - frame_start.ToEpochDelta(),
38 expected_frame_delta);
39 condition_variable = true;
40 });
41}
42
43void AwaitVsyncChecked(FlatlandConnection& flatland_connection,
44 bool& condition_variable,
45 fml::TimePoint expected_frame_end) {
46 flatland_connection.AwaitVsync(
47 [&condition_variable, expected_frame_end = std::move(expected_frame_end)](
48 fml::TimePoint frame_start, fml::TimePoint frame_end) {
49 EXPECT_EQ(frame_end, expected_frame_end);
50 condition_variable = true;
51 });
52}
53
54std::vector<fuchsia::scenic::scheduling::PresentationInfo>
55CreateFuturePresentationInfos(const fml::TimePoint& presentation_time_1,
56 const fml::TimePoint& presentation_time_2) {
57 fuchsia::scenic::scheduling::PresentationInfo info_1;
58 info_1.set_presentation_time(
59 presentation_time_1.ToEpochDelta().ToNanoseconds());
60 std::vector<fuchsia::scenic::scheduling::PresentationInfo> infos;
61 infos.push_back(std::move(info_1));
62 fuchsia::scenic::scheduling::PresentationInfo info_2;
63 info_2.set_presentation_time(
64 presentation_time_2.ToEpochDelta().ToNanoseconds());
65 infos.push_back(std::move(info_2));
66 return infos;
67}
68
69} // namespace
70
71class FlatlandConnectionTest : public ::testing::Test {
72 protected:
74 : session_subloop_(loop_.StartNewLoop()),
75 flatland_handle_(
76 fake_flatland_.ConnectFlatland(session_subloop_->dispatcher())) {}
77 ~FlatlandConnectionTest() override = default;
78
79 async::TestLoop& loop() { return loop_; }
80
81 FakeFlatland& fake_flatland() { return fake_flatland_; }
82
83 fidl::InterfaceHandle<fuchsia::ui::composition::Flatland>
85 FML_CHECK(flatland_handle_.is_valid());
86 return std::move(flatland_handle_);
87 }
88
89 // Syntactic sugar for OnNextFrameBegin
90 void OnNextFrameBegin(int num_present_credits,
91 const fml::TimePoint& presentation_time_1,
92 const fml::TimePoint& presentation_time_2) {
93 fuchsia::ui::composition::OnNextFrameBeginValues on_next_frame_begin_values;
94 on_next_frame_begin_values.set_additional_present_credits(
95 num_present_credits);
96 on_next_frame_begin_values.set_future_presentation_infos(
97 CreateFuturePresentationInfos(presentation_time_1,
98 presentation_time_2));
100 std::move(on_next_frame_begin_values));
101 }
102 void OnNextFrameBegin(int num_present_credits) {
103 const auto now = fml::TimePoint::Now();
104 const auto kPresentationTime1 = now + fml::TimeDelta::FromSeconds(100);
105 const auto kPresentationTime2 = now + fml::TimeDelta::FromSeconds(200);
106 OnNextFrameBegin(num_present_credits, kPresentationTime1,
107 kPresentationTime2);
108 }
109
110 private:
111 async::TestLoop loop_;
112 std::unique_ptr<async::LoopInterface> session_subloop_;
113
114 FakeFlatland fake_flatland_;
115
116 fidl::InterfaceHandle<fuchsia::ui::composition::Flatland> flatland_handle_;
117};
118
120 // Create the FlatlandConnection but don't pump the loop. No FIDL calls are
121 // completed yet.
122 const std::string debug_name = GetCurrentTestName();
123 flutter_runner::FlatlandConnection flatland_connection(
124 debug_name, TakeFlatlandHandle(), []() { FAIL(); },
125 [](auto...) { FAIL(); });
126 EXPECT_EQ(fake_flatland().debug_name(), "");
127
128 // Simulate an AwaitVsync that returns immediately.
129 bool await_vsync_fired = false;
130 AwaitVsyncChecked(flatland_connection, await_vsync_fired,
132 EXPECT_TRUE(await_vsync_fired);
133
134 // Ensure the debug name is set.
135 loop().RunUntilIdle();
136 EXPECT_EQ(fake_flatland().debug_name(), debug_name);
137}
138
139TEST_F(FlatlandConnectionTest, FlatlandDisconnect) {
140 // Set up a callback which allows sensing of the error state.
141 bool error_fired = false;
142 fml::closure on_session_error = [&error_fired]() { error_fired = true; };
143
144 // Create the FlatlandConnection but don't pump the loop. No FIDL calls are
145 // completed yet.
146 flutter_runner::FlatlandConnection flatland_connection(
147 GetCurrentTestName(), TakeFlatlandHandle(), std::move(on_session_error),
148 [](auto...) { FAIL(); });
149 EXPECT_FALSE(error_fired);
150
151 // Simulate a flatland disconnection, then Pump the loop. The error callback
152 // will fire.
153 fake_flatland().Disconnect(
154 fuchsia::ui::composition::FlatlandError::BAD_OPERATION);
155 loop().RunUntilIdle();
156 EXPECT_TRUE(error_fired);
157}
158
160 // Set up callbacks which allow sensing of how many presents were handled.
161 size_t presents_called = 0u;
162 zx_handle_t release_fence_handle;
163 fake_flatland().SetPresentHandler([&presents_called,
164 &release_fence_handle](auto present_args) {
165 presents_called++;
166 release_fence_handle = present_args.release_fences().empty()
167 ? ZX_HANDLE_INVALID
168 : present_args.release_fences().front().get();
169 });
170
171 // Set up a callback which allows sensing of how many vsync's
172 // (`OnFramePresented` events) were handled.
173 size_t vsyncs_handled = 0u;
174 on_frame_presented_event on_frame_presented = [&vsyncs_handled](auto...) {
175 vsyncs_handled++;
176 };
177
178 // Create the FlatlandConnection but don't pump the loop. No FIDL calls are
179 // completed yet.
180 flutter_runner::FlatlandConnection flatland_connection(
181 GetCurrentTestName(), TakeFlatlandHandle(), []() { FAIL(); },
182 std::move(on_frame_presented));
183 EXPECT_EQ(presents_called, 0u);
184 EXPECT_EQ(vsyncs_handled, 0u);
185
186 // Pump the loop. Nothing is called.
187 loop().RunUntilIdle();
188 EXPECT_EQ(presents_called, 0u);
189 EXPECT_EQ(vsyncs_handled, 0u);
190
191 // Simulate an AwaitVsync that comes after the first call.
192 bool await_vsync_fired = false;
193 AwaitVsyncChecked(flatland_connection, await_vsync_fired,
195 EXPECT_TRUE(await_vsync_fired);
196
197 // Call Present and Pump the loop; `Present` and its callback is called. No
198 // release fence should be queued.
199 await_vsync_fired = false;
200 zx::event first_release_fence;
201 zx::event::create(0, &first_release_fence);
202 const zx_handle_t first_release_fence_handle = first_release_fence.get();
203 flatland_connection.EnqueueReleaseFence(std::move(first_release_fence));
204 flatland_connection.Present();
205 loop().RunUntilIdle();
206 EXPECT_EQ(presents_called, 1u);
207 EXPECT_EQ(release_fence_handle, ZX_HANDLE_INVALID);
208 EXPECT_EQ(vsyncs_handled, 0u);
209 EXPECT_FALSE(await_vsync_fired);
210
211 // Fire the `OnNextFrameBegin` event. AwaitVsync should be fired.
212 const auto now = fml::TimePoint::Now();
213 const auto kPresentationTime1 = now + fml::TimeDelta::FromSeconds(100);
214 const auto kPresentationTime2 = now + fml::TimeDelta::FromSeconds(200);
215 fuchsia::ui::composition::OnNextFrameBeginValues on_next_frame_begin_values;
216 on_next_frame_begin_values.set_additional_present_credits(3);
217 on_next_frame_begin_values.set_future_presentation_infos(
218 CreateFuturePresentationInfos(kPresentationTime1, kPresentationTime2));
219 fake_flatland().FireOnNextFrameBeginEvent(
220 std::move(on_next_frame_begin_values));
221 loop().RunUntilIdle();
222 AwaitVsyncChecked(flatland_connection, await_vsync_fired, kPresentationTime1);
223 EXPECT_TRUE(await_vsync_fired);
224
225 // Fire the `OnFramePresented` event associated with the first `Present`,
226 fake_flatland().FireOnFramePresentedEvent(
227 fuchsia::scenic::scheduling::FramePresentedInfo());
228 loop().RunUntilIdle();
229 EXPECT_EQ(vsyncs_handled, 1u);
230
231 // Call Present for a second time and Pump the loop; `Present` and its
232 // callback is called. Release fences for the earlier present is used.
233 await_vsync_fired = false;
234 flatland_connection.Present();
235 loop().RunUntilIdle();
236 EXPECT_EQ(presents_called, 2u);
237 EXPECT_EQ(release_fence_handle, first_release_fence_handle);
238
239 // AwaitVsync should be fired with the second present.
240 await_vsync_fired = false;
241 AwaitVsyncChecked(flatland_connection, await_vsync_fired, kPresentationTime2);
242 EXPECT_TRUE(await_vsync_fired);
243}
244
245TEST_F(FlatlandConnectionTest, AwaitVsyncsBeforeOnNextFrameBegin) {
246 // Set up callbacks which allow sensing of how many presents were handled.
247 size_t presents_called = 0u;
248 fake_flatland().SetPresentHandler(
249 [&presents_called](auto present_args) { presents_called++; });
250
251 // Create the FlatlandConnection but don't pump the loop. No FIDL calls are
252 // completed yet.
253 flutter_runner::FlatlandConnection flatland_connection(
254 GetCurrentTestName(), TakeFlatlandHandle(), []() { FAIL(); },
255 [](auto...) {});
256 EXPECT_EQ(presents_called, 0u);
257
258 // Pump the loop. Nothing is called.
259 loop().RunUntilIdle();
260 EXPECT_EQ(presents_called, 0u);
261
262 // Simulate an AwaitVsync that comes before the first Present.
263 bool await_vsync_callback_fired = false;
264 AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
266 EXPECT_TRUE(await_vsync_callback_fired);
267
268 // AwaitVsync that comes before the first Present.
269 bool await_vsync_secondary_callback_fired = false;
270 flatland_connection.AwaitVsyncForSecondaryCallback(
271 [&await_vsync_secondary_callback_fired](fml::TimePoint frame_start,
272 fml::TimePoint frame_end) {
273 await_vsync_secondary_callback_fired = true;
274 });
275 EXPECT_TRUE(await_vsync_secondary_callback_fired);
276}
277
278TEST_F(FlatlandConnectionTest, RunsOutOfFuturePresentationInfos) {
279 // Set up callbacks which allow sensing of how many presents were handled.
280 size_t presents_called = 0u;
281 fake_flatland().SetPresentHandler(
282 [&presents_called](auto present_args) { presents_called++; });
283
284 // Set up a callback which allows sensing of how many vsync's
285 // (`OnFramePresented` events) were handled.
286 size_t vsyncs_handled = 0u;
287 on_frame_presented_event on_frame_presented = [&vsyncs_handled](auto...) {
288 vsyncs_handled++;
289 };
290
291 // Create the FlatlandConnection but don't pump the loop. No FIDL calls are
292 // completed yet.
293 flutter_runner::FlatlandConnection flatland_connection(
294 GetCurrentTestName(), TakeFlatlandHandle(), []() { FAIL(); },
295 std::move(on_frame_presented));
296 EXPECT_EQ(presents_called, 0u);
297 EXPECT_EQ(vsyncs_handled, 0u);
298
299 // Pump the loop. Nothing is called.
300 loop().RunUntilIdle();
301 EXPECT_EQ(presents_called, 0u);
302 EXPECT_EQ(vsyncs_handled, 0u);
303
304 // Simulate an AwaitVsync that comes before the first Present.
305 bool await_vsync_callback_fired = false;
306 AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
308 EXPECT_TRUE(await_vsync_callback_fired);
309
310 // Queue Present.
311 flatland_connection.Present();
312 loop().RunUntilIdle();
313 EXPECT_EQ(presents_called, 1u);
314
315 // Fire the `OnNextFrameBegin` event. AwaitVsync callback should be fired with
316 // the first presentation time.
317 await_vsync_callback_fired = false;
318 const auto kPresentationTime1 =
320 const auto kVsyncInterval = fml::TimeDelta::FromSeconds(234);
321 const auto kPresentationTime2 = kPresentationTime1 + kVsyncInterval;
322 OnNextFrameBegin(1, kPresentationTime1, kPresentationTime2);
323 loop().RunUntilIdle();
324 AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
325 kPresentationTime1);
326 EXPECT_TRUE(await_vsync_callback_fired);
327
328 // Second consecutive AwaitVsync callback should be fired with
329 // the second presentation time.
330 await_vsync_callback_fired = false;
331 AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
332 kPresentationTime2);
333 EXPECT_TRUE(await_vsync_callback_fired);
334
335 // Another AwaitVsync callback should be fired with vsync_interval.
336 await_vsync_callback_fired = false;
337 AwaitVsyncChecked(flatland_connection, await_vsync_callback_fired,
338 kPresentationTime2 + kVsyncInterval);
339 EXPECT_TRUE(await_vsync_callback_fired);
340}
341
342TEST_F(FlatlandConnectionTest, PresentCreditExhaustion) {
343 // Set up callbacks which allow sensing of how many presents were handled.
344 size_t num_presents_called = 0u;
345 size_t num_release_fences = 0u;
346 size_t num_acquire_fences = 0u;
347
348 auto reset_test_counters = [&num_presents_called, &num_acquire_fences,
349 &num_release_fences]() {
350 num_presents_called = 0u;
351 num_release_fences = 0u;
352 num_acquire_fences = 0u;
353 };
354
355 fake_flatland().SetPresentHandler(
356 [&num_presents_called, &num_acquire_fences, &num_release_fences](
357 fuchsia::ui::composition::PresentArgs present_args) {
358 num_presents_called++;
359 num_acquire_fences = present_args.acquire_fences().size();
360 num_release_fences = present_args.release_fences().size();
361 });
362
363 // Create the FlatlandConnection but don't pump the loop. No FIDL calls are
364 // completed yet.
365 on_frame_presented_event on_frame_presented = [](auto...) {};
366 flutter_runner::FlatlandConnection flatland_connection(
367 GetCurrentTestName(), TakeFlatlandHandle(), []() { FAIL(); },
368 std::move(on_frame_presented));
369 EXPECT_EQ(num_presents_called, 0u);
370
371 // Pump the loop. Nothing is called.
372 loop().RunUntilIdle();
373 EXPECT_EQ(num_presents_called, 0u);
374
375 // Simulate an AwaitVsync that comes before the first Present.
376 flatland_connection.AwaitVsync([](fml::TimePoint, fml::TimePoint) {});
377 loop().RunUntilIdle();
378 EXPECT_EQ(num_presents_called, 0u);
379
380 // This test uses a fire callback that triggers Present() with a single
381 // acquire and release fence in order to approximate the behavior of the real
382 // flutter fire callback and let us drive presents with ONFBs
383 auto fire_callback = [dispatcher = loop().dispatcher(), &flatland_connection](
384 fml::TimePoint frame_start,
385 fml::TimePoint frame_end) {
386 async::PostTask(dispatcher, [&flatland_connection]() {
387 zx::event acquire_fence;
388 zx::event::create(0, &acquire_fence);
389 zx::event release_fence;
390 zx::event::create(0, &release_fence);
391 flatland_connection.EnqueueAcquireFence(std::move(acquire_fence));
392 flatland_connection.EnqueueReleaseFence(std::move(release_fence));
393 flatland_connection.Present();
394 });
395 };
396
397 // Call Await Vsync with a callback that triggers Present and consumes the one
398 // and only present credit we start with.
399 reset_test_counters();
400 flatland_connection.AwaitVsync(fire_callback);
401 loop().RunUntilIdle();
402 EXPECT_EQ(num_presents_called, 1u);
403 EXPECT_EQ(num_acquire_fences, 1u);
404 EXPECT_EQ(num_release_fences, 0u);
405
406 // Do it again, but this time we should not get a present because the client
407 // has exhausted its present credits.
408 reset_test_counters();
409 flatland_connection.AwaitVsync(fire_callback);
410 OnNextFrameBegin(0);
411 loop().RunUntilIdle();
412 EXPECT_EQ(num_presents_called, 0u);
413
414 // Supply a present credit but dont set a new fire callback. Fire callback
415 // from previous ONFB should fire and trigger a Present()
416 reset_test_counters();
417 OnNextFrameBegin(1);
418 loop().RunUntilIdle();
419 EXPECT_EQ(num_presents_called, 1u);
420 EXPECT_EQ(num_acquire_fences, 1u);
421 EXPECT_EQ(num_release_fences, 1u);
422
423 // From here on we are testing handling of a race condition where a fire
424 // callback is fired but another ONFB arrives before the present from the
425 // first fire callback comes in, causing present_credits to be negative
426 // within Present().
427
428 uint num_onfb = 5;
429 uint num_deferred_callbacks = 0;
430 // This callback will accumulate num_onfb+1 calls before firing all
431 // of their presents at once.
432 auto accumulating_fire_callback = [&](fml::TimePoint frame_start,
433 fml::TimePoint frame_end) {
434 num_deferred_callbacks++;
435 if (num_deferred_callbacks > num_onfb) {
437 for (uint i = 0; i < num_onfb + 1; i++) {
438 fire_callback(now, now);
439 num_deferred_callbacks--;
440 }
441 }
442 };
443
444 reset_test_counters();
445 for (uint i = 0; i < num_onfb; i++) {
446 flatland_connection.AwaitVsync(accumulating_fire_callback);
447 // only supply a present credit on the first call. Since Presents are being
448 // deferred this credit will not be used up, but we need a credit to call
449 // the accumulating_fire_callback
450 OnNextFrameBegin(i == 0 ? 1 : 0);
451 loop().RunUntilIdle();
452 EXPECT_EQ(num_presents_called, 0u);
453 }
454
455 // This is the num_onfb+1 call to accumulating_fire_callback which triggers
456 // all of the "racing" presents to fire. the first one should be fired,
457 // but the other num_onfb Presents should be deferred.
458 flatland_connection.AwaitVsync(accumulating_fire_callback);
459 OnNextFrameBegin(0);
460 loop().RunUntilIdle();
461 EXPECT_EQ(num_presents_called, 1u);
462 EXPECT_EQ(num_acquire_fences, 1u);
463 EXPECT_EQ(num_release_fences, 1u);
464
465 // Supply a present credit, but pass an empty lambda to AwaitVsync so
466 // that it doesnt call Present(). Should get a deferred present with
467 // all the accumulate acuire fences
468 reset_test_counters();
469 flatland_connection.AwaitVsync([](fml::TimePoint, fml::TimePoint) {});
470 OnNextFrameBegin(1);
471 loop().RunUntilIdle();
472 EXPECT_EQ(num_presents_called, 1u);
473 EXPECT_EQ(num_acquire_fences, num_onfb);
474 EXPECT_EQ(num_release_fences, 1u);
475
476 // Pump another frame to check that release fences accumulate as expected
477 reset_test_counters();
478 flatland_connection.AwaitVsync(fire_callback);
479 OnNextFrameBegin(1);
480 loop().RunUntilIdle();
481 EXPECT_EQ(num_presents_called, 1u);
482 EXPECT_EQ(num_acquire_fences, 1u);
483 EXPECT_EQ(num_release_fences, num_onfb);
484}
485
486} // namespace flutter_runner::testing
void FireOnNextFrameBeginEvent(fuchsia::ui::composition::OnNextFrameBeginValues on_next_frame_begin_values)
void OnNextFrameBegin(int num_present_credits, const fml::TimePoint &presentation_time_1, const fml::TimePoint &presentation_time_2)
fidl::InterfaceHandle< fuchsia::ui::composition::Flatland > TakeFlatlandHandle()
static constexpr TimeDelta FromSeconds(int64_t seconds)
Definition time_delta.h:49
constexpr int64_t ToNanoseconds() const
Definition time_delta.h:61
TimeDelta ToEpochDelta() const
Definition time_point.h:52
static TimePoint Now()
Definition time_point.cc:49
#define FAIL(name, result)
EMSCRIPTEN_KEEPALIVE void empty()
#define FML_CHECK(condition)
Definition logging.h:85
std::string GetCurrentTestName()
Gets the name of the currently running test. This is useful in generating logs or assets based on tes...
Definition testing.cc:15
TEST_F(FocusDelegateTest, WatchCallbackSeries)
std::function< void(fuchsia::scenic::scheduling::FramePresentedInfo)> on_frame_presented_event
static constexpr fml::TimeDelta kInitialFlatlandVsyncOffset
std::function< void()> closure
Definition closure.h:14
#define EXPECT_TRUE(handle)
Definition unit_test.h:685