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// FLUTTER_NOLINT
5
6#define RAPIDJSON_HAS_STDSTRING 1
7#include "flutter/shell/common/shell.h"
8
9#include <memory>
10#include <sstream>
11#include <vector>
12
13#include "flutter/assets/directory_asset_bundle.h"
14#include "flutter/fml/file.h"
15#include "flutter/fml/icu_util.h"
16#include "flutter/fml/log_settings.h"
17#include "flutter/fml/logging.h"
18#include "flutter/fml/make_copyable.h"
19#include "flutter/fml/message_loop.h"
20#include "flutter/fml/paths.h"
21#include "flutter/fml/trace_event.h"
22#include "flutter/fml/unique_fd.h"
23#include "flutter/runtime/dart_vm.h"
24#include "flutter/shell/common/engine.h"
25#include "flutter/shell/common/persistent_cache.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/core/SkGraphics.h"
33#include "third_party/skia/include/utils/SkBase64.h"
34#include "third_party/tonic/common/log.h"
35
36namespace flutter {
37
38constexpr char kSkiaChannel[] = "flutter/skia";
39constexpr char kSystemChannel[] = "flutter/system";
40constexpr char kTypeKey[] = "type";
41constexpr char kFontChange[] = "fontsChange";
42
43std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
44 DartVMRef vm,
45 TaskRunners task_runners,
46 const PlatformData platform_data,
47 Settings settings,
48 fml::RefPtr<const DartSnapshot> isolate_snapshot,
49 const Shell::CreateCallback<PlatformView>& on_create_platform_view,
50 const Shell::CreateCallback<Rasterizer>& on_create_rasterizer) {
51 if (!task_runners.IsValid()) {
52 FML_LOG(ERROR) << "Task runners to run the shell were invalid.";
53 return nullptr;
54 }
55
56 auto shell =
57 std::unique_ptr<Shell>(new Shell(std::move(vm), task_runners, settings));
58
59 // Create the rasterizer on the raster thread.
60 std::promise<std::unique_ptr<Rasterizer>> rasterizer_promise;
61 auto rasterizer_future = rasterizer_promise.get_future();
62 std::promise<fml::WeakPtr<SnapshotDelegate>> snapshot_delegate_promise;
63 auto snapshot_delegate_future = snapshot_delegate_promise.get_future();
64 fml::TaskRunner::RunNowOrPostTask(
65 task_runners.GetRasterTaskRunner(), [&rasterizer_promise, //
66 &snapshot_delegate_promise,
67 on_create_rasterizer, //
68 shell = shell.get() //
69 ]() {
70 TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem");
71 std::unique_ptr<Rasterizer> rasterizer(on_create_rasterizer(*shell));
72 snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate());
73 rasterizer_promise.set_value(std::move(rasterizer));
74 });
75
76 // Create the platform view on the platform thread (this thread).
77 auto platform_view = on_create_platform_view(*shell.get());
78 if (!platform_view || !platform_view->GetWeakPtr()) {
79 return nullptr;
80 }
81
82 // Ask the platform view for the vsync waiter. This will be used by the engine
83 // to create the animator.
84 auto vsync_waiter = platform_view->CreateVSyncWaiter();
85 if (!vsync_waiter) {
86 return nullptr;
87 }
88
89 // Create the IO manager on the IO thread. The IO manager must be initialized
90 // first because it has state that the other subsystems depend on. It must
91 // first be booted and the necessary references obtained to initialize the
92 // other subsystems.
93 std::promise<std::unique_ptr<ShellIOManager>> io_manager_promise;
94 auto io_manager_future = io_manager_promise.get_future();
95 std::promise<fml::WeakPtr<ShellIOManager>> weak_io_manager_promise;
96 auto weak_io_manager_future = weak_io_manager_promise.get_future();
97 std::promise<fml::RefPtr<SkiaUnrefQueue>> unref_queue_promise;
98 auto unref_queue_future = unref_queue_promise.get_future();
99 auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
100
101 // TODO(gw280): The WeakPtr here asserts that we are derefing it on the
102 // same thread as it was created on. We are currently on the IO thread
103 // inside this lambda but we need to deref the PlatformView, which was
104 // constructed on the platform thread.
105 //
106 // https://github.com/flutter/flutter/issues/42948
107 fml::TaskRunner::RunNowOrPostTask(
108 io_task_runner,
109 [&io_manager_promise, //
110 &weak_io_manager_promise, //
111 &unref_queue_promise, //
112 platform_view = platform_view->GetWeakPtr(), //
113 io_task_runner, //
114 is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() //
115 ]() {
116 TRACE_EVENT0("flutter", "ShellSetupIOSubsystem");
117 auto io_manager = std::make_unique<ShellIOManager>(
118 platform_view.getUnsafe()->CreateResourceContext(),
119 is_backgrounded_sync_switch, io_task_runner);
120 weak_io_manager_promise.set_value(io_manager->GetWeakPtr());
121 unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue());
122 io_manager_promise.set_value(std::move(io_manager));
123 });
124
125 // Send dispatcher_maker to the engine constructor because shell won't have
126 // platform_view set until Shell::Setup is called later.
127 auto dispatcher_maker = platform_view->GetDispatcherMaker();
128
129 // Create the engine on the UI thread.
130 std::promise<std::unique_ptr<Engine>> engine_promise;
131 auto engine_future = engine_promise.get_future();
132 fml::TaskRunner::RunNowOrPostTask(
133 shell->GetTaskRunners().GetUITaskRunner(),
134 fml::MakeCopyable([&engine_promise, //
135 shell = shell.get(), //
136 &dispatcher_maker, //
137 &platform_data, //
138 isolate_snapshot = std::move(isolate_snapshot), //
139 vsync_waiter = std::move(vsync_waiter), //
140 &weak_io_manager_future, //
141 &snapshot_delegate_future, //
142 &unref_queue_future //
143 ]() mutable {
144 TRACE_EVENT0("flutter", "ShellSetupUISubsystem");
145 const auto& task_runners = shell->GetTaskRunners();
146
147 // The animator is owned by the UI thread but it gets its vsync pulses
148 // from the platform.
149 auto animator = std::make_unique<Animator>(*shell, task_runners,
150 std::move(vsync_waiter));
151
152 engine_promise.set_value(std::make_unique<Engine>(
153 *shell, //
154 dispatcher_maker, //
155 *shell->GetDartVM(), //
156 std::move(isolate_snapshot), //
157 task_runners, //
158 platform_data, //
159 shell->GetSettings(), //
160 std::move(animator), //
161 weak_io_manager_future.get(), //
162 unref_queue_future.get(), //
163 snapshot_delegate_future.get() //
164 ));
165 }));
166
167 if (!shell->Setup(std::move(platform_view), //
168 engine_future.get(), //
169 rasterizer_future.get(), //
170 io_manager_future.get()) //
171 ) {
172 return nullptr;
173 }
174
175 return shell;
176}
177
178static void Tokenize(const std::string& input,
179 std::vector<std::string>* results,
180 char delimiter) {
181 std::istringstream ss(input);
182 std::string token;
183 while (std::getline(ss, token, delimiter)) {
184 results->push_back(token);
185 }
186}
187
188// Though there can be multiple shells, some settings apply to all components in
189// the process. These have to be setup before the shell or any of its
190// sub-components can be initialized. In a perfect world, this would be empty.
191// TODO(chinmaygarde): The unfortunate side effect of this call is that settings
192// that cause shell initialization failures will still lead to some of their
193// settings being applied.
194static void PerformInitializationTasks(Settings& settings) {
195 {
196 fml::LogSettings log_settings;
197 log_settings.min_log_level =
198 settings.verbose_logging ? fml::LOG_INFO : fml::LOG_ERROR;
199 fml::SetLogSettings(log_settings);
200 }
201
202 static std::once_flag gShellSettingsInitialization = {};
203 std::call_once(gShellSettingsInitialization, [&settings] {
204 if (settings.engine_start_timestamp.count() == 0) {
205 settings.engine_start_timestamp =
206 std::chrono::microseconds(Dart_TimelineGetMicros());
207 }
208
209 tonic::SetLogHandler(
210 [](const char* message) { FML_LOG(ERROR) << message; });
211
212 if (settings.trace_skia) {
213 InitSkiaEventTracer(settings.trace_skia);
214 }
215
216 if (!settings.trace_allowlist.empty()) {
217 std::vector<std::string> prefixes;
218 Tokenize(settings.trace_allowlist, &prefixes, ',');
219 fml::tracing::TraceSetAllowlist(prefixes);
220 }
221
222 if (!settings.skia_deterministic_rendering_on_cpu) {
223 SkGraphics::Init();
224 } else {
225 FML_DLOG(INFO) << "Skia deterministic rendering is enabled.";
226 }
227
228 if (settings.icu_initialization_required) {
229 if (settings.icu_data_path.size() != 0) {
230 fml::icu::InitializeICU(settings.icu_data_path);
231 } else if (settings.icu_mapper) {
232 fml::icu::InitializeICUFromMapping(settings.icu_mapper());
233 } else {
234 FML_DLOG(WARNING) << "Skipping ICU initialization in the shell.";
235 }
236 }
237 });
238}
239
240std::unique_ptr<Shell> Shell::Create(
241 TaskRunners task_runners,
242 Settings settings,
243 const Shell::CreateCallback<PlatformView>& on_create_platform_view,
244 const Shell::CreateCallback<Rasterizer>& on_create_rasterizer) {
245 return Shell::Create(std::move(task_runners), //
246 PlatformData{/* default platform data */}, //
247 std::move(settings), //
248 std::move(on_create_platform_view), //
249 std::move(on_create_rasterizer) //
250 );
251}
252
253std::unique_ptr<Shell> Shell::Create(
254 TaskRunners task_runners,
255 const PlatformData platform_data,
256 Settings settings,
257 Shell::CreateCallback<PlatformView> on_create_platform_view,
258 Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
259 PerformInitializationTasks(settings);
260 PersistentCache::SetCacheSkSL(settings.cache_sksl);
261
262 TRACE_EVENT0("flutter", "Shell::Create");
263
264 auto vm = DartVMRef::Create(settings);
265 FML_CHECK(vm) << "Must be able to initialize the VM.";
266
267 auto vm_data = vm->GetVMData();
268
269 return Shell::Create(std::move(task_runners), //
270 std::move(platform_data), //
271 std::move(settings), //
272 vm_data->GetIsolateSnapshot(), // isolate snapshot
273 on_create_platform_view, //
274 on_create_rasterizer, //
275 std::move(vm) //
276 );
277}
278
279std::unique_ptr<Shell> Shell::Create(
280 TaskRunners task_runners,
281 const PlatformData platform_data,
282 Settings settings,
283 fml::RefPtr<const DartSnapshot> isolate_snapshot,
284 const Shell::CreateCallback<PlatformView>& on_create_platform_view,
285 const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
286 DartVMRef vm) {
287 PerformInitializationTasks(settings);
288 PersistentCache::SetCacheSkSL(settings.cache_sksl);
289
290 TRACE_EVENT0("flutter", "Shell::CreateWithSnapshots");
291
292 if (!task_runners.IsValid() || !on_create_platform_view ||
293 !on_create_rasterizer) {
294 return nullptr;
295 }
296
297 fml::AutoResetWaitableEvent latch;
298 std::unique_ptr<Shell> shell;
299 fml::TaskRunner::RunNowOrPostTask(
300 task_runners.GetPlatformTaskRunner(),
301 fml::MakeCopyable([&latch, //
302 vm = std::move(vm), //
303 &shell, //
304 task_runners = std::move(task_runners), //
305 platform_data, //
306 settings, //
307 isolate_snapshot = std::move(isolate_snapshot), //
308 on_create_platform_view, //
309 on_create_rasterizer //
310 ]() mutable {
311 shell = CreateShellOnPlatformThread(std::move(vm),
312 std::move(task_runners), //
313 platform_data, //
314 settings, //
315 std::move(isolate_snapshot), //
316 on_create_platform_view, //
317 on_create_rasterizer //
318 );
319 latch.Signal();
320 }));
321 latch.Wait();
322 return shell;
323}
324
325Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings)
326 : task_runners_(std::move(task_runners)),
327 settings_(std::move(settings)),
328 vm_(std::move(vm)),
329 is_gpu_disabled_sync_switch_(new fml::SyncSwitch()),
330 weak_factory_(this),
331 weak_factory_gpu_(nullptr) {
332 FML_CHECK(vm_) << "Must have access to VM to create a shell.";
333 FML_DCHECK(task_runners_.IsValid());
334 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
335
336 // Generate a WeakPtrFactory for use with the raster thread. This does not
337 // need to wait on a latch because it can only ever be used from the raster
338 // thread from this class, so we have ordering guarantees.
339 fml::TaskRunner::RunNowOrPostTask(
340 task_runners_.GetRasterTaskRunner(), fml::MakeCopyable([this]() mutable {
341 this->weak_factory_gpu_ =
342 std::make_unique<fml::TaskRunnerAffineWeakPtrFactory<Shell>>(this);
343 }));
344
345 // Install service protocol handlers.
346
347 service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName] = {
348 task_runners_.GetRasterTaskRunner(),
349 std::bind(&Shell::OnServiceProtocolScreenshot, this,
350 std::placeholders::_1, std::placeholders::_2)};
351 service_protocol_handlers_[ServiceProtocol::kScreenshotSkpExtensionName] = {
352 task_runners_.GetRasterTaskRunner(),
353 std::bind(&Shell::OnServiceProtocolScreenshotSKP, this,
354 std::placeholders::_1, std::placeholders::_2)};
355 service_protocol_handlers_[ServiceProtocol::kRunInViewExtensionName] = {
356 task_runners_.GetUITaskRunner(),
357 std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1,
358 std::placeholders::_2)};
359 service_protocol_handlers_
360 [ServiceProtocol::kFlushUIThreadTasksExtensionName] = {
361 task_runners_.GetUITaskRunner(),
362 std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this,
363 std::placeholders::_1, std::placeholders::_2)};
364 service_protocol_handlers_
365 [ServiceProtocol::kSetAssetBundlePathExtensionName] = {
366 task_runners_.GetUITaskRunner(),
367 std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this,
368 std::placeholders::_1, std::placeholders::_2)};
369 service_protocol_handlers_
370 [ServiceProtocol::kGetDisplayRefreshRateExtensionName] = {
371 task_runners_.GetUITaskRunner(),
372 std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this,
373 std::placeholders::_1, std::placeholders::_2)};
374 service_protocol_handlers_[ServiceProtocol::kGetSkSLsExtensionName] = {
375 task_runners_.GetIOTaskRunner(),
376 std::bind(&Shell::OnServiceProtocolGetSkSLs, this, std::placeholders::_1,
377 std::placeholders::_2)};
378 service_protocol_handlers_
379 [ServiceProtocol::kEstimateRasterCacheMemoryExtensionName] = {
380 task_runners_.GetRasterTaskRunner(),
381 std::bind(&Shell::OnServiceProtocolEstimateRasterCacheMemory, this,
382 std::placeholders::_1, std::placeholders::_2)};
383}
384
385Shell::~Shell() {
386 PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner(
387 task_runners_.GetIOTaskRunner());
388
389 vm_->GetServiceProtocol()->RemoveHandler(this);
390
391 fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch;
392
393 fml::TaskRunner::RunNowOrPostTask(
394 task_runners_.GetUITaskRunner(),
395 fml::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable {
396 engine.reset();
397 ui_latch.Signal();
398 }));
399 ui_latch.Wait();
400
401 fml::TaskRunner::RunNowOrPostTask(
402 task_runners_.GetRasterTaskRunner(),
403 fml::MakeCopyable([rasterizer = std::move(rasterizer_),
404 weak_factory_gpu = std::move(weak_factory_gpu_),
405 &gpu_latch]() mutable {
406 rasterizer.reset();
407 weak_factory_gpu.reset();
408 gpu_latch.Signal();
409 }));
410 gpu_latch.Wait();
411
412 fml::TaskRunner::RunNowOrPostTask(
413 task_runners_.GetIOTaskRunner(),
414 fml::MakeCopyable([io_manager = std::move(io_manager_),
415 platform_view = platform_view_.get(),
416 &io_latch]() mutable {
417 io_manager.reset();
418 if (platform_view) {
419 platform_view->ReleaseResourceContext();
420 }
421 io_latch.Signal();
422 }));
423
424 io_latch.Wait();
425
426 // The platform view must go last because it may be holding onto platform side
427 // counterparts to resources owned by subsystems running on other threads. For
428 // example, the NSOpenGLContext on the Mac.
429 fml::TaskRunner::RunNowOrPostTask(
430 task_runners_.GetPlatformTaskRunner(),
431 fml::MakeCopyable([platform_view = std::move(platform_view_),
432 &platform_latch]() mutable {
433 platform_view.reset();
434 platform_latch.Signal();
435 }));
436 platform_latch.Wait();
437}
438
439void Shell::NotifyLowMemoryWarning() const {
440 auto trace_id = fml::tracing::TraceNonce();
441 TRACE_EVENT_ASYNC_BEGIN0("flutter", "Shell::NotifyLowMemoryWarning",
442 trace_id);
443 // This does not require a current isolate but does require a running VM.
444 // Since a valid shell will not be returned to the embedder without a valid
445 // DartVMRef, we can be certain that this is a safe spot to assume a VM is
446 // running.
447 ::Dart_NotifyLowMemory();
448
449 task_runners_.GetRasterTaskRunner()->PostTask(
450 [rasterizer = rasterizer_->GetWeakPtr(), trace_id = trace_id]() {
451 if (rasterizer) {
452 rasterizer->NotifyLowMemoryWarning();
453 }
454 TRACE_EVENT_ASYNC_END0("flutter", "Shell::NotifyLowMemoryWarning",
455 trace_id);
456 });
457 // The IO Manager uses resource cache limits of 0, so it is not necessary
458 // to purge them.
459}
460
461void Shell::RunEngine(RunConfiguration run_configuration) {
462 RunEngine(std::move(run_configuration), nullptr);
463}
464
465void Shell::RunEngine(
466 RunConfiguration run_configuration,
467 const std::function<void(Engine::RunStatus)>& result_callback) {
468 auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(),
469 result_callback](Engine::RunStatus run_result) {
470 if (!result_callback) {
471 return;
472 }
473 platform_runner->PostTask(
474 [result_callback, run_result]() { result_callback(run_result); });
475 };
476 FML_DCHECK(is_setup_);
477 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
478
479 fml::TaskRunner::RunNowOrPostTask(
480 task_runners_.GetUITaskRunner(),
481 fml::MakeCopyable(
482 [run_configuration = std::move(run_configuration),
483 weak_engine = weak_engine_, result]() mutable {
484 if (!weak_engine) {
485 FML_LOG(ERROR)
486 << "Could not launch engine with configuration - no engine.";
487 result(Engine::RunStatus::Failure);
488 return;
489 }
490 auto run_result = weak_engine->Run(std::move(run_configuration));
491 if (run_result == flutter::Engine::RunStatus::Failure) {
492 FML_LOG(ERROR) << "Could not launch engine with configuration.";
493 }
494 result(run_result);
495 }));
496}
497
498std::optional<DartErrorCode> Shell::GetUIIsolateLastError() const {
499 FML_DCHECK(is_setup_);
500 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
501
502 if (!weak_engine_) {
503 return std::nullopt;
504 }
505 switch (weak_engine_->GetUIIsolateLastError()) {
506 case tonic::kCompilationErrorType:
507 return DartErrorCode::CompilationError;
508 case tonic::kApiErrorType:
509 return DartErrorCode::ApiError;
510 case tonic::kUnknownErrorType:
511 return DartErrorCode::UnknownError;
512 case tonic::kNoError:
513 return DartErrorCode::NoError;
514 }
515 return DartErrorCode::UnknownError;
516}
517
518bool Shell::EngineHasLivePorts() const {
519 FML_DCHECK(is_setup_);
520 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
521
522 if (!weak_engine_) {
523 return false;
524 }
525
526 return weak_engine_->UIIsolateHasLivePorts();
527}
528
529bool Shell::IsSetup() const {
530 return is_setup_;
531}
532
533bool Shell::Setup(std::unique_ptr<PlatformView> platform_view,
534 std::unique_ptr<Engine> engine,
535 std::unique_ptr<Rasterizer> rasterizer,
536 std::unique_ptr<ShellIOManager> io_manager) {
537 if (is_setup_) {
538 return false;
539 }
540
541 if (!platform_view || !engine || !rasterizer || !io_manager) {
542 return false;
543 }
544
545 platform_view_ = std::move(platform_view);
546 engine_ = std::move(engine);
547 rasterizer_ = std::move(rasterizer);
548 io_manager_ = std::move(io_manager);
549
550 // The weak ptr must be generated in the platform thread which owns the unique
551 // ptr.
552 weak_engine_ = engine_->GetWeakPtr();
553 weak_rasterizer_ = rasterizer_->GetWeakPtr();
554 weak_platform_view_ = platform_view_->GetWeakPtr();
555
556 // Setup the time-consuming default font manager right after engine created.
557 fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
558 [engine = weak_engine_] {
559 if (engine) {
560 engine->SetupDefaultFontManager();
561 }
562 });
563
564 is_setup_ = true;
565
566 vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription());
567
568 PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner(
569 task_runners_.GetIOTaskRunner());
570
571 PersistentCache::GetCacheForProcess()->SetIsDumpingSkp(
572 settings_.dump_skp_on_shader_compilation);
573
574 if (settings_.purge_persistent_cache) {
575 PersistentCache::GetCacheForProcess()->Purge();
576 }
577
578 // TODO(gw280): The WeakPtr here asserts that we are derefing it on the
579 // same thread as it was created on. Shell is constructed on the platform
580 // thread but we need to call into the Engine on the UI thread, so we need
581 // to use getUnsafe() here to avoid failing the assertion.
582 //
583 // https://github.com/flutter/flutter/issues/42947
584 display_refresh_rate_ = weak_engine_.getUnsafe()->GetDisplayRefreshRate();
585
586 return true;
587}
588
589const Settings& Shell::GetSettings() const {
590 return settings_;
591}
592
593const TaskRunners& Shell::GetTaskRunners() const {
594 return task_runners_;
595}
596
597fml::TaskRunnerAffineWeakPtr<Rasterizer> Shell::GetRasterizer() const {
598 FML_DCHECK(is_setup_);
599 return weak_rasterizer_;
600}
601
602fml::WeakPtr<Engine> Shell::GetEngine() {
603 FML_DCHECK(is_setup_);
604 return weak_engine_;
605}
606
607fml::WeakPtr<PlatformView> Shell::GetPlatformView() {
608 FML_DCHECK(is_setup_);
609 return weak_platform_view_;
610}
611
612DartVM* Shell::GetDartVM() {
613 return &vm_;
614}
615
616// |PlatformView::Delegate|
617void Shell::OnPlatformViewCreated(std::unique_ptr<Surface> surface) {
618 TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated");
619 FML_DCHECK(is_setup_);
620 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
621
622 // Note:
623 // This is a synchronous operation because certain platforms depend on
624 // setup/suspension of all activities that may be interacting with the GPU in
625 // a synchronous fashion.
626 fml::AutoResetWaitableEvent latch;
627 auto raster_task =
628 fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_,
629 rasterizer = rasterizer_->GetWeakPtr(), //
630 surface = std::move(surface), //
631 &latch]() mutable {
632 if (rasterizer) {
633 rasterizer->Setup(std::move(surface));
634 }
635
636 waiting_for_first_frame.store(true);
637
638 // Step 3: All done. Signal the latch that the platform thread is
639 // waiting on.
640 latch.Signal();
641 });
642
643 // The normal flow executed by this method is that the platform thread is
644 // starting the sequence and waiting on the latch. Later the UI thread posts
645 // raster_task to the raster thread which signals the latch. If the raster and
646 // the platform threads are the same this results in a deadlock as the
647 // raster_task will never be posted to the plaform/raster thread that is
648 // blocked on a latch. To avoid the described deadlock, if the raster and the
649 // platform threads are the same, should_post_raster_task will be false, and
650 // then instead of posting a task to the raster thread, the ui thread just
651 // signals the latch and the platform/raster thread follows with executing
652 // raster_task.
653 bool should_post_raster_task = task_runners_.GetRasterTaskRunner() !=
654 task_runners_.GetPlatformTaskRunner();
655
656 auto ui_task = [engine = engine_->GetWeakPtr(), //
657 raster_task_runner = task_runners_.GetRasterTaskRunner(), //
658 raster_task, should_post_raster_task,
659 &latch //
660 ] {
661 if (engine) {
662 engine->OnOutputSurfaceCreated();
663 }
664 // Step 2: Next, tell the raster thread that it should create a surface for
665 // its rasterizer.
666 if (should_post_raster_task) {
667 fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task);
668 } else {
669 // See comment on should_post_raster_task, in this case we just unblock
670 // the platform thread.
671 latch.Signal();
672 }
673 };
674
675 // Threading: Capture platform view by raw pointer and not the weak pointer.
676 // We are going to use the pointer on the IO thread which is not safe with a
677 // weak pointer. However, we are preventing the platform view from being
678 // collected by using a latch.
679 auto* platform_view = platform_view_.get();
680
681 FML_DCHECK(platform_view);
682
683 auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view,
684 ui_task_runner = task_runners_.GetUITaskRunner(), ui_task] {
685 if (io_manager && !io_manager->GetResourceContext()) {
686 io_manager->NotifyResourceContextAvailable(
687 platform_view->CreateResourceContext());
688 }
689 // Step 1: Next, post a task on the UI thread to tell the engine that it has
690 // an output surface.
691 fml::TaskRunner::RunNowOrPostTask(ui_task_runner, ui_task);
692 };
693
694 fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task);
695
696 latch.Wait();
697 if (!should_post_raster_task) {
698 // See comment on should_post_raster_task, in this case the raster_task
699 // wasn't executed, and we just run it here as the platform thread
700 // is the raster thread.
701 raster_task();
702 }
703}
704
705// |PlatformView::Delegate|
706void Shell::OnPlatformViewDestroyed() {
707 TRACE_EVENT0("flutter", "Shell::OnPlatformViewDestroyed");
708 FML_DCHECK(is_setup_);
709 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
710
711 // Note:
712 // This is a synchronous operation because certain platforms depend on
713 // setup/suspension of all activities that may be interacting with the GPU in
714 // a synchronous fashion.
715
716 fml::AutoResetWaitableEvent latch;
717
718 auto io_task = [io_manager = io_manager_.get(), &latch]() {
719 // Execute any pending Skia object deletions while GPU access is still
720 // allowed.
721 io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
722 fml::SyncSwitch::Handlers().SetIfFalse(
723 [&] { io_manager->GetSkiaUnrefQueue()->Drain(); }));
724 // Step 3: All done. Signal the latch that the platform thread is waiting
725 // on.
726 latch.Signal();
727 };
728
729 auto raster_task = [rasterizer = rasterizer_->GetWeakPtr(),
730 io_task_runner = task_runners_.GetIOTaskRunner(),
731 io_task]() {
732 if (rasterizer) {
733 rasterizer->Teardown();
734 }
735 // Step 2: Next, tell the IO thread to complete its remaining work.
736 fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task);
737 };
738
739 // The normal flow executed by this method is that the platform thread is
740 // starting the sequence and waiting on the latch. Later the UI thread posts
741 // raster_task to the raster thread triggers signaling the latch(on the IO
742 // thread). If the raster and the platform threads are the same this results
743 // in a deadlock as the raster_task will never be posted to the plaform/raster
744 // thread that is blocked on a latch. To avoid the described deadlock, if the
745 // raster and the platform threads are the same, should_post_raster_task will
746 // be false, and then instead of posting a task to the raster thread, the ui
747 // thread just signals the latch and the platform/raster thread follows with
748 // executing raster_task.
749 bool should_post_raster_task = task_runners_.GetRasterTaskRunner() !=
750 task_runners_.GetPlatformTaskRunner();
751
752 auto ui_task = [engine = engine_->GetWeakPtr(),
753 raster_task_runner = task_runners_.GetRasterTaskRunner(),
754 raster_task, should_post_raster_task, &latch]() {
755 if (engine) {
756 engine->OnOutputSurfaceDestroyed();
757 }
758 // Step 1: Next, tell the raster thread that its rasterizer should suspend
759 // access to the underlying surface.
760 if (should_post_raster_task) {
761 fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task);
762 } else {
763 // See comment on should_post_raster_task, in this case we just unblock
764 // the platform thread.
765 latch.Signal();
766 }
767 };
768
769 // Step 0: Post a task onto the UI thread to tell the engine that its output
770 // surface is about to go away.
771 fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task);
772 latch.Wait();
773 if (!should_post_raster_task) {
774 // See comment on should_post_raster_task, in this case the raster_task
775 // wasn't executed, and we just run it here as the platform thread
776 // is the raster thread.
777 raster_task();
778 latch.Wait();
779 }
780}
781
782// |PlatformView::Delegate|
783void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) {
784 FML_DCHECK(is_setup_);
785 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
786
787 // This is the formula Android uses.
788 // https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41
789 size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4;
790 task_runners_.GetRasterTaskRunner()->PostTask(
791 [rasterizer = rasterizer_->GetWeakPtr(), max_bytes] {
792 if (rasterizer) {
793 rasterizer->SetResourceCacheMaxBytes(max_bytes, false);
794 }
795 });
796
797 task_runners_.GetUITaskRunner()->PostTask(
798 [engine = engine_->GetWeakPtr(), metrics]() {
799 if (engine) {
800 engine->SetViewportMetrics(metrics);
801 }
802 });
803}
804
805// |PlatformView::Delegate|
806void Shell::OnPlatformViewDispatchPlatformMessage(
807 fml::RefPtr<PlatformMessage> message) {
808 FML_DCHECK(is_setup_);
809 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
810
811 task_runners_.GetUITaskRunner()->PostTask(
812 [engine = engine_->GetWeakPtr(), message = std::move(message)] {
813 if (engine) {
814 engine->DispatchPlatformMessage(std::move(message));
815 }
816 });
817}
818
819// |PlatformView::Delegate|
820void Shell::OnPlatformViewDispatchPointerDataPacket(
821 std::unique_ptr<PointerDataPacket> packet) {
822 TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchPointerDataPacket");
823 TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_);
824 FML_DCHECK(is_setup_);
825 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
826 task_runners_.GetUITaskRunner()->PostTask(
827 fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet),
828 flow_id = next_pointer_flow_id_]() mutable {
829 if (engine) {
830 engine->DispatchPointerDataPacket(std::move(packet), flow_id);
831 }
832 }));
833 next_pointer_flow_id_++;
834}
835
836// |PlatformView::Delegate|
837void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id,
838 SemanticsAction action,
839 std::vector<uint8_t> args) {
840 FML_DCHECK(is_setup_);
841 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
842
843 task_runners_.GetUITaskRunner()->PostTask(
844 [engine = engine_->GetWeakPtr(), id, action, args = std::move(args)] {
845 if (engine) {
846 engine->DispatchSemanticsAction(id, action, std::move(args));
847 }
848 });
849}
850
851// |PlatformView::Delegate|
852void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) {
853 FML_DCHECK(is_setup_);
854 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
855
856 task_runners_.GetUITaskRunner()->PostTask(
857 [engine = engine_->GetWeakPtr(), enabled] {
858 if (engine) {
859 engine->SetSemanticsEnabled(enabled);
860 }
861 });
862}
863
864// |PlatformView::Delegate|
865void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) {
866 FML_DCHECK(is_setup_);
867 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
868
869 task_runners_.GetUITaskRunner()->PostTask(
870 [engine = engine_->GetWeakPtr(), flags] {
871 if (engine) {
872 engine->SetAccessibilityFeatures(flags);
873 }
874 });
875}
876
877// |PlatformView::Delegate|
878void Shell::OnPlatformViewRegisterTexture(
879 std::shared_ptr<flutter::Texture> texture) {
880 FML_DCHECK(is_setup_);
881 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
882
883 task_runners_.GetRasterTaskRunner()->PostTask(
884 [rasterizer = rasterizer_->GetWeakPtr(), texture] {
885 if (rasterizer) {
886 if (auto* registry = rasterizer->GetTextureRegistry()) {
887 registry->RegisterTexture(texture);
888 }
889 }
890 });
891}
892
893// |PlatformView::Delegate|
894void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) {
895 FML_DCHECK(is_setup_);
896 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
897
898 task_runners_.GetRasterTaskRunner()->PostTask(
899 [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() {
900 if (rasterizer) {
901 if (auto* registry = rasterizer->GetTextureRegistry()) {
902 registry->UnregisterTexture(texture_id);
903 }
904 }
905 });
906}
907
908// |PlatformView::Delegate|
909void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) {
910 FML_DCHECK(is_setup_);
911 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
912
913 // Tell the rasterizer that one of its textures has a new frame available.
914 task_runners_.GetRasterTaskRunner()->PostTask(
915 [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() {
916 auto* registry = rasterizer->GetTextureRegistry();
917
918 if (!registry) {
919 return;
920 }
921
922 auto texture = registry->GetTexture(texture_id);
923
924 if (!texture) {
925 return;
926 }
927
928 texture->MarkNewFrameAvailable();
929 });
930
931 // Schedule a new frame without having to rebuild the layer tree.
932 task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() {
933 if (engine) {
934 engine->ScheduleFrame(false);
935 }
936 });
937}
938
939// |PlatformView::Delegate|
940void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {
941 FML_DCHECK(is_setup_);
942 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
943
944 task_runners_.GetRasterTaskRunner()->PostTask(
945 [rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() {
946 if (rasterizer) {
947 rasterizer->SetNextFrameCallback(std::move(closure));
948 }
949 });
950}
951
952// |Animator::Delegate|
953void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time) {
954 FML_DCHECK(is_setup_);
955 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
956
957 // record the target time for use by rasterizer.
958 {
959 std::scoped_lock time_recorder_lock(time_recorder_mutex_);
960 latest_frame_target_time_.emplace(frame_target_time);
961 }
962 if (engine_) {
963 engine_->BeginFrame(frame_target_time);
964 }
965}
966
967// |Animator::Delegate|
968void Shell::OnAnimatorNotifyIdle(int64_t deadline) {
969 FML_DCHECK(is_setup_);
970 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
971
972 if (engine_) {
973 engine_->NotifyIdle(deadline);
974 }
975}
976
977// |Animator::Delegate|
978void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline,
979 fml::TimePoint frame_target_time) {
980 FML_DCHECK(is_setup_);
981
982 // record the target time for use by rasterizer.
983 {
984 std::scoped_lock time_recorder_lock(time_recorder_mutex_);
985 if (!latest_frame_target_time_) {
986 latest_frame_target_time_ = frame_target_time;
987 } else if (latest_frame_target_time_ < frame_target_time) {
988 latest_frame_target_time_ = frame_target_time;
989 }
990 }
991
992 task_runners_.GetRasterTaskRunner()->PostTask(
993 [&waiting_for_first_frame = waiting_for_first_frame_,
994 &waiting_for_first_frame_condition = waiting_for_first_frame_condition_,
995 rasterizer = rasterizer_->GetWeakPtr(),
996 pipeline = std::move(pipeline)]() {
997 if (rasterizer) {
998 rasterizer->Draw(pipeline);
999
1000 if (waiting_for_first_frame.load()) {
1001 waiting_for_first_frame.store(false);
1002 waiting_for_first_frame_condition.notify_all();
1003 }
1004 }
1005 });
1006}
1007
1008// |Animator::Delegate|
1009void Shell::OnAnimatorDrawLastLayerTree() {
1010 FML_DCHECK(is_setup_);
1011
1012 task_runners_.GetRasterTaskRunner()->PostTask(
1013 [rasterizer = rasterizer_->GetWeakPtr()]() {
1014 if (rasterizer) {
1015 rasterizer->DrawLastLayerTree();
1016 }
1017 });
1018}
1019
1020// |Engine::Delegate|
1021void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update,
1022 CustomAccessibilityActionUpdates actions) {
1023 FML_DCHECK(is_setup_);
1024 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1025
1026 task_runners_.GetPlatformTaskRunner()->PostTask(
1027 [view = platform_view_->GetWeakPtr(), update = std::move(update),
1028 actions = std::move(actions)] {
1029 if (view) {
1030 view->UpdateSemantics(std::move(update), std::move(actions));
1031 }
1032 });
1033}
1034
1035// |Engine::Delegate|
1036void Shell::OnEngineHandlePlatformMessage(
1037 fml::RefPtr<PlatformMessage> message) {
1038 FML_DCHECK(is_setup_);
1039 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1040
1041 if (message->channel() == kSkiaChannel) {
1042 HandleEngineSkiaMessage(std::move(message));
1043 return;
1044 }
1045
1046 task_runners_.GetPlatformTaskRunner()->PostTask(
1047 [view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
1048 if (view) {
1049 view->HandlePlatformMessage(std::move(message));
1050 }
1051 });
1052}
1053
1054void Shell::HandleEngineSkiaMessage(fml::RefPtr<PlatformMessage> message) {
1055 const auto& data = message->data();
1056
1057 rapidjson::Document document;
1058 document.Parse(reinterpret_cast<const char*>(data.data()), data.size());
1059 if (document.HasParseError() || !document.IsObject())
1060 return;
1061 auto root = document.GetObject();
1062 auto method = root.FindMember("method");
1063 if (method->value != "Skia.setResourceCacheMaxBytes")
1064 return;
1065 auto args = root.FindMember("args");
1066 if (args == root.MemberEnd() || !args->value.IsInt())
1067 return;
1068
1069 task_runners_.GetRasterTaskRunner()->PostTask(
1070 [rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt(),
1071 response = std::move(message->response())] {
1072 if (rasterizer) {
1073 rasterizer->SetResourceCacheMaxBytes(static_cast<size_t>(max_bytes),
1074 true);
1075 }
1076 if (response) {
1077 // The framework side expects this to be valid json encoded as a list.
1078 // Return `[true]` to signal success.
1079 std::vector<uint8_t> data = {'[', 't', 'r', 'u', 'e', ']'};
1080 response->Complete(
1081 std::make_unique<fml::DataMapping>(std::move(data)));
1082 }
1083 });
1084}
1085
1086// |Engine::Delegate|
1087void Shell::OnPreEngineRestart() {
1088 FML_DCHECK(is_setup_);
1089 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1090
1091 fml::AutoResetWaitableEvent latch;
1092 fml::TaskRunner::RunNowOrPostTask(
1093 task_runners_.GetPlatformTaskRunner(),
1094 [view = platform_view_->GetWeakPtr(), &latch]() {
1095 if (view) {
1096 view->OnPreEngineRestart();
1097 }
1098 latch.Signal();
1099 });
1100 // This is blocking as any embedded platform views has to be flushed before
1101 // we re-run the Dart code.
1102 latch.Wait();
1103}
1104
1105// |Engine::Delegate|
1106void Shell::UpdateIsolateDescription(const std::string isolate_name,
1107 int64_t isolate_port) {
1108 Handler::Description description(isolate_port, isolate_name);
1109 vm_->GetServiceProtocol()->SetHandlerDescription(this, description);
1110}
1111
1112void Shell::SetNeedsReportTimings(bool value) {
1113 needs_report_timings_ = value;
1114}
1115
1116// |Engine::Delegate|
1117std::unique_ptr<std::vector<std::string>> Shell::ComputePlatformResolvedLocale(
1118 const std::vector<std::string>& supported_locale_data) {
1119 return ComputePlatformViewResolvedLocale(supported_locale_data);
1120}
1121
1122// |PlatformView::Delegate|
1123std::unique_ptr<std::vector<std::string>>
1124Shell::ComputePlatformViewResolvedLocale(
1125 const std::vector<std::string>& supported_locale_data) {
1126 return platform_view_->ComputePlatformResolvedLocales(supported_locale_data);
1127}
1128
1129void Shell::ReportTimings() {
1130 FML_DCHECK(is_setup_);
1131 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1132
1133 auto timings = std::move(unreported_timings_);
1134 unreported_timings_ = {};
1135 task_runners_.GetUITaskRunner()->PostTask([timings, engine = weak_engine_] {
1136 if (engine) {
1137 engine->ReportTimings(std::move(timings));
1138 }
1139 });
1140}
1141
1142size_t Shell::UnreportedFramesCount() const {
1143 // Check that this is running on the raster thread to avoid race conditions.
1144 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1145 FML_DCHECK(unreported_timings_.size() % FrameTiming::kCount == 0);
1146 return unreported_timings_.size() / FrameTiming::kCount;
1147}
1148
1149void Shell::OnFrameRasterized(const FrameTiming& timing) {
1150 FML_DCHECK(is_setup_);
1151 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1152
1153 // The C++ callback defined in settings.h and set by Flutter runner. This is
1154 // independent of the timings report to the Dart side.
1155 if (settings_.frame_rasterized_callback) {
1156 settings_.frame_rasterized_callback(timing);
1157 }
1158
1159 if (!needs_report_timings_) {
1160 return;
1161 }
1162
1163 for (auto phase : FrameTiming::kPhases) {
1164 unreported_timings_.push_back(
1165 timing.Get(phase).ToEpochDelta().ToMicroseconds());
1166 }
1167
1168 // In tests using iPhone 6S with profile mode, sending a batch of 1 frame or a
1169 // batch of 100 frames have roughly the same cost of less than 0.1ms. Sending
1170 // a batch of 500 frames costs about 0.2ms. The 1 second threshold usually
1171 // kicks in before we reaching the following 100 frames threshold. The 100
1172 // threshold here is mainly for unit tests (so we don't have to write a
1173 // 1-second unit test), and make sure that our vector won't grow too big with
1174 // future 120fps, 240fps, or 1000fps displays.
1175 //
1176 // In the profile/debug mode, the timings are used by development tools which
1177 // require a latency of no more than 100ms. Hence we lower that 1-second
1178 // threshold to 100ms because performance overhead isn't that critical in
1179 // those cases.
1180 if (!first_frame_rasterized_ || UnreportedFramesCount() >= 100) {
1181 first_frame_rasterized_ = true;
1182 ReportTimings();
1183 } else if (!frame_timings_report_scheduled_) {
1184#if FLUTTER_RELEASE
1185 constexpr int kBatchTimeInMilliseconds = 1000;
1186#else
1187 constexpr int kBatchTimeInMilliseconds = 100;
1188#endif
1189
1190 // Also make sure that frame times get reported with a max latency of 1
1191 // second. Otherwise, the timings of last few frames of an animation may
1192 // never be reported until the next animation starts.
1193 frame_timings_report_scheduled_ = true;
1194 task_runners_.GetRasterTaskRunner()->PostDelayedTask(
1195 [self = weak_factory_gpu_->GetWeakPtr()]() {
1196 if (!self.get()) {
1197 return;
1198 }
1199 self->frame_timings_report_scheduled_ = false;
1200 if (self->UnreportedFramesCount() > 0) {
1201 self->ReportTimings();
1202 }
1203 },
1204 fml::TimeDelta::FromMilliseconds(kBatchTimeInMilliseconds));
1205 }
1206}
1207
1208fml::Milliseconds Shell::GetFrameBudget() {
1209 if (display_refresh_rate_ > 0) {
1210 return fml::RefreshRateToFrameBudget(display_refresh_rate_.load());
1211 } else {
1212 return fml::kDefaultFrameBudget;
1213 }
1214}
1215
1216fml::TimePoint Shell::GetLatestFrameTargetTime() const {
1217 std::scoped_lock time_recorder_lock(time_recorder_mutex_);
1218 FML_CHECK(latest_frame_target_time_.has_value())
1219 << "GetLatestFrameTargetTime called before OnAnimatorBeginFrame";
1220 return latest_frame_target_time_.value();
1221}
1222
1223// |ServiceProtocol::Handler|
1224fml::RefPtr<fml::TaskRunner> Shell::GetServiceProtocolHandlerTaskRunner(
1225 std::string_view method) const {
1226 FML_DCHECK(is_setup_);
1227 auto found = service_protocol_handlers_.find(method);
1228 if (found != service_protocol_handlers_.end()) {
1229 return found->second.first;
1230 }
1231 return task_runners_.GetUITaskRunner();
1232}
1233
1234// |ServiceProtocol::Handler|
1235bool Shell::HandleServiceProtocolMessage(
1236 std::string_view method, // one if the extension names specified above.
1237 const ServiceProtocolMap& params,
1238 rapidjson::Document* response) {
1239 auto found = service_protocol_handlers_.find(method);
1240 if (found != service_protocol_handlers_.end()) {
1241 return found->second.second(params, response);
1242 }
1243 return false;
1244}
1245
1246// |ServiceProtocol::Handler|
1247ServiceProtocol::Handler::Description Shell::GetServiceProtocolDescription()
1248 const {
1249 return {
1250 engine_->GetUIIsolateMainPort(),
1251 engine_->GetUIIsolateName(),
1252 };
1253}
1254
1255static void ServiceProtocolParameterError(rapidjson::Document* response,
1256 std::string error_details) {
1257 auto& allocator = response->GetAllocator();
1258 response->SetObject();
1259 const int64_t kInvalidParams = -32602;
1260 response->AddMember("code", kInvalidParams, allocator);
1261 response->AddMember("message", "Invalid params", allocator);
1262 {
1263 rapidjson::Value details(rapidjson::kObjectType);
1264 details.AddMember("details", error_details, allocator);
1265 response->AddMember("data", details, allocator);
1266 }
1267}
1268
1269static void ServiceProtocolFailureError(rapidjson::Document* response,
1270 std::string message) {
1271 auto& allocator = response->GetAllocator();
1272 response->SetObject();
1273 const int64_t kJsonServerError = -32000;
1274 response->AddMember("code", kJsonServerError, allocator);
1275 response->AddMember("message", message, allocator);
1276}
1277
1278// Service protocol handler
1279bool Shell::OnServiceProtocolScreenshot(
1280 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1281 rapidjson::Document* response) {
1282 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1283 auto screenshot = rasterizer_->ScreenshotLastLayerTree(
1284 Rasterizer::ScreenshotType::CompressedImage, true);
1285 if (screenshot.data) {
1286 response->SetObject();
1287 auto& allocator = response->GetAllocator();
1288 response->AddMember("type", "Screenshot", allocator);
1289 rapidjson::Value image;
1290 image.SetString(static_cast<const char*>(screenshot.data->data()),
1291 screenshot.data->size(), allocator);
1292 response->AddMember("screenshot", image, allocator);
1293 return true;
1294 }
1295 ServiceProtocolFailureError(response, "Could not capture image screenshot.");
1296 return false;
1297}
1298
1299// Service protocol handler
1300bool Shell::OnServiceProtocolScreenshotSKP(
1301 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1302 rapidjson::Document* response) {
1303 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1304 auto screenshot = rasterizer_->ScreenshotLastLayerTree(
1305 Rasterizer::ScreenshotType::SkiaPicture, true);
1306 if (screenshot.data) {
1307 response->SetObject();
1308 auto& allocator = response->GetAllocator();
1309 response->AddMember("type", "ScreenshotSkp", allocator);
1310 rapidjson::Value skp;
1311 skp.SetString(static_cast<const char*>(screenshot.data->data()),
1312 screenshot.data->size(), allocator);
1313 response->AddMember("skp", skp, allocator);
1314 return true;
1315 }
1316 ServiceProtocolFailureError(response, "Could not capture SKP screenshot.");
1317 return false;
1318}
1319
1320// Service protocol handler
1321bool Shell::OnServiceProtocolRunInView(
1322 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1323 rapidjson::Document* response) {
1324 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1325
1326 if (params.count("mainScript") == 0) {
1327 ServiceProtocolParameterError(response,
1328 "'mainScript' parameter is missing.");
1329 return false;
1330 }
1331
1332 if (params.count("assetDirectory") == 0) {
1333 ServiceProtocolParameterError(response,
1334 "'assetDirectory' parameter is missing.");
1335 return false;
1336 }
1337
1338 std::string main_script_path =
1339 fml::paths::FromURI(params.at("mainScript").data());
1340 std::string asset_directory_path =
1341 fml::paths::FromURI(params.at("assetDirectory").data());
1342
1343 auto main_script_file_mapping =
1344 std::make_unique<fml::FileMapping>(fml::OpenFile(
1345 main_script_path.c_str(), false, fml::FilePermission::kRead));
1346
1347 auto isolate_configuration = IsolateConfiguration::CreateForKernel(
1348 std::move(main_script_file_mapping));
1349
1350 RunConfiguration configuration(std::move(isolate_configuration));
1351
1352 configuration.SetEntrypointAndLibrary(engine_->GetLastEntrypoint(),
1353 engine_->GetLastEntrypointLibrary());
1354
1355 configuration.AddAssetResolver(
1356 std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
1357 asset_directory_path.c_str(), false, fml::FilePermission::kRead)));
1358
1359 auto& allocator = response->GetAllocator();
1360 response->SetObject();
1361 if (engine_->Restart(std::move(configuration))) {
1362 response->AddMember("type", "Success", allocator);
1363 auto new_description = GetServiceProtocolDescription();
1364 rapidjson::Value view(rapidjson::kObjectType);
1365 new_description.Write(this, view, allocator);
1366 response->AddMember("view", view, allocator);
1367 return true;
1368 } else {
1369 FML_DLOG(ERROR) << "Could not run configuration in engine.";
1370 ServiceProtocolFailureError(response,
1371 "Could not run configuration in engine.");
1372 return false;
1373 }
1374
1375 FML_DCHECK(false);
1376 return false;
1377}
1378
1379// Service protocol handler
1380bool Shell::OnServiceProtocolFlushUIThreadTasks(
1381 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1382 rapidjson::Document* response) {
1383 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1384 // This API should not be invoked by production code.
1385 // It can potentially starve the service isolate if the main isolate pauses
1386 // at a breakpoint or is in an infinite loop.
1387 //
1388 // It should be invoked from the VM Service and and blocks it until UI thread
1389 // tasks are processed.
1390 response->SetObject();
1391 response->AddMember("type", "Success", response->GetAllocator());
1392 return true;
1393}
1394
1395bool Shell::OnServiceProtocolGetDisplayRefreshRate(
1396 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1397 rapidjson::Document* response) {
1398 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1399 response->SetObject();
1400 response->AddMember("type", "DisplayRefreshRate", response->GetAllocator());
1401 response->AddMember("fps", engine_->GetDisplayRefreshRate(),
1402 response->GetAllocator());
1403 return true;
1404}
1405
1406bool Shell::OnServiceProtocolGetSkSLs(
1407 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1408 rapidjson::Document* response) {
1409 FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread());
1410 response->SetObject();
1411 response->AddMember("type", "GetSkSLs", response->GetAllocator());
1412
1413 rapidjson::Value shaders_json(rapidjson::kObjectType);
1414 PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
1415 std::vector<PersistentCache::SkSLCache> sksls = persistent_cache->LoadSkSLs();
1416 for (const auto& sksl : sksls) {
1417 size_t b64_size =
1418 SkBase64::Encode(sksl.second->data(), sksl.second->size(), nullptr);
1419 sk_sp<SkData> b64_data = SkData::MakeUninitialized(b64_size + 1);
1420 char* b64_char = static_cast<char*>(b64_data->writable_data());
1421 SkBase64::Encode(sksl.second->data(), sksl.second->size(), b64_char);
1422 b64_char[b64_size] = 0; // make it null terminated for printing
1423 rapidjson::Value shader_value(b64_char, response->GetAllocator());
1424 rapidjson::Value shader_key(PersistentCache::SkKeyToFilePath(*sksl.first),
1425 response->GetAllocator());
1426 shaders_json.AddMember(shader_key, shader_value, response->GetAllocator());
1427 }
1428 response->AddMember("SkSLs", shaders_json, response->GetAllocator());
1429 return true;
1430}
1431
1432bool Shell::OnServiceProtocolEstimateRasterCacheMemory(
1433 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1434 rapidjson::Document* response) {
1435 FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
1436 const auto& raster_cache = rasterizer_->compositor_context()->raster_cache();
1437 response->SetObject();
1438 response->AddMember("type", "EstimateRasterCacheMemory",
1439 response->GetAllocator());
1440 response->AddMember<uint64_t>("layerBytes",
1441 raster_cache.EstimateLayerCacheByteSize(),
1442 response->GetAllocator());
1443 response->AddMember<uint64_t>("pictureBytes",
1444 raster_cache.EstimatePictureCacheByteSize(),
1445 response->GetAllocator());
1446 return true;
1447}
1448
1449// Service protocol handler
1450bool Shell::OnServiceProtocolSetAssetBundlePath(
1451 const ServiceProtocol::Handler::ServiceProtocolMap& params,
1452 rapidjson::Document* response) {
1453 FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1454
1455 if (params.count("assetDirectory") == 0) {
1456 ServiceProtocolParameterError(response,
1457 "'assetDirectory' parameter is missing.");
1458 return false;
1459 }
1460
1461 auto& allocator = response->GetAllocator();
1462 response->SetObject();
1463
1464 auto asset_manager = std::make_shared<AssetManager>();
1465
1466 asset_manager->PushFront(std::make_unique<DirectoryAssetBundle>(
1467 fml::OpenDirectory(params.at("assetDirectory").data(), false,
1468 fml::FilePermission::kRead)));
1469
1470 if (engine_->UpdateAssetManager(std::move(asset_manager))) {
1471 response->AddMember("type", "Success", allocator);
1472 auto new_description = GetServiceProtocolDescription();
1473 rapidjson::Value view(rapidjson::kObjectType);
1474 new_description.Write(this, view, allocator);
1475 response->AddMember("view", view, allocator);
1476 return true;
1477 } else {
1478 FML_DLOG(ERROR) << "Could not update asset directory.";
1479 ServiceProtocolFailureError(response, "Could not update asset directory.");
1480 return false;
1481 }
1482
1483 FML_DCHECK(false);
1484 return false;
1485}
1486
1487Rasterizer::Screenshot Shell::Screenshot(
1488 Rasterizer::ScreenshotType screenshot_type,
1489 bool base64_encode) {
1490 TRACE_EVENT0("flutter", "Shell::Screenshot");
1491 fml::AutoResetWaitableEvent latch;
1492 Rasterizer::Screenshot screenshot;
1493 fml::TaskRunner::RunNowOrPostTask(
1494 task_runners_.GetRasterTaskRunner(), [&latch, //
1495 rasterizer = GetRasterizer(), //
1496 &screenshot, //
1497 screenshot_type, //
1498 base64_encode //
1499 ]() {
1500 if (rasterizer) {
1501 screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type,
1502 base64_encode);
1503 }
1504 latch.Signal();
1505 });
1506 latch.Wait();
1507 return screenshot;
1508}
1509
1510fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) {
1511 FML_DCHECK(is_setup_);
1512 if (task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread() ||
1513 task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()) {
1514 return fml::Status(fml::StatusCode::kFailedPrecondition,
1515 "WaitForFirstFrame called from thread that can't wait "
1516 "because it is responsible for generating the frame.");
1517 }
1518
1519 std::unique_lock<std::mutex> lock(waiting_for_first_frame_mutex_);
1520 bool success = waiting_for_first_frame_condition_.wait_for(
1521 lock, std::chrono::milliseconds(timeout.ToMilliseconds()),
1522 [&waiting_for_first_frame = waiting_for_first_frame_] {
1523 return !waiting_for_first_frame.load();
1524 });
1525 if (success) {
1526 return fml::Status();
1527 } else {
1528 return fml::Status(fml::StatusCode::kDeadlineExceeded, "timeout");
1529 }
1530}
1531
1532bool Shell::ReloadSystemFonts() {
1533 FML_DCHECK(is_setup_);
1534 FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
1535
1536 if (!engine_) {
1537 return false;
1538 }
1539 engine_->GetFontCollection().GetFontCollection()->SetupDefaultFontManager();
1540 engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache();
1541 // After system fonts are reloaded, we send a system channel message
1542 // to notify flutter framework.
1543 rapidjson::Document document;
1544 document.SetObject();
1545 auto& allocator = document.GetAllocator();
1546 rapidjson::Value message_value;
1547 message_value.SetString(kFontChange, allocator);
1548 document.AddMember(kTypeKey, message_value, allocator);
1549
1550 rapidjson::StringBuffer buffer;
1551 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1552 document.Accept(writer);
1553 std::string message = buffer.GetString();
1554 fml::RefPtr<PlatformMessage> fontsChangeMessage =
1555 fml::MakeRefCounted<flutter::PlatformMessage>(
1556 kSystemChannel, std::vector<uint8_t>(message.begin(), message.end()),
1557 nullptr);
1558
1559 OnPlatformViewDispatchPlatformMessage(fontsChangeMessage);
1560 return true;
1561}
1562
1563std::shared_ptr<fml::SyncSwitch> Shell::GetIsGpuDisabledSyncSwitch() const {
1564 return is_gpu_disabled_sync_switch_;
1565}
1566
1567} // namespace flutter
1568