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