mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[fuchsia] Migrate flutter runner to use Present2 (#14162)
No change in behavior expected. We have 2 frames in flight as before. By switching to Present2 and specifying a kMaxFramesInFlight however, we allow us greater flexibility to change how Flutter schedules its frames. This change also adds tests for SessionConnection and VsyncRecorder.
This commit is contained in:
parent
c107969ecd
commit
11b770489f
1
BUILD.gn
1
BUILD.gn
@ -135,6 +135,7 @@ if (is_fuchsia) {
|
||||
deps = [
|
||||
"//flutter/flow:flow_tests",
|
||||
"//flutter/fml:fml_tests",
|
||||
"//flutter/shell/platform/fuchsia/flutter:flutter_runner_scenic_tests",
|
||||
"//flutter/shell/platform/fuchsia/flutter:flutter_runner_tests",
|
||||
]
|
||||
}
|
||||
|
||||
@ -1036,6 +1036,7 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_product_r
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_scenic_tests.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/flutter_runner_tzdata_tests.cmx
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/meta/jit_product_runtime
|
||||
|
||||
@ -317,6 +317,7 @@ executable("flutter_runner_unittests") {
|
||||
"task_observers.h",
|
||||
"task_runner_adapter.cc",
|
||||
"task_runner_adapter.h",
|
||||
"tests/vsync_recorder_unittests.cc",
|
||||
"thread.cc",
|
||||
"thread.h",
|
||||
"vsync_recorder.cc",
|
||||
@ -452,3 +453,114 @@ fuchsia_archive("flutter_runner_tzdata_tests") {
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
executable("flutter_runner_scenic_unittests") {
|
||||
testonly = true
|
||||
|
||||
output_name = "flutter_runner_scenic_tests"
|
||||
|
||||
sources = [
|
||||
"component.cc",
|
||||
"component.h",
|
||||
"compositor_context.cc",
|
||||
"compositor_context.h",
|
||||
"engine.cc",
|
||||
"engine.h",
|
||||
"fuchsia_intl.cc",
|
||||
"fuchsia_intl.h",
|
||||
"isolate_configurator.cc",
|
||||
"isolate_configurator.h",
|
||||
"logging.h",
|
||||
"loop.cc",
|
||||
"loop.h",
|
||||
"platform_view.cc",
|
||||
"platform_view.h",
|
||||
"runner.cc",
|
||||
"runner.h",
|
||||
"session_connection.cc",
|
||||
"session_connection.h",
|
||||
"surface.cc",
|
||||
"surface.h",
|
||||
"task_observers.cc",
|
||||
"task_observers.h",
|
||||
"task_runner_adapter.cc",
|
||||
"task_runner_adapter.h",
|
||||
"tests/session_connection_unittests.cc",
|
||||
"thread.cc",
|
||||
"thread.h",
|
||||
"unique_fdio_ns.h",
|
||||
"vsync_recorder.cc",
|
||||
"vsync_recorder.h",
|
||||
"vsync_waiter.cc",
|
||||
"vsync_waiter.h",
|
||||
"vsync_waiter_unittests.cc",
|
||||
"vulkan_surface.cc",
|
||||
"vulkan_surface.h",
|
||||
"vulkan_surface_pool.cc",
|
||||
"vulkan_surface_pool.h",
|
||||
"vulkan_surface_producer.cc",
|
||||
"vulkan_surface_producer.h",
|
||||
]
|
||||
|
||||
# This is needed for //third_party/googletest for linking zircon symbols.
|
||||
libs = [ "//fuchsia/sdk/$host_os/arch/$target_cpu/sysroot/lib/libzircon.so" ]
|
||||
|
||||
deps = [
|
||||
":flutter_runner_fixtures",
|
||||
":jit",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.policy",
|
||||
"$fuchsia_sdk_root/pkg:trace-provider-so",
|
||||
"//build/fuchsia/fidl:fuchsia.accessibility.semantics",
|
||||
"//build/fuchsia/pkg:async-default",
|
||||
"//build/fuchsia/pkg:async-loop-cpp",
|
||||
"//build/fuchsia/pkg:async-loop-default",
|
||||
"//build/fuchsia/pkg:scenic_cpp",
|
||||
"//build/fuchsia/pkg:sys_cpp_testing",
|
||||
"//flutter/common:common",
|
||||
"//flutter/flow:flow",
|
||||
"//flutter/lib/ui",
|
||||
"//flutter/runtime",
|
||||
"//flutter/shell/common",
|
||||
"//flutter/shell/platform/fuchsia/dart-pkg/fuchsia:sdk_ext",
|
||||
"//flutter/shell/platform/fuchsia/dart-pkg/zircon:zircon",
|
||||
"//flutter/shell/platform/fuchsia/runtime/dart/utils",
|
||||
"//flutter/testing",
|
||||
"//flutter/vulkan:vulkan",
|
||||
"//third_party/dart/runtime:libdart_jit",
|
||||
"//third_party/dart/runtime/platform:libdart_platform_jit",
|
||||
"//third_party/icu",
|
||||
"//third_party/skia",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
"//third_party/googletest:gtest",
|
||||
]
|
||||
}
|
||||
|
||||
fuchsia_archive("flutter_runner_scenic_tests") {
|
||||
testonly = true
|
||||
|
||||
deps = [
|
||||
":flutter_runner_scenic_unittests",
|
||||
]
|
||||
|
||||
binary = "$target_name"
|
||||
|
||||
resources = [
|
||||
{
|
||||
path = rebase_path("//third_party/icu/common/icudtl.dat")
|
||||
dest = "icudtl.dat"
|
||||
},
|
||||
]
|
||||
|
||||
meta_dir = "//flutter/shell/platform/fuchsia/flutter/meta"
|
||||
|
||||
libraries = common_libs
|
||||
|
||||
meta = [
|
||||
{
|
||||
path = rebase_path("meta/$target_name.cmx")
|
||||
dest = "$target_name.cmx"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@ -51,7 +51,8 @@ class ScopedFrame final : public flutter::CompositorContext::ScopedFrame {
|
||||
{
|
||||
// Flush all pending session ops.
|
||||
TRACE_EVENT0("flutter", "SessionPresent");
|
||||
session_connection_.Present(*this);
|
||||
|
||||
session_connection_.Present(this);
|
||||
}
|
||||
|
||||
return flutter::RasterStatus::kSuccess;
|
||||
@ -68,12 +69,14 @@ CompositorContext::CompositorContext(
|
||||
fml::closure session_error_callback,
|
||||
zx_handle_t vsync_event_handle)
|
||||
: debug_label_(std::move(debug_label)),
|
||||
session_connection_(debug_label_,
|
||||
std::move(view_token),
|
||||
std::move(view_ref_pair),
|
||||
std::move(session),
|
||||
session_error_callback,
|
||||
vsync_event_handle) {}
|
||||
session_connection_(
|
||||
debug_label_,
|
||||
std::move(view_token),
|
||||
std::move(view_ref_pair),
|
||||
std::move(session),
|
||||
session_error_callback,
|
||||
[](auto) {},
|
||||
vsync_event_handle) {}
|
||||
|
||||
void CompositorContext::OnSessionMetricsDidChange(
|
||||
const fuchsia::ui::gfx::Metrics& metrics) {
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
{
|
||||
"program": {
|
||||
"binary": "bin/app"
|
||||
},
|
||||
"sandbox": {
|
||||
"features": [
|
||||
"config-data",
|
||||
"deprecated-ambient-replace-as-executable",
|
||||
"root-ssl-certificates",
|
||||
"vulkan"
|
||||
],
|
||||
"services": [
|
||||
"fuchsia.logger.LogSink",
|
||||
"fuchsia.sys.Environment",
|
||||
"fuchsia.sysmem.Allocator",
|
||||
"fuchsia.tracing.provider.Registry",
|
||||
"fuchsia.ui.policy.Presenter",
|
||||
"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@ SessionConnection::SessionConnection(
|
||||
scenic::ViewRefPair view_ref_pair,
|
||||
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)
|
||||
: debug_label_(std::move(debug_label)),
|
||||
session_wrapper_(session.Bind(), nullptr),
|
||||
@ -30,48 +31,95 @@ SessionConnection::SessionConnection(
|
||||
surface_producer_(
|
||||
std::make_unique<VulkanSurfaceProducer>(&session_wrapper_)),
|
||||
scene_update_context_(&session_wrapper_, surface_producer_.get()),
|
||||
on_frame_presented_callback_(std::move(on_frame_presented_callback)),
|
||||
vsync_event_handle_(vsync_event_handle) {
|
||||
session_wrapper_.set_error_handler(
|
||||
[callback = session_error_callback](zx_status_t status) { callback(); });
|
||||
|
||||
// Set the |fuchsia::ui::scenic::OnFramePresented()| event handler that will
|
||||
// fire every time a set of one or more frames is presented.
|
||||
session_wrapper_.set_on_frame_presented_handler(
|
||||
[this, handle = vsync_event_handle_](
|
||||
fuchsia::scenic::scheduling::FramePresentedInfo info) {
|
||||
// Update Scenic's limit for our remaining frames in flight allowed.
|
||||
size_t num_presents_handled = info.presentation_infos.size();
|
||||
frames_in_flight_allowed_ = info.num_presents_allowed;
|
||||
|
||||
// A frame was presented: Update our |frames_in_flight| to match the
|
||||
// updated unfinalized present requests.
|
||||
frames_in_flight_ -= num_presents_handled;
|
||||
FML_DCHECK(frames_in_flight_ >= 0);
|
||||
|
||||
VsyncRecorder::GetInstance().UpdateFramePresentedInfo(
|
||||
zx::time(info.actual_presentation_time));
|
||||
|
||||
// Call the client-provided callback once we are done using |info|.
|
||||
on_frame_presented_callback_(std::move(info));
|
||||
|
||||
if (present_session_pending_) {
|
||||
PresentSession();
|
||||
}
|
||||
ToggleSignal(handle, true);
|
||||
} // callback
|
||||
);
|
||||
|
||||
session_wrapper_.SetDebugName(debug_label_);
|
||||
|
||||
root_view_.AddChild(root_node_);
|
||||
root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask |
|
||||
fuchsia::ui::gfx::kSizeChangeHintEventMask);
|
||||
|
||||
// Signal is initially high indicating availability of the session.
|
||||
ToggleSignal(vsync_event_handle_, true);
|
||||
// Get information to finish initialization and only then allow Present()s.
|
||||
session_wrapper_.RequestPresentationTimes(
|
||||
/*requested_prediction_span=*/0,
|
||||
[this](fuchsia::scenic::scheduling::FuturePresentationTimes info) {
|
||||
frames_in_flight_allowed_ = info.remaining_presents_in_flight_allowed;
|
||||
|
||||
PresentSession();
|
||||
// If Scenic alloted us 0 frames to begin with, we should fail here.
|
||||
FML_CHECK(frames_in_flight_allowed_ > 0);
|
||||
|
||||
VsyncRecorder::GetInstance().UpdateNextPresentationInfo(
|
||||
std::move(info));
|
||||
|
||||
// Signal is initially high indicating availability of the session.
|
||||
ToggleSignal(vsync_event_handle_, true);
|
||||
initialized_ = true;
|
||||
|
||||
PresentSession();
|
||||
});
|
||||
}
|
||||
|
||||
SessionConnection::~SessionConnection() = default;
|
||||
|
||||
void SessionConnection::Present(
|
||||
flutter::CompositorContext::ScopedFrame& frame) {
|
||||
flutter::CompositorContext::ScopedFrame* frame) {
|
||||
TRACE_EVENT0("gfx", "SessionConnection::Present");
|
||||
TRACE_FLOW_BEGIN("gfx", "SessionConnection::PresentSession",
|
||||
next_present_session_trace_id_);
|
||||
next_present_session_trace_id_++;
|
||||
|
||||
// Throttle vsync if presentation callback is already pending. This allows
|
||||
// the paint tasks for this frame to execute in parallel with presentation
|
||||
// of last frame but still provides back-pressure to prevent us from queuing
|
||||
// even more work.
|
||||
if (presentation_callback_pending_) {
|
||||
// Throttle frame submission to Scenic if we already have the maximum amount
|
||||
// of frames in flight. This allows the paint tasks for this frame to execute
|
||||
// in parallel with the presentation of previous frame but still provides
|
||||
// back-pressure to prevent us from enqueuing even more work.
|
||||
if (initialized_ && frames_in_flight_ < kMaxFramesInFlight) {
|
||||
TRACE_FLOW_BEGIN("gfx", "SessionConnection::PresentSession",
|
||||
next_present_session_trace_id_);
|
||||
next_present_session_trace_id_++;
|
||||
PresentSession();
|
||||
} else {
|
||||
// We should never exceed the max frames in flight.
|
||||
FML_CHECK(frames_in_flight_ <= kMaxFramesInFlight);
|
||||
|
||||
present_session_pending_ = true;
|
||||
ToggleSignal(vsync_event_handle_, false);
|
||||
} else {
|
||||
PresentSession();
|
||||
}
|
||||
|
||||
// Execute paint tasks and signal fences.
|
||||
auto surfaces_to_submit = scene_update_context_.ExecutePaintTasks(frame);
|
||||
if (frame) {
|
||||
// Execute paint tasks and signal fences.
|
||||
auto surfaces_to_submit = scene_update_context_.ExecutePaintTasks(*frame);
|
||||
|
||||
// Tell the surface producer that a present has occurred so it can perform
|
||||
// book-keeping on buffer caches.
|
||||
surface_producer_->OnSurfacesPresented(std::move(surfaces_to_submit));
|
||||
// Tell the surface producer that a present has occurred so it can perform
|
||||
// book-keeping on buffer caches.
|
||||
surface_producer_->OnSurfacesPresented(std::move(surfaces_to_submit));
|
||||
}
|
||||
}
|
||||
|
||||
void SessionConnection::OnSessionSizeChangeHint(float width_change_factor,
|
||||
@ -93,6 +141,17 @@ void SessionConnection::EnqueueClearOps() {
|
||||
|
||||
void SessionConnection::PresentSession() {
|
||||
TRACE_EVENT0("gfx", "SessionConnection::PresentSession");
|
||||
|
||||
// If we cannot call Present2() because we have no more Scenic frame budget,
|
||||
// then we must wait until the OnFramePresented() event fires so we can
|
||||
// continue our work.
|
||||
if (frames_in_flight_allowed_ == 0) {
|
||||
FML_CHECK(!initialized_ || present_session_pending_);
|
||||
return;
|
||||
}
|
||||
|
||||
present_session_pending_ = false;
|
||||
|
||||
while (processed_present_session_trace_id_ < next_present_session_trace_id_) {
|
||||
TRACE_FLOW_END("gfx", "SessionConnection::PresentSession",
|
||||
processed_present_session_trace_id_);
|
||||
@ -101,26 +160,19 @@ void SessionConnection::PresentSession() {
|
||||
TRACE_FLOW_BEGIN("gfx", "Session::Present", next_present_trace_id_);
|
||||
next_present_trace_id_++;
|
||||
|
||||
// Presentation callback is pending as a result of Present() call below.
|
||||
presentation_callback_pending_ = true;
|
||||
++frames_in_flight_;
|
||||
|
||||
// Flush all session ops. Paint tasks may not yet have executed but those are
|
||||
// fenced. The compositor can start processing ops while we finalize paint
|
||||
// tasks.
|
||||
session_wrapper_.Present(
|
||||
0, // presentation_time. (placeholder).
|
||||
[this, handle = vsync_event_handle_](
|
||||
fuchsia::images::PresentationInfo presentation_info) {
|
||||
presentation_callback_pending_ = false;
|
||||
VsyncRecorder::GetInstance().UpdateVsyncInfo(presentation_info);
|
||||
// Process pending PresentSession() calls.
|
||||
if (present_session_pending_) {
|
||||
present_session_pending_ = false;
|
||||
PresentSession();
|
||||
}
|
||||
ToggleSignal(handle, true);
|
||||
} // callback
|
||||
);
|
||||
session_wrapper_.Present2(
|
||||
/*requested_presentation_time=*/0,
|
||||
/*requested_prediction_span=*/0,
|
||||
[this](fuchsia::scenic::scheduling::FuturePresentationTimes info) {
|
||||
frames_in_flight_allowed_ = info.remaining_presents_in_flight_allowed;
|
||||
VsyncRecorder::GetInstance().UpdateNextPresentationInfo(
|
||||
std::move(info));
|
||||
});
|
||||
|
||||
// Prepare for the next frame. These ops won't be processed till the next
|
||||
// present.
|
||||
|
||||
@ -24,6 +24,9 @@
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
using on_frame_presented_event =
|
||||
std::function<void(fuchsia::scenic::scheduling::FramePresentedInfo)>;
|
||||
|
||||
// The component residing on the GPU thread that is responsible for
|
||||
// maintaining the Scenic session connection and presenting node updates.
|
||||
class SessionConnection final {
|
||||
@ -33,6 +36,7 @@ class SessionConnection final {
|
||||
scenic::ViewRefPair view_ref_pair,
|
||||
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);
|
||||
|
||||
~SessionConnection();
|
||||
@ -59,7 +63,7 @@ class SessionConnection final {
|
||||
scenic::ContainerNode& root_node() { return root_node_; }
|
||||
scenic::View* root_view() { return &root_view_; }
|
||||
|
||||
void Present(flutter::CompositorContext::ScopedFrame& frame);
|
||||
void Present(flutter::CompositorContext::ScopedFrame* frame);
|
||||
|
||||
void OnSessionSizeChangeHint(float width_change_factor,
|
||||
float height_change_factor);
|
||||
@ -73,9 +77,12 @@ class SessionConnection final {
|
||||
|
||||
std::unique_ptr<VulkanSurfaceProducer> surface_producer_;
|
||||
flutter::SceneUpdateContext scene_update_context_;
|
||||
on_frame_presented_event on_frame_presented_callback_;
|
||||
|
||||
zx_handle_t vsync_event_handle_;
|
||||
|
||||
bool initialized_ = false;
|
||||
|
||||
// A flow event trace id for following |Session::Present| calls into
|
||||
// Scenic. This will be incremented each |Session::Present| call. By
|
||||
// convention, the Scenic side will also contain its own trace id that
|
||||
@ -84,7 +91,14 @@ class SessionConnection final {
|
||||
uint64_t next_present_session_trace_id_ = 0;
|
||||
uint64_t processed_present_session_trace_id_ = 0;
|
||||
|
||||
bool presentation_callback_pending_ = false;
|
||||
// The maximum number of frames Flutter sent to Scenic that it can have
|
||||
// outstanding at any time. This is equivalent to how many times it has
|
||||
// called Present2() before receiving an OnFramePresented() event.
|
||||
static constexpr int kMaxFramesInFlight = 2;
|
||||
int frames_in_flight_ = 0;
|
||||
|
||||
int frames_in_flight_allowed_ = 0;
|
||||
|
||||
bool present_session_pending_ = false;
|
||||
|
||||
void EnqueueClearOps();
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
// 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 "gtest/gtest.h"
|
||||
|
||||
#include <lib/async-loop/default.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
#include <lib/ui/scenic/cpp/view_token_pair.h>
|
||||
|
||||
#include <fuchsia/sys/cpp/fidl.h>
|
||||
#include <fuchsia/ui/policy/cpp/fidl.h>
|
||||
|
||||
#include "flutter/shell/platform/fuchsia/flutter/logging.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/runner.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/session_connection.h"
|
||||
|
||||
using namespace flutter_runner;
|
||||
|
||||
namespace flutter_runner_test {
|
||||
|
||||
class SessionConnectionTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
context_ = sys::ComponentContext::Create();
|
||||
scenic_ = context_->svc()->Connect<fuchsia::ui::scenic::Scenic>();
|
||||
presenter_ = context_->svc()->Connect<fuchsia::ui::policy::Presenter>();
|
||||
|
||||
FML_CHECK(ZX_OK ==
|
||||
loop_.StartThread("SessionConnectionTestThread", &fidl_thread_));
|
||||
|
||||
auto session_listener_request = session_listener_.NewRequest();
|
||||
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
|
||||
view_token_ = std::move(view_token);
|
||||
|
||||
scenic_->CreateSession(session_.NewRequest(), session_listener_.Bind());
|
||||
presenter_->PresentView(std::move(view_holder_token), nullptr);
|
||||
|
||||
FML_CHECK(zx::event::create(0, &vsync_event_) == ZX_OK);
|
||||
}
|
||||
// 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_;
|
||||
fuchsia::ui::views::ViewToken view_token_;
|
||||
zx::event vsync_event_;
|
||||
thrd_t fidl_thread_;
|
||||
};
|
||||
|
||||
TEST_F(SessionConnectionTest, SimplePresentTest) {
|
||||
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::SessionConnection session_connection(
|
||||
"debug label", std::move(view_token_), scenic::ViewRefPair::New(),
|
||||
std::move(session_), on_session_error_callback,
|
||||
on_frame_presented_callback, vsync_event_.get());
|
||||
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
session_connection.Present(nullptr);
|
||||
}
|
||||
|
||||
EXPECT_GT(num_presents_handled, 0u);
|
||||
}
|
||||
|
||||
TEST_F(SessionConnectionTest, BatchedPresentTest) {
|
||||
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::SessionConnection session_connection(
|
||||
"debug label", std::move(view_token_), scenic::ViewRefPair::New(),
|
||||
std::move(session_), on_session_error_callback,
|
||||
on_frame_presented_callback, vsync_event_.get());
|
||||
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
if (i % 10 == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
session_connection.Present(nullptr);
|
||||
}
|
||||
|
||||
EXPECT_GT(num_presents_handled, 0u);
|
||||
}
|
||||
|
||||
} // namespace flutter_runner_test
|
||||
134
shell/platform/fuchsia/flutter/tests/vsync_recorder_unittests.cc
Normal file
134
shell/platform/fuchsia/flutter/tests/vsync_recorder_unittests.cc
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2019 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 <gtest/gtest.h>
|
||||
|
||||
#include <fuchsia/sys/cpp/fidl.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
|
||||
#include "flutter/shell/platform/fuchsia/flutter/logging.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/vsync_recorder.h"
|
||||
|
||||
#include "flutter/shell/platform/fuchsia/flutter/runner.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/session_connection.h"
|
||||
|
||||
using namespace flutter_runner;
|
||||
|
||||
namespace flutter_runner_test {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE: Because there only exists one VsyncRecorder, the order of
|
||||
// these tests matter.
|
||||
|
||||
TEST(VsyncRecorderTest, DefaultVsyncInfoValues_AreReasonable) {
|
||||
VsyncInfo vsync_info = VsyncRecorder::GetInstance().GetCurrentVsyncInfo();
|
||||
|
||||
EXPECT_GE(vsync_info.presentation_time,
|
||||
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(0)));
|
||||
|
||||
EXPECT_GE(vsync_info.presentation_interval,
|
||||
fml::TimeDelta::FromMilliseconds(10));
|
||||
}
|
||||
|
||||
TEST(VsyncRecorderTest, DefaultLastPresentationTime_IsReasonable) {
|
||||
fml::TimePoint last_presentation_time =
|
||||
VsyncRecorder::GetInstance().GetLastPresentationTime();
|
||||
|
||||
EXPECT_LE(last_presentation_time, fml::TimePoint::Now());
|
||||
}
|
||||
|
||||
TEST(VsyncRecorderTest, SinglePresentationInfo_IsUpdatedCorrectly) {
|
||||
std::vector<fuchsia::scenic::scheduling::PresentationInfo>
|
||||
future_presentations = {};
|
||||
|
||||
// Update the |vsync_info|.
|
||||
future_presentations.push_back(
|
||||
CreatePresentationInfo(/*latch_point=*/5, /*presentation_time=*/10));
|
||||
VsyncRecorder::GetInstance().UpdateNextPresentationInfo(
|
||||
{.future_presentations = std::move(future_presentations),
|
||||
.remaining_presents_in_flight_allowed = 1});
|
||||
|
||||
// Check that |vsync_info| was correctly updated.
|
||||
VsyncInfo vsync_info = VsyncRecorder::GetInstance().GetCurrentVsyncInfo();
|
||||
EXPECT_EQ(
|
||||
vsync_info.presentation_time,
|
||||
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(10)));
|
||||
|
||||
EXPECT_GE(vsync_info.presentation_interval,
|
||||
fml::TimeDelta::FromMilliseconds(10));
|
||||
}
|
||||
|
||||
TEST(VsyncRecorderTest, MultiplePresentationInfos_AreUpdatedCorrectly) {
|
||||
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));
|
||||
VsyncRecorder::GetInstance().UpdateNextPresentationInfo(
|
||||
{.future_presentations = std::move(future_presentations),
|
||||
.remaining_presents_in_flight_allowed = 1});
|
||||
|
||||
// Check that |vsync_info| was correctly updated with the first time.
|
||||
VsyncInfo vsync_info = VsyncRecorder::GetInstance().GetCurrentVsyncInfo();
|
||||
EXPECT_EQ(
|
||||
vsync_info.presentation_time,
|
||||
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(20)));
|
||||
EXPECT_GE(vsync_info.presentation_interval,
|
||||
fml::TimeDelta::FromMilliseconds(10));
|
||||
|
||||
// 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));
|
||||
VsyncRecorder::GetInstance().UpdateNextPresentationInfo(
|
||||
{.future_presentations = std::move(future_presentations),
|
||||
.remaining_presents_in_flight_allowed = 1});
|
||||
|
||||
// Check that |vsync_info| was correctly updated with the first time.
|
||||
vsync_info = VsyncRecorder::GetInstance().GetCurrentVsyncInfo();
|
||||
EXPECT_EQ(
|
||||
vsync_info.presentation_time,
|
||||
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(30)));
|
||||
EXPECT_GE(vsync_info.presentation_interval,
|
||||
fml::TimeDelta::FromMilliseconds(10));
|
||||
}
|
||||
|
||||
TEST(VsyncRecorderTest, FramePresentedInfo_IsUpdatedCorrectly) {
|
||||
int64_t time1 = 10;
|
||||
int64_t time2 = 30;
|
||||
int64_t time3 = 35;
|
||||
|
||||
VsyncRecorder::GetInstance().UpdateFramePresentedInfo(zx::time(time1));
|
||||
|
||||
EXPECT_EQ(
|
||||
VsyncRecorder::GetInstance().GetLastPresentationTime(),
|
||||
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(time1)));
|
||||
|
||||
VsyncRecorder::GetInstance().UpdateFramePresentedInfo(zx::time(time2));
|
||||
VsyncRecorder::GetInstance().UpdateFramePresentedInfo(zx::time(time3));
|
||||
|
||||
EXPECT_EQ(
|
||||
VsyncRecorder::GetInstance().GetLastPresentationTime(),
|
||||
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(time3)));
|
||||
}
|
||||
|
||||
} // namespace flutter_runner_test
|
||||
@ -12,9 +12,9 @@ namespace {
|
||||
|
||||
std::mutex g_mutex;
|
||||
|
||||
// Since we don't have any presentation info until we call |Present| for the
|
||||
// first time, assume a 60hz refresh rate in the meantime.
|
||||
constexpr fml::TimeDelta kDefaultPresentationInterval =
|
||||
// Assume a 60hz refresh rate before we have enough past
|
||||
// |fuchsia::scenic::scheduling::PresentationInfo|s to calculate it ourselves.
|
||||
static constexpr fml::TimeDelta kDefaultPresentationInterval =
|
||||
fml::TimeDelta::FromSecondsF(1.0 / 60.0);
|
||||
|
||||
} // namespace
|
||||
@ -27,26 +27,35 @@ VsyncRecorder& VsyncRecorder::GetInstance() {
|
||||
VsyncInfo VsyncRecorder::GetCurrentVsyncInfo() const {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(g_mutex);
|
||||
if (last_presentation_info_) {
|
||||
return {fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(
|
||||
last_presentation_info_->presentation_time)),
|
||||
fml::TimeDelta::FromNanoseconds(
|
||||
last_presentation_info_->presentation_interval)};
|
||||
}
|
||||
return {fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromNanoseconds(
|
||||
next_presentation_info_.presentation_time())),
|
||||
kDefaultPresentationInterval};
|
||||
}
|
||||
return {fml::TimePoint::Now(), kDefaultPresentationInterval};
|
||||
}
|
||||
|
||||
void VsyncRecorder::UpdateVsyncInfo(
|
||||
fuchsia::images::PresentationInfo presentation_info) {
|
||||
void VsyncRecorder::UpdateNextPresentationInfo(
|
||||
fuchsia::scenic::scheduling::FuturePresentationTimes info) {
|
||||
std::unique_lock<std::mutex> lock(g_mutex);
|
||||
if (last_presentation_info_ &&
|
||||
presentation_info.presentation_time >
|
||||
last_presentation_info_->presentation_time) {
|
||||
last_presentation_info_ = presentation_info;
|
||||
} else if (!last_presentation_info_) {
|
||||
last_presentation_info_ = presentation_info;
|
||||
|
||||
auto next_time = next_presentation_info_.presentation_time();
|
||||
// Get the earliest vsync time that is after our recorded |presentation_time|.
|
||||
for (auto& presentation_info : info.future_presentations) {
|
||||
auto current_time = presentation_info.presentation_time();
|
||||
|
||||
if (current_time > next_time) {
|
||||
next_presentation_info_.set_presentation_time(current_time);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VsyncRecorder::UpdateFramePresentedInfo(zx::time presentation_time) {
|
||||
last_presentation_time_ = fml::TimePoint::FromEpochDelta(
|
||||
fml::TimeDelta::FromNanoseconds(presentation_time.get()));
|
||||
}
|
||||
|
||||
fml::TimePoint VsyncRecorder::GetLastPresentationTime() const {
|
||||
return last_presentation_time_;
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
|
||||
@ -26,18 +26,24 @@ class VsyncRecorder {
|
||||
// This function is safe to call from any thread.
|
||||
VsyncInfo GetCurrentVsyncInfo() const;
|
||||
|
||||
// Update the current Vsync info to |presentation_info|. This is expected
|
||||
// to be called in |scenic::Sesssion::Present| callbacks with the
|
||||
// presentation info provided by scenic. Only the most recent vsync
|
||||
// Update the next Vsync info to |next_presentation_info_|. This is expected
|
||||
// to be called in |scenic::Session::Present2| immedaite callbacks with the
|
||||
// presentation info provided by Scenic. Only the next vsync
|
||||
// information will be saved (in order to handle edge cases involving
|
||||
// multiple scenic sessions in the same process). This function is safe to
|
||||
// multiple Scenic sessions in the same process). This function is safe to
|
||||
// call from any thread.
|
||||
void UpdateVsyncInfo(fuchsia::images::PresentationInfo presentation_info);
|
||||
void UpdateNextPresentationInfo(
|
||||
fuchsia::scenic::scheduling::FuturePresentationTimes info);
|
||||
|
||||
void UpdateFramePresentedInfo(zx::time presentation_time);
|
||||
|
||||
fml::TimePoint GetLastPresentationTime() const;
|
||||
|
||||
private:
|
||||
VsyncRecorder() = default;
|
||||
VsyncRecorder() { next_presentation_info_.set_presentation_time(0); }
|
||||
|
||||
std::optional<fuchsia::images::PresentationInfo> last_presentation_info_;
|
||||
fuchsia::scenic::scheduling::PresentationInfo next_presentation_info_;
|
||||
fml::TimePoint last_presentation_time_ = fml::TimePoint::Now();
|
||||
|
||||
// Disallow copy and assignment.
|
||||
VsyncRecorder(const VsyncRecorder&) = delete;
|
||||
|
||||
@ -127,7 +127,9 @@ void VsyncWaiter::AwaitVSync() {
|
||||
VsyncInfo vsync_info = VsyncRecorder::GetInstance().GetCurrentVsyncInfo();
|
||||
|
||||
fml::TimePoint now = fml::TimePoint::Now();
|
||||
fml::TimePoint next_vsync = SnapToNextPhase(now, vsync_info.presentation_time,
|
||||
fml::TimePoint last_presentation_time =
|
||||
VsyncRecorder::GetInstance().GetLastPresentationTime();
|
||||
fml::TimePoint next_vsync = SnapToNextPhase(now, last_presentation_time,
|
||||
vsync_info.presentation_interval);
|
||||
task_runners_.GetUITaskRunner()->PostDelayedTask(
|
||||
[& weak_factory_ui = this->weak_factory_ui_] {
|
||||
@ -158,7 +160,9 @@ void VsyncWaiter::FireCallbackNow() {
|
||||
VsyncInfo vsync_info = VsyncRecorder::GetInstance().GetCurrentVsyncInfo();
|
||||
|
||||
fml::TimePoint now = fml::TimePoint::Now();
|
||||
fml::TimePoint next_vsync = SnapToNextPhase(now, vsync_info.presentation_time,
|
||||
fml::TimePoint last_presentation_time =
|
||||
VsyncRecorder::GetInstance().GetLastPresentationTime();
|
||||
fml::TimePoint next_vsync = SnapToNextPhase(now, last_presentation_time,
|
||||
vsync_info.presentation_interval);
|
||||
fml::TimePoint previous_vsync = next_vsync - vsync_info.presentation_interval;
|
||||
|
||||
|
||||
@ -44,6 +44,11 @@ done
|
||||
-f flutter_runner_tests-0.far \
|
||||
-t flutter_runner_tests
|
||||
|
||||
./fuchsia_ctl -d $device_name test \
|
||||
-f flutter_aot_runner-0.far \
|
||||
-f flutter_runner_scenic_tests-0.far \
|
||||
-t flutter_runner_scenic_tests
|
||||
|
||||
./fuchsia_ctl -d $device_name test \
|
||||
-f fml_tests-0.far \
|
||||
-t fml_tests
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
flutter_runner_tests-0.far
|
||||
flutter_runner_scenic_tests-0.far
|
||||
fml_tests-0.far
|
||||
flow_tests-0.far
|
||||
flow_tests-0.far
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user