mirror of
https://github.com/encounter/engine.git
synced 2026-03-30 11:09:55 -07:00
e0a24d6369
Build rules still reference creating share snapshot data and instructions. This makes the engine to always pass them as empty to the dart vm. To be followed up with a change to alter the build rules to stop referencing the shared snapshots. This is not being used currently and the fact that the runner will be built outside of the flutter tree means that the apps will not have much to gain via shared snapshots. The rationale behind this change is to partially make migrating the runner out of topaz tree easier. Change-Id: Ibc4dd6a298d65082416af753522f5a17c88a750a
474 lines
17 KiB
C++
474 lines
17 KiB
C++
// 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 <lib/async/cpp/task.h>
|
|
#include <sstream>
|
|
|
|
#include "flutter/common/task_runners.h"
|
|
#include "flutter/fml/make_copyable.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 "platform_view.h"
|
|
#include "runtime/dart/utils/files.h"
|
|
#include "task_runner_adapter.h"
|
|
#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
|
|
#include "thread.h"
|
|
|
|
namespace flutter_runner {
|
|
|
|
static void UpdateNativeThreadLabelNames(const std::string& label,
|
|
const flutter::TaskRunners& runners) {
|
|
auto set_thread_name = [](fml::RefPtr<fml::TaskRunner> runner,
|
|
std::string prefix, std::string suffix) {
|
|
if (!runner) {
|
|
return;
|
|
}
|
|
fml::TaskRunner::RunNowOrPostTask(runner, [name = prefix + suffix]() {
|
|
zx::thread::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size());
|
|
});
|
|
};
|
|
set_thread_name(runners.GetPlatformTaskRunner(), label, ".platform");
|
|
set_thread_name(runners.GetUITaskRunner(), label, ".ui");
|
|
set_thread_name(runners.GetGPUTaskRunner(), label, ".gpu");
|
|
set_thread_name(runners.GetIOTaskRunner(), label, ".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,
|
|
fml::RefPtr<const flutter::DartSnapshot> isolate_snapshot,
|
|
fuchsia::ui::views::ViewToken view_token,
|
|
fuchsia::ui::views::ViewRefControl view_ref_control,
|
|
fuchsia::ui::views::ViewRef view_ref,
|
|
UniqueFDIONS fdio_ns,
|
|
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request)
|
|
: delegate_(delegate),
|
|
thread_label_(std::move(thread_label)),
|
|
settings_(std::move(settings)),
|
|
weak_factory_(this) {
|
|
if (zx::event::create(0, &vsync_event_) != ZX_OK) {
|
|
FML_DLOG(ERROR) << "Could not create the vsync event.";
|
|
return;
|
|
}
|
|
|
|
// Launch the threads that will be used to run the shell. These threads will
|
|
// be joined in the destructor.
|
|
for (auto& thread : threads_) {
|
|
thread.reset(new Thread());
|
|
}
|
|
|
|
// Set up the session connection.
|
|
auto scenic = svc->Connect<fuchsia::ui::scenic::Scenic>();
|
|
fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session;
|
|
fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> session_listener;
|
|
auto session_listener_request = session_listener.NewRequest();
|
|
scenic->CreateSession(session.NewRequest(), session_listener.Bind());
|
|
|
|
// Grab the parent environment services. The platform view may want to access
|
|
// some of these services.
|
|
fuchsia::sys::EnvironmentPtr environment;
|
|
svc->Connect(environment.NewRequest());
|
|
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider>
|
|
parent_environment_service_provider;
|
|
environment->GetServices(parent_environment_service_provider.NewRequest());
|
|
environment.Unbind();
|
|
|
|
// We need to manually schedule a frame when the session metrics change.
|
|
OnMetricsUpdate on_session_metrics_change_callback = std::bind(
|
|
&Engine::OnSessionMetricsDidChange, this, std::placeholders::_1);
|
|
|
|
OnSizeChangeHint on_session_size_change_hint_callback =
|
|
std::bind(&Engine::OnSessionSizeChangeHint, this, std::placeholders::_1,
|
|
std::placeholders::_2);
|
|
|
|
OnEnableWireframe on_enable_wireframe_callback = std::bind(
|
|
&Engine::OnDebugWireframeSettingsChanged, this, std::placeholders::_1);
|
|
|
|
// 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 =
|
|
[dispatcher = async_get_default_dispatcher(),
|
|
weak = weak_factory_.GetWeakPtr()]() {
|
|
async::PostTask(dispatcher, [weak]() {
|
|
if (weak) {
|
|
weak->Terminate();
|
|
}
|
|
});
|
|
};
|
|
|
|
// Setup the callback that will instantiate the platform view.
|
|
flutter::Shell::CreateCallback<flutter::PlatformView>
|
|
on_create_platform_view = fml::MakeCopyable(
|
|
[debug_label = thread_label_,
|
|
view_ref_control = std::move(view_ref_control),
|
|
view_ref = std::move(view_ref),
|
|
runner_services = std::move(runner_services),
|
|
parent_environment_service_provider =
|
|
std::move(parent_environment_service_provider),
|
|
session_listener_request = std::move(session_listener_request),
|
|
on_session_listener_error_callback =
|
|
std::move(on_session_listener_error_callback),
|
|
on_session_metrics_change_callback =
|
|
std::move(on_session_metrics_change_callback),
|
|
on_session_size_change_hint_callback =
|
|
std::move(on_session_size_change_hint_callback),
|
|
on_enable_wireframe_callback =
|
|
std::move(on_enable_wireframe_callback),
|
|
vsync_handle = vsync_event_.get()](flutter::Shell& shell) mutable {
|
|
return std::make_unique<flutter_runner::PlatformView>(
|
|
shell, // delegate
|
|
debug_label, // debug label
|
|
std::move(view_ref_control), // view control ref
|
|
std::move(view_ref), // view ref
|
|
shell.GetTaskRunners(), // task runners
|
|
std::move(runner_services),
|
|
std::move(parent_environment_service_provider), // services
|
|
std::move(session_listener_request), // session listener
|
|
std::move(on_session_listener_error_callback),
|
|
std::move(on_session_metrics_change_callback),
|
|
std::move(on_session_size_change_hint_callback),
|
|
std::move(on_enable_wireframe_callback),
|
|
vsync_handle // vsync handle
|
|
);
|
|
});
|
|
|
|
// Session can be terminated on the GPU 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 on_session_error_callback =
|
|
[dispatcher = async_get_default_dispatcher(),
|
|
weak = weak_factory_.GetWeakPtr()]() {
|
|
async::PostTask(dispatcher, [weak]() {
|
|
if (weak) {
|
|
weak->Terminate();
|
|
}
|
|
});
|
|
};
|
|
|
|
// Get the task runners from the managed threads. The current thread will be
|
|
// used as the "platform" thread.
|
|
const flutter::TaskRunners task_runners(
|
|
thread_label_, // Dart thread labels
|
|
CreateFMLTaskRunner(async_get_default_dispatcher()), // platform
|
|
CreateFMLTaskRunner(threads_[0]->dispatcher()), // gpu
|
|
CreateFMLTaskRunner(threads_[1]->dispatcher()), // ui
|
|
CreateFMLTaskRunner(threads_[2]->dispatcher()) // io
|
|
);
|
|
|
|
// Setup the callback that will instantiate the rasterizer.
|
|
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
|
|
fml::MakeCopyable([thread_label = thread_label_, //
|
|
view_token = std::move(view_token), //
|
|
session = std::move(session), //
|
|
on_session_error_callback, //
|
|
vsync_event = vsync_event_.get() //
|
|
](flutter::Shell& shell) mutable {
|
|
std::unique_ptr<flutter_runner::CompositorContext> compositor_context;
|
|
{
|
|
TRACE_DURATION("flutter", "CreateCompositorContext");
|
|
compositor_context =
|
|
std::make_unique<flutter_runner::CompositorContext>(
|
|
thread_label, // debug label
|
|
std::move(view_token), // scenic view we attach our tree to
|
|
std::move(session), // scenic session
|
|
on_session_error_callback, // session did encounter error
|
|
vsync_event // vsync event handle
|
|
);
|
|
}
|
|
|
|
return std::make_unique<flutter::Rasterizer>(
|
|
shell.GetTaskRunners(), // task runners
|
|
std::move(compositor_context) // compositor context
|
|
);
|
|
});
|
|
|
|
UpdateNativeThreadLabelNames(thread_label_, task_runners);
|
|
|
|
settings_.verbose_logging = true;
|
|
|
|
settings_.advisory_script_uri = thread_label_;
|
|
|
|
settings_.advisory_script_entrypoint = thread_label_;
|
|
|
|
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();
|
|
}
|
|
});
|
|
});
|
|
|
|
auto vm = flutter::DartVMRef::Create(settings_);
|
|
|
|
if (!isolate_snapshot) {
|
|
isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
|
|
}
|
|
|
|
{
|
|
TRACE_EVENT0("flutter", "CreateShell");
|
|
shell_ = flutter::Shell::Create(
|
|
task_runners, // host task runners
|
|
settings_, // shell launch settings
|
|
std::move(isolate_snapshot), // isolate snapshot
|
|
flutter::DartSnapshot::Empty(), // shared snapshot
|
|
on_create_platform_view, // platform view create callback
|
|
on_create_rasterizer, // rasterizer create callback
|
|
std::move(vm) // vm reference
|
|
);
|
|
}
|
|
|
|
if (!shell_) {
|
|
FML_LOG(ERROR) << "Could not launch the shell.";
|
|
return;
|
|
}
|
|
|
|
// Shell has been created. Before we run the engine, setup 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() //
|
|
);
|
|
}
|
|
|
|
// This platform does not get a separate surface platform view creation
|
|
// notification. Fire one eagerly.
|
|
shell_->GetPlatformView()->NotifyCreated();
|
|
|
|
// Launch the engine in the appropriate configuration.
|
|
auto run_configuration = flutter::RunConfiguration::InferFromSettings(
|
|
settings_, task_runners.GetIOTaskRunner());
|
|
|
|
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();
|
|
}
|
|
};
|
|
|
|
// Connect to the system font provider.
|
|
fuchsia::fonts::ProviderSyncPtr sync_font_provider;
|
|
svc->Connect(sync_font_provider.NewRequest());
|
|
|
|
shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
|
|
fml::MakeCopyable([engine = shell_->GetEngine(), //
|
|
run_configuration = std::move(run_configuration), //
|
|
sync_font_provider = std::move(sync_font_provider), //
|
|
on_run_failure //
|
|
]() mutable {
|
|
if (!engine) {
|
|
return;
|
|
}
|
|
|
|
// Set default font manager.
|
|
engine->GetFontCollection().GetFontCollection()->SetDefaultFontManager(
|
|
SkFontMgr_New_Fuchsia(std::move(sync_font_provider)));
|
|
|
|
if (engine->Run(std::move(run_configuration)) ==
|
|
flutter::Engine::RunStatus::Failure) {
|
|
on_run_failure();
|
|
}
|
|
}));
|
|
}
|
|
|
|
Engine::~Engine() {
|
|
shell_.reset();
|
|
for (const auto& thread : threads_) {
|
|
thread->Quit();
|
|
}
|
|
for (const auto& thread : threads_) {
|
|
thread->Join();
|
|
}
|
|
}
|
|
|
|
std::pair<bool, uint32_t> Engine::GetEngineReturnCode() const {
|
|
std::pair<bool, uint32_t> code(false, 0);
|
|
if (!shell_) {
|
|
return 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;
|
|
}
|
|
|
|
static void CreateCompilationTrace(Dart_Isolate isolate) {
|
|
Dart_EnterIsolate(isolate);
|
|
|
|
{
|
|
Dart_EnterScope();
|
|
uint8_t* trace = nullptr;
|
|
intptr_t trace_length = 0;
|
|
Dart_Handle result = Dart_SaveCompilationTrace(&trace, &trace_length);
|
|
tonic::LogIfError(result);
|
|
|
|
for (intptr_t start = 0; start < trace_length;) {
|
|
intptr_t end = start;
|
|
while ((end < trace_length) && trace[end] != '\n')
|
|
end++;
|
|
|
|
std::string line(reinterpret_cast<char*>(&trace[start]), end - start);
|
|
FML_LOG(INFO) << "compilation-trace: " << line;
|
|
|
|
start = end + 1;
|
|
}
|
|
|
|
Dart_ExitScope();
|
|
}
|
|
|
|
// Re-enter Dart scope to release the compilation trace's memory.
|
|
|
|
{
|
|
Dart_EnterScope();
|
|
uint8_t* feedback = nullptr;
|
|
intptr_t feedback_length = 0;
|
|
Dart_Handle result = Dart_SaveTypeFeedback(&feedback, &feedback_length);
|
|
tonic::LogIfError(result);
|
|
const std::string kTypeFeedbackFile = "/data/dart_type_feedback.bin";
|
|
if (dart_utils::WriteFile(kTypeFeedbackFile,
|
|
reinterpret_cast<const char*>(feedback),
|
|
feedback_length)) {
|
|
FML_LOG(INFO) << "Dart type feedback written to " << kTypeFeedbackFile;
|
|
} else {
|
|
FML_LOG(ERROR) << "Could not write Dart type feedback to "
|
|
<< kTypeFeedbackFile;
|
|
}
|
|
Dart_ExitScope();
|
|
}
|
|
|
|
Dart_ExitIsolate();
|
|
}
|
|
|
|
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.";
|
|
|
|
const intptr_t kCompilationTraceDelayInSeconds = 0;
|
|
if (kCompilationTraceDelayInSeconds != 0) {
|
|
Dart_Isolate isolate = Dart_CurrentIsolate();
|
|
FML_CHECK(isolate);
|
|
shell_->GetTaskRunners().GetUITaskRunner()->PostDelayedTask(
|
|
[engine = shell_->GetEngine(), isolate]() {
|
|
if (!engine) {
|
|
return;
|
|
}
|
|
CreateCompilationTrace(isolate);
|
|
},
|
|
fml::TimeDelta::FromSeconds(kCompilationTraceDelayInSeconds));
|
|
}
|
|
}
|
|
|
|
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::OnSessionMetricsDidChange(
|
|
const fuchsia::ui::gfx::Metrics& metrics) {
|
|
if (!shell_) {
|
|
return;
|
|
}
|
|
|
|
shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
|
|
[rasterizer = shell_->GetRasterizer(), metrics]() {
|
|
if (rasterizer) {
|
|
auto compositor_context =
|
|
reinterpret_cast<flutter_runner::CompositorContext*>(
|
|
rasterizer->compositor_context());
|
|
|
|
compositor_context->OnSessionMetricsDidChange(metrics);
|
|
}
|
|
});
|
|
}
|
|
|
|
void Engine::OnDebugWireframeSettingsChanged(bool enabled) {
|
|
if (!shell_) {
|
|
return;
|
|
}
|
|
|
|
shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
|
|
[rasterizer = shell_->GetRasterizer(), enabled]() {
|
|
if (rasterizer) {
|
|
auto compositor_context =
|
|
reinterpret_cast<flutter_runner::CompositorContext*>(
|
|
rasterizer->compositor_context());
|
|
|
|
compositor_context->OnWireframeEnabled(enabled);
|
|
}
|
|
});
|
|
}
|
|
|
|
void Engine::OnSessionSizeChangeHint(float width_change_factor,
|
|
float height_change_factor) {
|
|
if (!shell_) {
|
|
return;
|
|
}
|
|
|
|
shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
|
|
[rasterizer = shell_->GetRasterizer(), width_change_factor,
|
|
height_change_factor]() {
|
|
if (rasterizer) {
|
|
auto compositor_context = reinterpret_cast<CompositorContext*>(
|
|
rasterizer->compositor_context());
|
|
|
|
compositor_context->OnSessionSizeChangeHint(width_change_factor,
|
|
height_change_factor);
|
|
}
|
|
});
|
|
}
|
|
|
|
#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)
|
|
|
|
} // namespace flutter_runner
|