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