blob: 75b1ac41c0c6b9cc3c5d89ec0b3b60009f5694cf [file] [log] [blame]
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/scenic/scheduling/cpp/fidl.h>
#include <fuchsia/ui/policy/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/component_context.h>
#include "flutter/shell/platform/fuchsia/flutter/default_session_connection.h"
#include "flutter/shell/platform/fuchsia/flutter/logging.h"
#include "flutter/shell/platform/fuchsia/flutter/runner.h"
#include "gtest/gtest.h"
using namespace flutter_runner;
namespace flutter_runner_test {
static fml::TimePoint TimePointFromInt(int i) {
return fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(i));
}
static fml::TimeDelta TimeDeltaFromInt(int i) {
return fml::TimeDelta::FromNanoseconds(i);
}
static int TimePointToInt(fml::TimePoint time) {
return time.ToEpochDelta().ToNanoseconds();
}
static fuchsia::scenic::scheduling::PresentationInfo CreatePresentationInfo(
zx_time_t latch_point,
zx_time_t presentation_time) {
fuchsia::scenic::scheduling::PresentationInfo info;
info.set_latch_point(latch_point);
info.set_presentation_time(presentation_time);
return info;
}
class DefaultSessionConnectionTest : public ::testing::Test {
public:
void SetUp() override {
context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
scenic_ = context_->svc()->Connect<fuchsia::ui::scenic::Scenic>();
presenter_ = context_->svc()->Connect<fuchsia::ui::policy::Presenter>();
FML_CHECK(ZX_OK == loop_.StartThread("DefaultSessionConnectionTestThread",
&fidl_thread_));
auto session_listener_request = session_listener_.NewRequest();
scenic_->CreateSession(session_.NewRequest(), session_listener_.Bind());
// Ensure Scenic has had time to wake up before the test logic begins.
// TODO(61768) Find a better solution than sleeping periodically checking a
// condition.
int scenic_initialized = false;
scenic_->GetDisplayInfo(
[&scenic_initialized](fuchsia::ui::gfx::DisplayInfo display_info) {
scenic_initialized = true;
});
while (!scenic_initialized) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
// Warning: Initialization order matters here. |loop_| must be initialized
// before |SetUp()| so that we have a dispatcher already initialized.
async::Loop loop_ = async::Loop(&kAsyncLoopConfigAttachToCurrentThread);
std::unique_ptr<sys::ComponentContext> context_;
fuchsia::ui::scenic::ScenicPtr scenic_;
fuchsia::ui::policy::PresenterPtr presenter_;
fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session_;
fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> session_listener_;
thrd_t fidl_thread_;
};
TEST_F(DefaultSessionConnectionTest, SimplePresent) {
fml::closure on_session_error_callback = []() { FML_CHECK(false); };
uint64_t num_presents_handled = 0;
on_frame_presented_event on_frame_presented_callback =
[&num_presents_handled](
fuchsia::scenic::scheduling::FramePresentedInfo info) {
num_presents_handled += info.presentation_infos.size();
};
flutter_runner::DefaultSessionConnection session_connection(
"debug label", std::move(session_), on_session_error_callback,
on_frame_presented_callback, 3, fml::TimeDelta::FromSeconds(0));
for (int i = 0; i < 200; ++i) {
session_connection.Present();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
EXPECT_GT(num_presents_handled, 0u);
}
TEST_F(DefaultSessionConnectionTest, BatchedPresent) {
fml::closure on_session_error_callback = []() { FML_CHECK(false); };
uint64_t num_presents_handled = 0;
on_frame_presented_event on_frame_presented_callback =
[&num_presents_handled](
fuchsia::scenic::scheduling::FramePresentedInfo info) {
num_presents_handled += info.presentation_infos.size();
};
flutter_runner::DefaultSessionConnection session_connection(
"debug label", std::move(session_), on_session_error_callback,
on_frame_presented_callback, 3, fml::TimeDelta::FromSeconds(0));
for (int i = 0; i < 200; ++i) {
session_connection.Present();
if (i % 10 == 9) {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
EXPECT_GT(num_presents_handled, 0u);
}
TEST_F(DefaultSessionConnectionTest, AwaitVsync) {
fml::closure on_session_error_callback = []() { FML_CHECK(false); };
uint64_t num_presents_handled = 0;
on_frame_presented_event on_frame_presented_callback =
[&num_presents_handled](
fuchsia::scenic::scheduling::FramePresentedInfo info) {
num_presents_handled += info.presentation_infos.size();
};
flutter_runner::DefaultSessionConnection session_connection(
"debug label", std::move(session_), on_session_error_callback,
on_frame_presented_callback, 3, fml::TimeDelta::FromSeconds(0));
uint64_t await_vsyncs_handled = 0;
for (int i = 0; i < 5; ++i) {
session_connection.Present();
session_connection.AwaitVsync(
[&await_vsyncs_handled](auto...) { await_vsyncs_handled++; });
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
EXPECT_GT(num_presents_handled, 0u);
EXPECT_GT(await_vsyncs_handled, 0u);
}
TEST_F(DefaultSessionConnectionTest, EnsureBackpressureForAwaitVsync) {
fml::closure on_session_error_callback = []() { FML_CHECK(false); };
uint64_t num_presents_handled = 0;
on_frame_presented_event on_frame_presented_callback =
[&num_presents_handled](
fuchsia::scenic::scheduling::FramePresentedInfo info) {
num_presents_handled += info.presentation_infos.size();
};
flutter_runner::DefaultSessionConnection session_connection(
"debug label", std::move(session_), on_session_error_callback,
on_frame_presented_callback, 0, fml::TimeDelta::FromSeconds(0));
uint64_t await_vsyncs_handled = 0;
for (int i = 0; i < 5; ++i) {
session_connection.Present();
session_connection.AwaitVsync(
[&await_vsyncs_handled](auto...) { await_vsyncs_handled++; });
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
EXPECT_EQ(num_presents_handled, 1u);
EXPECT_EQ(await_vsyncs_handled, 0u);
}
TEST_F(DefaultSessionConnectionTest, SecondaryCallbackShouldFireRegardless) {
fml::closure on_session_error_callback = []() { FML_CHECK(false); };
uint64_t num_presents_handled = 0;
on_frame_presented_event on_frame_presented_callback =
[&num_presents_handled](
fuchsia::scenic::scheduling::FramePresentedInfo info) {
num_presents_handled += info.presentation_infos.size();
};
flutter_runner::DefaultSessionConnection session_connection(
"debug label", std::move(session_), on_session_error_callback,
on_frame_presented_callback, 0, fml::TimeDelta::FromSeconds(0));
// We're going to expect *only* secondary callbacks to be triggered.
uint64_t await_vsyncs_handled = 0;
uint64_t await_vsync_for_secondary_callbacks_handled = 0;
for (int i = 0; i < 5; ++i) {
session_connection.Present();
session_connection.AwaitVsync(
[&await_vsyncs_handled](auto...) { await_vsyncs_handled++; });
session_connection.AwaitVsyncForSecondaryCallback(
[&await_vsync_for_secondary_callbacks_handled](auto...) {
await_vsync_for_secondary_callbacks_handled++;
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
EXPECT_EQ(num_presents_handled, 1u);
EXPECT_EQ(await_vsyncs_handled, 0u);
EXPECT_GT(await_vsync_for_secondary_callbacks_handled, 0u);
}
TEST_F(DefaultSessionConnectionTest, AwaitVsyncBackpressureRelief) {
fml::closure on_session_error_callback = []() { FML_CHECK(false); };
uint64_t num_presents_handled = 0;
on_frame_presented_event on_frame_presented_callback =
[&num_presents_handled](
fuchsia::scenic::scheduling::FramePresentedInfo info) {
num_presents_handled += info.presentation_infos.size();
};
flutter_runner::DefaultSessionConnection session_connection(
"debug label", std::move(session_), on_session_error_callback,
on_frame_presented_callback, 1, fml::TimeDelta::FromSeconds(0));
uint64_t await_vsyncs_handled = 0;
// Max out our present budget.
for (int i = 0; i < 5; ++i) {
session_connection.Present();
}
// AwaitVsyncs().
for (int i = 0; i < 5; ++i) {
session_connection.AwaitVsync(
[&await_vsyncs_handled](auto...) { await_vsyncs_handled++; });
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
EXPECT_GT(num_presents_handled, 0u);
EXPECT_GT(await_vsyncs_handled, 0u);
}
// The first set of tests has an empty |future_presentation_infos| passed in.
// Therefore these tests are to ensure that on startup and after not presenting
// for some time that we have correct, reasonable behavior.
TEST(CalculateNextLatchPointTest, PresentAsSoonAsPossible) {
fml::TimePoint present_requested_time = TimePointFromInt(0);
fml::TimePoint now = TimePointFromInt(0);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(0);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(0);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point), TimePointToInt(now));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, LongFrameBuildTime) {
fml::TimePoint present_requested_time = TimePointFromInt(500);
fml::TimePoint now = TimePointFromInt(600);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(0);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(2500);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
EXPECT_GT(flutter_frame_build_time, vsync_interval);
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 3)));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, DelayedPresentRequestWithLongFrameBuildTime) {
fml::TimePoint present_requested_time = TimePointFromInt(0);
fml::TimePoint now = TimePointFromInt(1500);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(0);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(2000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
EXPECT_GT(flutter_frame_build_time, vsync_interval);
EXPECT_GT(now, present_requested_time + vsync_interval);
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point), TimePointToInt(now));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, LastLastPointTargetedLate) {
fml::TimePoint present_requested_time = TimePointFromInt(2000);
fml::TimePoint now = TimePointFromInt(2000);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(2600);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(1000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
EXPECT_GT(last_latch_point_targeted, present_requested_time);
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
// This set of tests provides (latch_point, vsync_time) pairs in
// |future_presentation_infos|. This tests steady state behavior where we're
// presenting frames virtually every vsync interval.
TEST(CalculateNextLatchPointTest, SteadyState_OnTimeFrames) {
fml::TimePoint present_requested_time = TimePointFromInt(5000);
fml::TimePoint now = TimePointFromInt(5000);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(4500);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(1000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {
{TimePointFromInt(3500), TimePointFromInt(4000)},
{TimePointFromInt(4500), TimePointFromInt(5000)},
{TimePointFromInt(5500), TimePointFromInt(6000)},
{TimePointFromInt(6500), TimePointFromInt(7000)},
{TimePointFromInt(7500), TimePointFromInt(8000)},
};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_EQ(TimePointToInt(calculated_latch_point), 6500);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, SteadyState_LongFrameBuildTimes) {
fml::TimePoint present_requested_time = TimePointFromInt(5000);
fml::TimePoint now = TimePointFromInt(5000);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(4500);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(2000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {
{TimePointFromInt(3500), TimePointFromInt(4000)},
{TimePointFromInt(4500), TimePointFromInt(5000)},
{TimePointFromInt(5500), TimePointFromInt(6000)},
{TimePointFromInt(6500), TimePointFromInt(7000)},
{TimePointFromInt(7500), TimePointFromInt(8000)},
};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
EXPECT_GT(flutter_frame_build_time, vsync_interval);
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 3)));
EXPECT_EQ(TimePointToInt(calculated_latch_point), 7500);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, SteadyState_LateLastLatchPointTargeted) {
fml::TimePoint present_requested_time = TimePointFromInt(5000);
fml::TimePoint now = TimePointFromInt(5000);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(6500);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(1000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {
{TimePointFromInt(4500), TimePointFromInt(5000)},
{TimePointFromInt(5500), TimePointFromInt(6000)},
{TimePointFromInt(6500), TimePointFromInt(7000)},
{TimePointFromInt(7500), TimePointFromInt(8000)},
{TimePointFromInt(8500), TimePointFromInt(9000)},
};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
EXPECT_GT(last_latch_point_targeted, now + vsync_interval);
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_EQ(TimePointToInt(calculated_latch_point), 6500);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest,
SteadyState_DelayedPresentRequestWithLongFrameBuildTime) {
fml::TimePoint present_requested_time = TimePointFromInt(4000);
fml::TimePoint now = TimePointFromInt(5500);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(3500);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(2000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {
{TimePointFromInt(4500), TimePointFromInt(5000)},
{TimePointFromInt(5500), TimePointFromInt(6000)},
{TimePointFromInt(6500), TimePointFromInt(7000)},
};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
EXPECT_GT(flutter_frame_build_time, vsync_interval);
EXPECT_GT(now, present_requested_time + vsync_interval);
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_EQ(TimePointToInt(calculated_latch_point), 6500);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, SteadyState_FuzzyLatchPointsBeforeTarget) {
fml::TimePoint present_requested_time = TimePointFromInt(4000);
fml::TimePoint now = TimePointFromInt(4000);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(5490);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(1000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {
{TimePointFromInt(4510), TimePointFromInt(5000)},
{TimePointFromInt(5557), TimePointFromInt(6000)},
{TimePointFromInt(6482), TimePointFromInt(7000)},
{TimePointFromInt(7356), TimePointFromInt(8000)},
};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + vsync_interval));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_EQ(TimePointToInt(calculated_latch_point), 5557);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(CalculateNextLatchPointTest, SteadyState_FuzzyLatchPointsAfterTarget) {
fml::TimePoint present_requested_time = TimePointFromInt(4000);
fml::TimePoint now = TimePointFromInt(4000);
fml::TimePoint last_latch_point_targeted = TimePointFromInt(5557);
fml::TimeDelta flutter_frame_build_time = TimeDeltaFromInt(1000);
fml::TimeDelta vsync_interval = TimeDeltaFromInt(1000);
std::deque<std::pair<fml::TimePoint, fml::TimePoint>>
future_presentation_infos = {
{TimePointFromInt(4510), TimePointFromInt(5000)},
{TimePointFromInt(5490), TimePointFromInt(6000)},
{TimePointFromInt(6482), TimePointFromInt(7000)},
{TimePointFromInt(7356), TimePointFromInt(8000)},
};
// Assertions about given values.
EXPECT_GE(now, present_requested_time);
EXPECT_GE(flutter_frame_build_time, TimeDeltaFromInt(0));
EXPECT_GT(vsync_interval, TimeDeltaFromInt(0));
fml::TimePoint calculated_latch_point =
DefaultSessionConnection::CalculateNextLatchPoint(
present_requested_time, now, last_latch_point_targeted,
flutter_frame_build_time, vsync_interval, future_presentation_infos);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 2)));
EXPECT_LE(TimePointToInt(calculated_latch_point),
TimePointToInt(now + (vsync_interval * 3)));
EXPECT_EQ(TimePointToInt(calculated_latch_point), 6482);
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(present_requested_time + flutter_frame_build_time));
EXPECT_GE(TimePointToInt(calculated_latch_point),
TimePointToInt(last_latch_point_targeted));
}
TEST(SnapToNextPhaseTest, SnapOverlapsWithNow) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(10);
const auto delta = fml::TimeDelta::FromNanoseconds(10);
const auto next_vsync =
flutter_runner::DefaultSessionConnection::SnapToNextPhase(
now, last_presentation_time, delta);
EXPECT_EQ(now + delta, next_vsync);
}
TEST(SnapToNextPhaseTest, SnapAfterNow) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(9);
const auto delta = fml::TimeDelta::FromNanoseconds(10);
const auto next_vsync =
flutter_runner::DefaultSessionConnection::SnapToNextPhase(
now, last_presentation_time, delta);
// math here: 10 - 9 = 1
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(1), next_vsync);
}
TEST(SnapToNextPhaseTest, SnapAfterNowMultiJump) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(34);
const auto delta = fml::TimeDelta::FromNanoseconds(10);
const auto next_vsync =
flutter_runner::DefaultSessionConnection::SnapToNextPhase(
now, last_presentation_time, delta);
// zeroes: -34, -24, -14, -4, 6, ...
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(6), next_vsync);
}
TEST(SnapToNextPhaseTest, SnapAfterNowMultiJumpAccountForCeils) {
const auto now = fml::TimePoint::Now();
const auto last_presentation_time = now - fml::TimeDelta::FromNanoseconds(20);
const auto delta = fml::TimeDelta::FromNanoseconds(16);
const auto next_vsync =
flutter_runner::DefaultSessionConnection::SnapToNextPhase(
now, last_presentation_time, delta);
// zeroes: -20, -4, 12, 28, ...
EXPECT_EQ(now + fml::TimeDelta::FromNanoseconds(12), next_vsync);
}
TEST(GetTargetTimesTest, ScheduleForNextVsync) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
const fml::TimePoint last_targeted_vsync = TimePointFromInt(10);
const fml::TimePoint now = TimePointFromInt(9);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 10);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 20);
}
TEST(GetTargetTimesTest, ScheduleForCurrentVsync_DueToOffset) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(3);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
const fml::TimePoint last_targeted_vsync = TimePointFromInt(0);
const fml::TimePoint now = TimePointFromInt(6);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 7);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 10);
}
TEST(GetTargetTimesTest, ScheduleForFollowingVsync_BecauseOfNow) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
const fml::TimePoint last_targeted_vsync = TimePointFromInt(10);
const fml::TimePoint now = TimePointFromInt(15);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 20);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 30);
}
TEST(GetTargetTimesTest, ScheduleForFollowingVsync_BecauseOfTargettedTime) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
const fml::TimePoint last_targeted_vsync = TimePointFromInt(20);
const fml::TimePoint now = TimePointFromInt(9);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 20);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 30);
}
TEST(GetTargetTimesTest, ScheduleForDistantVsync_BecauseOfTargettedTime) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
const fml::TimePoint last_targeted_vsync = TimePointFromInt(60);
const fml::TimePoint now = TimePointFromInt(9);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 60);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 70);
}
TEST(GetTargetTimesTest, ScheduleForFollowingVsync_WithSlightVsyncDrift) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
// Even though it appears as if the next vsync is at time 40, we should still
// present at time 50.
const fml::TimePoint last_targeted_vsync = TimePointFromInt(37);
const fml::TimePoint now = TimePointFromInt(9);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 40);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 50);
}
TEST(GetTargetTimesTest, ScheduleForAnOffsetFromVsync) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(4);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
const fml::TimePoint last_targeted_vsync = TimePointFromInt(10);
const fml::TimePoint now = TimePointFromInt(9);
const fml::TimePoint next_vsync = TimePointFromInt(10);
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 16);
EXPECT_EQ(TimePointToInt(target_times.frame_target), 20);
}
TEST(GetTargetTimesTest, ScheduleMultipleTimes) {
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
fml::TimePoint last_targeted_vsync = TimePointFromInt(0);
fml::TimePoint now = TimePointFromInt(5);
fml::TimePoint next_vsync = TimePointFromInt(10);
for (int i = 0; i < 100; ++i) {
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start), 10 * (i + 1));
EXPECT_EQ(TimePointToInt(target_times.frame_target), 10 * (i + 2));
// Simulate the passage of time.
now = now + vsync_interval;
next_vsync = next_vsync + vsync_interval;
last_targeted_vsync = target_times.frame_target;
}
}
TEST(GetTargetTimesTest, ScheduleMultipleTimes_WithDelayedWakeups) {
// It is often the case that Flutter does not wake up when it intends to due
// to CPU contention. This test has DefaultSessionConnection wake up to
// schedule 0-4ns after when |now| should be - and we verify that the results
// should be the same as if there were no delay.
const fml::TimeDelta vsync_offset = TimeDeltaFromInt(0);
const fml::TimeDelta vsync_interval = TimeDeltaFromInt(10);
fml::TimePoint last_targeted_vsync = TimePointFromInt(0);
fml::TimePoint now = TimePointFromInt(5);
fml::TimePoint next_vsync = TimePointFromInt(10);
for (int i = 0; i < 100; ++i) {
const auto target_times =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync, now, next_vsync);
const auto target_times_delay =
flutter_runner::DefaultSessionConnection::GetTargetTimes(
vsync_offset, vsync_interval, last_targeted_vsync,
now + TimeDeltaFromInt(i % 5), next_vsync);
EXPECT_EQ(TimePointToInt(target_times.frame_start),
TimePointToInt(target_times_delay.frame_start));
EXPECT_EQ(TimePointToInt(target_times.frame_target),
TimePointToInt(target_times_delay.frame_target));
// Simulate the passage of time.
now = now + vsync_interval;
next_vsync = next_vsync + vsync_interval;
last_targeted_vsync = target_times.frame_target;
}
}
// static fuchsia::scenic::scheduling::PresentationInfo UpdatePresentationInfo(
// fuchsia::scenic::scheduling::FuturePresentationTimes future_info,
// fuchsia::scenic::scheduling::PresentationInfo& presentation_info);
TEST(UpdatePresentationInfoTest, SingleUpdate) {
std::vector<fuchsia::scenic::scheduling::PresentationInfo>
future_presentations = {};
// Update the |vsync_info|.
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/5, /*presentation_time=*/10));
fuchsia::scenic::scheduling::FuturePresentationTimes future_info;
future_info.future_presentations = std::move(future_presentations);
future_info.remaining_presents_in_flight_allowed = 1;
fuchsia::scenic::scheduling::PresentationInfo presentation_info;
presentation_info.set_presentation_time(0);
fuchsia::scenic::scheduling::PresentationInfo new_presentation_info =
flutter_runner::DefaultSessionConnection::UpdatePresentationInfo(
std::move(future_info), presentation_info);
EXPECT_EQ(new_presentation_info.presentation_time(), 10);
}
TEST(UpdatePresentationInfoTest, MultipleUpdates) {
std::vector<fuchsia::scenic::scheduling::PresentationInfo>
future_presentations = {};
// Update the |vsync_info|.
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/15, /*presentation_time=*/20));
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/25, /*presentation_time=*/30));
fuchsia::scenic::scheduling::FuturePresentationTimes future_info;
future_info.future_presentations = std::move(future_presentations);
future_info.remaining_presents_in_flight_allowed = 1;
fuchsia::scenic::scheduling::PresentationInfo presentation_info;
presentation_info.set_presentation_time(0);
fuchsia::scenic::scheduling::PresentationInfo new_presentation_info =
flutter_runner::DefaultSessionConnection::UpdatePresentationInfo(
std::move(future_info), presentation_info);
EXPECT_EQ(new_presentation_info.presentation_time(), 20);
// Clear and re-try with more future times!
future_presentations.clear();
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/15, /*presentation_time=*/20));
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/25, /*presentation_time=*/30));
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/35, /*presentation_time=*/40));
future_presentations.push_back(
CreatePresentationInfo(/*latch_point=*/45, /*presentation_time=*/50));
future_info.future_presentations = std::move(future_presentations);
future_info.remaining_presents_in_flight_allowed = 1;
new_presentation_info =
flutter_runner::DefaultSessionConnection::UpdatePresentationInfo(
std::move(future_info), new_presentation_info);
EXPECT_EQ(new_presentation_info.presentation_time(), 30);
}
} // namespace flutter_runner_test