// 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 #include #include #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/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 "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/skia/include/utils/SkBase64.h" #include "third_party/tonic/common/log.h" namespace flutter { constexpr char kSkiaChannel[] = "flutter/skia"; constexpr char kSystemChannel[] = "flutter/system"; constexpr char kTypeKey[] = "type"; constexpr char kFontChange[] = "fontsChange"; std::unique_ptr Shell::CreateShellOnPlatformThread( DartVMRef vm, TaskRunners task_runners, const WindowData window_data, Settings settings, fml::RefPtr isolate_snapshot, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& 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(new Shell(std::move(vm), task_runners, settings)); // Create the rasterizer on the raster thread. std::promise> rasterizer_promise; auto rasterizer_future = rasterizer_promise.get_future(); std::promise> snapshot_delegate_promise; auto snapshot_delegate_future = snapshot_delegate_promise.get_future(); fml::TaskRunner::RunNowOrPostTask( task_runners.GetRasterTaskRunner(), [&rasterizer_promise, // &snapshot_delegate_promise, on_create_rasterizer, // shell = shell.get() // ]() { TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem"); std::unique_ptr rasterizer(on_create_rasterizer(*shell)); snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate()); rasterizer_promise.set_value(std::move(rasterizer)); }); // 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. std::promise> io_manager_promise; auto io_manager_future = io_manager_promise.get_future(); std::promise> weak_io_manager_promise; auto weak_io_manager_future = weak_io_manager_promise.get_future(); std::promise> unref_queue_promise; auto unref_queue_future = unref_queue_promise.get_future(); auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(); // TODO(gw280): The WeakPtr here asserts that we are derefing it on the // same thread as it was created on. We are currently on the IO thread // inside this lambda but we need to deref the PlatformView, which was // constructed on the platform thread. // // https://github.com/flutter/flutter/issues/42948 fml::TaskRunner::RunNowOrPostTask( io_task_runner, [&io_manager_promise, // &weak_io_manager_promise, // &unref_queue_promise, // platform_view = platform_view->GetWeakPtr(), // io_task_runner, // is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() // ]() { TRACE_EVENT0("flutter", "ShellSetupIOSubsystem"); auto io_manager = std::make_unique( platform_view.getUnsafe()->CreateResourceContext(), is_backgrounded_sync_switch, io_task_runner); weak_io_manager_promise.set_value(io_manager->GetWeakPtr()); unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue()); io_manager_promise.set_value(std::move(io_manager)); }); // Send dispatcher_maker to the engine constructor because shell won't have // platform_view set until Shell::Setup is called later. auto dispatcher_maker = platform_view->GetDispatcherMaker(); // Create the engine on the UI thread. std::promise> engine_promise; auto engine_future = engine_promise.get_future(); fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetUITaskRunner(), fml::MakeCopyable([&engine_promise, // shell = shell.get(), // &dispatcher_maker, // &window_data, // isolate_snapshot = std::move(isolate_snapshot), // vsync_waiter = std::move(vsync_waiter), // &weak_io_manager_future, // &snapshot_delegate_future, // &unref_queue_future // ]() 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(*shell, task_runners, std::move(vsync_waiter)); engine_promise.set_value(std::make_unique( *shell, // dispatcher_maker, // *shell->GetDartVM(), // std::move(isolate_snapshot), // task_runners, // window_data, // shell->GetSettings(), // std::move(animator), // weak_io_manager_future.get(), // unref_queue_future.get(), // snapshot_delegate_future.get() // )); })); if (!shell->Setup(std::move(platform_view), // engine_future.get(), // rasterizer_future.get(), // io_manager_future.get()) // ) { return nullptr; } return shell; } static void Tokenize(const std::string& input, std::vector* results, char delimiter) { std::istringstream ss(input); std::string token; while (std::getline(ss, token, delimiter)) { results->push_back(token); } } // 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(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] { if (settings.engine_start_timestamp.count() == 0) { settings.engine_start_timestamp = std::chrono::microseconds(Dart_TimelineGetMicros()); } tonic::SetLogHandler( [](const char* message) { FML_LOG(ERROR) << message; }); if (settings.trace_skia) { InitSkiaEventTracer(settings.trace_skia); } if (!settings.trace_whitelist.empty()) { std::vector prefixes; Tokenize(settings.trace_whitelist, &prefixes, ','); fml::tracing::TraceSetWhitelist(prefixes); } 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::Create( TaskRunners task_runners, Settings settings, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer) { return Shell::Create(std::move(task_runners), // WindowData{/* default window data */}, // std::move(settings), // std::move(on_create_platform_view), // std::move(on_create_rasterizer) // ); } std::unique_ptr Shell::Create( TaskRunners task_runners, const WindowData window_data, Settings settings, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer) { PerformInitializationTasks(settings); PersistentCache::SetCacheSkSL(settings.cache_sksl); 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(window_data), // std::move(settings), // vm_data->GetIsolateSnapshot(), // isolate snapshot on_create_platform_view, // on_create_rasterizer, // std::move(vm) // ); } std::unique_ptr Shell::Create( TaskRunners task_runners, const WindowData window_data, Settings settings, fml::RefPtr isolate_snapshot, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer, DartVMRef vm) { PerformInitializationTasks(settings); PersistentCache::SetCacheSkSL(settings.cache_sksl); 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; fml::TaskRunner::RunNowOrPostTask( task_runners.GetPlatformTaskRunner(), fml::MakeCopyable([&latch, // vm = std::move(vm), // &shell, // task_runners = std::move(task_runners), // window_data, // settings, // isolate_snapshot = std::move(isolate_snapshot), // on_create_platform_view, // on_create_rasterizer // ]() mutable { shell = CreateShellOnPlatformThread(std::move(vm), std::move(task_runners), // window_data, // settings, // std::move(isolate_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)), is_gpu_disabled_sync_switch_(new fml::SyncSwitch()), weak_factory_(this), weak_factory_gpu_(nullptr) { FML_CHECK(vm_) << "Must have access to VM to create a shell."; FML_DCHECK(task_runners_.IsValid()); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Generate a WeakPtrFactory for use with the raster thread. This does not // need to wait on a latch because it can only ever be used from the raster // thread from this class, so we have ordering guarantees. fml::TaskRunner::RunNowOrPostTask( task_runners_.GetRasterTaskRunner(), fml::MakeCopyable([this]() mutable { this->weak_factory_gpu_ = std::make_unique>(this); })); // Install service protocol handlers. service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName] = { task_runners_.GetRasterTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshot, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[ServiceProtocol::kScreenshotSkpExtensionName] = { task_runners_.GetRasterTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshotSKP, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[ServiceProtocol::kRunInViewExtensionName] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_ [ServiceProtocol::kFlushUIThreadTasksExtensionName] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_ [ServiceProtocol::kSetAssetBundlePathExtensionName] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_ [ServiceProtocol::kGetDisplayRefreshRateExtensionName] = { task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[ServiceProtocol::kGetSkSLsExtensionName] = { task_runners_.GetIOTaskRunner(), std::bind(&Shell::OnServiceProtocolGetSkSLs, 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_.GetRasterTaskRunner(), fml::MakeCopyable([rasterizer = std::move(rasterizer_), weak_factory_gpu = std::move(weak_factory_gpu_), &gpu_latch]() mutable { rasterizer.reset(); weak_factory_gpu.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(); } void Shell::NotifyLowMemoryWarning() const { // This does not require a current isolate but does require a running VM. // Since a valid shell will not be returned to the embedder without a valid // DartVMRef, we can be certain that this is a safe spot to assume a VM is // running. ::Dart_NotifyLowMemory(); task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr()]() { if (rasterizer) { rasterizer->NotifyLowMemoryWarning(); } }); // The IO Manager uses resource cache limits of 0, so it is not necessary // to purge them. } void Shell::RunEngine(RunConfiguration run_configuration) { RunEngine(std::move(run_configuration), nullptr); } void Shell::RunEngine( RunConfiguration run_configuration, const std::function& result_callback) { auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(), result_callback](Engine::RunStatus run_result) { if (!result_callback) { return; } platform_runner->PostTask( [result_callback, run_result]() { result_callback(run_result); }); }; FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), fml::MakeCopyable( [run_configuration = std::move(run_configuration), weak_engine = weak_engine_, result]() mutable { if (!weak_engine) { FML_LOG(ERROR) << "Could not launch engine with configuration - no engine."; result(Engine::RunStatus::Failure); return; } auto run_result = weak_engine->Run(std::move(run_configuration)); if (run_result == flutter::Engine::RunStatus::Failure) { FML_LOG(ERROR) << "Could not launch engine with configuration."; } result(run_result); })); } std::optional Shell::GetUIIsolateLastError() const { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!weak_engine_) { return std::nullopt; } switch (weak_engine_->GetUIIsolateLastError()) { case tonic::kCompilationErrorType: return DartErrorCode::CompilationError; case tonic::kApiErrorType: return DartErrorCode::ApiError; case tonic::kUnknownErrorType: return DartErrorCode::UnknownError; case tonic::kNoError: return DartErrorCode::NoError; } return DartErrorCode::UnknownError; } bool Shell::EngineHasLivePorts() const { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!weak_engine_) { return false; } return weak_engine_->UIIsolateHasLivePorts(); } bool Shell::IsSetup() const { return is_setup_; } bool Shell::Setup(std::unique_ptr platform_view, std::unique_ptr engine, std::unique_ptr rasterizer, std::unique_ptr 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); // The weak ptr must be generated in the platform thread which owns the unique // ptr. weak_engine_ = engine_->GetWeakPtr(); weak_rasterizer_ = rasterizer_->GetWeakPtr(); weak_platform_view_ = platform_view_->GetWeakPtr(); // Setup the time-consuming default font manager right after engine created. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = weak_engine_] { if (engine) { engine->SetupDefaultFontManager(); } }); is_setup_ = true; vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription()); PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( task_runners_.GetIOTaskRunner()); PersistentCache::GetCacheForProcess()->SetIsDumpingSkp( settings_.dump_skp_on_shader_compilation); // TODO(gw280): The WeakPtr here asserts that we are derefing it on the // same thread as it was created on. Shell is constructed on the platform // thread but we need to call into the Engine on the UI thread, so we need // to use getUnsafe() here to avoid failing the assertion. // // https://github.com/flutter/flutter/issues/42947 display_refresh_rate_ = weak_engine_.getUnsafe()->GetDisplayRefreshRate(); return true; } const Settings& Shell::GetSettings() const { return settings_; } const TaskRunners& Shell::GetTaskRunners() const { return task_runners_; } fml::TaskRunnerAffineWeakPtr Shell::GetRasterizer() const { FML_DCHECK(is_setup_); return weak_rasterizer_; } fml::WeakPtr Shell::GetEngine() { FML_DCHECK(is_setup_); return weak_engine_; } fml::WeakPtr Shell::GetPlatformView() { FML_DCHECK(is_setup_); return weak_platform_view_; } DartVM* Shell::GetDartVM() { return &vm_; } // |PlatformView::Delegate| void Shell::OnPlatformViewCreated(std::unique_ptr 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 raster_task = fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_, rasterizer = rasterizer_->GetWeakPtr(), // surface = std::move(surface), // &latch]() mutable { if (rasterizer) { rasterizer->Setup(std::move(surface)); } waiting_for_first_frame.store(true); // Step 3: All done. Signal the latch that the platform thread is // waiting on. latch.Signal(); }); // The normal flow executed by this method is that the platform thread is // starting the sequence and waiting on the latch. Later the UI thread posts // raster_task to the raster thread which signals the latch. If the raster and // the platform threads are the same this results in a deadlock as the // raster_task will never be posted to the plaform/raster thread that is // blocked on a latch. To avoid the described deadlock, if the raster and the // platform threads are the same, should_post_raster_task will be false, and // then instead of posting a task to the raster thread, the ui thread just // signals the latch and the platform/raster thread follows with executing // raster_task. bool should_post_raster_task = task_runners_.GetRasterTaskRunner() != task_runners_.GetPlatformTaskRunner(); auto ui_task = [engine = engine_->GetWeakPtr(), // raster_task_runner = task_runners_.GetRasterTaskRunner(), // raster_task, should_post_raster_task, &latch // ] { if (engine) { engine->OnOutputSurfaceCreated(); } // Step 2: Next, tell the raster thread that it should create a surface for // its rasterizer. if (should_post_raster_task) { fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } else { // See comment on should_post_raster_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_raster_task) { // See comment on should_post_raster_task, in this case the raster_task // wasn't executed, and we just run it here as the platform thread // is the raster thread. raster_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->GetIsGpuDisabledSyncSwitch()->Execute( fml::SyncSwitch::Handlers().SetIfFalse( [&] { io_manager->GetSkiaUnrefQueue()->Drain(); })); // Step 3: All done. Signal the latch that the platform thread is waiting // on. latch.Signal(); }; auto raster_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 executed by this method is that the platform thread is // starting the sequence and waiting on the latch. Later the UI thread posts // raster_task to the raster thread triggers signaling the latch(on the IO // thread). If the raster and the platform threads are the same this results // in a deadlock as the raster_task will never be posted to the plaform/raster // thread that is blocked on a latch. To avoid the described deadlock, if the // raster and the platform threads are the same, should_post_raster_task will // be false, and then instead of posting a task to the raster thread, the ui // thread just signals the latch and the platform/raster thread follows with // executing raster_task. bool should_post_raster_task = task_runners_.GetRasterTaskRunner() != task_runners_.GetPlatformTaskRunner(); auto ui_task = [engine = engine_->GetWeakPtr(), raster_task_runner = task_runners_.GetRasterTaskRunner(), raster_task, should_post_raster_task, &latch]() { if (engine) { engine->OnOutputSurfaceDestroyed(); } // Step 1: Next, tell the raster thread that its rasterizer should suspend // access to the underlying surface. if (should_post_raster_task) { fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } else { // See comment on should_post_raster_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_raster_task) { // See comment on should_post_raster_task, in this case the raster_task // wasn't executed, and we just run it here as the platform thread // is the raster thread. raster_task(); latch.Wait(); } } // |PlatformView::Delegate| void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // This is the formula Android uses. // https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41 size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4; task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), max_bytes] { if (rasterizer) { rasterizer->SetResourceCacheMaxBytes(max_bytes, false); } }); task_runners_.GetUITaskRunner()->PostTask( [engine = engine_->GetWeakPtr(), metrics]() { if (engine) { engine->SetViewportMetrics(metrics); } }); } // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchPlatformMessage( fml::RefPtr 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 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 = weak_engine_, packet = std::move(packet), flow_id = next_pointer_flow_id_]() mutable { if (engine) { engine->DispatchPointerDataPacket(std::move(packet), flow_id); } })); next_pointer_flow_id_++; } // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector 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 texture) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetRasterTaskRunner()->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_.GetRasterTaskRunner()->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_.GetRasterTaskRunner()->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(const fml::closure& closure) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() { if (rasterizer) { rasterizer->SetNextFrameCallback(std::move(closure)); } }); } // |Animator::Delegate| void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); // record the target time for use by rasterizer. { std::scoped_lock time_recorder_lock(time_recorder_mutex_); latest_frame_target_time_.emplace(frame_target_time); } if (engine_) { engine_->BeginFrame(frame_target_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, fml::TimePoint frame_target_time) { FML_DCHECK(is_setup_); // record the target time for use by rasterizer. { std::scoped_lock time_recorder_lock(time_recorder_mutex_); if (!latest_frame_target_time_) { latest_frame_target_time_ = frame_target_time; } else if (latest_frame_target_time_ < frame_target_time) { latest_frame_target_time_ = frame_target_time; } } task_runners_.GetRasterTaskRunner()->PostTask( [&waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), pipeline = std::move(pipeline)]() { if (rasterizer) { rasterizer->Draw(pipeline); if (waiting_for_first_frame.load()) { waiting_for_first_frame.store(false); waiting_for_first_frame_condition.notify_all(); } } }); } // |Animator::Delegate| void Shell::OnAnimatorDrawLastLayerTree() { FML_DCHECK(is_setup_); task_runners_.GetRasterTaskRunner()->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 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 message) { const auto& data = message->data(); rapidjson::Document document; document.Parse(reinterpret_cast(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_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt(), response = std::move(message->response())] { if (rasterizer) { rasterizer->SetResourceCacheMaxBytes(static_cast(max_bytes), true); } if (response) { // The framework side expects this to be valid json encoded as a list. // Return `[true]` to signal success. std::vector data = {'[', 't', 'r', 'u', 'e', ']'}; response->Complete( std::make_unique(std::move(data))); } }); } // |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); } void Shell::SetNeedsReportTimings(bool value) { needs_report_timings_ = value; } void Shell::ReportTimings() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); auto timings = std::move(unreported_timings_); unreported_timings_ = {}; task_runners_.GetUITaskRunner()->PostTask([timings, engine = weak_engine_] { if (engine) { engine->ReportTimings(std::move(timings)); } }); } size_t Shell::UnreportedFramesCount() const { // Check that this is running on the raster thread to avoid race conditions. FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); FML_DCHECK(unreported_timings_.size() % FrameTiming::kCount == 0); return unreported_timings_.size() / FrameTiming::kCount; } void Shell::OnFrameRasterized(const FrameTiming& timing) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); // The C++ callback defined in settings.h and set by Flutter runner. This is // independent of the timings report to the Dart side. if (settings_.frame_rasterized_callback) { settings_.frame_rasterized_callback(timing); } if (!needs_report_timings_) { return; } for (auto phase : FrameTiming::kPhases) { unreported_timings_.push_back( timing.Get(phase).ToEpochDelta().ToMicroseconds()); } // In tests using iPhone 6S with profile mode, sending a batch of 1 frame or a // batch of 100 frames have roughly the same cost of less than 0.1ms. Sending // a batch of 500 frames costs about 0.2ms. The 1 second threshold usually // kicks in before we reaching the following 100 frames threshold. The 100 // threshold here is mainly for unit tests (so we don't have to write a // 1-second unit test), and make sure that our vector won't grow too big with // future 120fps, 240fps, or 1000fps displays. // // In the profile/debug mode, the timings are used by development tools which // require a latency of no more than 100ms. Hence we lower that 1-second // threshold to 100ms because performance overhead isn't that critical in // those cases. if (!first_frame_rasterized_ || UnreportedFramesCount() >= 100) { first_frame_rasterized_ = true; ReportTimings(); } else if (!frame_timings_report_scheduled_) { #if FLUTTER_RELEASE constexpr int kBatchTimeInMilliseconds = 1000; #else constexpr int kBatchTimeInMilliseconds = 100; #endif // Also make sure that frame times get reported with a max latency of 1 // second. Otherwise, the timings of last few frames of an animation may // never be reported until the next animation starts. frame_timings_report_scheduled_ = true; task_runners_.GetRasterTaskRunner()->PostDelayedTask( [self = weak_factory_gpu_->GetWeakPtr()]() { if (!self.get()) { return; } self->frame_timings_report_scheduled_ = false; if (self->UnreportedFramesCount() > 0) { self->ReportTimings(); } }, fml::TimeDelta::FromMilliseconds(kBatchTimeInMilliseconds)); } } fml::Milliseconds Shell::GetFrameBudget() { if (display_refresh_rate_ > 0) { return fml::RefreshRateToFrameBudget(display_refresh_rate_.load()); } else { return fml::kDefaultFrameBudget; } } fml::TimePoint Shell::GetLatestFrameTargetTime() const { std::scoped_lock time_recorder_lock(time_recorder_mutex_); FML_CHECK(latest_frame_target_time_.has_value()) << "GetLatestFrameTargetTime called before OnAnimatorBeginFrame"; return latest_frame_target_time_.value(); } // |ServiceProtocol::Handler| fml::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( std::string_view method) const { FML_DCHECK(is_setup_); auto found = service_protocol_handlers_.find(method); if (found != service_protocol_handlers_.end()) { return found->second.first; } return task_runners_.GetUITaskRunner(); } // |ServiceProtocol::Handler| bool Shell::HandleServiceProtocolMessage( std::string_view method, // one if the extension names specified above. const ServiceProtocolMap& params, rapidjson::Document& response) { auto found = service_protocol_handlers_.find(method); 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_.GetRasterTaskRunner()->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(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_.GetRasterTaskRunner()->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(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; } if (params.count("assetDirectory") == 0) { ServiceProtocolParameterError(response, "'assetDirectory' parameter is missing."); return false; } std::string main_script_path = fml::paths::FromURI(params.at("mainScript").data()); std::string asset_directory_path = fml::paths::FromURI(params.at("assetDirectory").data()); auto main_script_file_mapping = std::make_unique(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.SetEntrypointAndLibrary(engine_->GetLastEntrypoint(), engine_->GetLastEntrypointLibrary()); configuration.AddAssetResolver( std::make_unique(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("type", "DisplayRefreshRate", response.GetAllocator()); response.AddMember("fps", engine_->GetDisplayRefreshRate(), response.GetAllocator()); return true; } bool Shell::OnServiceProtocolGetSkSLs( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); response.SetObject(); response.AddMember("type", "GetSkSLs", response.GetAllocator()); rapidjson::Value shaders_json(rapidjson::kObjectType); PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); std::vector sksls = persistent_cache->LoadSkSLs(); for (const auto& sksl : sksls) { size_t b64_size = SkBase64::Encode(sksl.second->data(), sksl.second->size(), nullptr); sk_sp b64_data = SkData::MakeUninitialized(b64_size + 1); char* b64_char = static_cast(b64_data->writable_data()); SkBase64::Encode(sksl.second->data(), sksl.second->size(), b64_char); b64_char[b64_size] = 0; // make it null terminated for printing rapidjson::Value shader_value(b64_char, response.GetAllocator()); rapidjson::Value shader_key(PersistentCache::SkKeyToFilePath(*sksl.first), response.GetAllocator()); shaders_json.AddMember(shader_key, shader_value, response.GetAllocator()); } response.AddMember("SkSLs", shaders_json, 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(); asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").data(), 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_.GetRasterTaskRunner(), [&latch, // rasterizer = GetRasterizer(), // &screenshot, // screenshot_type, // base64_encode // ]() { if (rasterizer) { screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type, base64_encode); } latch.Signal(); }); latch.Wait(); return screenshot; } fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) { FML_DCHECK(is_setup_); if (task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread() || task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()) { return fml::Status(fml::StatusCode::kFailedPrecondition, "WaitForFirstFrame called from thread that can't wait " "because it is responsible for generating the frame."); } std::unique_lock lock(waiting_for_first_frame_mutex_); bool success = waiting_for_first_frame_condition_.wait_for( lock, std::chrono::milliseconds(timeout.ToMilliseconds()), [&waiting_for_first_frame = waiting_for_first_frame_] { return !waiting_for_first_frame.load(); }); if (success) { return fml::Status(); } else { return fml::Status(fml::StatusCode::kDeadlineExceeded, "timeout"); } } bool Shell::ReloadSystemFonts() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); if (!engine_) { return false; } engine_->GetFontCollection().GetFontCollection()->SetupDefaultFontManager(); engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache(); // After system fonts are reloaded, we send a system channel message // to notify flutter framework. rapidjson::Document document; document.SetObject(); auto& allocator = document.GetAllocator(); rapidjson::Value message_value; message_value.SetString(kFontChange, allocator); document.AddMember(kTypeKey, message_value, allocator); rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); document.Accept(writer); std::string message = buffer.GetString(); fml::RefPtr fontsChangeMessage = fml::MakeRefCounted( kSystemChannel, std::vector(message.begin(), message.end()), nullptr); OnPlatformViewDispatchPlatformMessage(fontsChangeMessage); return true; } std::shared_ptr Shell::GetIsGpuDisabledSyncSwitch() const { return is_gpu_disabled_sync_switch_; } } // namespace flutter