blob: 4444d809b38dd3ad516a222033d796330f54cdce [file] [log] [blame]
// Copyright 2013 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 "engine.h"
#include <fuchsia/accessibility/semantics/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <memory>
#include "flutter/common/graphics/persistent_cache.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/task_runner.h"
#include "flutter/runtime/dart_vm_lifecycle.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
#include "flutter/shell/common/serialization_callbacks.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkSerialProcs.h"
#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
#include "../runtime/dart/utils/files.h"
#include "../runtime/dart/utils/root_inspect_node.h"
#include "flatland_platform_view.h"
#include "focus_delegate.h"
#include "fuchsia_intl.h"
#include "gfx_platform_view.h"
#include "surface.h"
#include "vsync_waiter.h"
namespace flutter_runner {
namespace {
zx_koid_t GetKoid(const fuchsia::ui::views::ViewRef& view_ref) {
zx_handle_t handle = view_ref.reference.get();
zx_info_handle_basic_t info;
zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info,
sizeof(info), nullptr, nullptr);
return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
}
std::unique_ptr<flutter::PlatformMessage> MakeLocalizationPlatformMessage(
const fuchsia::intl::Profile& intl_profile) {
return std::make_unique<flutter::PlatformMessage>(
"flutter/localization", MakeLocalizationPlatformMessageData(intl_profile),
nullptr);
}
} // namespace
flutter::ThreadHost Engine::CreateThreadHost(const std::string& name_prefix) {
fml::Thread::SetCurrentThreadName(name_prefix + ".platform");
return flutter::ThreadHost(name_prefix, flutter::ThreadHost::Type::RASTER |
flutter::ThreadHost::Type::UI |
flutter::ThreadHost::Type::IO);
}
Engine::Engine(Delegate& delegate,
std::string thread_label,
std::shared_ptr<sys::ServiceDirectory> svc,
std::shared_ptr<sys::ServiceDirectory> runner_services,
flutter::Settings settings,
fuchsia::ui::views::ViewToken view_token,
scenic::ViewRefPair view_ref_pair,
UniqueFDIONS fdio_ns,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
FlutterRunnerProductConfiguration product_config)
: delegate_(delegate),
thread_label_(std::move(thread_label)),
thread_host_(CreateThreadHost(thread_label_)),
view_token_(std::move(view_token)),
memory_pressure_watcher_binding_(this),
latest_memory_pressure_level_(fuchsia::memorypressure::Level::NORMAL),
intercept_all_input_(product_config.get_intercept_all_input()),
weak_factory_(this) {
Initialize(/*=use_flatland*/ false, std::move(view_ref_pair), std::move(svc),
std::move(runner_services), std::move(settings),
std::move(fdio_ns), std::move(directory_request),
std::move(product_config));
}
Engine::Engine(Delegate& delegate,
std::string thread_label,
std::shared_ptr<sys::ServiceDirectory> svc,
std::shared_ptr<sys::ServiceDirectory> runner_services,
flutter::Settings settings,
fuchsia::ui::views::ViewCreationToken view_creation_token,
scenic::ViewRefPair view_ref_pair,
UniqueFDIONS fdio_ns,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
FlutterRunnerProductConfiguration product_config)
: delegate_(delegate),
thread_label_(std::move(thread_label)),
thread_host_(CreateThreadHost(thread_label_)),
view_creation_token_(std::move(view_creation_token)),
memory_pressure_watcher_binding_(this),
latest_memory_pressure_level_(fuchsia::memorypressure::Level::NORMAL),
intercept_all_input_(product_config.get_intercept_all_input()),
weak_factory_(this) {
Initialize(/*=use_flatland*/ true, std::move(view_ref_pair), std::move(svc),
std::move(runner_services), std::move(settings),
std::move(fdio_ns), std::move(directory_request),
std::move(product_config));
}
void Engine::Initialize(
bool use_flatland,
scenic::ViewRefPair view_ref_pair,
std::shared_ptr<sys::ServiceDirectory> svc,
std::shared_ptr<sys::ServiceDirectory> runner_services,
flutter::Settings settings,
UniqueFDIONS fdio_ns,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
FlutterRunnerProductConfiguration product_config) {
// Flatland uses |view_creation_token_| for linking. Gfx uses |view_token_|.
FML_CHECK((use_flatland && view_creation_token_.value.is_valid()) ||
(!use_flatland && view_token_.value.is_valid()));
// Get the task runners from the managed threads. The current thread will be
// used as the "platform" thread.
fml::RefPtr<fml::TaskRunner> platform_task_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
const flutter::TaskRunners task_runners(
thread_label_, // Dart thread labels
platform_task_runner, // platform
thread_host_.raster_thread->GetTaskRunner(), // raster
thread_host_.ui_thread->GetTaskRunner(), // ui
thread_host_.io_thread->GetTaskRunner() // io
);
// Connect to Scenic.
auto scenic = runner_services->Connect<fuchsia::ui::scenic::Scenic>();
fuchsia::ui::scenic::SessionEndpoints gfx_protocols;
fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session;
gfx_protocols.set_session(session.NewRequest());
fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> session_listener;
auto session_listener_request = session_listener.NewRequest();
gfx_protocols.set_session_listener(session_listener.Bind());
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser;
fidl::InterfaceHandle<fuchsia::ui::views::ViewRefFocused> view_ref_focused;
fidl::InterfaceHandle<fuchsia::ui::pointer::TouchSource> touch_source;
fidl::InterfaceHandle<fuchsia::ui::pointer::MouseSource> mouse_source;
fuchsia::ui::composition::ViewBoundProtocols flatland_view_protocols;
if (use_flatland) {
flatland_view_protocols.set_view_focuser(focuser.NewRequest());
flatland_view_protocols.set_view_ref_focused(view_ref_focused.NewRequest());
flatland_view_protocols.set_touch_source(touch_source.NewRequest());
flatland_view_protocols.set_mouse_source(mouse_source.NewRequest());
} else {
gfx_protocols.set_view_focuser(focuser.NewRequest());
gfx_protocols.set_view_ref_focused(view_ref_focused.NewRequest());
// TODO(fxbug.dev/85125): Enable TouchSource for GFX.
// gfx_protocols.set_touch_source(touch_source.NewRequest());
}
scenic->CreateSessionT(std::move(gfx_protocols), [] {});
// Connect to Flatland.
fidl::InterfaceHandle<fuchsia::ui::composition::Flatland> flatland;
zx_status_t flatland_status =
runner_services->Connect<fuchsia::ui::composition::Flatland>(
flatland.NewRequest());
if (flatland_status != ZX_OK && use_flatland) {
FML_LOG(WARNING) << "fuchsia::ui::composition::Flatland connection failed: "
<< zx_status_get_string(flatland_status);
}
// Connect to SemanticsManager service.
fidl::InterfaceHandle<fuchsia::accessibility::semantics::SemanticsManager>
semantics_manager;
zx_status_t semantics_status =
runner_services
->Connect<fuchsia::accessibility::semantics::SemanticsManager>(
semantics_manager.NewRequest());
if (semantics_status != ZX_OK) {
FML_LOG(WARNING)
<< "fuchsia::accessibility::semantics::SemanticsManager connection "
"failed: "
<< zx_status_get_string(semantics_status);
}
// Connect to ImeService service.
fidl::InterfaceHandle<fuchsia::ui::input::ImeService> ime_service;
zx_status_t ime_status =
runner_services->Connect<fuchsia::ui::input::ImeService>(
ime_service.NewRequest());
if (ime_status != ZX_OK) {
FML_LOG(WARNING) << "fuchsia::ui::input::ImeService connection failed: "
<< zx_status_get_string(ime_status);
}
// Connect to Keyboard service.
fidl::InterfaceHandle<fuchsia::ui::input3::Keyboard> keyboard;
zx_status_t keyboard_status =
runner_services->Connect<fuchsia::ui::input3::Keyboard>(
keyboard.NewRequest());
FML_DCHECK(keyboard_status == ZX_OK)
<< "fuchsia::ui::input3::Keyboard connection failed: "
<< zx_status_get_string(keyboard_status);
// Make clones of the `ViewRef` before sending it to various places.
fuchsia::ui::views::ViewRef platform_view_ref;
view_ref_pair.view_ref.Clone(&platform_view_ref);
fuchsia::ui::views::ViewRef accessibility_view_ref;
view_ref_pair.view_ref.Clone(&accessibility_view_ref);
fuchsia::ui::views::ViewRef isolate_view_ref;
view_ref_pair.view_ref.Clone(&isolate_view_ref);
// Session is terminated on the raster thread, but we must terminate ourselves
// on the platform thread.
//
// This handles the fidl error callback when the Session connection is
// broken. The SessionListener interface also has an OnError method, which is
// invoked on the platform thread (in PlatformView).
fml::closure session_error_callback = [task_runner = platform_task_runner,
weak = weak_factory_.GetWeakPtr()]() {
task_runner->PostTask([weak]() {
if (weak) {
weak->Terminate();
}
});
};
// Set up the session connection and other Scenic helpers on the raster
// thread. We also need to wait for the external view embedder to be set up
// before creating the shell.
fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
fml::AutoResetWaitableEvent view_embedder_latch;
auto session_inspect_node =
dart_utils::RootInspectNode::CreateRootChild("vsync_stats");
task_runners.GetRasterTaskRunner()->PostTask(fml::MakeCopyable(
[this, &view_embedder_latch,
session_inspect_node = std::move(session_inspect_node),
session = std::move(session), flatland = std::move(flatland),
session_error_callback = std::move(session_error_callback), use_flatland,
view_token = std::move(view_token_),
view_creation_token = std::move(view_creation_token_),
flatland_view_protocols = std::move(flatland_view_protocols),
request = parent_viewport_watcher.NewRequest(),
view_ref_pair = std::move(view_ref_pair),
max_frames_in_flight = product_config.get_max_frames_in_flight(),
vsync_offset = product_config.get_vsync_offset()]() mutable {
if (use_flatland) {
flatland_connection_ = std::make_shared<FlatlandConnection>(
thread_label_, std::move(flatland),
std::move(session_error_callback), [](auto) {},
max_frames_in_flight, vsync_offset);
surface_producer_.emplace(/*scenic_session=*/nullptr);
fuchsia::ui::views::ViewIdentityOnCreation view_identity = {
.view_ref = std::move(view_ref_pair.view_ref),
.view_ref_control = std::move(view_ref_pair.control_ref)};
flatland_view_embedder_ =
std::make_shared<FlatlandExternalViewEmbedder>(
thread_label_, std::move(view_creation_token),
std::move(view_identity), std::move(flatland_view_protocols),
std::move(request), *flatland_connection_.get(),
surface_producer_.value(), intercept_all_input_);
} else {
session_connection_ = std::make_shared<GfxSessionConnection>(
thread_label_, std::move(session_inspect_node),
std::move(session), std::move(session_error_callback),
[](auto) {}, max_frames_in_flight, vsync_offset);
surface_producer_.emplace(session_connection_->get());
external_view_embedder_ = std::make_shared<GfxExternalViewEmbedder>(
thread_label_, std::move(view_token), std::move(view_ref_pair),
*session_connection_.get(), surface_producer_.value(),
intercept_all_input_);
}
view_embedder_latch.Signal();
}));
view_embedder_latch.Wait();
AccessibilityBridge::SetSemanticsEnabledCallback
set_semantics_enabled_callback = [this](bool enabled) {
auto platform_view = shell_->GetPlatformView();
if (platform_view) {
platform_view->SetSemanticsEnabled(enabled);
}
};
AccessibilityBridge::DispatchSemanticsActionCallback
dispatch_semantics_action_callback =
[this](int32_t node_id, flutter::SemanticsAction action) {
auto platform_view = shell_->GetPlatformView();
if (platform_view) {
platform_view->DispatchSemanticsAction(node_id, action, {});
}
};
const std::string accessibility_inspect_name =
std::to_string(GetKoid(accessibility_view_ref));
accessibility_bridge_ = std::make_unique<AccessibilityBridge>(
std::move(set_semantics_enabled_callback),
std::move(dispatch_semantics_action_callback),
std::move(semantics_manager), std::move(accessibility_view_ref),
dart_utils::RootInspectNode::CreateRootChild(
std::move(accessibility_inspect_name)));
OnEnableWireframe on_enable_wireframe_callback = std::bind(
&Engine::DebugWireframeSettingsChanged, this, std::placeholders::_1);
OnCreateGfxView on_create_gfx_view_callback =
std::bind(&Engine::CreateGfxView, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
OnCreateFlatlandView on_create_flatland_view_callback =
std::bind(&Engine::CreateFlatlandView, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5);
OnUpdateView on_update_view_callback = std::bind(
&Engine::UpdateView, this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, use_flatland);
OnDestroyGfxView on_destroy_gfx_view_callback =
std::bind(&Engine::DestroyGfxView, this, std::placeholders::_1,
std::placeholders::_2);
OnDestroyFlatlandView on_destroy_flatland_view_callback =
std::bind(&Engine::DestroyFlatlandView, this, std::placeholders::_1,
std::placeholders::_2);
OnCreateSurface on_create_surface_callback =
std::bind(&Engine::CreateSurface, this);
// SessionListener has a OnScenicError method; invoke this callback on the
// platform thread when that happens. The Session itself should also be
// disconnected when this happens, and it will also attempt to terminate.
fit::closure on_session_listener_error_callback =
[task_runner = platform_task_runner,
weak = weak_factory_.GetWeakPtr()]() {
task_runner->PostTask([weak]() {
if (weak) {
weak->Terminate();
}
});
};
// Launch the engine in the appropriate configuration.
// Note: this initializes the Asset Manager on the global PersistantCache
// so it must be called before WarmupSkps() is called below.
auto run_configuration = flutter::RunConfiguration::InferFromSettings(
settings, task_runners.GetIOTaskRunner());
OnSemanticsNodeUpdate on_semantics_node_update_callback =
[this](flutter::SemanticsNodeUpdates updates, float pixel_ratio) {
accessibility_bridge_->AddSemanticsNodeUpdate(updates, pixel_ratio);
};
OnRequestAnnounce on_request_announce_callback =
[this](const std::string& message) {
accessibility_bridge_->RequestAnnounce(message);
};
// Setup the callback that will instantiate the platform view.
flutter::Shell::CreateCallback<flutter::PlatformView>
on_create_platform_view = fml::MakeCopyable(
[this, use_flatland, view_ref = std::move(platform_view_ref),
session_listener_request = std::move(session_listener_request),
parent_viewport_watcher = std::move(parent_viewport_watcher),
ime_service = std::move(ime_service), keyboard = std::move(keyboard),
focuser = std::move(focuser),
view_ref_focused = std::move(view_ref_focused),
touch_source = std::move(touch_source),
mouse_source = std::move(mouse_source),
on_session_listener_error_callback =
std::move(on_session_listener_error_callback),
on_enable_wireframe_callback =
std::move(on_enable_wireframe_callback),
on_create_gfx_view_callback = std::move(on_create_gfx_view_callback),
on_create_flatland_view_callback =
std::move(on_create_flatland_view_callback),
on_update_view_callback = std::move(on_update_view_callback),
on_destroy_gfx_view_callback =
std::move(on_destroy_gfx_view_callback),
on_destroy_flatland_view_callback =
std::move(on_destroy_flatland_view_callback),
on_create_surface_callback = std::move(on_create_surface_callback),
on_semantics_node_update_callback =
std::move(on_semantics_node_update_callback),
on_request_announce_callback =
std::move(on_request_announce_callback),
external_view_embedder = GetExternalViewEmbedder(),
await_vsync_callback =
[this, use_flatland](FireCallbackCallback cb) {
if (use_flatland) {
flatland_connection_->AwaitVsync(cb);
} else {
session_connection_->AwaitVsync(cb);
}
},
await_vsync_for_secondary_callback_callback =
[this, use_flatland](FireCallbackCallback cb) {
if (use_flatland) {
flatland_connection_->AwaitVsyncForSecondaryCallback(cb);
} else {
session_connection_->AwaitVsyncForSecondaryCallback(cb);
}
},
product_config](flutter::Shell& shell) mutable {
OnShaderWarmup on_shader_warmup = nullptr;
if (product_config.enable_shader_warmup()) {
FML_DCHECK(surface_producer_);
if (product_config.enable_shader_warmup_dart_hooks()) {
on_shader_warmup =
[this, &shell](
const std::vector<std::string>& skp_names,
std::function<void(uint32_t)> completion_callback,
uint64_t width, uint64_t height) {
WarmupSkps(
shell.GetDartVM()
->GetConcurrentMessageLoop()
->GetTaskRunner()
.get(),
shell.GetTaskRunners().GetRasterTaskRunner().get(),
surface_producer_.value(), width, height,
flutter::PersistentCache::GetCacheForProcess()
->asset_manager(),
skp_names, completion_callback);
};
} else {
WarmupSkps(shell.GetDartVM()
->GetConcurrentMessageLoop()
->GetTaskRunner()
.get(),
shell.GetTaskRunners().GetRasterTaskRunner().get(),
surface_producer_.value(), 1024, 600,
flutter::PersistentCache::GetCacheForProcess()
->asset_manager(),
std::nullopt, std::nullopt);
}
}
std::unique_ptr<flutter::PlatformView> platform_view;
if (use_flatland) {
platform_view =
std::make_unique<flutter_runner::FlatlandPlatformView>(
shell, shell.GetTaskRunners(), std::move(view_ref),
std::move(external_view_embedder), std::move(ime_service),
std::move(keyboard), std::move(touch_source),
std::move(mouse_source), std::move(focuser),
std::move(view_ref_focused),
std::move(parent_viewport_watcher),
std::move(on_enable_wireframe_callback),
std::move(on_create_flatland_view_callback),
std::move(on_update_view_callback),
std::move(on_destroy_flatland_view_callback),
std::move(on_create_surface_callback),
std::move(on_semantics_node_update_callback),
std::move(on_request_announce_callback),
std::move(on_shader_warmup),
std::move(await_vsync_callback),
std::move(await_vsync_for_secondary_callback_callback));
} else {
platform_view = std::make_unique<flutter_runner::GfxPlatformView>(
shell, shell.GetTaskRunners(), std::move(view_ref),
std::move(external_view_embedder), std::move(ime_service),
std::move(keyboard), std::move(touch_source),
std::move(mouse_source), std::move(focuser),
std::move(view_ref_focused),
std::move(session_listener_request),
std::move(on_session_listener_error_callback),
std::move(on_enable_wireframe_callback),
std::move(on_create_gfx_view_callback),
std::move(on_update_view_callback),
std::move(on_destroy_gfx_view_callback),
std::move(on_create_surface_callback),
std::move(on_semantics_node_update_callback),
std::move(on_request_announce_callback),
std::move(on_shader_warmup), std::move(await_vsync_callback),
std::move(await_vsync_for_secondary_callback_callback));
}
return platform_view;
});
// Setup the callback that will instantiate the rasterizer.
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
[](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell);
};
settings.root_isolate_create_callback =
std::bind(&Engine::OnMainIsolateStart, this);
settings.root_isolate_shutdown_callback =
std::bind([weak = weak_factory_.GetWeakPtr(),
runner = task_runners.GetPlatformTaskRunner()]() {
runner->PostTask([weak = std::move(weak)] {
if (weak) {
weak->OnMainIsolateShutdown();
}
});
});
// Connect and set up the system font provider.
fuchsia::fonts::ProviderSyncPtr sync_font_provider;
runner_services->Connect(sync_font_provider.NewRequest());
settings.font_initialization_data =
sync_font_provider.Unbind().TakeChannel().release();
{
TRACE_EVENT0("flutter", "CreateShell");
shell_ = flutter::Shell::Create(
flutter::PlatformData(), // default window data
std::move(task_runners), // host task runners
std::move(settings), // shell launch settings
std::move(on_create_platform_view), // platform view create callback
std::move(on_create_rasterizer) // rasterizer create callback
);
}
if (!shell_) {
FML_LOG(ERROR) << "Could not launch the shell.";
return;
}
// Shell has been created. Before we run the engine, set up the isolate
// configurator.
{
fuchsia::sys::EnvironmentPtr environment;
svc->Connect(environment.NewRequest());
isolate_configurator_ = std::make_unique<IsolateConfigurator>(
std::move(fdio_ns), //
std::move(environment), //
directory_request.TakeChannel(), //
std::move(isolate_view_ref.reference) //
);
}
// This platform does not get a separate surface platform view creation
// notification. Fire one eagerly.
shell_->GetPlatformView()->NotifyCreated();
// Connect to the memory pressure provider. If the connection fails, the
// initialization of the engine will simply proceed, printing a warning
// message. The engine will be fully functional, except that the Flutter
// shell will not be notified when memory is low.
{
memory_pressure_provider_.set_error_handler([](zx_status_t status) {
FML_LOG(WARNING)
<< "Failed to connect to " << fuchsia::memorypressure::Provider::Name_
<< ": " << zx_status_get_string(status)
<< " This is not a fatal error, but the heap will not be "
<< " compacted when memory is low.";
});
// Note that we're using the runner's services, not the component's.
// The Flutter Shell should be notified when memory is low regardless of
// whether the component has direct access to the
// fuchsia.memorypressure.Provider service.
ZX_ASSERT(runner_services->Connect(
memory_pressure_provider_.NewRequest()) == ZX_OK);
FML_VLOG(-1) << "Registering memorypressure watcher";
// Register for changes, which will make the request for the initial
// memory level.
memory_pressure_provider_->RegisterWatcher(
memory_pressure_watcher_binding_.NewBinding());
}
// Connect to the intl property provider. If the connection fails, the
// initialization of the engine will simply proceed, printing a warning
// message. The engine will be fully functional, except that the user's
// locale preferences would not be communicated to flutter engine.
{
intl_property_provider_.set_error_handler([](zx_status_t status) {
FML_LOG(WARNING) << "Failed to connect to "
<< fuchsia::intl::PropertyProvider::Name_ << ": "
<< zx_status_get_string(status)
<< " This is not a fatal error, but the user locale "
<< " preferences will not be forwarded to flutter apps";
});
// Note that we're using the runner's services, not the component's.
// Flutter locales should be updated regardless of whether the component has
// direct access to the fuchsia.intl.PropertyProvider service.
ZX_ASSERT(runner_services->Connect(intl_property_provider_.NewRequest()) ==
ZX_OK);
auto get_profile_callback = [weak = weak_factory_.GetWeakPtr()](
const fuchsia::intl::Profile& profile) {
if (!weak) {
return;
}
if (!profile.has_locales()) {
FML_LOG(WARNING) << "Got intl Profile without locales";
}
auto message = MakeLocalizationPlatformMessage(profile);
FML_VLOG(-1) << "Sending LocalizationPlatformMessage";
weak->shell_->GetPlatformView()->DispatchPlatformMessage(
std::move(message));
};
FML_VLOG(-1) << "Requesting intl Profile";
// Make the initial request
intl_property_provider_->GetProfile(get_profile_callback);
// And register for changes
intl_property_provider_.events().OnChange = [this, runner_services,
get_profile_callback]() {
FML_VLOG(-1) << fuchsia::intl::PropertyProvider::Name_ << ": OnChange";
runner_services->Connect(intl_property_provider_.NewRequest());
intl_property_provider_->GetProfile(get_profile_callback);
};
}
auto on_run_failure = [weak = weak_factory_.GetWeakPtr()]() {
// The engine could have been killed by the caller right after the
// constructor was called but before it could run on the UI thread.
if (weak) {
weak->Terminate();
}
};
shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
fml::MakeCopyable([engine = shell_->GetEngine(), //
run_configuration = std::move(run_configuration), //
on_run_failure //
]() mutable {
if (!engine) {
return;
}
if (engine->Run(std::move(run_configuration)) ==
flutter::Engine::RunStatus::Failure) {
on_run_failure();
}
}));
}
Engine::~Engine() {
shell_.reset();
// Destroy rendering objects on the raster thread.
fml::AutoResetWaitableEvent view_embedder_latch;
thread_host_.raster_thread->GetTaskRunner()->PostTask(
fml::MakeCopyable([this, &view_embedder_latch]() mutable {
flatland_view_embedder_.reset();
external_view_embedder_.reset();
surface_producer_.reset();
flatland_connection_.reset();
session_connection_.reset();
view_embedder_latch.Signal();
}));
view_embedder_latch.Wait();
}
std::optional<uint32_t> Engine::GetEngineReturnCode() const {
if (!shell_) {
return std::nullopt;
}
std::optional<uint32_t> code;
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(
shell_->GetTaskRunners().GetUITaskRunner(),
[&latch, &code, engine = shell_->GetEngine()]() {
if (engine) {
code = engine->GetUIIsolateReturnCode();
}
latch.Signal();
});
latch.Wait();
return code;
}
void Engine::OnMainIsolateStart() {
if (!isolate_configurator_ ||
!isolate_configurator_->ConfigureCurrentIsolate()) {
FML_LOG(ERROR) << "Could not configure some native embedder bindings for a "
"new root isolate.";
}
FML_DLOG(INFO) << "Main isolate for engine '" << thread_label_
<< "' was started.";
}
void Engine::OnMainIsolateShutdown() {
FML_DLOG(INFO) << "Main isolate for engine '" << thread_label_
<< "' shutting down.";
Terminate();
}
void Engine::Terminate() {
delegate_.OnEngineTerminate(this);
// Warning. Do not do anything after this point as the delegate may have
// collected this object.
}
void Engine::DebugWireframeSettingsChanged(bool enabled) {
FML_CHECK(shell_);
// TODO(fxbug.dev/64201): Investigate if we can add flatland wireframe code
// for debugging.
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, enabled]() {
if (external_view_embedder_) {
external_view_embedder_->EnableWireframe(enabled);
}
});
}
void Engine::CreateGfxView(int64_t view_id,
ViewCallback on_view_created,
GfxViewIdCallback on_view_bound,
bool hit_testable,
bool focusable) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, hit_testable, focusable,
on_view_created = std::move(on_view_created),
on_view_bound = std::move(on_view_bound)]() {
FML_CHECK(external_view_embedder_);
external_view_embedder_->CreateView(view_id, std::move(on_view_created),
std::move(on_view_bound));
external_view_embedder_->SetViewProperties(view_id, SkRect::MakeEmpty(),
hit_testable, focusable);
});
}
void Engine::CreateFlatlandView(int64_t view_id,
ViewCallback on_view_created,
FlatlandViewCreatedCallback on_view_bound,
bool hit_testable,
bool focusable) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, hit_testable, focusable,
on_view_created = std::move(on_view_created),
on_view_bound = std::move(on_view_bound)]() {
FML_CHECK(flatland_view_embedder_);
flatland_view_embedder_->CreateView(view_id, std::move(on_view_created),
std::move(on_view_bound));
flatland_view_embedder_->SetViewProperties(view_id, SkRect::MakeEmpty(),
hit_testable, focusable);
});
}
void Engine::UpdateView(int64_t view_id,
SkRect occlusion_hint,
bool hit_testable,
bool focusable,
bool use_flatland) {
FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, use_flatland, view_id, occlusion_hint, hit_testable, focusable]() {
if (use_flatland) {
FML_CHECK(flatland_view_embedder_);
flatland_view_embedder_->SetViewProperties(view_id, occlusion_hint,
hit_testable, focusable);
} else {
FML_CHECK(external_view_embedder_);
external_view_embedder_->SetViewProperties(view_id, occlusion_hint,
hit_testable, focusable);
}
});
}
void Engine::DestroyGfxView(int64_t view_id,
GfxViewIdCallback on_view_unbound) {
FML_CHECK(shell_);
// TODO(fxbug.dev/64201): Add flatland hookup. |view_id| may be interpreted
// based on use_flatland from the initializer.
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, on_view_unbound = std::move(on_view_unbound)]() {
FML_CHECK(external_view_embedder_);
external_view_embedder_->DestroyView(view_id,
std::move(on_view_unbound));
});
}
void Engine::DestroyFlatlandView(int64_t view_id,
FlatlandViewIdCallback on_view_unbound) {
FML_CHECK(shell_);
// TODO(fxbug.dev/64201): Add flatland hookup. |view_id| may be interpreted
// based on use_flatland from the initializer.
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, on_view_unbound = std::move(on_view_unbound)]() {
FML_CHECK(flatland_view_embedder_);
flatland_view_embedder_->DestroyView(view_id,
std::move(on_view_unbound));
});
}
std::unique_ptr<flutter::Surface> Engine::CreateSurface() {
return std::make_unique<Surface>(thread_label_, GetExternalViewEmbedder(),
surface_producer_->gr_context());
}
std::shared_ptr<flutter::ExternalViewEmbedder>
Engine::GetExternalViewEmbedder() {
FML_CHECK(external_view_embedder_ || flatland_view_embedder_);
if (external_view_embedder_) {
return external_view_embedder_;
}
return flatland_view_embedder_;
}
#if !defined(DART_PRODUCT)
void Engine::WriteProfileToTrace() const {
Dart_Port main_port = shell_->GetEngine()->GetUIIsolateMainPort();
char* error = NULL;
bool success = Dart_WriteProfileToTimeline(main_port, &error);
if (!success) {
FML_LOG(ERROR) << "Failed to write Dart profile to trace: " << error;
free(error);
}
}
#endif // !defined(DART_PRODUCT)
void Engine::WarmupSkps(
fml::BasicTaskRunner* concurrent_task_runner,
fml::BasicTaskRunner* raster_task_runner,
VulkanSurfaceProducer& surface_producer,
uint64_t width,
uint64_t height,
std::shared_ptr<flutter::AssetManager> asset_manager,
std::optional<const std::vector<std::string>> skp_names,
std::optional<std::function<void(uint32_t)>> completion_callback) {
SkISize size = SkISize::Make(width, height);
// We use a raw pointer here because we want to keep this alive until all gpu
// work is done and the callbacks skia takes for this are function pointers
// so we are unable to use a lambda that captures the smart pointer.
SurfaceProducerSurface* skp_warmup_surface =
surface_producer.ProduceOffscreenSurface(size).release();
if (!skp_warmup_surface) {
FML_LOG(ERROR) << "Failed to create offscreen warmup surface";
// Tell client that zero shaders were warmed up because warmup failed.
if (completion_callback.has_value() && completion_callback.value()) {
completion_callback.value()(0);
}
return;
}
// tell concurrent task runner to deserialize all skps available from
// the asset manager
concurrent_task_runner->PostTask([raster_task_runner, skp_warmup_surface,
&surface_producer, asset_manager, skp_names,
completion_callback]() {
TRACE_DURATION("flutter", "DeserializeSkps");
std::vector<std::unique_ptr<fml::Mapping>> skp_mappings;
if (skp_names) {
for (auto& skp_name : skp_names.value()) {
auto skp_mapping = asset_manager->GetAsMapping(skp_name);
if (skp_mapping) {
skp_mappings.push_back(std::move(skp_mapping));
} else {
FML_LOG(ERROR) << "Failed to get mapping for " << skp_name;
}
}
} else {
skp_mappings = asset_manager->GetAsMappings(".*\\.skp$", "shaders");
}
size_t total_size = 0;
for (auto& mapping : skp_mappings) {
total_size += mapping->GetSize();
}
FML_LOG(INFO) << "Shader warmup got " << skp_mappings.size()
<< " skp's with a total size of " << total_size << " bytes";
std::vector<sk_sp<SkPicture>> pictures;
unsigned int i = 0;
for (auto& mapping : skp_mappings) {
std::unique_ptr<SkMemoryStream> stream =
SkMemoryStream::MakeDirect(mapping->GetMapping(), mapping->GetSize());
SkDeserialProcs procs = {0};
procs.fImageProc = flutter::DeserializeImageWithoutData;
procs.fTypefaceProc = flutter::DeserializeTypefaceWithoutData;
sk_sp<SkPicture> picture =
SkPicture::MakeFromStream(stream.get(), &procs);
if (!picture) {
FML_LOG(ERROR) << "Failed to deserialize picture " << i;
continue;
}
// Tell raster task runner to warmup have the compositor
// context warm up the newly deserialized picture
raster_task_runner->PostTask([skp_warmup_surface, picture,
&surface_producer, completion_callback, i,
count = skp_mappings.size()] {
TRACE_DURATION("flutter", "WarmupSkp");
skp_warmup_surface->GetSkiaSurface()->getCanvas()->drawPicture(picture);
if (i < count - 1) {
// For all but the last skp we fire and forget
surface_producer.gr_context()->flushAndSubmit();
} else {
// For the last skp we provide a callback that frees the warmup
// surface and calls the completion callback
struct GrFlushInfo flush_info;
flush_info.fFinishedContext = skp_warmup_surface;
flush_info.fFinishedProc = [](void* skp_warmup_surface) {
delete static_cast<SurfaceProducerSurface*>(skp_warmup_surface);
};
surface_producer.gr_context()->flush(flush_info);
surface_producer.gr_context()->submit();
// We call this here instead of inside fFinishedProc above because
// we want to unblock the dart animation code as soon as the raster
// thread is free to enque work, rather than waiting for the GPU work
// itself to finish.
if (completion_callback.has_value() && completion_callback.value()) {
completion_callback.value()(count);
}
}
});
i++;
}
});
}
void Engine::OnLevelChanged(
fuchsia::memorypressure::Level level,
fuchsia::memorypressure::Watcher::OnLevelChangedCallback callback) {
// The callback must be invoked immediately to acknowledge the message.
// This is the "Throttle push using acknowledgements" pattern:
// https://fuchsia.dev/fuchsia-src/concepts/api/fidl#throttle_push_using_acknowledgements
callback();
FML_LOG(WARNING) << "memorypressure watcher: OnLevelChanged from "
<< static_cast<int>(latest_memory_pressure_level_) << " to "
<< static_cast<int>(level);
if (latest_memory_pressure_level_ == fuchsia::memorypressure::Level::NORMAL &&
(level == fuchsia::memorypressure::Level::WARNING ||
level == fuchsia::memorypressure::Level::CRITICAL)) {
FML_LOG(WARNING)
<< "memorypressure watcher: notifying Flutter that memory is low";
shell_->NotifyLowMemoryWarning();
}
latest_memory_pressure_level_ = level;
}
} // namespace flutter_runner