Flutter Engine
 
Loading...
Searching...
No Matches
engine.cc
Go to the documentation of this file.
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 <fuchsia/accessibility/semantics/cpp/fidl.h>
8#include <fuchsia/media/cpp/fidl.h>
9#include <lib/async/cpp/task.h>
10#include <lib/zx/eventpair.h>
11#include <lib/zx/thread.h>
12#include <zircon/rights.h>
13#include <zircon/status.h>
14#include <zircon/types.h>
15#include <memory>
16
28#include "third_party/skia/include/core/SkPicture.h"
29#include "third_party/skia/include/core/SkSerialProcs.h"
30#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
31#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
32
33#include "../runtime/dart/utils/files.h"
34#include "../runtime/dart/utils/root_inspect_node.h"
35#include "focus_delegate.h"
36#include "fuchsia_intl.h"
37#include "platform_view.h"
39#include "surface.h"
40#include "vsync_waiter.h"
42
43namespace flutter_runner {
44namespace {
45constexpr static int64_t kImplicitViewId = 0;
46
47zx_koid_t GetKoid(const fuchsia::ui::views::ViewRef& view_ref) {
48 zx_handle_t handle = view_ref.reference.get();
49 zx_info_handle_basic_t info;
50 zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info,
51 sizeof(info), nullptr, nullptr);
52 return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
53}
54
55std::unique_ptr<flutter::PlatformMessage> MakeLocalizationPlatformMessage(
56 const fuchsia::intl::Profile& intl_profile) {
57 return std::make_unique<flutter::PlatformMessage>(
58 "flutter/localization", MakeLocalizationPlatformMessageData(intl_profile),
59 nullptr);
60}
61
62//
63// Fuchsia scheduler role naming scheme employed here:
64//
65// Roles based on thread function:
66// <prefix>.type.{platform,ui,raster,io,profiler}
67//
68// Roles based on fml::Thread::ThreadPriority:
69// <prefix>.thread.{background,display,raster,normal}
70//
71
72void SetThreadRole(
73 const fuchsia::media::ProfileProviderSyncPtr& profile_provider,
74 const std::string& role) {
75 ZX_ASSERT(profile_provider);
76
77 zx::thread dup;
78 const zx_status_t dup_status =
79 zx::thread::self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
80 if (dup_status != ZX_OK) {
81 FML_LOG(WARNING)
82 << "Failed to duplicate thread handle when setting thread config: "
83 << zx_status_get_string(dup_status)
84 << ". Thread will run at default priority.";
85 return;
86 }
87
88 int64_t unused_period;
89 int64_t unused_capacity;
90 const zx_status_t status = profile_provider->RegisterHandlerWithCapacity(
91 std::move(dup), role, 0, 0.f, &unused_period, &unused_capacity);
92 if (status != ZX_OK) {
93 FML_LOG(WARNING) << "Failed to set thread role to \"" << role
94 << "\": " << zx_status_get_string(status)
95 << ". Thread will run at default priority.";
96 return;
97 }
98}
99
100void SetThreadConfig(
101 const std::string& name_prefix,
102 const fuchsia::media::ProfileProviderSyncPtr& profile_provider,
103 const fml::Thread::ThreadConfig& config) {
104 ZX_ASSERT(profile_provider);
105
107
108 // Derive the role name from the prefix and priority. See comment above about
109 // the role naming scheme.
110 std::string role;
111 switch (config.priority) {
113 role = name_prefix + ".thread.background";
114 break;
116 role = name_prefix + ".thread.display";
117 break;
119 role = name_prefix + ".thread.raster";
120 break;
122 role = name_prefix + ".thread.normal";
123 break;
124 default:
125 FML_LOG(WARNING) << "Unknown thread priority "
126 << static_cast<int>(config.priority)
127 << ". Thread will run at default priority.";
128 return;
129 }
130 ZX_ASSERT(!role.empty());
131
132 SetThreadRole(profile_provider, role);
133}
134
135} // namespace
136
138 const std::string& name_prefix,
139 const std::shared_ptr<sys::ServiceDirectory>& services) {
141 fml::Thread::ThreadConfig(name_prefix + ".platform"));
142
143 // Default the config setter to setup the thread name only.
145
146 // Override the config setter if the media profile provider is available.
147 if (services) {
148 // Connect to the media profile provider to assign thread priorities using
149 // Fuchsia's scheduler role API. Failure to connect will print a warning and
150 // proceed with engine initialization, leaving threads created by the engine
151 // at default priority.
152 //
153 // The use of std::shared_ptr here is to work around the unfortunate
154 // requirement for flutter::ThreadConfigSetter (i.e. std::function<>) that
155 // the target callable be copy-constructible. This awkwardly conflicts with
156 // fuchsia::media::ProfileProviderSyncPtr being move-only. std::shared_ptr
157 // provides copyable object that references the move-only SyncPtr.
158 std::shared_ptr<fuchsia::media::ProfileProviderSyncPtr>
159 media_profile_provider =
160 std::make_shared<fuchsia::media::ProfileProviderSyncPtr>();
161
162 const zx_status_t connect_status =
163 services->Connect(media_profile_provider->NewRequest());
164 if (connect_status != ZX_OK) {
165 FML_LOG(WARNING)
166 << "Failed to connect to " << fuchsia::media::ProfileProvider::Name_
167 << ": " << zx_status_get_string(connect_status)
168 << " This is not a fatal error, but threads created by the engine "
169 "will run at default priority, regardless of the requested "
170 "priority.";
171 } else {
172 // Set the role for (this) platform thread. See comment above about the
173 // role naming scheme.
174 SetThreadRole(*media_profile_provider, name_prefix + ".type.platform");
175
176 // This lambda must be copyable or the assignment fails to compile,
177 // necessitating the use of std::shared_ptr for the profile provider.
178 config_setter = [name_prefix, media_profile_provider](
179 const fml::Thread::ThreadConfig& config) {
180 SetThreadConfig(name_prefix, *media_profile_provider, config);
181 };
182 }
183 }
184
185 flutter::ThreadHost::ThreadHostConfig thread_host_config{config_setter};
186
187 thread_host_config.SetRasterConfig(
191 thread_host_config.SetUIConfig(
193 flutter::ThreadHost::Type::kUi, name_prefix),
195 thread_host_config.SetIOConfig(
197 flutter::ThreadHost::Type::kIo, name_prefix),
199
200 return flutter::ThreadHost(thread_host_config);
201}
202
204 std::string thread_label,
205 std::shared_ptr<sys::ServiceDirectory> svc,
206 std::shared_ptr<sys::ServiceDirectory> runner_services,
207 flutter::Settings settings,
208 fuchsia::ui::views::ViewCreationToken view_creation_token,
209 std::pair<fuchsia::ui::views::ViewRefControl,
210 fuchsia::ui::views::ViewRef> view_ref_pair,
211 UniqueFDIONS fdio_ns,
212 fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
214 const std::vector<std::string>& dart_entrypoint_args)
215 : delegate_(delegate),
216 thread_label_(std::move(thread_label)),
217 thread_host_(CreateThreadHost(thread_label_, runner_services)),
218 view_creation_token_(std::move(view_creation_token)),
219 memory_pressure_watcher_binding_(this),
220 latest_memory_pressure_level_(fuchsia::memorypressure::Level::NORMAL),
221 intercept_all_input_(product_config.get_intercept_all_input()),
222 weak_factory_(this) {
223 Initialize(std::move(view_ref_pair), std::move(svc),
224 std::move(runner_services), std::move(settings),
225 std::move(fdio_ns), std::move(directory_request),
226 std::move(product_config), dart_entrypoint_args);
227}
228
229void Engine::Initialize(
230 std::pair<fuchsia::ui::views::ViewRefControl, fuchsia::ui::views::ViewRef>
231 view_ref_pair,
232 std::shared_ptr<sys::ServiceDirectory> svc,
233 std::shared_ptr<sys::ServiceDirectory> runner_services,
234 flutter::Settings settings,
235 UniqueFDIONS fdio_ns,
236 fidl::InterfaceRequest<fuchsia::io::Directory> directory_request,
238 const std::vector<std::string>& dart_entrypoint_args) {
239 // Flatland uses |view_creation_token_| for linking.
240 FML_CHECK(view_creation_token_.value.is_valid());
241
242 // Get the task runners from the managed threads. The current thread will be
243 // used as the "platform" thread.
244 fml::RefPtr<fml::TaskRunner> platform_task_runner =
246
247 const flutter::TaskRunners task_runners(
248 thread_label_, // Dart thread labels
249 platform_task_runner, // platform
250 thread_host_.raster_thread->GetTaskRunner(), // raster
251 thread_host_.ui_thread->GetTaskRunner(), // ui
252 thread_host_.io_thread->GetTaskRunner() // io
253 );
254
255 fuchsia::ui::views::FocuserHandle focuser;
256 fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused;
257 fuchsia::ui::pointer::TouchSourceHandle touch_source;
258 fuchsia::ui::pointer::MouseSourceHandle mouse_source;
259
260 fuchsia::ui::composition::ViewBoundProtocols view_protocols;
261 view_protocols.set_view_focuser(focuser.NewRequest());
262 view_protocols.set_view_ref_focused(view_ref_focused.NewRequest());
263 view_protocols.set_touch_source(touch_source.NewRequest());
264 view_protocols.set_mouse_source(mouse_source.NewRequest());
265
266 // Connect to Flatland.
267 fuchsia::ui::composition::FlatlandHandle flatland;
268 zx_status_t flatland_status =
269 runner_services->Connect<fuchsia::ui::composition::Flatland>(
270 flatland.NewRequest());
271 if (flatland_status != ZX_OK) {
272 FML_LOG(WARNING) << "fuchsia::ui::composition::Flatland connection failed: "
273 << zx_status_get_string(flatland_status);
274 }
275
276 // Connect to SemanticsManager service.
277 fuchsia::accessibility::semantics::SemanticsManagerHandle semantics_manager;
278 zx_status_t semantics_status =
279 runner_services
280 ->Connect<fuchsia::accessibility::semantics::SemanticsManager>(
281 semantics_manager.NewRequest());
282 if (semantics_status != ZX_OK) {
283 FML_LOG(WARNING)
284 << "fuchsia::accessibility::semantics::SemanticsManager connection "
285 "failed: "
286 << zx_status_get_string(semantics_status);
287 }
288
289 // Connect to ImeService service.
290 fuchsia::ui::input::ImeServiceHandle ime_service;
291 zx_status_t ime_status =
292 runner_services->Connect<fuchsia::ui::input::ImeService>(
293 ime_service.NewRequest());
294 if (ime_status != ZX_OK) {
295 FML_LOG(WARNING) << "fuchsia::ui::input::ImeService connection failed: "
296 << zx_status_get_string(ime_status);
297 }
298
299 // Connect to Keyboard service.
300 fuchsia::ui::input3::KeyboardHandle keyboard;
301 zx_status_t keyboard_status =
302 runner_services->Connect<fuchsia::ui::input3::Keyboard>(
303 keyboard.NewRequest());
304 FML_DCHECK(keyboard_status == ZX_OK)
305 << "fuchsia::ui::input3::Keyboard connection failed: "
306 << zx_status_get_string(keyboard_status);
307
308 // Connect to Pointerinjector service.
309 fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry;
310 zx_status_t pointerinjector_registry_status =
311 runner_services->Connect<fuchsia::ui::pointerinjector::Registry>(
312 pointerinjector_registry.NewRequest());
313 if (pointerinjector_registry_status != ZX_OK) {
314 FML_LOG(WARNING)
315 << "fuchsia::ui::pointerinjector::Registry connection failed: "
316 << zx_status_get_string(pointerinjector_registry_status);
317 }
318
319 // Make clones of the `ViewRef` before sending it to various places.
320 fuchsia::ui::views::ViewRef platform_view_ref;
321 view_ref_pair.second.Clone(&platform_view_ref);
322 fuchsia::ui::views::ViewRef accessibility_view_ref;
323 view_ref_pair.second.Clone(&accessibility_view_ref);
324 fuchsia::ui::views::ViewRef isolate_view_ref;
325 view_ref_pair.second.Clone(&isolate_view_ref);
326
327 // Session is terminated on the raster thread, but we must terminate ourselves
328 // on the platform thread.
329 //
330 // This handles the fidl error callback when the Session connection is
331 // broken. The SessionListener interface also has an OnError method, which is
332 // invoked on the platform thread (in PlatformView).
333 fml::closure session_error_callback = [task_runner = platform_task_runner,
334 weak = weak_factory_.GetWeakPtr()]() {
335 task_runner->PostTask([weak]() {
336 if (weak) {
337 FML_LOG(ERROR) << "Terminating from session_error_callback";
338 weak->Terminate();
339 }
340 });
341 };
342
343 // Set up the session connection and other Scenic helpers on the raster
344 // thread. We also need to wait for the external view embedder to be set up
345 // before creating the shell.
346 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
347 fml::AutoResetWaitableEvent view_embedder_latch;
348 auto session_inspect_node =
350 task_runners.GetRasterTaskRunner()->PostTask(fml::MakeCopyable(
351 [this, &view_embedder_latch,
352 session_inspect_node = std::move(session_inspect_node),
353 flatland = std::move(flatland),
354 session_error_callback = std::move(session_error_callback),
355 view_creation_token = std::move(view_creation_token_),
356 view_protocols = std::move(view_protocols),
357 request = parent_viewport_watcher.NewRequest(),
358 view_ref_pair = std::move(view_ref_pair),
359 software_rendering = product_config.software_rendering()]() mutable {
360 if (software_rendering) {
361 surface_producer_ = std::make_shared<SoftwareSurfaceProducer>();
362 } else {
363 surface_producer_ = std::make_shared<VulkanSurfaceProducer>();
364 }
365
366 flatland_connection_ = std::make_shared<FlatlandConnection>(
367 thread_label_, std::move(flatland),
368 std::move(session_error_callback), [](auto) {});
369
370 fuchsia::ui::views::ViewIdentityOnCreation view_identity = {
371 .view_ref = std::move(view_ref_pair.second),
372 .view_ref_control = std::move(view_ref_pair.first)};
373 view_embedder_ = std::make_shared<ExternalViewEmbedder>(
374 std::move(view_creation_token), std::move(view_identity),
375 std::move(view_protocols), std::move(request), flatland_connection_,
376 surface_producer_, intercept_all_input_);
377
378 view_embedder_latch.Signal();
379 }));
380 view_embedder_latch.Wait();
381
383 set_semantics_enabled_callback = [this](bool enabled) {
384 auto platform_view = shell_->GetPlatformView();
385
386 if (platform_view) {
387 platform_view->SetSemanticsEnabled(enabled);
388 }
389 };
390
392 dispatch_semantics_action_callback =
393 [this](int32_t node_id, flutter::SemanticsAction action) {
394 auto platform_view = shell_->GetPlatformView();
395
396 if (platform_view) {
397 // TODO(fuchsia): Remove implicit view assumption.
398 // https://github.com/flutter/flutter/issues/142845
399 platform_view->DispatchSemanticsAction(kImplicitViewId, node_id,
400 action, {});
401 }
402 };
403
404 const std::string accessibility_inspect_name =
405 std::to_string(GetKoid(accessibility_view_ref));
406 accessibility_bridge_ = std::make_unique<AccessibilityBridge>(
407 std::move(set_semantics_enabled_callback),
408 std::move(dispatch_semantics_action_callback),
409 std::move(semantics_manager), std::move(accessibility_view_ref),
411 std::move(accessibility_inspect_name)));
412
413 OnEnableWireframeCallback on_enable_wireframe_callback = std::bind(
414 &Engine::DebugWireframeSettingsChanged, this, std::placeholders::_1);
415
416 OnCreateViewCallback on_create_view_callback = std::bind(
417 &Engine::CreateView, this, std::placeholders::_1, std::placeholders::_2,
418 std::placeholders::_3, std::placeholders::_4, std::placeholders::_5);
419
420 OnUpdateViewCallback on_update_view_callback = std::bind(
421 &Engine::UpdateView, this, std::placeholders::_1, std::placeholders::_2,
422 std::placeholders::_3, std::placeholders::_4);
423
424 OnDestroyViewCallback on_destroy_view_callback = std::bind(
425 &Engine::DestroyView, this, std::placeholders::_1, std::placeholders::_2);
426
427 OnCreateSurfaceCallback on_create_surface_callback =
428 std::bind(&Engine::CreateSurface, this);
429
430 // SessionListener has a OnScenicError method; invoke this callback on the
431 // platform thread when that happens. The Session itself should also be
432 // disconnected when this happens, and it will also attempt to terminate.
433 fit::closure on_session_listener_error_callback =
434 [task_runner = platform_task_runner,
435 weak = weak_factory_.GetWeakPtr()]() {
436 task_runner->PostTask([weak]() {
437 if (weak) {
438 FML_LOG(ERROR) << "Terminating from "
439 "on_session_listener_error_callback";
440 weak->Terminate();
441 }
442 });
443 };
444
445 // Launch the engine in the appropriate configuration.
446 // Note: this initializes the Asset Manager on the global PersistantCache
447 // so it must be called before WarmupSkps() is called below.
448 auto run_configuration = flutter::RunConfiguration::InferFromSettings(
449 settings, task_runners.GetIOTaskRunner());
450 run_configuration.SetEntrypointArgs(std::move(dart_entrypoint_args));
451
452 OnSemanticsNodeUpdateCallback on_semantics_node_update_callback =
453 [this](flutter::SemanticsNodeUpdates updates, float pixel_ratio) {
454 accessibility_bridge_->AddSemanticsNodeUpdate(updates, pixel_ratio);
455 };
456
457 OnRequestAnnounceCallback on_request_announce_callback =
458 [this](const std::string& message) {
459 accessibility_bridge_->RequestAnnounce(message);
460 };
461
462 // Setup the callback that will instantiate the platform view.
464 on_create_platform_view = fml::MakeCopyable(
465 [this, view_ref = std::move(platform_view_ref),
466 parent_viewport_watcher = std::move(parent_viewport_watcher),
467 ime_service = std::move(ime_service), keyboard = std::move(keyboard),
468 focuser = std::move(focuser),
469 view_ref_focused = std::move(view_ref_focused),
470 touch_source = std::move(touch_source),
471 mouse_source = std::move(mouse_source),
472 pointerinjector_registry = std::move(pointerinjector_registry),
473 on_session_listener_error_callback =
474 std::move(on_session_listener_error_callback),
475 on_enable_wireframe_callback =
476 std::move(on_enable_wireframe_callback),
477 on_create_view_callback = std::move(on_create_view_callback),
478 on_update_view_callback = std::move(on_update_view_callback),
479 on_destroy_view_callback = std::move(on_destroy_view_callback),
480 on_create_surface_callback = std::move(on_create_surface_callback),
481 on_semantics_node_update_callback =
482 std::move(on_semantics_node_update_callback),
483 on_request_announce_callback =
484 std::move(on_request_announce_callback),
485 external_view_embedder = GetExternalViewEmbedder(),
486 await_vsync_callback =
487 [this](FireCallbackCallback cb) {
488 flatland_connection_->AwaitVsync(cb);
489 },
490 await_vsync_for_secondary_callback_callback =
491 [this](FireCallbackCallback cb) {
492 flatland_connection_->AwaitVsyncForSecondaryCallback(cb);
493 },
494 product_config, svc](flutter::Shell& shell) mutable {
495 OnShaderWarmupCallback on_shader_warmup_callback = nullptr;
496 if (product_config.enable_shader_warmup()) {
497 FML_DCHECK(surface_producer_);
498 if (product_config.enable_shader_warmup_dart_hooks()) {
499 on_shader_warmup_callback =
500 [this, &shell](
501 const std::vector<std::string>& skp_names,
502 std::function<void(uint32_t)> completion_callback,
503 uint64_t width, uint64_t height) {
504 WarmupSkps(
505 shell.GetDartVM()
506 ->GetConcurrentMessageLoop()
507 ->GetTaskRunner()
508 .get(),
509 shell.GetTaskRunners().GetRasterTaskRunner().get(),
510 surface_producer_, SkISize::Make(width, height),
512 ->asset_manager(),
513 skp_names, completion_callback);
514 };
515 } else {
516 WarmupSkps(shell.GetDartVM()
517 ->GetConcurrentMessageLoop()
518 ->GetTaskRunner()
519 .get(),
520 shell.GetTaskRunners().GetRasterTaskRunner().get(),
521 surface_producer_, SkISize::Make(1024, 600),
523 ->asset_manager(),
524 std::nullopt, std::nullopt);
525 }
526 }
527
528 return std::make_unique<flutter_runner::PlatformView>(
529 shell, shell.GetTaskRunners(), std::move(view_ref),
530 std::move(external_view_embedder), std::move(ime_service),
531 std::move(keyboard), std::move(touch_source),
532 std::move(mouse_source), std::move(focuser),
533 std::move(view_ref_focused), std::move(parent_viewport_watcher),
534 std::move(pointerinjector_registry),
535 std::move(on_enable_wireframe_callback),
536 std::move(on_create_view_callback),
537 std::move(on_update_view_callback),
538 std::move(on_destroy_view_callback),
539 std::move(on_create_surface_callback),
540 std::move(on_semantics_node_update_callback),
541 std::move(on_request_announce_callback),
542 std::move(on_shader_warmup_callback),
543 std::move(await_vsync_callback),
544 std::move(await_vsync_for_secondary_callback_callback),
545 std::move(svc));
546 });
547
548 // Setup the callback that will instantiate the rasterizer.
550 [](flutter::Shell& shell) {
551 return std::make_unique<flutter::Rasterizer>(shell);
552 };
553
555 std::bind(&Engine::OnMainIsolateStart, this);
557 std::bind([weak = weak_factory_.GetWeakPtr(),
558 runner = task_runners.GetPlatformTaskRunner()]() {
559 runner->PostTask([weak = std::move(weak)] {
560 if (weak) {
561 weak->OnMainIsolateShutdown();
562 }
563 });
564 });
565
566 // Connect and set up the system font provider.
567 fuchsia::fonts::ProviderSyncPtr sync_font_provider;
568 runner_services->Connect(sync_font_provider.NewRequest());
569 settings.font_initialization_data =
570 sync_font_provider.Unbind().TakeChannel().release();
571
572 {
573 TRACE_EVENT0("flutter", "CreateShell");
574 shell_ = flutter::Shell::Create(
575 flutter::PlatformData(), // default window data
576 std::move(task_runners), // host task runners
577 std::move(settings), // shell launch settings
578 std::move(on_create_platform_view), // platform view create callback
579 std::move(on_create_rasterizer) // rasterizer create callback
580 );
581 }
582
583 if (!shell_) {
584 FML_LOG(ERROR) << "Could not launch the shell.";
585 return;
586 }
587
588 // Shell has been created. Before we run the engine, set up the isolate
589 // configurator.
590 isolate_configurator_ = std::make_unique<IsolateConfigurator>(
591 std::move(fdio_ns), directory_request.TakeChannel(),
592 std::move(isolate_view_ref.reference));
593
594 // This platform does not get a separate surface platform view creation
595 // notification. Fire one eagerly.
596 shell_->GetPlatformView()->NotifyCreated();
597
598 // Connect to the memory pressure provider. If the connection fails, the
599 // initialization of the engine will simply proceed, printing a warning
600 // message. The engine will be fully functional, except that the Flutter
601 // shell will not be notified when memory is low.
602 {
603 memory_pressure_provider_.set_error_handler([](zx_status_t status) {
604 FML_LOG(WARNING)
605 << "Failed to connect to " << fuchsia::memorypressure::Provider::Name_
606 << ": " << zx_status_get_string(status)
607 << " This is not a fatal error, but the heap will not be "
608 << " compacted when memory is low.";
609 });
610
611 // Note that we're using the runner's services, not the component's.
612 // The Flutter Shell should be notified when memory is low regardless of
613 // whether the component has direct access to the
614 // fuchsia.memorypressure.Provider service.
615 ZX_ASSERT(runner_services->Connect(
616 memory_pressure_provider_.NewRequest()) == ZX_OK);
617
618 FML_VLOG(1) << "Registering memorypressure watcher";
619
620 // Register for changes, which will make the request for the initial
621 // memory level.
622 memory_pressure_provider_->RegisterWatcher(
623 memory_pressure_watcher_binding_.NewBinding());
624 }
625
626 // Connect to the intl property provider. If the connection fails, the
627 // initialization of the engine will simply proceed, printing a warning
628 // message. The engine will be fully functional, except that the user's
629 // locale preferences would not be communicated to flutter engine.
630 {
631 intl_property_provider_.set_error_handler([](zx_status_t status) {
632 FML_LOG(WARNING) << "Failed to connect to "
633 << fuchsia::intl::PropertyProvider::Name_ << ": "
634 << zx_status_get_string(status)
635 << " This is not a fatal error, but the user locale "
636 << " preferences will not be forwarded to flutter apps";
637 });
638
639 // Note that we're using the runner's services, not the component's.
640 // Flutter locales should be updated regardless of whether the component has
641 // direct access to the fuchsia.intl.PropertyProvider service.
642 ZX_ASSERT(runner_services->Connect(intl_property_provider_.NewRequest()) ==
643 ZX_OK);
644
645 auto get_profile_callback = [weak = weak_factory_.GetWeakPtr()](
646 const fuchsia::intl::Profile& profile) {
647 if (!weak) {
648 return;
649 }
650 if (!profile.has_locales()) {
651 FML_LOG(WARNING) << "Got intl Profile without locales";
652 }
653 auto message = MakeLocalizationPlatformMessage(profile);
654 FML_VLOG(1) << "Sending LocalizationPlatformMessage";
655 weak->shell_->GetPlatformView()->DispatchPlatformMessage(
656 std::move(message));
657 };
658
659 FML_VLOG(1) << "Requesting intl Profile";
660
661 // Make the initial request
662 intl_property_provider_->GetProfile(get_profile_callback);
663
664 // And register for changes
665 intl_property_provider_.events().OnChange = [this, runner_services,
666 get_profile_callback]() {
667 FML_VLOG(1) << fuchsia::intl::PropertyProvider::Name_ << ": OnChange";
668 runner_services->Connect(intl_property_provider_.NewRequest());
669 intl_property_provider_->GetProfile(get_profile_callback);
670 };
671 }
672
673 auto on_run_failure = [weak = weak_factory_.GetWeakPtr()]() {
674 // The engine could have been killed by the caller right after the
675 // constructor was called but before it could run on the UI thread.
676 if (weak) {
677 FML_LOG(ERROR) << "Terminating from on_run_failure";
678 weak->Terminate();
679 }
680 };
681
682 shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
683 fml::MakeCopyable([engine = shell_->GetEngine(), //
684 run_configuration = std::move(run_configuration), //
685 on_run_failure //
686 ]() mutable {
687 if (!engine) {
688 return;
689 }
690
691 if (engine->Run(std::move(run_configuration)) ==
693 on_run_failure();
694 }
695 }));
696}
697
698Engine::~Engine() {
699 shell_.reset();
700
701 // Destroy rendering objects on the raster thread.
702 fml::AutoResetWaitableEvent view_embedder_latch;
703 thread_host_.raster_thread->GetTaskRunner()->PostTask(
704 fml::MakeCopyable([this, &view_embedder_latch]() mutable {
705 view_embedder_.reset();
706 flatland_connection_.reset();
707 surface_producer_.reset();
708 view_embedder_latch.Signal();
709 }));
710 view_embedder_latch.Wait();
711}
712
713std::optional<uint32_t> Engine::GetEngineReturnCode() const {
714 if (!shell_) {
715 return std::nullopt;
716 }
717 std::optional<uint32_t> code;
720 shell_->GetTaskRunners().GetUITaskRunner(),
721 [&latch, &code, engine = shell_->GetEngine()]() {
722 if (engine) {
723 code = engine->GetUIIsolateReturnCode();
724 }
725 latch.Signal();
726 });
727 latch.Wait();
728 return code;
729}
730
731void Engine::OnMainIsolateStart() {
732 if (!isolate_configurator_ ||
733 !isolate_configurator_->ConfigureCurrentIsolate()) {
734 FML_LOG(ERROR) << "Could not configure some native embedder bindings for a "
735 "new root isolate.";
736 }
737}
738
739void Engine::OnMainIsolateShutdown() {
740 Terminate();
741}
742
743void Engine::Terminate() {
744 delegate_.OnEngineTerminate(this);
745 // Warning. Do not do anything after this point as the delegate may have
746 // collected this object.
747}
748
749void Engine::DebugWireframeSettingsChanged(bool enabled) {
750 FML_CHECK(shell_);
751
752 shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([]() {
753 // TODO(fxbug.dev/116000): Investigate if we can add flatland wireframe code
754 // for debugging.
755 });
756}
757
758void Engine::CreateView(int64_t view_id,
759 ViewCallback on_view_created,
760 ViewCreatedCallback on_view_bound,
761 bool hit_testable,
762 bool focusable) {
763 FML_CHECK(shell_);
764 shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
765 [this, view_id, hit_testable, focusable,
766 on_view_created = std::move(on_view_created),
767 on_view_bound = std::move(on_view_bound)]() {
768 FML_CHECK(view_embedder_);
769 view_embedder_->CreateView(view_id, std::move(on_view_created),
770 std::move(on_view_bound));
771 view_embedder_->SetViewProperties(view_id, SkRect::MakeEmpty(),
772 hit_testable, focusable);
773 });
774}
775
776void Engine::UpdateView(int64_t view_id,
777 SkRect occlusion_hint,
778 bool hit_testable,
779 bool focusable) {
780 FML_CHECK(shell_);
781 shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
782 [this, view_id, occlusion_hint, hit_testable, focusable]() {
783 FML_CHECK(view_embedder_);
784 view_embedder_->SetViewProperties(view_id, occlusion_hint, hit_testable,
785 focusable);
786 });
787}
788
789void Engine::DestroyView(int64_t view_id, ViewIdCallback on_view_unbound) {
790 FML_CHECK(shell_);
791
792 shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
793 [this, view_id, on_view_unbound = std::move(on_view_unbound)]() {
794 FML_CHECK(view_embedder_);
795 view_embedder_->DestroyView(view_id, std::move(on_view_unbound));
796 });
797}
798
799std::unique_ptr<flutter::Surface> Engine::CreateSurface() {
800 return std::make_unique<Surface>(thread_label_, GetExternalViewEmbedder(),
801 surface_producer_->gr_context());
802}
803
804std::shared_ptr<flutter::ExternalViewEmbedder>
805Engine::GetExternalViewEmbedder() {
806 FML_CHECK(view_embedder_);
807 return view_embedder_;
808}
809
810#if !defined(DART_PRODUCT)
811void Engine::WriteProfileToTrace() const {
812 Dart_Port main_port = shell_->GetEngine()->GetUIIsolateMainPort();
813 char* error = NULL;
814 bool success = Dart_WriteProfileToTimeline(main_port, &error);
815 if (!success) {
816 FML_LOG(ERROR) << "Failed to write Dart profile to trace: " << error;
817 free(error);
818 }
819}
820#endif // !defined(DART_PRODUCT)
821
822void Engine::WarmupSkps(
823 fml::BasicTaskRunner* concurrent_task_runner,
824 fml::BasicTaskRunner* raster_task_runner,
825 std::shared_ptr<SurfaceProducer> surface_producer,
826 SkISize size,
827 std::shared_ptr<flutter::AssetManager> asset_manager,
828 std::optional<const std::vector<std::string>> skp_names,
829 std::optional<std::function<void(uint32_t)>> maybe_completion_callback,
830 bool synchronous) {
831 // Wrap the optional validity checks up in a lambda to simplify the various
832 // callsites below
833 auto completion_callback = [maybe_completion_callback](uint32_t skp_count) {
834 if (maybe_completion_callback.has_value() &&
835 maybe_completion_callback.value()) {
836 maybe_completion_callback.value()(skp_count);
837 }
838 };
839
840 // We use this bizzare raw pointer to a smart pointer thing here because we
841 // want to keep the surface alive until all gpu work is done and the
842 // callbacks skia takes for this are function pointers so we are unable to
843 // use a lambda that captures the smart pointer. We need two levels of
844 // indirection because it needs to be the same across all invocations of the
845 // raster task lambda from a single invocation of WarmupSkps, but be
846 // different across different invocations of WarmupSkps (so we cant
847 // statically initialialize it in the lambda itself). Basically the result
848 // of a mashup of wierd call dynamics, multithreading, and lifecycle
849 // management with C style Skia callbacks.
850 std::unique_ptr<SurfaceProducerSurface>* skp_warmup_surface =
851 new std::unique_ptr<SurfaceProducerSurface>(nullptr);
852
853 // tell concurrent task runner to deserialize all skps available from
854 // the asset manager
855 concurrent_task_runner->PostTask([raster_task_runner, size,
856 skp_warmup_surface, surface_producer,
857 asset_manager, skp_names,
858 completion_callback, synchronous]() {
859 TRACE_DURATION("flutter", "DeserializeSkps");
860 std::vector<std::unique_ptr<fml::Mapping>> skp_mappings;
861 if (skp_names) {
862 for (auto& skp_name : skp_names.value()) {
863 auto skp_mapping = asset_manager->GetAsMapping(skp_name);
864 if (skp_mapping) {
865 skp_mappings.push_back(std::move(skp_mapping));
866 } else {
867 FML_LOG(ERROR) << "Failed to get mapping for " << skp_name;
868 }
869 }
870 } else {
871 skp_mappings = asset_manager->GetAsMappings(".*\\.skp$", "shaders");
872 }
873
874 if (skp_mappings.empty()) {
875 FML_LOG(WARNING)
876 << "Engine::WarmupSkps got zero SKP mappings, returning early";
877 completion_callback(0);
878 return;
879 }
880
881 size_t total_size = 0;
882 for (auto& mapping : skp_mappings) {
883 total_size += mapping->GetSize();
884 }
885
886 FML_LOG(INFO) << "Shader warmup got " << skp_mappings.size()
887 << " skp's with a total size of " << total_size << " bytes";
888
889 std::vector<sk_sp<SkPicture>> pictures;
890 unsigned int i = 0;
891 for (auto& mapping : skp_mappings) {
892 std::unique_ptr<SkMemoryStream> stream =
893 SkMemoryStream::MakeDirect(mapping->GetMapping(), mapping->GetSize());
894 SkDeserialProcs procs = {0};
895 procs.fImageProc = flutter::DeserializeImageWithoutData;
896 procs.fTypefaceProc = flutter::DeserializeTypefaceWithoutData;
897 sk_sp<SkPicture> picture =
898 SkPicture::MakeFromStream(stream.get(), &procs);
899 if (!picture) {
900 FML_LOG(ERROR) << "Failed to deserialize picture " << i;
901 continue;
902 }
903
904 // Tell raster task runner to warmup have the compositor
905 // context warm up the newly deserialized picture
906 raster_task_runner->PostTask([picture, skp_warmup_surface, size,
907 surface_producer, completion_callback, i,
908 count = skp_mappings.size(), synchronous] {
909 TRACE_DURATION("flutter", "WarmupSkp");
910 if (*skp_warmup_surface == nullptr) {
911 skp_warmup_surface->reset(
912 surface_producer->ProduceOffscreenSurface(size).release());
913
914 if (*skp_warmup_surface == nullptr) {
915 FML_LOG(ERROR) << "Failed to create offscreen warmup surface";
916 // Tell client that zero shaders were warmed up because warmup
917 // failed.
918 completion_callback(0);
919 return;
920 }
921 }
922
923 // Do the actual warmup
924 (*skp_warmup_surface)
925 ->GetSkiaSurface()
926 ->getCanvas()
927 ->drawPicture(picture);
928
929 if (i == count - 1) {
930 // We call this here instead of inside fFinishedProc below because
931 // we want to unblock the dart animation code as soon as the
932 // raster thread is free to enque work, rather than waiting for
933 // the GPU work itself to finish.
934 completion_callback(count);
935 }
936
937 if (surface_producer->gr_context()) {
938 if (i < count - 1) {
939 // For all but the last skp we fire and forget
940 surface_producer->gr_context()->flushAndSubmit();
941 } else {
942 // For the last skp we provide a callback that frees the warmup
943 // surface and calls the completion callback
944 struct GrFlushInfo flush_info;
945 flush_info.fFinishedContext = skp_warmup_surface;
946 flush_info.fFinishedProc = [](void* skp_warmup_surface) {
947 delete static_cast<std::unique_ptr<SurfaceProducerSurface>*>(
948 skp_warmup_surface);
949 };
950
951 surface_producer->gr_context()->flush(flush_info);
952 surface_producer->gr_context()->submit(
953 synchronous ? GrSyncCpu::kYes : GrSyncCpu::kNo);
954 }
955 } else {
956 if (i == count - 1) {
957 delete skp_warmup_surface;
958 }
959 }
960 });
961 i++;
962 }
963 });
964}
965
966void Engine::OnLevelChanged(
967 fuchsia::memorypressure::Level level,
968 fuchsia::memorypressure::Watcher::OnLevelChangedCallback callback) {
969 // The callback must be invoked immediately to acknowledge the message.
970 // This is the "Throttle push using acknowledgements" pattern:
971 // https://fuchsia.dev/fuchsia-src/concepts/api/fidl#throttle_push_using_acknowledgements
972 callback();
973
974 FML_LOG(WARNING) << "memorypressure watcher: OnLevelChanged from "
975 << static_cast<int>(latest_memory_pressure_level_) << " to "
976 << static_cast<int>(level);
977
978 if (latest_memory_pressure_level_ == fuchsia::memorypressure::Level::NORMAL &&
979 (level == fuchsia::memorypressure::Level::WARNING ||
980 level == fuchsia::memorypressure::Level::CRITICAL)) {
981 FML_LOG(WARNING)
982 << "memorypressure watcher: notifying Flutter that memory is low";
983 shell_->NotifyLowMemoryWarning();
984 }
985 latest_memory_pressure_level_ = level;
986}
987
988} // namespace flutter_runner
std::unique_ptr< flutter::PlatformViewIOS > platform_view
static inspect::Node CreateRootChild(const std::string &name)
static std::shared_ptr< AssetManager > asset_manager()
static PersistentCache * GetCacheForProcess()
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
static std::unique_ptr< Shell > Create(const PlatformData &platform_data, const TaskRunners &task_runners, Settings settings, const CreateCallback< PlatformView > &on_create_platform_view, const CreateCallback< Rasterizer > &on_create_rasterizer, bool is_gpu_disabled=false)
Creates a shell instance using the provided settings. The callbacks to create the various shell subco...
Definition shell.cc:221
std::function< std::unique_ptr< T >(Shell &)> CreateCallback
Definition shell.h:121
std::function< void(bool)> SetSemanticsEnabledCallback
std::function< void(int32_t, flutter::SemanticsAction)> DispatchSemanticsActionCallback
static flutter::ThreadHost CreateThreadHost(const std::string &name_prefix, const std::shared_ptr< sys::ServiceDirectory > &runner_services=nullptr)
Definition engine.cc:137
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, std::pair< fuchsia::ui::views::ViewRefControl, fuchsia::ui::views::ViewRef > view_ref_pair, UniqueFDIONS fdio_ns, fidl::InterfaceRequest< fuchsia::io::Directory > directory_request, FlutterRunnerProductConfiguration product_config, const std::vector< std::string > &dart_entrypoint_args)
Definition engine.cc:203
An interface over the ability to schedule tasks on a TaskRunner.
Definition task_runner.h:20
virtual void PostTask(const fml::closure &task)=0
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
@ kNormal
Default priority level.
@ kRaster
Suitable for thread which raster data.
@ kBackground
Suitable for threads that shouldn't disrupt high priority work.
@ kDisplay
Suitable for threads which generate data for the display.
static void SetCurrentThreadName(const ThreadConfig &config)
Definition thread.cc:135
int32_t value
ThreadHost thread_host_
MockDelegate delegate_
FlutterEngine engine
Definition main.cc:84
if(end==-1)
G_BEGIN_DECLS GBytes * message
const uint8_t uint32_t uint32_t GError ** error
G_BEGIN_DECLS FlutterViewId view_id
FlutterDesktopBinaryReply callback
#define FML_VLOG(verbose_level)
Definition logging.h:117
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
static constexpr FlutterViewId kImplicitViewId
std::function< void(const std::vector< std::string > &, std::function< void(uint32_t)>, uint64_t, uint64_t)> OnShaderWarmupCallback
fit::function< void(int64_t, ViewCallback, ViewCreatedCallback, bool, bool)> OnCreateViewCallback
fit::function< void(int64_t, ViewIdCallback)> OnDestroyViewCallback
std::function< void(fml::TimePoint, fml::TimePoint)> FireCallbackCallback
fit::function< void(int64_t, SkRect, bool, bool)> OnUpdateViewCallback
fit::function< std::unique_ptr< flutter::Surface >()> OnCreateSurfaceCallback
fit::function< void(flutter::SemanticsNodeUpdates, float)> OnSemanticsNodeUpdateCallback
fml::MallocMapping MakeLocalizationPlatformMessageData(const Profile &intl_profile)
fit::function< void(std::string)> OnRequestAnnounceCallback
fit::function< void(bool)> OnEnableWireframeCallback
sk_sp< SkImage > DeserializeImageWithoutData(const void *data, size_t length, void *ctx)
std::unordered_map< int32_t, SemanticsNode > SemanticsNodeUpdates
sk_sp< SkTypeface > DeserializeTypefaceWithoutData(const void *data, size_t length, void *ctx)
fml::Thread::ThreadConfigSetter ThreadConfigSetter
Definition thread_host.h:18
internal::CopyableLambda< T > MakeCopyable(T lambda)
std::function< void()> closure
Definition closure.h:14
Definition ref_ptr.h:261
int32_t height
int32_t width
std::function< void(const DartIsolate &)> root_isolate_create_callback
Definition settings.h:285
uint32_t font_initialization_data
Definition settings.h:262
fml::closure root_isolate_shutdown_callback
Definition settings.h:291
static std::string MakeThreadName(Type type, const std::string &prefix)
Use the prefix and thread type to generator a thread name.
void SetRasterConfig(const ThreadConfig &)
Specified the IO Thread Config, meanwhile set the mask.
The collection of all the threads used by the engine.
Definition thread_host.h:21
std::unique_ptr< fml::Thread > io_thread
Definition thread_host.h:86
std::unique_ptr< fml::Thread > raster_thread
Definition thread_host.h:85
std::unique_ptr< fml::Thread > ui_thread
Definition thread_host.h:84
The ThreadConfig is the thread info include thread name, thread priority.
Definition thread.h:35
ThreadPriority priority
Definition thread.h:45
#define TRACE_EVENT0(category_group, name)