• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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