1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "engine.h"
6
7 #include <lib/async/cpp/task.h>
8 #include <sstream>
9
10 #include "flutter/common/task_runners.h"
11 #include "flutter/fml/make_copyable.h"
12 #include "flutter/fml/synchronization/waitable_event.h"
13 #include "flutter/fml/task_runner.h"
14 #include "flutter/runtime/dart_vm_lifecycle.h"
15 #include "flutter/shell/common/rasterizer.h"
16 #include "flutter/shell/common/run_configuration.h"
17 #include "platform_view.h"
18 #include "runtime/dart/utils/files.h"
19 #include "task_runner_adapter.h"
20 #include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
21 #include "thread.h"
22
23 namespace flutter_runner {
24
UpdateNativeThreadLabelNames(const std::string & label,const flutter::TaskRunners & runners)25 static void UpdateNativeThreadLabelNames(const std::string& label,
26 const flutter::TaskRunners& runners) {
27 auto set_thread_name = [](fml::RefPtr<fml::TaskRunner> runner,
28 std::string prefix, std::string suffix) {
29 if (!runner) {
30 return;
31 }
32 fml::TaskRunner::RunNowOrPostTask(runner, [name = prefix + suffix]() {
33 zx::thread::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size());
34 });
35 };
36 set_thread_name(runners.GetPlatformTaskRunner(), label, ".platform");
37 set_thread_name(runners.GetUITaskRunner(), label, ".ui");
38 set_thread_name(runners.GetGPUTaskRunner(), label, ".gpu");
39 set_thread_name(runners.GetIOTaskRunner(), label, ".io");
40 }
41
Engine(Delegate & delegate,std::string thread_label,std::shared_ptr<sys::ServiceDirectory> svc,flutter::Settings settings,fml::RefPtr<const flutter::DartSnapshot> isolate_snapshot,fml::RefPtr<const flutter::DartSnapshot> shared_snapshot,fuchsia::ui::views::ViewToken view_token,UniqueFDIONS fdio_ns,fidl::InterfaceRequest<fuchsia::io::Directory> directory_request)42 Engine::Engine(Delegate& delegate,
43 std::string thread_label,
44 std::shared_ptr<sys::ServiceDirectory> svc,
45 flutter::Settings settings,
46 fml::RefPtr<const flutter::DartSnapshot> isolate_snapshot,
47 fml::RefPtr<const flutter::DartSnapshot> shared_snapshot,
48 fuchsia::ui::views::ViewToken view_token,
49 UniqueFDIONS fdio_ns,
50 fidl::InterfaceRequest<fuchsia::io::Directory> directory_request)
51 : delegate_(delegate),
52 thread_label_(std::move(thread_label)),
53 settings_(std::move(settings)),
54 weak_factory_(this) {
55 if (zx::event::create(0, &vsync_event_) != ZX_OK) {
56 FML_DLOG(ERROR) << "Could not create the vsync event.";
57 return;
58 }
59
60 // Launch the threads that will be used to run the shell. These threads will
61 // be joined in the destructor.
62 for (auto& thread : threads_) {
63 thread.reset(new Thread());
64 }
65
66 // Set up the session connection.
67 auto scenic = svc->Connect<fuchsia::ui::scenic::Scenic>();
68 fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session;
69 fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> session_listener;
70 auto session_listener_request = session_listener.NewRequest();
71 scenic->CreateSession(session.NewRequest(), session_listener.Bind());
72
73 // Grab the parent environment services. The platform view may want to access
74 // some of these services.
75 fuchsia::sys::EnvironmentPtr environment;
76 svc->Connect(environment.NewRequest());
77 fidl::InterfaceHandle<fuchsia::sys::ServiceProvider>
78 parent_environment_service_provider;
79 environment->GetServices(parent_environment_service_provider.NewRequest());
80 environment.Unbind();
81
82 // We need to manually schedule a frame when the session metrics change.
83 OnMetricsUpdate on_session_metrics_change_callback = std::bind(
84 &Engine::OnSessionMetricsDidChange, this, std::placeholders::_1);
85
86 OnSizeChangeHint on_session_size_change_hint_callback =
87 std::bind(&Engine::OnSessionSizeChangeHint, this, std::placeholders::_1,
88 std::placeholders::_2);
89
90 // SessionListener has a OnScenicError method; invoke this callback on the
91 // platform thread when that happens. The Session itself should also be
92 // disconnected when this happens, and it will also attempt to terminate.
93 fit::closure on_session_listener_error_callback =
94 [dispatcher = async_get_default_dispatcher(),
95 weak = weak_factory_.GetWeakPtr()]() {
96 async::PostTask(dispatcher, [weak]() {
97 if (weak) {
98 weak->Terminate();
99 }
100 });
101 };
102
103 // Setup the callback that will instantiate the platform view.
104 flutter::Shell::CreateCallback<flutter::PlatformView>
105 on_create_platform_view = fml::MakeCopyable(
106 [debug_label = thread_label_,
107 parent_environment_service_provider =
108 std::move(parent_environment_service_provider),
109 session_listener_request = std::move(session_listener_request),
110 on_session_listener_error_callback =
111 std::move(on_session_listener_error_callback),
112 on_session_metrics_change_callback =
113 std::move(on_session_metrics_change_callback),
114 on_session_size_change_hint_callback =
115 std::move(on_session_size_change_hint_callback),
116 vsync_handle = vsync_event_.get()](flutter::Shell& shell) mutable {
117 return std::make_unique<flutter_runner::PlatformView>(
118 shell, // delegate
119 debug_label, // debug label
120 shell.GetTaskRunners(), // task runners
121 std::move(parent_environment_service_provider), // services
122 std::move(session_listener_request), // session listener
123 std::move(on_session_listener_error_callback),
124 std::move(on_session_metrics_change_callback),
125 std::move(on_session_size_change_hint_callback),
126 vsync_handle // vsync handle
127 );
128 });
129
130 // Session can be terminated on the GPU thread, but we must terminate
131 // ourselves on the platform thread.
132 //
133 // This handles the fidl error callback when the Session connection is
134 // broken. The SessionListener interface also has an OnError method, which is
135 // invoked on the platform thread (in PlatformView).
136 fml::closure on_session_error_callback =
137 [dispatcher = async_get_default_dispatcher(),
138 weak = weak_factory_.GetWeakPtr()]() {
139 async::PostTask(dispatcher, [weak]() {
140 if (weak) {
141 weak->Terminate();
142 }
143 });
144 };
145
146 // Get the task runners from the managed threads. The current thread will be
147 // used as the "platform" thread.
148 const flutter::TaskRunners task_runners(
149 thread_label_, // Dart thread labels
150 CreateFMLTaskRunner(async_get_default_dispatcher()), // platform
151 CreateFMLTaskRunner(threads_[0]->dispatcher()), // gpu
152 CreateFMLTaskRunner(threads_[1]->dispatcher()), // ui
153 CreateFMLTaskRunner(threads_[2]->dispatcher()) // io
154 );
155
156 // Setup the callback that will instantiate the rasterizer.
157 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
158 fml::MakeCopyable([thread_label = thread_label_, //
159 view_token = std::move(view_token), //
160 session = std::move(session), //
161 on_session_error_callback, //
162 vsync_event = vsync_event_.get() //
163 ](flutter::Shell& shell) mutable {
164 std::unique_ptr<flutter_runner::CompositorContext> compositor_context;
165 {
166 TRACE_DURATION("flutter", "CreateCompositorContext");
167 compositor_context =
168 std::make_unique<flutter_runner::CompositorContext>(
169 thread_label, // debug label
170 std::move(view_token), // scenic view we attach our tree to
171 std::move(session), // scenic session
172 on_session_error_callback, // session did encounter error
173 vsync_event // vsync event handle
174 );
175 }
176
177 return std::make_unique<flutter::Rasterizer>(
178 shell.GetTaskRunners(), // task runners
179 std::move(compositor_context) // compositor context
180 );
181 });
182
183 UpdateNativeThreadLabelNames(thread_label_, task_runners);
184
185 settings_.verbose_logging = true;
186
187 settings_.advisory_script_uri = thread_label_;
188
189 settings_.advisory_script_entrypoint = thread_label_;
190
191 settings_.root_isolate_create_callback =
192 std::bind(&Engine::OnMainIsolateStart, this);
193
194 settings_.root_isolate_shutdown_callback =
195 std::bind([weak = weak_factory_.GetWeakPtr(),
196 runner = task_runners.GetPlatformTaskRunner()]() {
197 runner->PostTask([weak = std::move(weak)] {
198 if (weak) {
199 weak->OnMainIsolateShutdown();
200 }
201 });
202 });
203
204 auto vm = flutter::DartVMRef::Create(settings_);
205
206 if (!isolate_snapshot) {
207 isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
208 }
209
210 if (!shared_snapshot) {
211 shared_snapshot = flutter::DartSnapshot::Empty();
212 }
213
214 {
215 TRACE_EVENT0("flutter", "CreateShell");
216 shell_ = flutter::Shell::Create(
217 task_runners, // host task runners
218 settings_, // shell launch settings
219 std::move(isolate_snapshot), // isolate snapshot
220 std::move(shared_snapshot), // shared snapshot
221 on_create_platform_view, // platform view create callback
222 on_create_rasterizer, // rasterizer create callback
223 std::move(vm) // vm reference
224 );
225 }
226
227 if (!shell_) {
228 FML_LOG(ERROR) << "Could not launch the shell.";
229 return;
230 }
231
232 // Shell has been created. Before we run the engine, setup the isolate
233 // configurator.
234 {
235 fuchsia::sys::EnvironmentPtr environment;
236 svc->Connect(environment.NewRequest());
237
238 isolate_configurator_ = std::make_unique<IsolateConfigurator>(
239 std::move(fdio_ns), //
240 std::move(environment), //
241 directory_request.TakeChannel() //
242 );
243 }
244
245 // This platform does not get a separate surface platform view creation
246 // notification. Fire one eagerly.
247 shell_->GetPlatformView()->NotifyCreated();
248
249 // Launch the engine in the appropriate configuration.
250 auto run_configuration = flutter::RunConfiguration::InferFromSettings(
251 settings_, task_runners.GetIOTaskRunner());
252
253 auto on_run_failure = [weak = weak_factory_.GetWeakPtr()]() {
254 // The engine could have been killed by the caller right after the
255 // constructor was called but before it could run on the UI thread.
256 if (weak) {
257 weak->Terminate();
258 }
259 };
260
261 // Connect to the system font provider.
262 fuchsia::fonts::ProviderSyncPtr sync_font_provider;
263 svc->Connect(sync_font_provider.NewRequest());
264
265 shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
266 fml::MakeCopyable([engine = shell_->GetEngine(), //
267 run_configuration = std::move(run_configuration), //
268 sync_font_provider = std::move(sync_font_provider), //
269 on_run_failure //
270 ]() mutable {
271 if (!engine) {
272 return;
273 }
274
275 // Set default font manager.
276 engine->GetFontCollection().GetFontCollection()->SetDefaultFontManager(
277 SkFontMgr_New_Fuchsia(std::move(sync_font_provider)));
278
279 if (engine->Run(std::move(run_configuration)) ==
280 flutter::Engine::RunStatus::Failure) {
281 on_run_failure();
282 }
283 }));
284 }
285
~Engine()286 Engine::~Engine() {
287 shell_.reset();
288 for (const auto& thread : threads_) {
289 thread->Quit();
290 }
291 for (const auto& thread : threads_) {
292 thread->Join();
293 }
294 }
295
GetEngineReturnCode() const296 std::pair<bool, uint32_t> Engine::GetEngineReturnCode() const {
297 std::pair<bool, uint32_t> code(false, 0);
298 if (!shell_) {
299 return code;
300 }
301 fml::AutoResetWaitableEvent latch;
302 fml::TaskRunner::RunNowOrPostTask(
303 shell_->GetTaskRunners().GetUITaskRunner(),
304 [&latch, &code, engine = shell_->GetEngine()]() {
305 if (engine) {
306 code = engine->GetUIIsolateReturnCode();
307 }
308 latch.Signal();
309 });
310 latch.Wait();
311 return code;
312 }
313
CreateCompilationTrace(Dart_Isolate isolate)314 static void CreateCompilationTrace(Dart_Isolate isolate) {
315 Dart_EnterIsolate(isolate);
316
317 {
318 Dart_EnterScope();
319 uint8_t* trace = nullptr;
320 intptr_t trace_length = 0;
321 Dart_Handle result = Dart_SaveCompilationTrace(&trace, &trace_length);
322 tonic::LogIfError(result);
323
324 for (intptr_t start = 0; start < trace_length;) {
325 intptr_t end = start;
326 while ((end < trace_length) && trace[end] != '\n')
327 end++;
328
329 std::string line(reinterpret_cast<char*>(&trace[start]), end - start);
330 FML_LOG(INFO) << "compilation-trace: " << line;
331
332 start = end + 1;
333 }
334
335 Dart_ExitScope();
336 }
337
338 // Re-enter Dart scope to release the compilation trace's memory.
339
340 {
341 Dart_EnterScope();
342 uint8_t* feedback = nullptr;
343 intptr_t feedback_length = 0;
344 Dart_Handle result = Dart_SaveTypeFeedback(&feedback, &feedback_length);
345 tonic::LogIfError(result);
346 const std::string kTypeFeedbackFile = "/data/dart_type_feedback.bin";
347 if (dart_utils::WriteFile(kTypeFeedbackFile,
348 reinterpret_cast<const char*>(feedback),
349 feedback_length)) {
350 FML_LOG(INFO) << "Dart type feedback written to " << kTypeFeedbackFile;
351 } else {
352 FML_LOG(ERROR) << "Could not write Dart type feedback to "
353 << kTypeFeedbackFile;
354 }
355 Dart_ExitScope();
356 }
357
358 Dart_ExitIsolate();
359 }
360
OnMainIsolateStart()361 void Engine::OnMainIsolateStart() {
362 if (!isolate_configurator_ ||
363 !isolate_configurator_->ConfigureCurrentIsolate()) {
364 FML_LOG(ERROR) << "Could not configure some native embedder bindings for a "
365 "new root isolate.";
366 }
367 FML_DLOG(INFO) << "Main isolate for engine '" << thread_label_
368 << "' was started.";
369
370 const intptr_t kCompilationTraceDelayInSeconds = 0;
371 if (kCompilationTraceDelayInSeconds != 0) {
372 Dart_Isolate isolate = Dart_CurrentIsolate();
373 FML_CHECK(isolate);
374 shell_->GetTaskRunners().GetUITaskRunner()->PostDelayedTask(
375 [engine = shell_->GetEngine(), isolate]() {
376 if (!engine) {
377 return;
378 }
379 CreateCompilationTrace(isolate);
380 },
381 fml::TimeDelta::FromSeconds(kCompilationTraceDelayInSeconds));
382 }
383 }
384
OnMainIsolateShutdown()385 void Engine::OnMainIsolateShutdown() {
386 FML_DLOG(INFO) << "Main isolate for engine '" << thread_label_
387 << "' shutting down.";
388 Terminate();
389 }
390
Terminate()391 void Engine::Terminate() {
392 delegate_.OnEngineTerminate(this);
393 // Warning. Do not do anything after this point as the delegate may have
394 // collected this object.
395 }
396
OnSessionMetricsDidChange(const fuchsia::ui::gfx::Metrics & metrics)397 void Engine::OnSessionMetricsDidChange(
398 const fuchsia::ui::gfx::Metrics& metrics) {
399 if (!shell_) {
400 return;
401 }
402
403 shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
404 [rasterizer = shell_->GetRasterizer(), metrics]() {
405 if (rasterizer) {
406 auto compositor_context =
407 reinterpret_cast<flutter_runner::CompositorContext*>(
408 rasterizer->compositor_context());
409
410 compositor_context->OnSessionMetricsDidChange(metrics);
411 }
412 });
413 }
414
OnSessionSizeChangeHint(float width_change_factor,float height_change_factor)415 void Engine::OnSessionSizeChangeHint(float width_change_factor,
416 float height_change_factor) {
417 if (!shell_) {
418 return;
419 }
420
421 shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(
422 [rasterizer = shell_->GetRasterizer(), width_change_factor,
423 height_change_factor]() {
424 if (rasterizer) {
425 auto compositor_context = reinterpret_cast<CompositorContext*>(
426 rasterizer->compositor_context());
427
428 compositor_context->OnSessionSizeChangeHint(width_change_factor,
429 height_change_factor);
430 }
431 });
432 }
433
434 #if !defined(DART_PRODUCT)
WriteProfileToTrace() const435 void Engine::WriteProfileToTrace() const {
436 Dart_Port main_port = shell_->GetEngine()->GetUIIsolateMainPort();
437 char* error = NULL;
438 bool success = Dart_WriteProfileToTimeline(main_port, &error);
439 if (!success) {
440 FML_LOG(ERROR) << "Failed to write Dart profile to trace: " << error;
441 free(error);
442 }
443 }
444 #endif // !defined(DART_PRODUCT)
445
446 } // namespace flutter_runner
447