| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #define RAPIDJSON_HAS_STDSTRING 1 |
| |
| #include "flutter/shell/common/shell.h" |
| |
| #include <memory> |
| #include <sstream> |
| #include <vector> |
| |
| #include "flutter/assets/directory_asset_bundle.h" |
| #include "flutter/fml/file.h" |
| #include "flutter/fml/icu_util.h" |
| #include "flutter/fml/log_settings.h" |
| #include "flutter/fml/logging.h" |
| #include "flutter/fml/make_copyable.h" |
| #include "flutter/fml/message_loop.h" |
| #include "flutter/fml/paths.h" |
| #include "flutter/fml/trace_event.h" |
| #include "flutter/fml/unique_fd.h" |
| #include "flutter/runtime/dart_vm.h" |
| #include "flutter/runtime/start_up.h" |
| #include "flutter/shell/common/engine.h" |
| #include "flutter/shell/common/persistent_cache.h" |
| #include "flutter/shell/common/skia_event_tracer_impl.h" |
| #include "flutter/shell/common/switches.h" |
| #include "flutter/shell/common/vsync_waiter.h" |
| #include "third_party/dart/runtime/include/dart_tools_api.h" |
| #include "third_party/skia/include/core/SkGraphics.h" |
| #include "third_party/tonic/common/log.h" |
| |
| namespace flutter { |
| |
| constexpr char kSkiaChannel[] = "flutter/skia"; |
| |
| std::unique_ptr<Shell> Shell::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) { |
| if (!task_runners.IsValid()) { |
| FML_LOG(ERROR) << "Task runners to run the shell were invalid."; |
| return nullptr; |
| } |
| |
| auto shell = |
| std::unique_ptr<Shell>(new Shell(std::move(vm), task_runners, settings)); |
| |
| // Create the platform view on the platform thread (this thread). |
| auto platform_view = on_create_platform_view(*shell.get()); |
| if (!platform_view || !platform_view->GetWeakPtr()) { |
| return nullptr; |
| } |
| |
| // Ask the platform view for the vsync waiter. This will be used by the engine |
| // to create the animator. |
| auto vsync_waiter = platform_view->CreateVSyncWaiter(); |
| if (!vsync_waiter) { |
| return nullptr; |
| } |
| |
| // Create the IO manager on the IO thread. The IO manager must be initialized |
| // first because it has state that the other subsystems depend on. It must |
| // first be booted and the necessary references obtained to initialize the |
| // other subsystems. |
| fml::AutoResetWaitableEvent io_latch; |
| std::unique_ptr<ShellIOManager> io_manager; |
| auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(); |
| fml::TaskRunner::RunNowOrPostTask( |
| io_task_runner, |
| [&io_latch, // |
| &io_manager, // |
| &platform_view, // |
| io_task_runner // |
| ]() { |
| TRACE_EVENT0("flutter", "ShellSetupIOSubsystem"); |
| io_manager = std::make_unique<ShellIOManager>( |
| platform_view->CreateResourceContext(), io_task_runner); |
| io_latch.Signal(); |
| }); |
| io_latch.Wait(); |
| |
| // Create the rasterizer on the GPU thread. |
| fml::AutoResetWaitableEvent gpu_latch; |
| std::unique_ptr<Rasterizer> rasterizer; |
| fml::WeakPtr<SnapshotDelegate> snapshot_delegate; |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners.GetGPUTaskRunner(), [&gpu_latch, // |
| &rasterizer, // |
| on_create_rasterizer, // |
| shell = shell.get(), // |
| &snapshot_delegate // |
| ]() { |
| TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem"); |
| if (auto new_rasterizer = on_create_rasterizer(*shell)) { |
| rasterizer = std::move(new_rasterizer); |
| snapshot_delegate = rasterizer->GetSnapshotDelegate(); |
| } |
| gpu_latch.Signal(); |
| }); |
| |
| gpu_latch.Wait(); |
| |
| // Create the engine on the UI thread. |
| fml::AutoResetWaitableEvent ui_latch; |
| std::unique_ptr<Engine> engine; |
| fml::TaskRunner::RunNowOrPostTask( |
| shell->GetTaskRunners().GetUITaskRunner(), |
| fml::MakeCopyable([&ui_latch, // |
| &engine, // |
| shell = shell.get(), // |
| isolate_snapshot = std::move(isolate_snapshot), // |
| shared_snapshot = std::move(shared_snapshot), // |
| vsync_waiter = std::move(vsync_waiter), // |
| snapshot_delegate = std::move(snapshot_delegate), // |
| io_manager = io_manager->GetWeakPtr() // |
| ]() mutable { |
| TRACE_EVENT0("flutter", "ShellSetupUISubsystem"); |
| const auto& task_runners = shell->GetTaskRunners(); |
| |
| // The animator is owned by the UI thread but it gets its vsync pulses |
| // from the platform. |
| auto animator = std::make_unique<Animator>(*shell, task_runners, |
| std::move(vsync_waiter)); |
| |
| engine = std::make_unique<Engine>(*shell, // |
| *shell->GetDartVM(), // |
| std::move(isolate_snapshot), // |
| std::move(shared_snapshot), // |
| task_runners, // |
| shell->GetSettings(), // |
| std::move(animator), // |
| std::move(snapshot_delegate), // |
| std::move(io_manager) // |
| ); |
| ui_latch.Signal(); |
| })); |
| |
| ui_latch.Wait(); |
| // We are already on the platform thread. So there is no platform latch to |
| // wait on. |
| |
| if (!shell->Setup(std::move(platform_view), // |
| std::move(engine), // |
| std::move(rasterizer), // |
| std::move(io_manager)) // |
| ) { |
| return nullptr; |
| } |
| |
| return shell; |
| } |
| |
| static void RecordStartupTimestamp() { |
| if (engine_main_enter_ts == 0) { |
| engine_main_enter_ts = Dart_TimelineGetMicros(); |
| } |
| } |
| |
| // Though there can be multiple shells, some settings apply to all components in |
| // the process. These have to be setup before the shell or any of its |
| // sub-components can be initialized. In a perfect world, this would be empty. |
| // TODO(chinmaygarde): The unfortunate side effect of this call is that settings |
| // that cause shell initialization failures will still lead to some of their |
| // settings being applied. |
| static void PerformInitializationTasks(const Settings& settings) { |
| { |
| fml::LogSettings log_settings; |
| log_settings.min_log_level = |
| settings.verbose_logging ? fml::LOG_INFO : fml::LOG_ERROR; |
| fml::SetLogSettings(log_settings); |
| } |
| |
| static std::once_flag gShellSettingsInitialization = {}; |
| std::call_once(gShellSettingsInitialization, [&settings] { |
| RecordStartupTimestamp(); |
| |
| tonic::SetLogHandler( |
| [](const char* message) { FML_LOG(ERROR) << message; }); |
| |
| if (settings.trace_skia) { |
| InitSkiaEventTracer(settings.trace_skia); |
| } |
| |
| if (!settings.skia_deterministic_rendering_on_cpu) { |
| SkGraphics::Init(); |
| } else { |
| FML_DLOG(INFO) << "Skia deterministic rendering is enabled."; |
| } |
| |
| if (settings.icu_initialization_required) { |
| if (settings.icu_data_path.size() != 0) { |
| fml::icu::InitializeICU(settings.icu_data_path); |
| } else if (settings.icu_mapper) { |
| fml::icu::InitializeICUFromMapping(settings.icu_mapper()); |
| } else { |
| FML_DLOG(WARNING) << "Skipping ICU initialization in the shell."; |
| } |
| } |
| }); |
| } |
| |
| std::unique_ptr<Shell> Shell::Create( |
| TaskRunners task_runners, |
| Settings settings, |
| Shell::CreateCallback<PlatformView> on_create_platform_view, |
| Shell::CreateCallback<Rasterizer> on_create_rasterizer) { |
| PerformInitializationTasks(settings); |
| |
| TRACE_EVENT0("flutter", "Shell::Create"); |
| |
| auto vm = DartVMRef::Create(settings); |
| FML_CHECK(vm) << "Must be able to initialize the VM."; |
| |
| auto vm_data = vm->GetVMData(); |
| |
| return Shell::Create(std::move(task_runners), // |
| std::move(settings), // |
| vm_data->GetIsolateSnapshot(), // isolate snapshot |
| DartSnapshot::Empty(), // shared snapshot |
| std::move(on_create_platform_view), // |
| std::move(on_create_rasterizer), // |
| std::move(vm) // |
| ); |
| } |
| |
| std::unique_ptr<Shell> Shell::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) { |
| PerformInitializationTasks(settings); |
| |
| TRACE_EVENT0("flutter", "Shell::CreateWithSnapshots"); |
| |
| if (!task_runners.IsValid() || !on_create_platform_view || |
| !on_create_rasterizer) { |
| return nullptr; |
| } |
| |
| fml::AutoResetWaitableEvent latch; |
| std::unique_ptr<Shell> shell; |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners.GetPlatformTaskRunner(), |
| fml::MakeCopyable([&latch, // |
| vm = std::move(vm), // |
| &shell, // |
| task_runners = std::move(task_runners), // |
| settings, // |
| isolate_snapshot = std::move(isolate_snapshot), // |
| shared_snapshot = std::move(shared_snapshot), // |
| on_create_platform_view, // |
| on_create_rasterizer // |
| ]() mutable { |
| shell = CreateShellOnPlatformThread(std::move(vm), |
| std::move(task_runners), // |
| settings, // |
| std::move(isolate_snapshot), // |
| std::move(shared_snapshot), // |
| on_create_platform_view, // |
| on_create_rasterizer // |
| ); |
| latch.Signal(); |
| })); |
| latch.Wait(); |
| return shell; |
| } |
| |
| Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) |
| : task_runners_(std::move(task_runners)), |
| settings_(std::move(settings)), |
| vm_(std::move(vm)) { |
| FML_CHECK(vm_) << "Must have access to VM to create a shell."; |
| FML_DCHECK(task_runners_.IsValid()); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| // Install service protocol handlers. |
| |
| service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName |
| .ToString()] = { |
| task_runners_.GetGPUTaskRunner(), |
| std::bind(&Shell::OnServiceProtocolScreenshot, this, |
| std::placeholders::_1, std::placeholders::_2)}; |
| service_protocol_handlers_[ServiceProtocol::kScreenshotSkpExtensionName |
| .ToString()] = { |
| task_runners_.GetGPUTaskRunner(), |
| std::bind(&Shell::OnServiceProtocolScreenshotSKP, this, |
| std::placeholders::_1, std::placeholders::_2)}; |
| service_protocol_handlers_[ServiceProtocol::kRunInViewExtensionName |
| .ToString()] = { |
| task_runners_.GetUITaskRunner(), |
| std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1, |
| std::placeholders::_2)}; |
| service_protocol_handlers_[ServiceProtocol::kFlushUIThreadTasksExtensionName |
| .ToString()] = { |
| task_runners_.GetUITaskRunner(), |
| std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this, |
| std::placeholders::_1, std::placeholders::_2)}; |
| service_protocol_handlers_[ServiceProtocol::kSetAssetBundlePathExtensionName |
| .ToString()] = { |
| task_runners_.GetUITaskRunner(), |
| std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, |
| std::placeholders::_1, std::placeholders::_2)}; |
| service_protocol_handlers_ |
| [ServiceProtocol::kGetDisplayRefreshRateExtensionName.ToString()] = { |
| task_runners_.GetUITaskRunner(), |
| std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this, |
| std::placeholders::_1, std::placeholders::_2)}; |
| } |
| |
| Shell::~Shell() { |
| PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner( |
| task_runners_.GetIOTaskRunner()); |
| |
| vm_->GetServiceProtocol()->RemoveHandler(this); |
| |
| fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch; |
| |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners_.GetUITaskRunner(), |
| fml::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable { |
| engine.reset(); |
| ui_latch.Signal(); |
| })); |
| ui_latch.Wait(); |
| |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners_.GetGPUTaskRunner(), |
| fml::MakeCopyable( |
| [rasterizer = std::move(rasterizer_), &gpu_latch]() mutable { |
| rasterizer.reset(); |
| gpu_latch.Signal(); |
| })); |
| gpu_latch.Wait(); |
| |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners_.GetIOTaskRunner(), |
| fml::MakeCopyable([io_manager = std::move(io_manager_), |
| platform_view = platform_view_.get(), |
| &io_latch]() mutable { |
| io_manager.reset(); |
| if (platform_view) { |
| platform_view->ReleaseResourceContext(); |
| } |
| io_latch.Signal(); |
| })); |
| |
| io_latch.Wait(); |
| |
| // The platform view must go last because it may be holding onto platform side |
| // counterparts to resources owned by subsystems running on other threads. For |
| // example, the NSOpenGLContext on the Mac. |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners_.GetPlatformTaskRunner(), |
| fml::MakeCopyable([platform_view = std::move(platform_view_), |
| &platform_latch]() mutable { |
| platform_view.reset(); |
| platform_latch.Signal(); |
| })); |
| platform_latch.Wait(); |
| } |
| |
| bool Shell::IsSetup() const { |
| return is_setup_; |
| } |
| |
| bool Shell::Setup(std::unique_ptr<PlatformView> platform_view, |
| std::unique_ptr<Engine> engine, |
| std::unique_ptr<Rasterizer> rasterizer, |
| std::unique_ptr<ShellIOManager> io_manager) { |
| if (is_setup_) { |
| return false; |
| } |
| |
| if (!platform_view || !engine || !rasterizer || !io_manager) { |
| return false; |
| } |
| |
| platform_view_ = std::move(platform_view); |
| engine_ = std::move(engine); |
| rasterizer_ = std::move(rasterizer); |
| io_manager_ = std::move(io_manager); |
| |
| is_setup_ = true; |
| |
| vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription()); |
| |
| PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( |
| task_runners_.GetIOTaskRunner()); |
| |
| PersistentCache::GetCacheForProcess()->SetIsDumpingSkp( |
| settings_.dump_skp_on_shader_compilation); |
| |
| return true; |
| } |
| |
| const Settings& Shell::GetSettings() const { |
| return settings_; |
| } |
| |
| const TaskRunners& Shell::GetTaskRunners() const { |
| return task_runners_; |
| } |
| |
| fml::WeakPtr<Rasterizer> Shell::GetRasterizer() { |
| FML_DCHECK(is_setup_); |
| return rasterizer_->GetWeakPtr(); |
| } |
| |
| fml::WeakPtr<Engine> Shell::GetEngine() { |
| FML_DCHECK(is_setup_); |
| return engine_->GetWeakPtr(); |
| } |
| |
| fml::WeakPtr<PlatformView> Shell::GetPlatformView() { |
| FML_DCHECK(is_setup_); |
| return platform_view_->GetWeakPtr(); |
| } |
| |
| DartVM* Shell::GetDartVM() { |
| return &vm_; |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewCreated(std::unique_ptr<Surface> surface) { |
| TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated"); |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| // Note: |
| // This is a synchronous operation because certain platforms depend on |
| // setup/suspension of all activities that may be interacting with the GPU in |
| // a synchronous fashion. |
| |
| fml::AutoResetWaitableEvent latch; |
| auto gpu_task = fml::MakeCopyable([rasterizer = rasterizer_->GetWeakPtr(), // |
| surface = std::move(surface), // |
| &latch]() mutable { |
| if (rasterizer) { |
| rasterizer->Setup(std::move(surface)); |
| } |
| // Step 3: All done. Signal the latch that the platform thread is waiting |
| // on. |
| latch.Signal(); |
| }); |
| |
| // The normal flow exectued by this method is that the platform thread is |
| // starting the sequence and waiting on the latch. Later the UI thread posts |
| // gpu_task to the GPU thread which signals the latch. If the GPU the and |
| // platform threads are the same this results in a deadlock as the gpu_task |
| // will never be posted to the plaform/gpu thread that is blocked on a latch. |
| // To avoid the described deadlock, if the gpu and the platform threads are |
| // the same, should_post_gpu_task will be false, and then instead of posting a |
| // task to the gpu thread, the ui thread just signals the latch and the |
| // platform/gpu thread follows with executing gpu_task. |
| bool should_post_gpu_task = |
| task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); |
| |
| auto ui_task = [engine = engine_->GetWeakPtr(), // |
| gpu_task_runner = task_runners_.GetGPUTaskRunner(), // |
| gpu_task, should_post_gpu_task, |
| &latch // |
| ] { |
| if (engine) { |
| engine->OnOutputSurfaceCreated(); |
| } |
| // Step 2: Next, tell the GPU thread that it should create a surface for its |
| // rasterizer. |
| if (should_post_gpu_task) { |
| fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); |
| } else { |
| // See comment on should_post_gpu_task, in this case we just unblock |
| // the platform thread. |
| latch.Signal(); |
| } |
| }; |
| |
| // Threading: Capture platform view by raw pointer and not the weak pointer. |
| // We are going to use the pointer on the IO thread which is not safe with a |
| // weak pointer. However, we are preventing the platform view from being |
| // collected by using a latch. |
| auto* platform_view = platform_view_.get(); |
| |
| FML_DCHECK(platform_view); |
| |
| auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view, |
| ui_task_runner = task_runners_.GetUITaskRunner(), ui_task] { |
| if (io_manager && !io_manager->GetResourceContext()) { |
| io_manager->NotifyResourceContextAvailable( |
| platform_view->CreateResourceContext()); |
| } |
| // Step 1: Next, post a task on the UI thread to tell the engine that it has |
| // an output surface. |
| fml::TaskRunner::RunNowOrPostTask(ui_task_runner, ui_task); |
| }; |
| |
| fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task); |
| |
| latch.Wait(); |
| if (!should_post_gpu_task) { |
| // See comment on should_post_gpu_task, in this case the gpu_task |
| // wasn't executed, and we just run it here as the platform thread |
| // is the GPU thread. |
| gpu_task(); |
| } |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewDestroyed() { |
| TRACE_EVENT0("flutter", "Shell::OnPlatformViewDestroyed"); |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| // Note: |
| // This is a synchronous operation because certain platforms depend on |
| // setup/suspension of all activities that may be interacting with the GPU in |
| // a synchronous fashion. |
| |
| fml::AutoResetWaitableEvent latch; |
| |
| auto io_task = [io_manager = io_manager_.get(), &latch]() { |
| // Execute any pending Skia object deletions while GPU access is still |
| // allowed. |
| io_manager->GetSkiaUnrefQueue()->Drain(); |
| // Step 3: All done. Signal the latch that the platform thread is waiting |
| // on. |
| latch.Signal(); |
| }; |
| |
| auto gpu_task = [rasterizer = rasterizer_->GetWeakPtr(), |
| io_task_runner = task_runners_.GetIOTaskRunner(), |
| io_task]() { |
| if (rasterizer) { |
| rasterizer->Teardown(); |
| } |
| // Step 2: Next, tell the IO thread to complete its remaining work. |
| fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task); |
| }; |
| |
| // The normal flow exectued by this method is that the platform thread is |
| // starting the sequence and waiting on the latch. Later the UI thread posts |
| // gpu_task to the GPU thread triggers signaling the latch(on the IO thread). |
| // If the GPU the and platform threads are the same this results in a deadlock |
| // as the gpu_task will never be posted to the plaform/gpu thread that is |
| // blocked on a latch. To avoid the described deadlock, if the gpu and the |
| // platform threads are the same, should_post_gpu_task will be false, and then |
| // instead of posting a task to the gpu thread, the ui thread just signals the |
| // latch and the platform/gpu thread follows with executing gpu_task. |
| bool should_post_gpu_task = |
| task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); |
| |
| auto ui_task = [engine = engine_->GetWeakPtr(), |
| gpu_task_runner = task_runners_.GetGPUTaskRunner(), gpu_task, |
| should_post_gpu_task, &latch]() { |
| if (engine) { |
| engine->OnOutputSurfaceDestroyed(); |
| } |
| // Step 1: Next, tell the GPU thread that its rasterizer should suspend |
| // access to the underlying surface. |
| if (should_post_gpu_task) { |
| fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); |
| } else { |
| // See comment on should_post_gpu_task, in this case we just unblock |
| // the platform thread. |
| latch.Signal(); |
| } |
| }; |
| |
| // Step 0: Post a task onto the UI thread to tell the engine that its output |
| // surface is about to go away. |
| fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); |
| latch.Wait(); |
| if (!should_post_gpu_task) { |
| // See comment on should_post_gpu_task, in this case the gpu_task |
| // wasn't executed, and we just run it here as the platform thread |
| // is the GPU thread. |
| gpu_task(); |
| latch.Wait(); |
| } |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetUITaskRunner()->PostTask( |
| [engine = engine_->GetWeakPtr(), metrics]() { |
| if (engine) { |
| engine->SetViewportMetrics(metrics); |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewDispatchPlatformMessage( |
| fml::RefPtr<PlatformMessage> message) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetUITaskRunner()->PostTask( |
| [engine = engine_->GetWeakPtr(), message = std::move(message)] { |
| if (engine) { |
| engine->DispatchPlatformMessage(std::move(message)); |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewDispatchPointerDataPacket( |
| std::unique_ptr<PointerDataPacket> packet) { |
| TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchPointerDataPacket"); |
| TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( |
| [engine = engine_->GetWeakPtr(), packet = std::move(packet), |
| flow_id = next_pointer_flow_id_] { |
| if (engine) { |
| engine->DispatchPointerDataPacket(*packet, flow_id); |
| } |
| })); |
| next_pointer_flow_id_++; |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id, |
| SemanticsAction action, |
| std::vector<uint8_t> args) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetUITaskRunner()->PostTask( |
| [engine = engine_->GetWeakPtr(), id, action, args = std::move(args)] { |
| if (engine) { |
| engine->DispatchSemanticsAction(id, action, std::move(args)); |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetUITaskRunner()->PostTask( |
| [engine = engine_->GetWeakPtr(), enabled] { |
| if (engine) { |
| engine->SetSemanticsEnabled(enabled); |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetUITaskRunner()->PostTask( |
| [engine = engine_->GetWeakPtr(), flags] { |
| if (engine) { |
| engine->SetAccessibilityFeatures(flags); |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewRegisterTexture( |
| std::shared_ptr<flutter::Texture> texture) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr(), texture] { |
| if (rasterizer) { |
| if (auto* registry = rasterizer->GetTextureRegistry()) { |
| registry->RegisterTexture(texture); |
| } |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { |
| if (rasterizer) { |
| if (auto* registry = rasterizer->GetTextureRegistry()) { |
| registry->UnregisterTexture(texture_id); |
| } |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| // Tell the rasterizer that one of its textures has a new frame available. |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { |
| auto* registry = rasterizer->GetTextureRegistry(); |
| |
| if (!registry) { |
| return; |
| } |
| |
| auto texture = registry->GetTexture(texture_id); |
| |
| if (!texture) { |
| return; |
| } |
| |
| texture->MarkNewFrameAvailable(); |
| }); |
| |
| // Schedule a new frame without having to rebuild the layer tree. |
| task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() { |
| if (engine) { |
| engine->ScheduleFrame(false); |
| } |
| }); |
| } |
| |
| // |PlatformView::Delegate| |
| void Shell::OnPlatformViewSetNextFrameCallback(fml::closure closure) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr(), closure = std::move(closure)]() { |
| if (rasterizer) { |
| rasterizer->SetNextFrameCallback(std::move(closure)); |
| } |
| }); |
| } |
| |
| // |Animator::Delegate| |
| void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_time) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| if (engine_) { |
| engine_->BeginFrame(frame_time); |
| } |
| } |
| |
| // |Animator::Delegate| |
| void Shell::OnAnimatorNotifyIdle(int64_t deadline) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| if (engine_) { |
| engine_->NotifyIdle(deadline); |
| } |
| } |
| |
| // |Animator::Delegate| |
| void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) { |
| FML_DCHECK(is_setup_); |
| |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr(), |
| pipeline = std::move(pipeline)]() { |
| if (rasterizer) { |
| rasterizer->Draw(pipeline); |
| } |
| }); |
| } |
| |
| // |Animator::Delegate| |
| void Shell::OnAnimatorDrawLastLayerTree() { |
| FML_DCHECK(is_setup_); |
| |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr()]() { |
| if (rasterizer) { |
| rasterizer->DrawLastLayerTree(); |
| } |
| }); |
| } |
| |
| // |Engine::Delegate| |
| void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update, |
| CustomAccessibilityActionUpdates actions) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| task_runners_.GetPlatformTaskRunner()->PostTask( |
| [view = platform_view_->GetWeakPtr(), update = std::move(update), |
| actions = std::move(actions)] { |
| if (view) { |
| view->UpdateSemantics(std::move(update), std::move(actions)); |
| } |
| }); |
| } |
| |
| // |Engine::Delegate| |
| void Shell::OnEngineHandlePlatformMessage( |
| fml::RefPtr<PlatformMessage> message) { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| if (message->channel() == kSkiaChannel) { |
| HandleEngineSkiaMessage(std::move(message)); |
| return; |
| } |
| |
| task_runners_.GetPlatformTaskRunner()->PostTask( |
| [view = platform_view_->GetWeakPtr(), message = std::move(message)]() { |
| if (view) { |
| view->HandlePlatformMessage(std::move(message)); |
| } |
| }); |
| } |
| |
| void Shell::HandleEngineSkiaMessage(fml::RefPtr<PlatformMessage> message) { |
| const auto& data = message->data(); |
| |
| rapidjson::Document document; |
| document.Parse(reinterpret_cast<const char*>(data.data()), data.size()); |
| if (document.HasParseError() || !document.IsObject()) |
| return; |
| auto root = document.GetObject(); |
| auto method = root.FindMember("method"); |
| if (method->value != "Skia.setResourceCacheMaxBytes") |
| return; |
| auto args = root.FindMember("args"); |
| if (args == root.MemberEnd() || !args->value.IsInt()) |
| return; |
| |
| task_runners_.GetGPUTaskRunner()->PostTask( |
| [rasterizer = rasterizer_->GetWeakPtr(), |
| max_bytes = args->value.GetInt()] { |
| if (rasterizer) { |
| rasterizer->SetResourceCacheMaxBytes(max_bytes); |
| } |
| }); |
| } |
| |
| // |Engine::Delegate| |
| void Shell::OnPreEngineRestart() { |
| FML_DCHECK(is_setup_); |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| fml::AutoResetWaitableEvent latch; |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners_.GetPlatformTaskRunner(), |
| [view = platform_view_->GetWeakPtr(), &latch]() { |
| if (view) { |
| view->OnPreEngineRestart(); |
| } |
| latch.Signal(); |
| }); |
| // This is blocking as any embedded platform views has to be flushed before |
| // we re-run the Dart code. |
| latch.Wait(); |
| } |
| |
| // |Engine::Delegate| |
| void Shell::UpdateIsolateDescription(const std::string isolate_name, |
| int64_t isolate_port) { |
| Handler::Description description(isolate_port, isolate_name); |
| vm_->GetServiceProtocol()->SetHandlerDescription(this, description); |
| } |
| |
| // |ServiceProtocol::Handler| |
| fml::RefPtr<fml::TaskRunner> Shell::GetServiceProtocolHandlerTaskRunner( |
| fml::StringView method) const { |
| FML_DCHECK(is_setup_); |
| auto found = service_protocol_handlers_.find(method.ToString()); |
| if (found != service_protocol_handlers_.end()) { |
| return found->second.first; |
| } |
| return task_runners_.GetUITaskRunner(); |
| } |
| |
| // |ServiceProtocol::Handler| |
| bool Shell::HandleServiceProtocolMessage( |
| fml::StringView method, // one if the extension names specified above. |
| const ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| auto found = service_protocol_handlers_.find(method.ToString()); |
| if (found != service_protocol_handlers_.end()) { |
| return found->second.second(params, response); |
| } |
| return false; |
| } |
| |
| // |ServiceProtocol::Handler| |
| ServiceProtocol::Handler::Description Shell::GetServiceProtocolDescription() |
| const { |
| return { |
| engine_->GetUIIsolateMainPort(), |
| engine_->GetUIIsolateName(), |
| }; |
| } |
| |
| static void ServiceProtocolParameterError(rapidjson::Document& response, |
| std::string error_details) { |
| auto& allocator = response.GetAllocator(); |
| response.SetObject(); |
| const int64_t kInvalidParams = -32602; |
| response.AddMember("code", kInvalidParams, allocator); |
| response.AddMember("message", "Invalid params", allocator); |
| { |
| rapidjson::Value details(rapidjson::kObjectType); |
| details.AddMember("details", error_details, allocator); |
| response.AddMember("data", details, allocator); |
| } |
| } |
| |
| static void ServiceProtocolFailureError(rapidjson::Document& response, |
| std::string message) { |
| auto& allocator = response.GetAllocator(); |
| response.SetObject(); |
| const int64_t kJsonServerError = -32000; |
| response.AddMember("code", kJsonServerError, allocator); |
| response.AddMember("message", message, allocator); |
| } |
| |
| // Service protocol handler |
| bool Shell::OnServiceProtocolScreenshot( |
| const ServiceProtocol::Handler::ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); |
| auto screenshot = rasterizer_->ScreenshotLastLayerTree( |
| Rasterizer::ScreenshotType::CompressedImage, true); |
| if (screenshot.data) { |
| response.SetObject(); |
| auto& allocator = response.GetAllocator(); |
| response.AddMember("type", "Screenshot", allocator); |
| rapidjson::Value image; |
| image.SetString(static_cast<const char*>(screenshot.data->data()), |
| screenshot.data->size(), allocator); |
| response.AddMember("screenshot", image, allocator); |
| return true; |
| } |
| ServiceProtocolFailureError(response, "Could not capture image screenshot."); |
| return false; |
| } |
| |
| // Service protocol handler |
| bool Shell::OnServiceProtocolScreenshotSKP( |
| const ServiceProtocol::Handler::ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); |
| auto screenshot = rasterizer_->ScreenshotLastLayerTree( |
| Rasterizer::ScreenshotType::SkiaPicture, true); |
| if (screenshot.data) { |
| response.SetObject(); |
| auto& allocator = response.GetAllocator(); |
| response.AddMember("type", "ScreenshotSkp", allocator); |
| rapidjson::Value skp; |
| skp.SetString(static_cast<const char*>(screenshot.data->data()), |
| screenshot.data->size(), allocator); |
| response.AddMember("skp", skp, allocator); |
| return true; |
| } |
| ServiceProtocolFailureError(response, "Could not capture SKP screenshot."); |
| return false; |
| } |
| |
| // Service protocol handler |
| bool Shell::OnServiceProtocolRunInView( |
| const ServiceProtocol::Handler::ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| if (params.count("mainScript") == 0) { |
| ServiceProtocolParameterError(response, |
| "'mainScript' parameter is missing."); |
| return false; |
| } |
| |
| // TODO(chinmaygarde): In case of hot-reload from .dill files, the packages |
| // file is ignored. Currently, the tool is passing a junk packages file to |
| // pass this check. Update the service protocol interface and remove this |
| // workaround. |
| if (params.count("packagesFile") == 0) { |
| ServiceProtocolParameterError(response, |
| "'packagesFile' parameter is missing."); |
| return false; |
| } |
| |
| if (params.count("assetDirectory") == 0) { |
| ServiceProtocolParameterError(response, |
| "'assetDirectory' parameter is missing."); |
| return false; |
| } |
| |
| std::string main_script_path = |
| fml::paths::FromURI(params.at("mainScript").ToString()); |
| std::string packages_path = |
| fml::paths::FromURI(params.at("packagesFile").ToString()); |
| std::string asset_directory_path = |
| fml::paths::FromURI(params.at("assetDirectory").ToString()); |
| |
| auto main_script_file_mapping = |
| std::make_unique<fml::FileMapping>(fml::OpenFile( |
| main_script_path.c_str(), false, fml::FilePermission::kRead)); |
| |
| auto isolate_configuration = IsolateConfiguration::CreateForKernel( |
| std::move(main_script_file_mapping)); |
| |
| RunConfiguration configuration(std::move(isolate_configuration)); |
| |
| configuration.AddAssetResolver( |
| std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory( |
| asset_directory_path.c_str(), false, fml::FilePermission::kRead))); |
| |
| auto& allocator = response.GetAllocator(); |
| response.SetObject(); |
| if (engine_->Restart(std::move(configuration))) { |
| response.AddMember("type", "Success", allocator); |
| auto new_description = GetServiceProtocolDescription(); |
| rapidjson::Value view(rapidjson::kObjectType); |
| new_description.Write(this, view, allocator); |
| response.AddMember("view", view, allocator); |
| return true; |
| } else { |
| FML_DLOG(ERROR) << "Could not run configuration in engine."; |
| ServiceProtocolFailureError(response, |
| "Could not run configuration in engine."); |
| return false; |
| } |
| |
| FML_DCHECK(false); |
| return false; |
| } |
| |
| // Service protocol handler |
| bool Shell::OnServiceProtocolFlushUIThreadTasks( |
| const ServiceProtocol::Handler::ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| // This API should not be invoked by production code. |
| // It can potentially starve the service isolate if the main isolate pauses |
| // at a breakpoint or is in an infinite loop. |
| // |
| // It should be invoked from the VM Service and and blocks it until UI thread |
| // tasks are processed. |
| response.SetObject(); |
| response.AddMember("type", "Success", response.GetAllocator()); |
| return true; |
| } |
| |
| bool Shell::OnServiceProtocolGetDisplayRefreshRate( |
| const ServiceProtocol::Handler::ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| response.SetObject(); |
| response.AddMember("fps", engine_->GetDisplayRefreshRate(), |
| response.GetAllocator()); |
| return true; |
| } |
| |
| // Service protocol handler |
| bool Shell::OnServiceProtocolSetAssetBundlePath( |
| const ServiceProtocol::Handler::ServiceProtocolMap& params, |
| rapidjson::Document& response) { |
| FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
| |
| if (params.count("assetDirectory") == 0) { |
| ServiceProtocolParameterError(response, |
| "'assetDirectory' parameter is missing."); |
| return false; |
| } |
| |
| auto& allocator = response.GetAllocator(); |
| response.SetObject(); |
| |
| auto asset_manager = std::make_shared<AssetManager>(); |
| |
| asset_manager->PushFront(std::make_unique<DirectoryAssetBundle>( |
| fml::OpenDirectory(params.at("assetDirectory").ToString().c_str(), false, |
| fml::FilePermission::kRead))); |
| |
| if (engine_->UpdateAssetManager(std::move(asset_manager))) { |
| response.AddMember("type", "Success", allocator); |
| auto new_description = GetServiceProtocolDescription(); |
| rapidjson::Value view(rapidjson::kObjectType); |
| new_description.Write(this, view, allocator); |
| response.AddMember("view", view, allocator); |
| return true; |
| } else { |
| FML_DLOG(ERROR) << "Could not update asset directory."; |
| ServiceProtocolFailureError(response, "Could not update asset directory."); |
| return false; |
| } |
| |
| FML_DCHECK(false); |
| return false; |
| } |
| |
| Rasterizer::Screenshot Shell::Screenshot( |
| Rasterizer::ScreenshotType screenshot_type, |
| bool base64_encode) { |
| TRACE_EVENT0("flutter", "Shell::Screenshot"); |
| fml::AutoResetWaitableEvent latch; |
| Rasterizer::Screenshot screenshot; |
| fml::TaskRunner::RunNowOrPostTask( |
| task_runners_.GetGPUTaskRunner(), [&latch, // |
| rasterizer = GetRasterizer(), // |
| &screenshot, // |
| screenshot_type, // |
| base64_encode // |
| ]() { |
| if (rasterizer) { |
| screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type, |
| base64_encode); |
| } |
| latch.Signal(); |
| }); |
| latch.Wait(); |
| return screenshot; |
| } |
| |
| } // namespace flutter |