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#define RAPIDJSON_HAS_STDSTRING 1
6#include "flutter/shell/common/shell.h"
7
8#include <memory>
9#include <sstream>
10#include <utility>
11#include <vector>
12
13#include "flutter/assets/directory_asset_bundle.h"
14#include "flutter/common/graphics/persistent_cache.h"
15#include "flutter/fml/base32.h"
16#include "flutter/fml/file.h"
17#include "flutter/fml/icu_util.h"
18#include "flutter/fml/log_settings.h"
19#include "flutter/fml/logging.h"
20#include "flutter/fml/make_copyable.h"
21#include "flutter/fml/message_loop.h"
22#include "flutter/fml/paths.h"
23#include "flutter/fml/trace_event.h"
24#include "flutter/runtime/dart_vm.h"
25#include "flutter/shell/common/engine.h"
26#include "flutter/shell/common/skia_event_tracer_impl.h"
27#include "flutter/shell/common/switches.h"
28#include "flutter/shell/common/vsync_waiter.h"
29#include "rapidjson/stringbuffer.h"
30#include "rapidjson/writer.h"
31#include "third_party/dart/runtime/include/dart_tools_api.h"
32#include "third_party/skia/include/codec/SkBmpDecoder.h"
33#include "third_party/skia/include/codec/SkCodec.h"
34#include "third_party/skia/include/codec/SkGifDecoder.h"
35#include "third_party/skia/include/codec/SkIcoDecoder.h"
36#include "third_party/skia/include/codec/SkJpegDecoder.h"
37#include "third_party/skia/include/codec/SkPngDecoder.h"
38#include "third_party/skia/include/codec/SkWbmpDecoder.h"
39#include "third_party/skia/include/codec/SkWebpDecoder.h"
40#include "third_party/skia/include/core/SkGraphics.h"
41#include "third_party/skia/include/utils/SkBase64.h"
42#include "third_party/tonic/common/log.h"
43
44namespace flutter {
45
46constexpr char kSkiaChannel[] = "flutter/skia";
47constexpr char kSystemChannel[] = "flutter/system";
48constexpr char kTypeKey[] = "type";
49constexpr char kFontChange[] = "fontsChange";
50
51namespace {
52
53std::unique_ptr<Engine> CreateEngine(
54 Engine::Delegate& delegate,
55 const PointerDataDispatcherMaker& dispatcher_maker,
56 DartVM& vm,
57 const fml::RefPtr<const DartSnapshot>& isolate_snapshot,
58 const TaskRunners& task_runners,
59 const PlatformData& platform_data,
60 const Settings& settings,
61 std::unique_ptr<Animator> animator,
62 const fml::WeakPtr<IOManager>& io_manager,
63 const fml::RefPtr<SkiaUnrefQueue>& unref_queue,
64 const fml::TaskRunnerAffineWeakPtr<SnapshotDelegate>& snapshot_delegate,
65 const std::shared_ptr<VolatilePathTracker>& volatile_path_tracker,
66 const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch) {
67 return std::make_unique<Engine>(args&: delegate, //
68 args: dispatcher_maker, //
69 args&: vm, //
70 args: isolate_snapshot, //
71 args: task_runners, //
72 args: platform_data, //
73 args: settings, //
74 args: std::move(animator), //
75 args: io_manager, //
76 args: unref_queue, //
77 args: snapshot_delegate, //
78 args: volatile_path_tracker, //
79 args: gpu_disabled_switch);
80}
81
82void RegisterCodecsWithSkia() {
83 // These are in the order they will be attempted to be decoded from.
84 // If we have data to back it up, we can order these by "frequency used in
85 // the wild" for a very small performance bump, but for now we mirror the
86 // order Skia had them in.
87 SkCodecs::Register(d: SkPngDecoder::Decoder());
88 SkCodecs::Register(d: SkJpegDecoder::Decoder());
89 SkCodecs::Register(d: SkWebpDecoder::Decoder());
90 SkCodecs::Register(d: SkGifDecoder::Decoder());
91 SkCodecs::Register(d: SkBmpDecoder::Decoder());
92 SkCodecs::Register(d: SkWbmpDecoder::Decoder());
93 SkCodecs::Register(d: SkIcoDecoder::Decoder());
94}
95
96// Though there can be multiple shells, some settings apply to all components in
97// the process. These have to be set up before the shell or any of its
98// sub-components can be initialized. In a perfect world, this would be empty.
99// TODO(chinmaygarde): The unfortunate side effect of this call is that settings
100// that cause shell initialization failures will still lead to some of their
101// settings being applied.
102void PerformInitializationTasks(Settings& settings) {
103 {
104 fml::LogSettings log_settings;
105 log_settings.min_log_level =
106 settings.verbose_logging ? fml::LOG_INFO : fml::LOG_ERROR;
107 fml::SetLogSettings(log_settings);
108 }
109
110 static std::once_flag gShellSettingsInitialization = {};
111 std::call_once(flag&: gShellSettingsInitialization, func: [&settings] {
112 tonic::SetLogHandler(
113 [](const char* message) { FML_LOG(ERROR) << message; });
114
115 if (settings.trace_skia) {
116 InitSkiaEventTracer(enabled: settings.trace_skia, allowlist: settings.trace_skia_allowlist);
117 }
118
119 if (!settings.trace_allowlist.empty()) {
120 fml::tracing::TraceSetAllowlist(allowlist: settings.trace_allowlist);
121 }
122
123 if (!settings.skia_deterministic_rendering_on_cpu) {
124 SkGraphics::Init();
125 } else {
126 FML_DLOG(INFO) << "Skia deterministic rendering is enabled.";
127 }
128 RegisterCodecsWithSkia();
129
130 if (settings.icu_initialization_required) {
131 if (!settings.icu_data_path.empty()) {
132 fml::icu::InitializeICU(icu_data_path: settings.icu_data_path);
133 } else if (settings.icu_mapper) {
134 fml::icu::InitializeICUFromMapping(mapping: settings.icu_mapper());
135 } else {
136 FML_DLOG(WARNING) << "Skipping ICU initialization in the shell.";
137 }
138 }
139 });
140
141 PersistentCache::SetCacheSkSL(settings.cache_sksl);
142}
143
144} // namespace
145
146std::unique_ptr<Shell> Shell::Create(
147 const PlatformData& platform_data,
148 const TaskRunners& task_runners,
149 Settings settings,
150 const Shell::CreateCallback<PlatformView>& on_create_platform_view,
151 const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
152 bool is_gpu_disabled) {
153 // This must come first as it initializes tracing.
154 PerformInitializationTasks(settings);
155
156 TRACE_EVENT0("flutter", "Shell::Create");
157
158 // Always use the `vm_snapshot` and `isolate_snapshot` provided by the
159 // settings to launch the VM. If the VM is already running, the snapshot
160 // arguments are ignored.
161 auto vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
162 auto isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
163 auto vm = DartVMRef::Create(settings, vm_snapshot, isolate_snapshot);
164 FML_CHECK(vm) << "Must be able to initialize the VM.";
165
166 // If the settings did not specify an `isolate_snapshot`, fall back to the
167 // one the VM was launched with.
168 if (!isolate_snapshot) {
169 isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
170 }
171 auto resource_cache_limit_calculator =
172 std::make_shared<ResourceCacheLimitCalculator>(
173 args&: settings.resource_cache_max_bytes_threshold);
174 return CreateWithSnapshot(platform_data, //
175 task_runners, //
176 /*parent_merger=*/parent_thread_merger: nullptr, //
177 /*parent_io_manager=*/nullptr, //
178 resource_cache_limit_calculator, //
179 settings, //
180 vm: std::move(vm), //
181 isolate_snapshot: std::move(isolate_snapshot), //
182 on_create_platform_view, //
183 on_create_rasterizer, //
184 on_create_engine: CreateEngine, is_gpu_disabled);
185}
186
187std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
188 DartVMRef vm,
189 fml::RefPtr<fml::RasterThreadMerger> parent_merger,
190 std::shared_ptr<ShellIOManager> parent_io_manager,
191 const std::shared_ptr<ResourceCacheLimitCalculator>&
192 resource_cache_limit_calculator,
193 const TaskRunners& task_runners,
194 const PlatformData& platform_data,
195 const Settings& settings,
196 fml::RefPtr<const DartSnapshot> isolate_snapshot,
197 const Shell::CreateCallback<PlatformView>& on_create_platform_view,
198 const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
199 const Shell::EngineCreateCallback& on_create_engine,
200 bool is_gpu_disabled) {
201 if (!task_runners.IsValid()) {
202 FML_LOG(ERROR) << "Task runners to run the shell were invalid.";
203 return nullptr;
204 }
205
206 auto shell = std::unique_ptr<Shell>(
207 new Shell(std::move(vm), task_runners, std::move(parent_merger),
208 resource_cache_limit_calculator, settings,
209 std::make_shared<VolatilePathTracker>(
210 args: task_runners.GetUITaskRunner(),
211 args: !settings.skia_deterministic_rendering_on_cpu),
212 is_gpu_disabled));
213
214 // Create the platform view on the platform thread (this thread).
215 auto platform_view = on_create_platform_view(*shell.get());
216 if (!platform_view || !platform_view->GetWeakPtr()) {
217 return nullptr;
218 }
219
220 // Create the rasterizer on the raster thread.
221 std::promise<std::unique_ptr<Rasterizer>> rasterizer_promise;
222 auto rasterizer_future = rasterizer_promise.get_future();
223 std::promise<fml::TaskRunnerAffineWeakPtr<SnapshotDelegate>>
224 snapshot_delegate_promise;
225 auto snapshot_delegate_future = snapshot_delegate_promise.get_future();
226 fml::TaskRunner::RunNowOrPostTask(
227 runner: task_runners.GetRasterTaskRunner(),
228 task: [&rasterizer_promise, //
229 &snapshot_delegate_promise,
230 on_create_rasterizer, //
231 shell = shell.get(), //
232 impeller_context = platform_view->GetImpellerContext() //
233 ]() {
234 TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem");
235 std::unique_ptr<Rasterizer> rasterizer(on_create_rasterizer(*shell));
236 rasterizer->SetImpellerContext(impeller_context);
237 snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate());
238 rasterizer_promise.set_value(std::move(rasterizer));
239 });
240
241 // Ask the platform view for the vsync waiter. This will be used by the engine
242 // to create the animator.
243 auto vsync_waiter = platform_view->CreateVSyncWaiter();
244 if (!vsync_waiter) {
245 return nullptr;
246 }
247
248 // Create the IO manager on the IO thread. The IO manager must be initialized
249 // first because it has state that the other subsystems depend on. It must
250 // first be booted and the necessary references obtained to initialize the
251 // other subsystems.
252 std::promise<std::shared_ptr<ShellIOManager>> io_manager_promise;
253 auto io_manager_future = io_manager_promise.get_future();
254 std::promise<fml::WeakPtr<ShellIOManager>> weak_io_manager_promise;
255 auto weak_io_manager_future = weak_io_manager_promise.get_future();
256 std::promise<fml::RefPtr<SkiaUnrefQueue>> unref_queue_promise;
257 auto unref_queue_future = unref_queue_promise.get_future();
258 auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
259
260 // The platform_view will be stored into shell's platform_view_ in
261 // shell->Setup(std::move(platform_view), ...) at the end.
262 PlatformView* platform_view_ptr = platform_view.get();
263 fml::TaskRunner::RunNowOrPostTask(
264 runner: io_task_runner,
265 task: [&io_manager_promise, //
266 &weak_io_manager_promise, //
267 &parent_io_manager, //
268 &unref_queue_promise, //
269 platform_view_ptr, //
270 io_task_runner, //
271 is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() //
272 ]() {
273 TRACE_EVENT0("flutter", "ShellSetupIOSubsystem");
274 std::shared_ptr<ShellIOManager> io_manager;
275 if (parent_io_manager) {
276 io_manager = parent_io_manager;
277 } else {
278 io_manager = std::make_shared<ShellIOManager>(
279 args: platform_view_ptr->CreateResourceContext(), // resource context
280 args: is_backgrounded_sync_switch, // sync switch
281 args: io_task_runner, // unref queue task runner
282 args: platform_view_ptr->GetImpellerContext() // impeller context
283 );
284 }
285 weak_io_manager_promise.set_value(io_manager->GetWeakPtr());
286 unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue());
287 io_manager_promise.set_value(io_manager);
288 });
289
290 // Send dispatcher_maker to the engine constructor because shell won't have
291 // platform_view set until Shell::Setup is called later.
292 auto dispatcher_maker = platform_view->GetDispatcherMaker();
293
294 // Create the engine on the UI thread.
295 std::promise<std::unique_ptr<Engine>> engine_promise;
296 auto engine_future = engine_promise.get_future();
297 fml::TaskRunner::RunNowOrPostTask(
298 runner: shell->GetTaskRunners().GetUITaskRunner(),
299 task: fml::MakeCopyable(lambda: [&engine_promise, //
300 shell = shell.get(), //
301 &dispatcher_maker, //
302 &platform_data, //
303 isolate_snapshot = std::move(isolate_snapshot), //
304 vsync_waiter = std::move(vsync_waiter), //
305 &weak_io_manager_future, //
306 &snapshot_delegate_future, //
307 &unref_queue_future, //
308 &on_create_engine]() mutable {
309 TRACE_EVENT0("flutter", "ShellSetupUISubsystem");
310 const auto& task_runners = shell->GetTaskRunners();
311
312 // The animator is owned by the UI thread but it gets its vsync pulses
313 // from the platform.
314 auto animator = std::make_unique<Animator>(args&: *shell, args: task_runners,
315 args: std::move(vsync_waiter));
316
317 engine_promise.set_value(
318 on_create_engine(*shell, //
319 dispatcher_maker, //
320 *shell->GetDartVM(), //
321 std::move(isolate_snapshot), //
322 task_runners, //
323 platform_data, //
324 shell->GetSettings(), //
325 std::move(animator), //
326 weak_io_manager_future.get(), //
327 unref_queue_future.get(), //
328 snapshot_delegate_future.get(), //
329 shell->volatile_path_tracker_,
330 shell->is_gpu_disabled_sync_switch_));
331 }));
332
333 if (!shell->Setup(platform_view: std::move(platform_view), //
334 engine: engine_future.get(), //
335 rasterizer: rasterizer_future.get(), //
336 io_manager: io_manager_future.get()) //
337 ) {
338 return nullptr;
339 }
340
341 return shell;
342}
343
344std::unique_ptr<Shell> Shell::CreateWithSnapshot(
345 const PlatformData& platform_data,
346 const TaskRunners& task_runners,
347 const fml::RefPtr<fml::RasterThreadMerger>& parent_thread_merger,
348 const std::shared_ptr<ShellIOManager>& parent_io_manager,
349 const std::shared_ptr<ResourceCacheLimitCalculator>&
350 resource_cache_limit_calculator,
351 Settings settings,
352 DartVMRef vm,
353 fml::RefPtr<const DartSnapshot> isolate_snapshot,
354 const Shell::CreateCallback<PlatformView>& on_create_platform_view,
355 const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
356 const Shell::EngineCreateCallback& on_create_engine,
357 bool is_gpu_disabled) {
358 // This must come first as it initializes tracing.
359 PerformInitializationTasks(settings);
360
361 TRACE_EVENT0("flutter", "Shell::CreateWithSnapshot");
362
363 const bool callbacks_valid =
364 on_create_platform_view && on_create_rasterizer && on_create_engine;
365 if (!task_runners.IsValid() || !callbacks_valid) {
366 return nullptr;
367 }
368
369 fml::AutoResetWaitableEvent latch;
370 std::unique_ptr<Shell> shell;
371 auto platform_task_runner = task_runners.GetPlatformTaskRunner();
372 fml::TaskRunner::RunNowOrPostTask(
373 runner: platform_task_runner,
374 task: fml::MakeCopyable(lambda: [&latch, //
375 &shell, //
376 parent_thread_merger, //
377 parent_io_manager, //
378 resource_cache_limit_calculator, //
379 task_runners = task_runners, //
380 platform_data = platform_data, //
381 settings = settings, //
382 vm = std::move(vm), //
383 isolate_snapshot = std::move(isolate_snapshot), //
384 on_create_platform_view = on_create_platform_view, //
385 on_create_rasterizer = on_create_rasterizer, //
386 on_create_engine = on_create_engine,
387 is_gpu_disabled]() mutable {
388 shell = CreateShellOnPlatformThread(vm: std::move(vm), //
389 parent_merger: parent_thread_merger, //
390 parent_io_manager, //
391 resource_cache_limit_calculator, //
392 task_runners, //
393 platform_data, //
394 settings, //
395 isolate_snapshot: std::move(isolate_snapshot), //
396 on_create_platform_view, //
397 on_create_rasterizer, //
398 on_create_engine, is_gpu_disabled);
399 latch.Signal();
400 }));
401 latch.Wait();
402 return shell;
403}
404
405Shell::Shell(DartVMRef vm,
406 const TaskRunners& task_runners,
407 fml::RefPtr<fml::RasterThreadMerger> parent_merger,
408 const std::shared_ptr<ResourceCacheLimitCalculator>&
409 resource_cache_limit_calculator,
410 const Settings& settings,
411 std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
412 bool is_gpu_disabled)
413 : task_runners_(task_runners),
414 parent_raster_thread_merger_(std::move(parent_merger)),
415 resource_cache_limit_calculator_(resource_cache_limit_calculator),
416 settings_(settings),
417 vm_(std::move(vm)),
418 is_gpu_disabled_sync_switch_(new fml::SyncSwitch(is_gpu_disabled)),
419 volatile_path_tracker_(std::move(volatile_path_tracker)),
420 weak_factory_gpu_(nullptr),
421 weak_factory_(this) {
422 FML_CHECK(!settings.enable_software_rendering || !settings.enable_impeller)
423 << "Software rendering is incompatible with Impeller.";
424 FML_CHECK(vm_) << "Must have access to VM to create a shell.";
425 FML_DCHECK(task_runners_.IsValid());
426 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
427
428 display_manager_ = std::make_unique<DisplayManager>();
429 resource_cache_limit_calculator->AddResourceCacheLimitItem(
430 item: weak_factory_.GetWeakPtr());
431
432 // Generate a WeakPtrFactory for use with the raster thread. This does not
433 // need to wait on a latch because it can only ever be used from the raster
434 // thread from this class, so we have ordering guarantees.
435 fml::TaskRunner::RunNowOrPostTask(
436 runner: task_runners_.GetRasterTaskRunner(), task: fml::MakeCopyable(lambda: [this]() mutable {
437 this->weak_factory_gpu_ =
438 std::make_unique<fml::TaskRunnerAffineWeakPtrFactory<Shell>>(args: this);
439 }));
440
441 // Install service protocol handlers.
442
443 service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName] = {
444 task_runners_.GetRasterTaskRunner(),
445 std::bind(f: &Shell::OnServiceProtocolScreenshot, bound_args: this,
446 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
447 service_protocol_handlers_[ServiceProtocol::kScreenshotSkpExtensionName] = {
448 task_runners_.GetRasterTaskRunner(),
449 std::bind(f: &Shell::OnServiceProtocolScreenshotSKP, bound_args: this,
450 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
451 service_protocol_handlers_[ServiceProtocol::kRunInViewExtensionName] = {
452 task_runners_.GetUITaskRunner(),
453 std::bind(f: &Shell::OnServiceProtocolRunInView, bound_args: this, bound_args: std::placeholders::_1,
454 bound_args: std::placeholders::_2)};
455 service_protocol_handlers_
456 [ServiceProtocol::kFlushUIThreadTasksExtensionName] = {
457 task_runners_.GetUITaskRunner(),
458 std::bind(f: &Shell::OnServiceProtocolFlushUIThreadTasks, bound_args: this,
459 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
460 service_protocol_handlers_
461 [ServiceProtocol::kSetAssetBundlePathExtensionName] = {
462 task_runners_.GetUITaskRunner(),
463 std::bind(f: &Shell::OnServiceProtocolSetAssetBundlePath, bound_args: this,
464 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
465 service_protocol_handlers_
466 [ServiceProtocol::kGetDisplayRefreshRateExtensionName] = {
467 task_runners_.GetUITaskRunner(),
468 std::bind(f: &Shell::OnServiceProtocolGetDisplayRefreshRate, bound_args: this,
469 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
470 service_protocol_handlers_[ServiceProtocol::kGetSkSLsExtensionName] = {
471 task_runners_.GetIOTaskRunner(),
472 std::bind(f: &Shell::OnServiceProtocolGetSkSLs, bound_args: this, bound_args: std::placeholders::_1,
473 bound_args: std::placeholders::_2)};
474 service_protocol_handlers_
475 [ServiceProtocol::kEstimateRasterCacheMemoryExtensionName] = {
476 task_runners_.GetRasterTaskRunner(),
477 std::bind(f: &Shell::OnServiceProtocolEstimateRasterCacheMemory, bound_args: this,
478 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
479 service_protocol_handlers_
480 [ServiceProtocol::kRenderFrameWithRasterStatsExtensionName] = {
481 task_runners_.GetRasterTaskRunner(),
482 std::bind(f: &Shell::OnServiceProtocolRenderFrameWithRasterStats, bound_args: this,
483 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
484 service_protocol_handlers_[ServiceProtocol::kReloadAssetFonts] = {
485 task_runners_.GetPlatformTaskRunner(),
486 std::bind(f: &Shell::OnServiceProtocolReloadAssetFonts, bound_args: this,
487 bound_args: std::placeholders::_1, bound_args: std::placeholders::_2)};
488}
489
490Shell::~Shell() {
491 PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner(
492 task_runner: task_runners_.GetIOTaskRunner());
493
494 vm_->GetServiceProtocol()->RemoveHandler(handler: this);
495
496 fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch;
497
498 fml::TaskRunner::RunNowOrPostTask(
499 runner: task_runners_.GetUITaskRunner(),
500 task: fml::MakeCopyable(lambda: [this, &ui_latch]() mutable {
501 engine_.reset();
502 ui_latch.Signal();
503 }));
504 ui_latch.Wait();
505
506 fml::TaskRunner::RunNowOrPostTask(
507 runner: task_runners_.GetRasterTaskRunner(),
508 task: fml::MakeCopyable(
509 lambda: [this, rasterizer = std::move(rasterizer_), &gpu_latch]() mutable {
510 rasterizer.reset();
511 this->weak_factory_gpu_.reset();
512 gpu_latch.Signal();
513 }));
514 gpu_latch.Wait();
515
516 fml::TaskRunner::RunNowOrPostTask(
517 runner: task_runners_.GetIOTaskRunner(),
518 task: fml::MakeCopyable(lambda: [io_manager = std::move(io_manager_),
519 platform_view = platform_view_.get(),
520 &io_latch]() mutable {
521 io_manager.reset();
522 if (platform_view) {
523 platform_view->ReleaseResourceContext();
524 }
525 io_latch.Signal();
526 }));
527
528 io_latch.Wait();
529
530 // The platform view must go last because it may be holding onto platform side
531 // counterparts to resources owned by subsystems running on other threads. For
532 // example, the NSOpenGLContext on the Mac.
533 fml::TaskRunner::RunNowOrPostTask(
534 runner: task_runners_.GetPlatformTaskRunner(),
535 task: fml::MakeCopyable(lambda: [platform_view = std::move(platform_view_),
536 &platform_latch]() mutable {
537 platform_view.reset();
538 platform_latch.Signal();
539 }));
540 platform_latch.Wait();
541}
542
543std::unique_ptr<Shell> Shell::Spawn(
544 RunConfiguration run_configuration,
545 const std::string& initial_route,
546 const CreateCallback<PlatformView>& on_create_platform_view,
547 const CreateCallback<Rasterizer>& on_create_rasterizer) const {
548 FML_DCHECK(task_runners_.IsValid());
549 // It's safe to store this value since it is set on the platform thread.
550 bool is_gpu_disabled = false;
551 GetIsGpuDisabledSyncSwitch()->Execute(
552 handlers: fml::SyncSwitch::Handlers()
553 .SetIfFalse([&is_gpu_disabled] { is_gpu_disabled = false; })
554 .SetIfTrue([&is_gpu_disabled] { is_gpu_disabled = true; }));
555 std::unique_ptr<Shell> result = CreateWithSnapshot(
556 platform_data: PlatformData{}, task_runners: task_runners_, parent_thread_merger: rasterizer_->GetRasterThreadMerger(),
557 parent_io_manager: io_manager_, resource_cache_limit_calculator: resource_cache_limit_calculator_, settings: GetSettings(), vm: vm_,
558 isolate_snapshot: vm_->GetVMData()->GetIsolateSnapshot(), on_create_platform_view,
559 on_create_rasterizer,
560 on_create_engine: [engine = this->engine_.get(), initial_route](
561 Engine::Delegate& delegate,
562 const PointerDataDispatcherMaker& dispatcher_maker, DartVM& vm,
563 const fml::RefPtr<const DartSnapshot>& isolate_snapshot,
564 const TaskRunners& task_runners, const PlatformData& platform_data,
565 const Settings& settings, std::unique_ptr<Animator> animator,
566 const fml::WeakPtr<IOManager>& io_manager,
567 const fml::RefPtr<SkiaUnrefQueue>& unref_queue,
568 fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate,
569 const std::shared_ptr<VolatilePathTracker>& volatile_path_tracker,
570 const std::shared_ptr<fml::SyncSwitch>& is_gpu_disabled_sync_switch) {
571 return engine->Spawn(
572 /*delegate=*/delegate,
573 /*dispatcher_maker=*/dispatcher_maker,
574 /*settings=*/settings,
575 /*animator=*/std::move(animator),
576 /*initial_route=*/initial_route,
577 /*io_manager=*/io_manager,
578 /*snapshot_delegate=*/std::move(snapshot_delegate),
579 /*gpu_disabled_switch=*/is_gpu_disabled_sync_switch);
580 },
581 is_gpu_disabled);
582 result->RunEngine(run_configuration: std::move(run_configuration));
583 return result;
584}
585
586void Shell::NotifyLowMemoryWarning() const {
587 auto trace_id = fml::tracing::TraceNonce();
588 TRACE_EVENT_ASYNC_BEGIN0("flutter", "Shell::NotifyLowMemoryWarning",
589 trace_id);
590 // This does not require a current isolate but does require a running VM.
591 // Since a valid shell will not be returned to the embedder without a valid
592 // DartVMRef, we can be certain that this is a safe spot to assume a VM is
593 // running.
594 ::Dart_NotifyLowMemory();
595
596 task_runners_.GetRasterTaskRunner()->PostTask(
597 task: [rasterizer = rasterizer_->GetWeakPtr(), trace_id = trace_id]() {
598 if (rasterizer) {
599 rasterizer->NotifyLowMemoryWarning();
600 }
601 TRACE_EVENT_ASYNC_END0("flutter", "Shell::NotifyLowMemoryWarning",
602 trace_id);
603 });
604 // The IO Manager uses resource cache limits of 0, so it is not necessary
605 // to purge them.
606}
607
608void Shell::RunEngine(RunConfiguration run_configuration) {
609 RunEngine(run_configuration: std::move(run_configuration), result_callback: nullptr);
610}
611
612void Shell::RunEngine(
613 RunConfiguration run_configuration,
614 const std::function<void(Engine::RunStatus)>& result_callback) {
615 auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(),
616 result_callback](Engine::RunStatus run_result) {
617 if (!result_callback) {
618 return;
619 }
620 platform_runner->PostTask(
621 task: [result_callback, run_result]() { result_callback(run_result); });
622 };
623 FML_DCHECK(is_set_up_);
624 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
625
626 fml::TaskRunner::RunNowOrPostTask(
627 runner: task_runners_.GetUITaskRunner(),
628 task: fml::MakeCopyable(
629 lambda: [run_configuration = std::move(run_configuration),
630 weak_engine = weak_engine_, result]() mutable {
631 if (!weak_engine) {
632 FML_LOG(ERROR)
633 << "Could not launch engine with configuration - no engine.";
634 result(Engine::RunStatus::Failure);
635 return;
636 }
637 auto run_result = weak_engine->Run(configuration: std::move(run_configuration));
638 if (run_result == flutter::Engine::RunStatus::Failure) {
639 FML_LOG(ERROR) << "Could not launch engine with configuration.";
640 }
641
642 result(run_result);
643 }));
644}
645
646std::optional<DartErrorCode> Shell::GetUIIsolateLastError() const {
647 FML_DCHECK(is_set_up_);
648 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
649
650 if (!weak_engine_) {
651 return std::nullopt;
652 }
653 switch (weak_engine_->GetUIIsolateLastError()) {
654 case tonic::kCompilationErrorType:
655 return DartErrorCode::CompilationError;
656 case tonic::kApiErrorType:
657 return DartErrorCode::ApiError;
658 case tonic::kUnknownErrorType:
659 return DartErrorCode::UnknownError;
660 case tonic::kNoError:
661 return DartErrorCode::NoError;
662 }
663 return DartErrorCode::UnknownError;
664}
665
666bool Shell::EngineHasLivePorts() const {
667 FML_DCHECK(is_set_up_);
668 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
669
670 if (!weak_engine_) {
671 return false;
672 }
673
674 return weak_engine_->UIIsolateHasLivePorts();
675}
676
677bool Shell::IsSetup() const {
678 return is_set_up_;
679}
680
681bool Shell::Setup(std::unique_ptr<PlatformView> platform_view,
682 std::unique_ptr<Engine> engine,
683 std::unique_ptr<Rasterizer> rasterizer,
684 const std::shared_ptr<ShellIOManager>& io_manager) {
685 if (is_set_up_) {
686 return false;
687 }
688
689 if (!platform_view || !engine || !rasterizer || !io_manager) {
690 return false;
691 }
692
693 platform_view_ = std::move(platform_view);
694 platform_message_handler_ = platform_view_->GetPlatformMessageHandler();
695 route_messages_through_platform_thread_.store(d: true);
696 task_runners_.GetPlatformTaskRunner()->PostTask(
697 task: [self = weak_factory_.GetWeakPtr()] {
698 if (self) {
699 self->route_messages_through_platform_thread_.store(d: false);
700 }
701 });
702 engine_ = std::move(engine);
703 rasterizer_ = std::move(rasterizer);
704 io_manager_ = io_manager;
705
706 // Set the external view embedder for the rasterizer.
707 auto view_embedder = platform_view_->CreateExternalViewEmbedder();
708 rasterizer_->SetExternalViewEmbedder(view_embedder);
709 rasterizer_->SetSnapshotSurfaceProducer(
710 platform_view_->CreateSnapshotSurfaceProducer());
711
712 // The weak ptr must be generated in the platform thread which owns the unique
713 // ptr.
714 weak_engine_ = engine_->GetWeakPtr();
715 weak_rasterizer_ = rasterizer_->GetWeakPtr();
716 weak_platform_view_ = platform_view_->GetWeakPtr();
717
718 // Setup the time-consuming default font manager right after engine created.
719 if (!settings_.prefetched_default_font_manager) {
720 fml::TaskRunner::RunNowOrPostTask(runner: task_runners_.GetUITaskRunner(),
721 task: [engine = weak_engine_] {
722 if (engine) {
723 engine->SetupDefaultFontManager();
724 }
725 });
726 }
727
728 is_set_up_ = true;
729
730 PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner(
731 task_runner: task_runners_.GetIOTaskRunner());
732
733 PersistentCache::GetCacheForProcess()->SetIsDumpingSkp(
734 settings_.dump_skp_on_shader_compilation);
735
736 if (settings_.purge_persistent_cache) {
737 PersistentCache::GetCacheForProcess()->Purge();
738 }
739
740 return true;
741}
742
743const Settings& Shell::GetSettings() const {
744 return settings_;
745}
746
747const TaskRunners& Shell::GetTaskRunners() const {
748 return task_runners_;
749}
750
751const fml::RefPtr<fml::RasterThreadMerger> Shell::GetParentRasterThreadMerger()
752 const {
753 return parent_raster_thread_merger_;
754}
755
756fml::TaskRunnerAffineWeakPtr<Rasterizer> Shell::GetRasterizer() const {
757 FML_DCHECK(is_set_up_);
758 return weak_rasterizer_;
759}
760
761fml::WeakPtr<Engine> Shell::GetEngine() {
762 FML_DCHECK(is_set_up_);
763 return weak_engine_;
764}
765
766fml::WeakPtr<PlatformView> Shell::GetPlatformView() {
767 FML_DCHECK(is_set_up_);
768 return weak_platform_view_;
769}
770
771fml::WeakPtr<ShellIOManager> Shell::GetIOManager() {
772 FML_DCHECK(is_set_up_);
773 return io_manager_->GetWeakPtr();
774}
775
776DartVM* Shell::GetDartVM() {
777 return &vm_;
778}
779
780// |PlatformView::Delegate|
781void Shell::OnPlatformViewCreated(std::unique_ptr<Surface> surface) {
782 TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated");
783 FML_DCHECK(is_set_up_);
784 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
785
786 // Prevent any request to change the thread configuration for raster and
787 // platform queues while the platform view is being created.
788 //
789 // This prevents false positives such as this method starts assuming that the
790 // raster and platform queues have a given thread configuration, but then the
791 // configuration is changed by a task, and the assumption is no longer true.
792 //
793 // This incorrect assumption can lead to deadlock.
794 // See `should_post_raster_task` for more.
795 rasterizer_->DisableThreadMergerIfNeeded();
796
797 // The normal flow executed by this method is that the platform thread is
798 // starting the sequence and waiting on the latch. Later the UI thread posts
799 // raster_task to the raster thread which signals the latch. If the raster and
800 // the platform threads are the same this results in a deadlock as the
801 // raster_task will never be posted to the platform/raster thread that is
802 // blocked on a latch. To avoid the described deadlock, if the raster and the
803 // platform threads are the same, should_post_raster_task will be false, and
804 // then instead of posting a task to the raster thread, the ui thread just
805 // signals the latch and the platform/raster thread follows with executing
806 // raster_task.
807 const bool should_post_raster_task =
808 !task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread();
809
810 fml::AutoResetWaitableEvent latch;
811 auto raster_task =
812 fml::MakeCopyable(lambda: [&waiting_for_first_frame = waiting_for_first_frame_,
813 rasterizer = rasterizer_->GetWeakPtr(), //
814 surface = std::move(surface)]() mutable {
815 if (rasterizer) {
816 // Enables the thread merger which may be used by the external view
817 // embedder.
818 rasterizer->EnableThreadMergerIfNeeded();
819 rasterizer->Setup(std::move(surface));
820 }
821
822 waiting_for_first_frame.store(d: true);
823 });
824
825 auto ui_task = [engine = engine_->GetWeakPtr()] {
826 if (engine) {
827 engine->ScheduleFrame();
828 }
829 };
830
831 // Threading: Capture platform view by raw pointer and not the weak pointer.
832 // We are going to use the pointer on the IO thread which is not safe with a
833 // weak pointer. However, we are preventing the platform view from being
834 // collected by using a latch.
835 auto* platform_view = platform_view_.get();
836
837 FML_DCHECK(platform_view);
838
839 auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view,
840 ui_task_runner = task_runners_.GetUITaskRunner(), ui_task,
841 raster_task_runner = task_runners_.GetRasterTaskRunner(),
842 raster_task, should_post_raster_task, &latch] {
843 if (io_manager && !io_manager->GetResourceContext()) {
844 sk_sp<GrDirectContext> resource_context =
845 platform_view->CreateResourceContext();
846 io_manager->NotifyResourceContextAvailable(resource_context);
847 }
848 // Step 1: Post a task on the UI thread to tell the engine that it has
849 // an output surface.
850 fml::TaskRunner::RunNowOrPostTask(runner: ui_task_runner, task: ui_task);
851
852 // Step 2: Tell the raster thread that it should create a surface for
853 // its rasterizer.
854 if (should_post_raster_task) {
855 fml::TaskRunner::RunNowOrPostTask(runner: raster_task_runner, task: raster_task);
856 }
857 latch.Signal();
858 };
859
860 fml::TaskRunner::RunNowOrPostTask(runner: task_runners_.GetIOTaskRunner(), task: io_task);
861
862 latch.Wait();
863 if (!should_post_raster_task) {
864 // See comment on should_post_raster_task, in this case the raster_task
865 // wasn't executed, and we just run it here as the platform thread
866 // is the raster thread.
867 raster_task();
868 }
869}
870
871// |PlatformView::Delegate|
872void Shell::OnPlatformViewDestroyed() {
873 TRACE_EVENT0("flutter", "Shell::OnPlatformViewDestroyed");
874 FML_DCHECK(is_set_up_);
875 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
876
877 // Prevent any request to change the thread configuration for raster and
878 // platform queues while the platform view is being destroyed.
879 //
880 // This prevents false positives such as this method starts assuming that the
881 // raster and platform queues have a given thread configuration, but then the
882 // configuration is changed by a task, and the assumption is no longer true.
883 //
884 // This incorrect assumption can lead to deadlock.
885 rasterizer_->DisableThreadMergerIfNeeded();
886
887 // Notify the Dart VM that the PlatformView has been destroyed and some
888 // cleanup activity can be done (e.g: garbage collect the Dart heap).
889 task_runners_.GetUITaskRunner()->PostTask(task: [engine = engine_->GetWeakPtr()]() {
890 if (engine) {
891 engine->NotifyDestroyed();
892 }
893 });
894
895 // Note:
896 // This is a synchronous operation because certain platforms depend on
897 // setup/suspension of all activities that may be interacting with the GPU in
898 // a synchronous fashion.
899 // The UI thread does not need to be serialized here - there is sufficient
900 // guardrailing in the rasterizer to allow the UI thread to post work to it
901 // even after the surface has been torn down.
902
903 fml::AutoResetWaitableEvent latch;
904
905 auto io_task = [io_manager = io_manager_.get(), &latch]() {
906 // Execute any pending Skia object deletions while GPU access is still
907 // allowed.
908 io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
909 handlers: fml::SyncSwitch::Handlers().SetIfFalse(
910 [&] { io_manager->GetSkiaUnrefQueue()->Drain(); }));
911 // Step 4: All done. Signal the latch that the platform thread is waiting
912 // on.
913 latch.Signal();
914 };
915
916 auto raster_task = [rasterizer = rasterizer_->GetWeakPtr(),
917 io_task_runner = task_runners_.GetIOTaskRunner(),
918 io_task]() {
919 if (rasterizer) {
920 // Enables the thread merger which is required prior tearing down the
921 // rasterizer. If the raster and platform threads are merged, tearing down
922 // the rasterizer unmerges the threads.
923 rasterizer->EnableThreadMergerIfNeeded();
924 rasterizer->Teardown();
925 }
926 // Step 2: Tell the IO thread to complete its remaining work.
927 fml::TaskRunner::RunNowOrPostTask(runner: io_task_runner, task: io_task);
928 };
929
930 // Step 1: Post a task to the Raster thread (possibly this thread) to tell the
931 // rasterizer the output surface is going away.
932 fml::TaskRunner::RunNowOrPostTask(runner: task_runners_.GetRasterTaskRunner(),
933 task: raster_task);
934 latch.Wait();
935 // On Android, the external view embedder may post a task to the platform
936 // thread, and wait until it completes if overlay surfaces must be released.
937 // However, the platform thread might be blocked when Dart is initializing.
938 // In this situation, calling TeardownExternalViewEmbedder is safe because no
939 // platform views have been created before Flutter renders the first frame.
940 // Overall, the longer term plan is to remove this implementation once
941 // https://github.com/flutter/flutter/issues/96679 is fixed.
942 rasterizer_->TeardownExternalViewEmbedder();
943}
944
945// |PlatformView::Delegate|
946void Shell::OnPlatformViewScheduleFrame() {
947 TRACE_EVENT0("flutter", "Shell::OnPlatformViewScheduleFrame");
948 FML_DCHECK(is_set_up_);
949 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
950
951 task_runners_.GetUITaskRunner()->PostTask(task: [engine = engine_->GetWeakPtr()]() {
952 if (engine) {
953 engine->ScheduleFrame();
954 }
955 });
956}
957
958// |PlatformView::Delegate|
959void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id,
960 const ViewportMetrics& metrics) {
961 FML_DCHECK(is_set_up_);
962 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
963
964 if (metrics.device_pixel_ratio <= 0 || metrics.physical_width <= 0 ||
965 metrics.physical_height <= 0) {
966 // Ignore invalid view-port metrics.
967 return;
968 }
969
970 // This is the formula Android uses.
971 // https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45
972 resource_cache_limit_ =
973 metrics.physical_width * metrics.physical_height * 12 * 4;
974 size_t resource_cache_max_bytes =
975 resource_cache_limit_calculator_->GetResourceCacheMaxBytes();
976 task_runners_.GetRasterTaskRunner()->PostTask(
977 task: [rasterizer = rasterizer_->GetWeakPtr(), resource_cache_max_bytes] {
978 if (rasterizer) {
979 rasterizer->SetResourceCacheMaxBytes(max_bytes: resource_cache_max_bytes, from_user: false);
980 }
981 });
982
983 task_runners_.GetUITaskRunner()->PostTask(
984 task: [engine = engine_->GetWeakPtr(), view_id, metrics]() {
985 if (engine) {
986 engine->SetViewportMetrics(view_id, metrics);
987 }
988 });
989
990 {
991 std::scoped_lock<std::mutex> lock(resize_mutex_);
992 expected_frame_size_ =
993 SkISize::Make(w: metrics.physical_width, h: metrics.physical_height);
994 device_pixel_ratio_ = metrics.device_pixel_ratio;
995 }
996}
997
998// |PlatformView::Delegate|
999void Shell::OnPlatformViewDispatchPlatformMessage(
1000 std::unique_ptr<PlatformMessage> message) {
1001 FML_DCHECK(is_set_up_);
1002#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
1003 if (!task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()) {
1004 std::scoped_lock lock(misbehaving_message_channels_mutex_);
1005 auto inserted = misbehaving_message_channels_.insert(v: message->channel());
1006 if (inserted.second) {
1007 FML_LOG(ERROR)
1008 << "The '" << message->channel()
1009 << "' channel sent a message from native to Flutter on a "
1010 "non-platform thread. Platform channel messages must be sent on "
1011 "the platform thread. Failure to do so may result in data loss or "
1012 "crashes, and must be fixed in the plugin or application code "
1013 "creating that channel.\n"
1014 "See https://docs.flutter.dev/platform-integration/"
1015 "platform-channels#channels-and-platform-threading for more "
1016 "information.";
1017 }
1018 }
1019#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
1020
1021 // The static leak checker gets confused by the use of fml::MakeCopyable.
1022 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
1023 task_runners_.GetUITaskRunner()->PostTask(task: fml::MakeCopyable(
1024 lambda: [engine = engine_->GetWeakPtr(), message = std::move(message)]() mutable {
1025 if (engine) {
1026 engine->DispatchPlatformMessage(message: std::move(message));
1027 }
1028 }));
1029}
1030
1031// |PlatformView::Delegate|
1032void Shell::OnPlatformViewDispatchPointerDataPacket(
1033 std::unique_ptr<PointerDataPacket> packet) {
1034 TRACE_EVENT0_WITH_FLOW_IDS(
1035 "flutter", "Shell::OnPlatformViewDispatchPointerDataPacket",
1036 /*flow_id_count=*/1, /*flow_ids=*/&next_pointer_flow_id_);
1037 TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_);
1038 FML_DCHECK(is_set_up_);
1039 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1040 task_runners_.GetUITaskRunner()->PostTask(
1041 task: fml::MakeCopyable(lambda: [engine = weak_engine_, packet = std::move(packet),
1042 flow_id = next_pointer_flow_id_]() mutable {
1043 if (engine) {
1044 engine->DispatchPointerDataPacket(packet: std::move(packet), trace_flow_id: flow_id);
1045 }
1046 }));
1047 next_pointer_flow_id_++;
1048}
1049
1050// |PlatformView::Delegate|
1051void Shell::OnPlatformViewDispatchSemanticsAction(int32_t node_id,
1052 SemanticsAction action,
1053 fml::MallocMapping args) {
1054 FML_DCHECK(is_set_up_);
1055 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1056
1057 task_runners_.GetUITaskRunner()->PostTask(
1058 task: fml::MakeCopyable(lambda: [engine = engine_->GetWeakPtr(), node_id, action,
1059 args = std::move(args)]() mutable {
1060 if (engine) {
1061 engine->DispatchSemanticsAction(node_id, action, args: std::move(args));
1062 }
1063 }));
1064}
1065
1066// |PlatformView::Delegate|
1067void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) {
1068 FML_DCHECK(is_set_up_);
1069 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1070
1071 task_runners_.GetUITaskRunner()->PostTask(
1072 task: [engine = engine_->GetWeakPtr(), enabled] {
1073 if (engine) {
1074 engine->SetSemanticsEnabled(enabled);
1075 }
1076 });
1077}
1078
1079// |PlatformView::Delegate|
1080void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) {
1081 FML_DCHECK(is_set_up_);
1082 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1083
1084 task_runners_.GetUITaskRunner()->PostTask(
1085 task: [engine = engine_->GetWeakPtr(), flags] {
1086 if (engine) {
1087 engine->SetAccessibilityFeatures(flags);
1088 }
1089 });
1090}
1091
1092// |PlatformView::Delegate|
1093void Shell::OnPlatformViewRegisterTexture(
1094 std::shared_ptr<flutter::Texture> texture) {
1095 FML_DCHECK(is_set_up_);
1096 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1097
1098 task_runners_.GetRasterTaskRunner()->PostTask(
1099 task: [rasterizer = rasterizer_->GetWeakPtr(), texture] {
1100 if (rasterizer) {
1101 if (auto registry = rasterizer->GetTextureRegistry()) {
1102 registry->RegisterTexture(texture);
1103 }
1104 }
1105 });
1106}
1107
1108// |PlatformView::Delegate|
1109void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) {
1110 FML_DCHECK(is_set_up_);
1111 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1112
1113 task_runners_.GetRasterTaskRunner()->PostTask(
1114 task: [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() {
1115 if (rasterizer) {
1116 if (auto registry = rasterizer->GetTextureRegistry()) {
1117 registry->UnregisterTexture(id: texture_id);
1118 }
1119 }
1120 });
1121}
1122
1123// |PlatformView::Delegate|
1124void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) {
1125 FML_DCHECK(is_set_up_);
1126 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1127
1128 // Tell the rasterizer that one of its textures has a new frame available.
1129 task_runners_.GetRasterTaskRunner()->PostTask(
1130 task: [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() {
1131 auto registry = rasterizer->GetTextureRegistry();
1132
1133 if (!registry) {
1134 return;
1135 }
1136
1137 auto texture = registry->GetTexture(id: texture_id);
1138
1139 if (!texture) {
1140 return;
1141 }
1142
1143 texture->MarkNewFrameAvailable();
1144 });
1145
1146 // Schedule a new frame without having to rebuild the layer tree.
1147 task_runners_.GetUITaskRunner()->PostTask(task: [engine = engine_->GetWeakPtr()]() {
1148 if (engine) {
1149 engine->ScheduleFrame(regenerate_layer_tree: false);
1150 }
1151 });
1152}
1153
1154// |PlatformView::Delegate|
1155void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {
1156 FML_DCHECK(is_set_up_);
1157 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1158
1159 task_runners_.GetRasterTaskRunner()->PostTask(
1160 task: [rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() {
1161 if (rasterizer) {
1162 rasterizer->SetNextFrameCallback(closure);
1163 }
1164 });
1165}
1166
1167// |PlatformView::Delegate|
1168const Settings& Shell::OnPlatformViewGetSettings() const {
1169 return settings_;
1170}
1171
1172// |Animator::Delegate|
1173void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
1174 uint64_t frame_number) {
1175 FML_DCHECK(is_set_up_);
1176 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1177
1178 // record the target time for use by rasterizer.
1179 {
1180 std::scoped_lock time_recorder_lock(time_recorder_mutex_);
1181 latest_frame_target_time_.emplace(args&: frame_target_time);
1182 }
1183 if (engine_) {
1184 engine_->BeginFrame(frame_time: frame_target_time, frame_number);
1185 }
1186}
1187
1188// |Animator::Delegate|
1189void Shell::OnAnimatorNotifyIdle(fml::TimeDelta deadline) {
1190 FML_DCHECK(is_set_up_);
1191 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1192
1193 if (engine_) {
1194 engine_->NotifyIdle(deadline);
1195 volatile_path_tracker_->OnFrame();
1196 }
1197}
1198
1199void Shell::OnAnimatorUpdateLatestFrameTargetTime(
1200 fml::TimePoint frame_target_time) {
1201 FML_DCHECK(is_set_up_);
1202
1203 // record the target time for use by rasterizer.
1204 {
1205 std::scoped_lock time_recorder_lock(time_recorder_mutex_);
1206 if (!latest_frame_target_time_) {
1207 latest_frame_target_time_ = frame_target_time;
1208 } else if (latest_frame_target_time_ < frame_target_time) {
1209 latest_frame_target_time_ = frame_target_time;
1210 }
1211 }
1212}
1213
1214// |Animator::Delegate|
1215void Shell::OnAnimatorDraw(std::shared_ptr<LayerTreePipeline> pipeline) {
1216 FML_DCHECK(is_set_up_);
1217
1218 auto discard_callback = [this](flutter::LayerTree& tree) {
1219 std::scoped_lock<std::mutex> lock(resize_mutex_);
1220 return !expected_frame_size_.isEmpty() &&
1221 tree.frame_size() != expected_frame_size_;
1222 };
1223
1224 task_runners_.GetRasterTaskRunner()->PostTask(task: fml::MakeCopyable(
1225 lambda: [&waiting_for_first_frame = waiting_for_first_frame_,
1226 &waiting_for_first_frame_condition = waiting_for_first_frame_condition_,
1227 rasterizer = rasterizer_->GetWeakPtr(),
1228 weak_pipeline = std::weak_ptr<LayerTreePipeline>(pipeline),
1229 discard_callback = std::move(discard_callback)]() mutable {
1230 if (rasterizer) {
1231 std::shared_ptr<LayerTreePipeline> pipeline = weak_pipeline.lock();
1232 if (pipeline) {
1233 rasterizer->Draw(pipeline, discard_callback: std::move(discard_callback));
1234 }
1235
1236 if (waiting_for_first_frame.load()) {
1237 waiting_for_first_frame.store(d: false);
1238 waiting_for_first_frame_condition.notify_all();
1239 }
1240 }
1241 }));
1242}
1243
1244// |Animator::Delegate|
1245void Shell::OnAnimatorDrawLastLayerTree(
1246 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
1247 FML_DCHECK(is_set_up_);
1248
1249 auto task = fml::MakeCopyable(
1250 lambda: [rasterizer = rasterizer_->GetWeakPtr(),
1251 frame_timings_recorder = std::move(frame_timings_recorder)]() mutable {
1252 if (rasterizer) {
1253 rasterizer->DrawLastLayerTree(frame_timings_recorder: std::move(frame_timings_recorder));
1254 }
1255 });
1256
1257 task_runners_.GetRasterTaskRunner()->PostTask(task);
1258}
1259
1260// |Engine::Delegate|
1261void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update,
1262 CustomAccessibilityActionUpdates actions) {
1263 FML_DCHECK(is_set_up_);
1264 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1265
1266 task_runners_.GetPlatformTaskRunner()->PostTask(
1267 task: [view = platform_view_->GetWeakPtr(), update = std::move(update),
1268 actions = std::move(actions)] {
1269 if (view) {
1270 view->UpdateSemantics(updates: update, actions);
1271 }
1272 });
1273}
1274
1275// |Engine::Delegate|
1276void Shell::OnEngineHandlePlatformMessage(
1277 std::unique_ptr<PlatformMessage> message) {
1278 FML_DCHECK(is_set_up_);
1279 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1280
1281 if (message->channel() == kSkiaChannel) {
1282 HandleEngineSkiaMessage(message: std::move(message));
1283 return;
1284 }
1285
1286 if (platform_message_handler_) {
1287 if (route_messages_through_platform_thread_ &&
1288 !platform_message_handler_
1289 ->DoesHandlePlatformMessageOnPlatformThread()) {
1290#if _WIN32
1291 // On Windows capturing a TaskRunner with a TaskRunner will cause an
1292 // uncaught exception in process shutdown because of the deletion order of
1293 // global variables. See also
1294 // https://github.com/flutter/flutter/issues/111575.
1295 // This won't be an issue until Windows supports background platform
1296 // channels (https://github.com/flutter/flutter/issues/93945). Then this
1297 // can potentially be addressed by capturing a weak_ptr to an object that
1298 // retains the ui TaskRunner, instead of the TaskRunner directly.
1299 FML_DCHECK(false);
1300#endif
1301 // We route messages through the platform thread temporarily when the
1302 // shell is being initialized to be backwards compatible with setting
1303 // message handlers in the same event as starting the isolate, but after
1304 // it is started.
1305 auto ui_task_runner = task_runners_.GetUITaskRunner();
1306 task_runners_.GetPlatformTaskRunner()->PostTask(task: fml::MakeCopyable(
1307 lambda: [weak_platform_message_handler =
1308 std::weak_ptr<PlatformMessageHandler>(platform_message_handler_),
1309 message = std::move(message), ui_task_runner]() mutable {
1310 ui_task_runner->PostTask(
1311 task: fml::MakeCopyable(lambda: [weak_platform_message_handler,
1312 message = std::move(message)]() mutable {
1313 auto platform_message_handler =
1314 weak_platform_message_handler.lock();
1315 if (platform_message_handler) {
1316 platform_message_handler->HandlePlatformMessage(
1317 message: std::move(message));
1318 }
1319 }));
1320 }));
1321 } else {
1322 platform_message_handler_->HandlePlatformMessage(message: std::move(message));
1323 }
1324 } else {
1325 task_runners_.GetPlatformTaskRunner()->PostTask(
1326 task: fml::MakeCopyable(lambda: [view = platform_view_->GetWeakPtr(),
1327 message = std::move(message)]() mutable {
1328 if (view) {
1329 view->HandlePlatformMessage(message: std::move(message));
1330 }
1331 }));
1332 }
1333}
1334
1335void Shell::HandleEngineSkiaMessage(std::unique_ptr<PlatformMessage> message) {
1336 const auto& data = message->data();
1337
1338 rapidjson::Document document;
1339 document.Parse(str: reinterpret_cast<const char*>(data.GetMapping()),
1340 length: data.GetSize());
1341 if (document.HasParseError() || !document.IsObject()) {
1342 return;
1343 }
1344 auto root = document.GetObject();
1345 auto method = root.FindMember(name: "method");
1346 if (method->value != "Skia.setResourceCacheMaxBytes") {
1347 return;
1348 }
1349 auto args = root.FindMember(name: "args");
1350 if (args == root.MemberEnd() || !args->value.IsInt()) {
1351 return;
1352 }
1353
1354 task_runners_.GetRasterTaskRunner()->PostTask(
1355 task: [rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt(),
1356 response = message->response()] {
1357 if (rasterizer) {
1358 rasterizer->SetResourceCacheMaxBytes(max_bytes: static_cast<size_t>(max_bytes),
1359 from_user: true);
1360 }
1361 if (response) {
1362 // The framework side expects this to be valid json encoded as a list.
1363 // Return `[true]` to signal success.
1364 std::vector<uint8_t> data = {'[', 't', 'r', 'u', 'e', ']'};
1365 response->Complete(
1366 data: std::make_unique<fml::DataMapping>(args: std::move(data)));
1367 }
1368 });
1369}
1370
1371// |Engine::Delegate|
1372void Shell::OnPreEngineRestart() {
1373 FML_DCHECK(is_set_up_);
1374 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1375
1376 fml::AutoResetWaitableEvent latch;
1377 fml::TaskRunner::RunNowOrPostTask(
1378 runner: task_runners_.GetPlatformTaskRunner(),
1379 task: [view = platform_view_->GetWeakPtr(), &latch]() {
1380 if (view) {
1381 view->OnPreEngineRestart();
1382 }
1383 latch.Signal();
1384 });
1385 // This is blocking as any embedded platform views has to be flushed before
1386 // we re-run the Dart code.
1387 latch.Wait();
1388}
1389
1390// |Engine::Delegate|
1391void Shell::OnRootIsolateCreated() {
1392 if (is_added_to_service_protocol_) {
1393 return;
1394 }
1395 auto description = GetServiceProtocolDescription();
1396 fml::TaskRunner::RunNowOrPostTask(
1397 runner: task_runners_.GetPlatformTaskRunner(),
1398 task: [self = weak_factory_.GetWeakPtr(),
1399 description = std::move(description)]() {
1400 if (self) {
1401 self->vm_->GetServiceProtocol()->AddHandler(handler: self.get(), description);
1402 }
1403 });
1404 is_added_to_service_protocol_ = true;
1405}
1406
1407// |Engine::Delegate|
1408void Shell::UpdateIsolateDescription(const std::string isolate_name,
1409 int64_t isolate_port) {
1410 Handler::Description description(isolate_port, isolate_name);
1411 vm_->GetServiceProtocol()->SetHandlerDescription(handler: this, description);
1412}
1413
1414void Shell::SetNeedsReportTimings(bool value) {
1415 needs_report_timings_ = value;
1416}
1417
1418// |Engine::Delegate|
1419std::unique_ptr<std::vector<std::string>> Shell::ComputePlatformResolvedLocale(
1420 const std::vector<std::string>& supported_locale_data) {
1421 return platform_view_->ComputePlatformResolvedLocales(supported_locale_data);
1422}
1423
1424void Shell::LoadDartDeferredLibrary(
1425 intptr_t loading_unit_id,
1426 std::unique_ptr<const fml::Mapping> snapshot_data,
1427 std::unique_ptr<const fml::Mapping> snapshot_instructions) {
1428 task_runners_.GetUITaskRunner()->PostTask(task: fml::MakeCopyable(
1429 lambda: [engine = engine_->GetWeakPtr(), loading_unit_id,
1430 data = std::move(snapshot_data),
1431 instructions = std::move(snapshot_instructions)]() mutable {
1432 if (engine) {
1433 engine->LoadDartDeferredLibrary(loading_unit_id, snapshot_data: std::move(data),
1434 snapshot_instructions: std::move(instructions));
1435 }
1436 }));
1437}
1438
1439void Shell::LoadDartDeferredLibraryError(intptr_t loading_unit_id,
1440 const std::string error_message,
1441 bool transient) {
1442 fml::TaskRunner::RunNowOrPostTask(
1443 runner: task_runners_.GetUITaskRunner(),
1444 task: [engine = weak_engine_, loading_unit_id, error_message, transient] {
1445 if (engine) {
1446 engine->LoadDartDeferredLibraryError(loading_unit_id, error_message,
1447 transient);
1448 }
1449 });
1450}
1451
1452void Shell::UpdateAssetResolverByType(
1453 std::unique_ptr<AssetResolver> updated_asset_resolver,
1454 AssetResolver::AssetResolverType type) {
1455 fml::TaskRunner::RunNowOrPostTask(
1456 runner: task_runners_.GetUITaskRunner(),
1457 task: fml::MakeCopyable(
1458 lambda: [engine = weak_engine_, type,
1459 asset_resolver = std::move(updated_asset_resolver)]() mutable {
1460 if (engine) {
1461 engine->GetAssetManager()->UpdateResolverByType(
1462 updated_asset_resolver: std::move(asset_resolver), type);
1463 }
1464 }));
1465}
1466
1467// |Engine::Delegate|
1468void Shell::RequestDartDeferredLibrary(intptr_t loading_unit_id) {
1469 task_runners_.GetPlatformTaskRunner()->PostTask(
1470 task: [view = platform_view_->GetWeakPtr(), loading_unit_id] {
1471 if (view) {
1472 view->RequestDartDeferredLibrary(loading_unit_id);
1473 }
1474 });
1475}
1476
1477void Shell::ReportTimings() {
1478 FML_DCHECK(is_set_up_);
1479 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1480
1481 auto timings = std::move(unreported_timings_);
1482 unreported_timings_ = {};
1483 task_runners_.GetUITaskRunner()->PostTask(task: [timings, engine = weak_engine_] {
1484 if (engine) {
1485 engine->ReportTimings(timings);
1486 }
1487 });
1488}
1489
1490size_t Shell::UnreportedFramesCount() const {
1491 // Check that this is running on the raster thread to avoid race conditions.
1492 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1493 FML_DCHECK(unreported_timings_.size() % (FrameTiming::kStatisticsCount) == 0);
1494 return unreported_timings_.size() / (FrameTiming::kStatisticsCount);
1495}
1496
1497void Shell::OnFrameRasterized(const FrameTiming& timing) {
1498 FML_DCHECK(is_set_up_);
1499 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1500
1501 // The C++ callback defined in settings.h and set by Flutter runner. This is
1502 // independent of the timings report to the Dart side.
1503 if (settings_.frame_rasterized_callback) {
1504 settings_.frame_rasterized_callback(timing);
1505 }
1506
1507 if (!needs_report_timings_) {
1508 return;
1509 }
1510
1511 size_t old_count = unreported_timings_.size();
1512 (void)old_count;
1513 for (auto phase : FrameTiming::kPhases) {
1514 unreported_timings_.push_back(
1515 x: timing.Get(phase).ToEpochDelta().ToMicroseconds());
1516 }
1517 unreported_timings_.push_back(x: timing.GetLayerCacheCount());
1518 unreported_timings_.push_back(x: timing.GetLayerCacheBytes());
1519 unreported_timings_.push_back(x: timing.GetPictureCacheCount());
1520 unreported_timings_.push_back(x: timing.GetPictureCacheBytes());
1521 unreported_timings_.push_back(x: timing.GetFrameNumber());
1522 FML_DCHECK(unreported_timings_.size() ==
1523 old_count + FrameTiming::kStatisticsCount);
1524
1525 // In tests using iPhone 6S with profile mode, sending a batch of 1 frame or a
1526 // batch of 100 frames have roughly the same cost of less than 0.1ms. Sending
1527 // a batch of 500 frames costs about 0.2ms. The 1 second threshold usually
1528 // kicks in before we reaching the following 100 frames threshold. The 100
1529 // threshold here is mainly for unit tests (so we don't have to write a
1530 // 1-second unit test), and make sure that our vector won't grow too big with
1531 // future 120fps, 240fps, or 1000fps displays.
1532 //
1533 // In the profile/debug mode, the timings are used by development tools which
1534 // require a latency of no more than 100ms. Hence we lower that 1-second
1535 // threshold to 100ms because performance overhead isn't that critical in
1536 // those cases.
1537 if (!first_frame_rasterized_ || UnreportedFramesCount() >= 100) {
1538 first_frame_rasterized_ = true;
1539 ReportTimings();
1540 } else if (!frame_timings_report_scheduled_) {
1541#if FLUTTER_RELEASE
1542 constexpr int kBatchTimeInMilliseconds = 1000;
1543#else
1544 constexpr int kBatchTimeInMilliseconds = 100;
1545#endif
1546
1547 // Also make sure that frame times get reported with a max latency of 1
1548 // second. Otherwise, the timings of last few frames of an animation may
1549 // never be reported until the next animation starts.
1550 frame_timings_report_scheduled_ = true;
1551 task_runners_.GetRasterTaskRunner()->PostDelayedTask(
1552 task: [self = weak_factory_gpu_->GetWeakPtr()]() {
1553 if (!self) {
1554 return;
1555 }
1556 self->frame_timings_report_scheduled_ = false;
1557 if (self->UnreportedFramesCount() > 0) {
1558 self->ReportTimings();
1559 }
1560 },
1561 delay: fml::TimeDelta::FromMilliseconds(millis: kBatchTimeInMilliseconds));
1562 }
1563}
1564
1565fml::Milliseconds Shell::GetFrameBudget() {
1566 double display_refresh_rate = display_manager_->GetMainDisplayRefreshRate();
1567 if (display_refresh_rate > 0) {
1568 return fml::RefreshRateToFrameBudget(refresh_rate: display_refresh_rate);
1569 } else {
1570 return fml::kDefaultFrameBudget;
1571 }
1572}
1573
1574fml::TimePoint Shell::GetLatestFrameTargetTime() const {
1575 std::scoped_lock time_recorder_lock(time_recorder_mutex_);
1576 FML_CHECK(latest_frame_target_time_.has_value())
1577 << "GetLatestFrameTargetTime called before OnAnimatorBeginFrame";
1578 // Covered by FML_CHECK().
1579 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1580 return latest_frame_target_time_.value();
1581}
1582
1583// |ServiceProtocol::Handler|
1584fml::RefPtr<fml::TaskRunner> Shell::GetServiceProtocolHandlerTaskRunner(
1585 std::string_view method) const {
1586 FML_DCHECK(is_set_up_);
1587 auto found = service_protocol_handlers_.find(k: method);
1588 if (found != service_protocol_handlers_.end()) {
1589 return found->second.first;
1590 }
1591 return task_runners_.GetUITaskRunner();
1592}
1593
1594// |ServiceProtocol::Handler|
1595bool Shell::HandleServiceProtocolMessage(
1596 std::string_view method, // one if the extension names specified above.
1597 const ServiceProtocolMap& params,
1598 rapidjson::Document* response) {
1599 auto found = service_protocol_handlers_.find(k: method);
1600 if (found != service_protocol_handlers_.end()) {
1601 return found->second.second(params, response);
1602 }
1603 return false;
1604}
1605
1606// |ServiceProtocol::Handler|
1607ServiceProtocol::Handler::Description Shell::GetServiceProtocolDescription()
1608 const {
1609 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1610
1611 if (!weak_engine_) {
1612 return ServiceProtocol::Handler::Description();
1613 }
1614
1615 return {
1616 weak_engine_->GetUIIsolateMainPort(),
1617 weak_engine_->GetUIIsolateName(),
1618 };
1619}
1620
1621static void ServiceProtocolParameterError(rapidjson::Document* response,
1622 std::string error_details) {
1623 auto& allocator = response->GetAllocator();
1624 response->SetObject();
1625 const int64_t kInvalidParams = -32602;
1626 response->AddMember(name: "code", value: kInvalidParams, allocator);
1627 response->AddMember(name: "message", value: "Invalid params", allocator);
1628 {
1629 rapidjson::Value details(rapidjson::kObjectType);
1630 details.AddMember(name: "details", value: std::move(error_details), allocator);
1631 response->AddMember(name: "data", value&: details, allocator);
1632 }
1633}
1634
1635static void ServiceProtocolFailureError(rapidjson::Document* response,
1636 std::string message) {
1637 auto& allocator = response->GetAllocator();
1638 response->SetObject();
1639 const int64_t kJsonServerError = -32000;
1640 response->AddMember(name: "code", value: kJsonServerError, allocator);
1641 response->AddMember(name: "message", value: std::move(message), allocator);
1642}
1643
1644// Service protocol handler
1645bool Shell::OnServiceProtocolScreenshot(
1646 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1647 rapidjson::Document* response) {
1648 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1649 auto screenshot = rasterizer_->ScreenshotLastLayerTree(
1650 type: Rasterizer::ScreenshotType::CompressedImage, base64_encode: true);
1651 if (screenshot.data) {
1652 response->SetObject();
1653 auto& allocator = response->GetAllocator();
1654 response->AddMember(name: "type", value: "Screenshot", allocator);
1655 rapidjson::Value image;
1656 image.SetString(s: static_cast<const char*>(screenshot.data->data()),
1657 length: screenshot.data->size(), allocator);
1658 response->AddMember(name: "screenshot", value&: image, allocator);
1659 return true;
1660 }
1661 ServiceProtocolFailureError(response, message: "Could not capture image screenshot.");
1662 return false;
1663}
1664
1665// Service protocol handler
1666bool Shell::OnServiceProtocolScreenshotSKP(
1667 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1668 rapidjson::Document* response) {
1669 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1670 auto screenshot = rasterizer_->ScreenshotLastLayerTree(
1671 type: Rasterizer::ScreenshotType::SkiaPicture, base64_encode: true);
1672 if (screenshot.data) {
1673 response->SetObject();
1674 auto& allocator = response->GetAllocator();
1675 response->AddMember(name: "type", value: "ScreenshotSkp", allocator);
1676 rapidjson::Value skp;
1677 skp.SetString(s: static_cast<const char*>(screenshot.data->data()),
1678 length: screenshot.data->size(), allocator);
1679 response->AddMember(name: "skp", value&: skp, allocator);
1680 return true;
1681 }
1682 ServiceProtocolFailureError(response, message: "Could not capture SKP screenshot.");
1683 return false;
1684}
1685
1686// Service protocol handler
1687bool Shell::OnServiceProtocolRunInView(
1688 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1689 rapidjson::Document* response) {
1690 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1691
1692 if (params.count(k: "mainScript") == 0) {
1693 ServiceProtocolParameterError(response,
1694 error_details: "'mainScript' parameter is missing.");
1695 return false;
1696 }
1697
1698 if (params.count(k: "assetDirectory") == 0) {
1699 ServiceProtocolParameterError(response,
1700 error_details: "'assetDirectory' parameter is missing.");
1701 return false;
1702 }
1703
1704 std::string main_script_path =
1705 fml::paths::FromURI(uri: params.at(k: "mainScript").data());
1706 std::string asset_directory_path =
1707 fml::paths::FromURI(uri: params.at(k: "assetDirectory").data());
1708
1709 auto main_script_file_mapping =
1710 std::make_unique<fml::FileMapping>(args: fml::OpenFile(
1711 path: main_script_path.c_str(), create_if_necessary: false, permission: fml::FilePermission::kRead));
1712
1713 auto isolate_configuration = IsolateConfiguration::CreateForKernel(
1714 kernel: std::move(main_script_file_mapping));
1715
1716 RunConfiguration configuration(std::move(isolate_configuration));
1717
1718 configuration.SetEntrypointAndLibrary(entrypoint: engine_->GetLastEntrypoint(),
1719 library: engine_->GetLastEntrypointLibrary());
1720 configuration.SetEntrypointArgs(engine_->GetLastEntrypointArgs());
1721
1722 configuration.AddAssetResolver(resolver: std::make_unique<DirectoryAssetBundle>(
1723 args: fml::OpenDirectory(path: asset_directory_path.c_str(), create_if_necessary: false,
1724 permission: fml::FilePermission::kRead),
1725 args: false));
1726
1727 // Preserve any original asset resolvers to avoid syncing unchanged assets
1728 // over the DevFS connection.
1729 auto old_asset_manager = engine_->GetAssetManager();
1730 if (old_asset_manager != nullptr) {
1731 for (auto& old_resolver : old_asset_manager->TakeResolvers()) {
1732 if (old_resolver->IsValidAfterAssetManagerChange()) {
1733 configuration.AddAssetResolver(resolver: std::move(old_resolver));
1734 }
1735 }
1736 }
1737
1738 auto& allocator = response->GetAllocator();
1739 response->SetObject();
1740 if (engine_->Restart(configuration: std::move(configuration))) {
1741 response->AddMember(name: "type", value: "Success", allocator);
1742 auto new_description = GetServiceProtocolDescription();
1743 rapidjson::Value view(rapidjson::kObjectType);
1744 new_description.Write(handler: this, value&: view, allocator);
1745 response->AddMember(name: "view", value&: view, allocator);
1746 return true;
1747 } else {
1748 FML_DLOG(ERROR) << "Could not run configuration in engine.";
1749 ServiceProtocolFailureError(response,
1750 message: "Could not run configuration in engine.");
1751 return false;
1752 }
1753
1754 FML_DCHECK(false);
1755 return false;
1756}
1757
1758// Service protocol handler
1759bool Shell::OnServiceProtocolFlushUIThreadTasks(
1760 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1761 rapidjson::Document* response) {
1762 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1763 // This API should not be invoked by production code.
1764 // It can potentially starve the service isolate if the main isolate pauses
1765 // at a breakpoint or is in an infinite loop.
1766 //
1767 // It should be invoked from the VM Service and blocks it until UI thread
1768 // tasks are processed.
1769 response->SetObject();
1770 response->AddMember(name: "type", value: "Success", allocator&: response->GetAllocator());
1771 return true;
1772}
1773
1774bool Shell::OnServiceProtocolGetDisplayRefreshRate(
1775 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1776 rapidjson::Document* response) {
1777 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1778 response->SetObject();
1779 response->AddMember(name: "type", value: "DisplayRefreshRate", allocator&: response->GetAllocator());
1780 response->AddMember(name: "fps", value: display_manager_->GetMainDisplayRefreshRate(),
1781 allocator&: response->GetAllocator());
1782 return true;
1783}
1784
1785double Shell::GetMainDisplayRefreshRate() {
1786 return display_manager_->GetMainDisplayRefreshRate();
1787}
1788
1789void Shell::RegisterImageDecoder(ImageGeneratorFactory factory,
1790 int32_t priority) {
1791 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1792 FML_DCHECK(is_set_up_);
1793
1794 fml::TaskRunner::RunNowOrPostTask(
1795 runner: task_runners_.GetUITaskRunner(),
1796 task: [engine = engine_->GetWeakPtr(), factory = std::move(factory),
1797 priority]() {
1798 if (engine) {
1799 engine->GetImageGeneratorRegistry()->AddFactory(factory, priority);
1800 }
1801 });
1802}
1803
1804bool Shell::OnServiceProtocolGetSkSLs(
1805 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1806 rapidjson::Document* response) {
1807 FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread());
1808 response->SetObject();
1809 response->AddMember(name: "type", value: "GetSkSLs", allocator&: response->GetAllocator());
1810
1811 rapidjson::Value shaders_json(rapidjson::kObjectType);
1812 PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
1813 std::vector<PersistentCache::SkSLCache> sksls = persistent_cache->LoadSkSLs();
1814 for (const auto& sksl : sksls) {
1815 size_t b64_size =
1816 SkBase64::Encode(src: sksl.value->data(), length: sksl.value->size(), dst: nullptr);
1817 sk_sp<SkData> b64_data = SkData::MakeUninitialized(length: b64_size + 1);
1818 char* b64_char = static_cast<char*>(b64_data->writable_data());
1819 SkBase64::Encode(src: sksl.value->data(), length: sksl.value->size(), dst: b64_char);
1820 b64_char[b64_size] = 0; // make it null terminated for printing
1821 rapidjson::Value shader_value(b64_char, response->GetAllocator());
1822 std::string_view key_view(reinterpret_cast<const char*>(sksl.key->data()),
1823 sksl.key->size());
1824 auto encode_result = fml::Base32Encode(input: key_view);
1825 if (!encode_result.first) {
1826 continue;
1827 }
1828 rapidjson::Value shader_key(encode_result.second, response->GetAllocator());
1829 shaders_json.AddMember(name&: shader_key, value&: shader_value, allocator&: response->GetAllocator());
1830 }
1831 response->AddMember(name: "SkSLs", value&: shaders_json, allocator&: response->GetAllocator());
1832 return true;
1833}
1834
1835bool Shell::OnServiceProtocolEstimateRasterCacheMemory(
1836 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1837 rapidjson::Document* response) {
1838 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1839 const auto& raster_cache = rasterizer_->compositor_context()->raster_cache();
1840 response->SetObject();
1841 response->AddMember(name: "type", value: "EstimateRasterCacheMemory",
1842 allocator&: response->GetAllocator());
1843 response->AddMember<uint64_t>(name: "layerBytes",
1844 value: raster_cache.EstimateLayerCacheByteSize(),
1845 allocator&: response->GetAllocator());
1846 response->AddMember<uint64_t>(name: "pictureBytes",
1847 value: raster_cache.EstimatePictureCacheByteSize(),
1848 allocator&: response->GetAllocator());
1849 return true;
1850}
1851
1852// Service protocol handler
1853bool Shell::OnServiceProtocolSetAssetBundlePath(
1854 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1855 rapidjson::Document* response) {
1856 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1857
1858 if (params.count(k: "assetDirectory") == 0) {
1859 ServiceProtocolParameterError(response,
1860 error_details: "'assetDirectory' parameter is missing.");
1861 return false;
1862 }
1863
1864 auto& allocator = response->GetAllocator();
1865 response->SetObject();
1866
1867 auto asset_manager = std::make_shared<AssetManager>();
1868
1869 if (!asset_manager->PushFront(resolver: std::make_unique<DirectoryAssetBundle>(
1870 args: fml::OpenDirectory(path: params.at(k: "assetDirectory").data(), create_if_necessary: false,
1871 permission: fml::FilePermission::kRead),
1872 args: false))) {
1873 // The new asset directory path was invalid.
1874 FML_DLOG(ERROR) << "Could not update asset directory.";
1875 ServiceProtocolFailureError(response, message: "Could not update asset directory.");
1876 return false;
1877 }
1878
1879 // Preserve any original asset resolvers to avoid syncing unchanged assets
1880 // over the DevFS connection.
1881 auto old_asset_manager = engine_->GetAssetManager();
1882 if (old_asset_manager != nullptr) {
1883 for (auto& old_resolver : old_asset_manager->TakeResolvers()) {
1884 if (old_resolver->IsValidAfterAssetManagerChange()) {
1885 asset_manager->PushBack(resolver: std::move(old_resolver));
1886 }
1887 }
1888 }
1889
1890 if (engine_->UpdateAssetManager(asset_manager)) {
1891 response->AddMember(name: "type", value: "Success", allocator);
1892 auto new_description = GetServiceProtocolDescription();
1893 rapidjson::Value view(rapidjson::kObjectType);
1894 new_description.Write(handler: this, value&: view, allocator);
1895 response->AddMember(name: "view", value&: view, allocator);
1896 return true;
1897 } else {
1898 FML_DLOG(ERROR) << "Could not update asset directory.";
1899 ServiceProtocolFailureError(response, message: "Could not update asset directory.");
1900 return false;
1901 }
1902
1903 FML_DCHECK(false);
1904 return false;
1905}
1906
1907static rapidjson::Value SerializeLayerSnapshot(
1908 double device_pixel_ratio,
1909 const LayerSnapshotData& snapshot,
1910 rapidjson::Document* response) {
1911 auto& allocator = response->GetAllocator();
1912 rapidjson::Value result;
1913 result.SetObject();
1914 result.AddMember(name: "layer_unique_id", value: snapshot.GetLayerUniqueId(), allocator);
1915 result.AddMember(name: "duration_micros", value: snapshot.GetDuration().ToMicroseconds(),
1916 allocator);
1917
1918 const SkRect bounds = snapshot.GetBounds();
1919 result.AddMember(name: "top", value: bounds.top(), allocator);
1920 result.AddMember(name: "left", value: bounds.left(), allocator);
1921 result.AddMember(name: "width", value: bounds.width(), allocator);
1922 result.AddMember(name: "height", value: bounds.height(), allocator);
1923
1924 sk_sp<SkData> snapshot_bytes = snapshot.GetSnapshot();
1925 if (snapshot_bytes) {
1926 rapidjson::Value image;
1927 image.SetArray();
1928 const uint8_t* data =
1929 reinterpret_cast<const uint8_t*>(snapshot_bytes->data());
1930 for (size_t i = 0; i < snapshot_bytes->size(); i++) {
1931 image.PushBack(value: data[i], allocator);
1932 }
1933 result.AddMember(name: "snapshot", value&: image, allocator);
1934 }
1935 return result;
1936}
1937
1938bool Shell::OnServiceProtocolRenderFrameWithRasterStats(
1939 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1940 rapidjson::Document* response) {
1941 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1942
1943 if (auto last_layer_tree = rasterizer_->GetLastLayerTree()) {
1944 auto& allocator = response->GetAllocator();
1945 response->SetObject();
1946 response->AddMember(name: "type", value: "RenderFrameWithRasterStats", allocator);
1947
1948 // When rendering the last layer tree, we do not need to build a frame,
1949 // invariants in FrameTimingRecorder enforce that raster timings can not be
1950 // set before build-end.
1951 auto frame_timings_recorder = std::make_unique<FrameTimingsRecorder>();
1952 const auto now = fml::TimePoint::Now();
1953 frame_timings_recorder->RecordVsync(vsync_start: now, vsync_target: now);
1954 frame_timings_recorder->RecordBuildStart(build_start: now);
1955 frame_timings_recorder->RecordBuildEnd(build_end: now);
1956
1957 last_layer_tree->enable_leaf_layer_tracing(enable: true);
1958 rasterizer_->DrawLastLayerTree(frame_timings_recorder: std::move(frame_timings_recorder));
1959 last_layer_tree->enable_leaf_layer_tracing(enable: false);
1960
1961 rapidjson::Value snapshots;
1962 snapshots.SetArray();
1963
1964 LayerSnapshotStore& store =
1965 rasterizer_->compositor_context()->snapshot_store();
1966 for (const LayerSnapshotData& data : store) {
1967 snapshots.PushBack(
1968 value: SerializeLayerSnapshot(device_pixel_ratio: device_pixel_ratio_, snapshot: data, response),
1969 allocator);
1970 }
1971
1972 response->AddMember(name: "snapshots", value&: snapshots, allocator);
1973
1974 const auto& frame_size = expected_frame_size_;
1975 response->AddMember(name: "frame_width", value: frame_size.width(), allocator);
1976 response->AddMember(name: "frame_height", value: frame_size.height(), allocator);
1977
1978 return true;
1979 } else {
1980 const char* error =
1981 "Failed to render the last frame with raster stats."
1982 " Rasterizer does not hold a valid last layer tree."
1983 " This could happen if this method was invoked before a frame was "
1984 "rendered";
1985 FML_DLOG(ERROR) << error;
1986 ServiceProtocolFailureError(response, message: error);
1987 return false;
1988 }
1989}
1990
1991void Shell::SendFontChangeNotification() {
1992 // After system fonts are reloaded, we send a system channel message
1993 // to notify flutter framework.
1994 rapidjson::Document document;
1995 document.SetObject();
1996 auto& allocator = document.GetAllocator();
1997 rapidjson::Value message_value;
1998 message_value.SetString(s: kFontChange, allocator);
1999 document.AddMember(name: kTypeKey, value&: message_value, allocator);
2000
2001 rapidjson::StringBuffer buffer;
2002 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
2003 document.Accept(handler&: writer);
2004 std::string message = buffer.GetString();
2005 std::unique_ptr<PlatformMessage> fontsChangeMessage =
2006 std::make_unique<flutter::PlatformMessage>(
2007 args: kSystemChannel,
2008 args: fml::MallocMapping::Copy(begin: message.c_str(), length: message.length()), args: nullptr);
2009 OnPlatformViewDispatchPlatformMessage(message: std::move(fontsChangeMessage));
2010}
2011
2012bool Shell::OnServiceProtocolReloadAssetFonts(
2013 const ServiceProtocol::Handler::ServiceProtocolMap& params,
2014 rapidjson::Document* response) {
2015 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
2016 if (!engine_) {
2017 return false;
2018 }
2019 engine_->GetFontCollection().RegisterFonts(asset_manager: engine_->GetAssetManager());
2020 engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache();
2021 SendFontChangeNotification();
2022
2023 auto& allocator = response->GetAllocator();
2024 response->SetObject();
2025 response->AddMember(name: "type", value: "Success", allocator);
2026
2027 return true;
2028}
2029
2030Rasterizer::Screenshot Shell::Screenshot(
2031 Rasterizer::ScreenshotType screenshot_type,
2032 bool base64_encode) {
2033 TRACE_EVENT0("flutter", "Shell::Screenshot");
2034 fml::AutoResetWaitableEvent latch;
2035 Rasterizer::Screenshot screenshot;
2036 fml::TaskRunner::RunNowOrPostTask(
2037 runner: task_runners_.GetRasterTaskRunner(), task: [&latch, //
2038 rasterizer = GetRasterizer(), //
2039 &screenshot, //
2040 screenshot_type, //
2041 base64_encode //
2042 ]() {
2043 if (rasterizer) {
2044 screenshot = rasterizer->ScreenshotLastLayerTree(type: screenshot_type,
2045 base64_encode);
2046 }
2047 latch.Signal();
2048 });
2049 latch.Wait();
2050 return screenshot;
2051}
2052
2053fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) {
2054 FML_DCHECK(is_set_up_);
2055 if (task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread() ||
2056 task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()) {
2057 return fml::Status(fml::StatusCode::kFailedPrecondition,
2058 "WaitForFirstFrame called from thread that can't wait "
2059 "because it is responsible for generating the frame.");
2060 }
2061
2062 // Check for overflow.
2063 auto now = std::chrono::steady_clock::now();
2064 auto max_duration = std::chrono::steady_clock::time_point::max() - now;
2065 auto desired_duration = std::chrono::milliseconds(timeout.ToMilliseconds());
2066 auto duration =
2067 now + (desired_duration > max_duration ? max_duration : desired_duration);
2068
2069 std::unique_lock<std::mutex> lock(waiting_for_first_frame_mutex_);
2070 bool success = waiting_for_first_frame_condition_.wait_until(
2071 lk&: lock, t: duration, pred: [&waiting_for_first_frame = waiting_for_first_frame_] {
2072 return !waiting_for_first_frame.load();
2073 });
2074 if (success) {
2075 return fml::Status();
2076 } else {
2077 return fml::Status(fml::StatusCode::kDeadlineExceeded, "timeout");
2078 }
2079}
2080
2081bool Shell::ReloadSystemFonts() {
2082 FML_DCHECK(is_set_up_);
2083 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
2084
2085 if (!engine_) {
2086 return false;
2087 }
2088 engine_->SetupDefaultFontManager();
2089 engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache();
2090 // After system fonts are reloaded, we send a system channel message
2091 // to notify flutter framework.
2092 SendFontChangeNotification();
2093 return true;
2094}
2095
2096std::shared_ptr<const fml::SyncSwitch> Shell::GetIsGpuDisabledSyncSwitch()
2097 const {
2098 return is_gpu_disabled_sync_switch_;
2099}
2100
2101void Shell::SetGpuAvailability(GpuAvailability availability) {
2102 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
2103 switch (availability) {
2104 case GpuAvailability::kAvailable:
2105 is_gpu_disabled_sync_switch_->SetSwitch(false);
2106 return;
2107 case GpuAvailability::kFlushAndMakeUnavailable: {
2108 fml::AutoResetWaitableEvent latch;
2109 fml::TaskRunner::RunNowOrPostTask(
2110 runner: task_runners_.GetIOTaskRunner(),
2111 task: [io_manager = io_manager_.get(), &latch]() {
2112 io_manager->GetSkiaUnrefQueue()->Drain();
2113 latch.Signal();
2114 });
2115 latch.Wait();
2116 }
2117 // FALLTHROUGH
2118 case GpuAvailability::kUnavailable:
2119 is_gpu_disabled_sync_switch_->SetSwitch(true);
2120 return;
2121 default:
2122 FML_DCHECK(false);
2123 }
2124}
2125
2126void Shell::OnDisplayUpdates(std::vector<std::unique_ptr<Display>> displays) {
2127 FML_DCHECK(is_set_up_);
2128 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
2129
2130 std::vector<DisplayData> display_data;
2131 for (const auto& display : displays) {
2132 display_data.push_back(x: display->GetDisplayData());
2133 }
2134 task_runners_.GetUITaskRunner()->PostTask(
2135 task: [engine = engine_->GetWeakPtr(),
2136 display_data = std::move(display_data)]() {
2137 if (engine) {
2138 engine->SetDisplays(display_data);
2139 }
2140 });
2141
2142 display_manager_->HandleDisplayUpdates(displays: std::move(displays));
2143}
2144
2145fml::TimePoint Shell::GetCurrentTimePoint() {
2146 return fml::TimePoint::Now();
2147}
2148
2149const std::shared_ptr<PlatformMessageHandler>&
2150Shell::GetPlatformMessageHandler() const {
2151 return platform_message_handler_;
2152}
2153
2154const std::weak_ptr<VsyncWaiter> Shell::GetVsyncWaiter() const {
2155 if (!engine_) {
2156 return {};
2157 }
2158 return engine_->GetVsyncWaiter();
2159}
2160
2161const std::shared_ptr<fml::ConcurrentTaskRunner>
2162Shell::GetConcurrentWorkerTaskRunner() const {
2163 FML_DCHECK(vm_);
2164 if (!vm_) {
2165 return nullptr;
2166 }
2167 return vm_->GetConcurrentWorkerTaskRunner();
2168}
2169
2170} // namespace flutter
2171

source code of flutter_engine/flutter/shell/common/shell.cc