Skip to content

Commit

Permalink
[fuchsia] Migrate flutter runner to use Present2 (flutter#14162)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
farchond committed Feb 5, 2020
1 parent c107969 commit 11b7704
Show file tree
Hide file tree
Showing 14 changed files with 541 additions and 73 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
}
Expand Down
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
112 changes: 112 additions & 0 deletions shell/platform/fuchsia/flutter/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
},
]
}
17 changes: 10 additions & 7 deletions shell/platform/fuchsia/flutter/compositor_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
124 changes: 88 additions & 36 deletions shell/platform/fuchsia/flutter/session_connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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;

// If Scenic alloted us 0 frames to begin with, we should fail here.
FML_CHECK(frames_in_flight_allowed_ > 0);

PresentSession();
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,
Expand All @@ -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_);
Expand All @@ -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.
Expand Down
Loading

0 comments on commit 11b7704

Please sign in to comment.