// 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 #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/constants.h" #include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/base32.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/runtime/dart_vm.h" #include "flutter/shell/common/base64.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/vsync_waiter.h" #include "impeller/runtime_stage/runtime_stage.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/codec/SkBmpDecoder.h" #include "third_party/skia/include/codec/SkCodec.h" #include "third_party/skia/include/codec/SkGifDecoder.h" #include "third_party/skia/include/codec/SkIcoDecoder.h" #include "third_party/skia/include/codec/SkJpegDecoder.h" #include "third_party/skia/include/codec/SkPngDecoder.h" #include "third_party/skia/include/codec/SkWbmpDecoder.h" #include "third_party/skia/include/codec/SkWebpDecoder.h" #include "third_party/skia/include/core/SkGraphics.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"; namespace { std::unique_ptr CreateEngine( Engine::Delegate& delegate, const PointerDataDispatcherMaker& dispatcher_maker, DartVM& vm, const fml::RefPtr& isolate_snapshot, const TaskRunners& task_runners, const PlatformData& platform_data, const Settings& settings, std::unique_ptr animator, const fml::WeakPtr& io_manager, const fml::RefPtr& unref_queue, const fml::TaskRunnerAffineWeakPtr& snapshot_delegate, const std::shared_ptr& gpu_disabled_switch, impeller::RuntimeStageBackend runtime_stage_backend) { return std::make_unique(delegate, // dispatcher_maker, // vm, // isolate_snapshot, // task_runners, // platform_data, // settings, // std::move(animator), // io_manager, // unref_queue, // snapshot_delegate, // gpu_disabled_switch, // runtime_stage_backend); } void RegisterCodecsWithSkia() { // These are in the order they will be attempted to be decoded from. // If we have data to back it up, we can order these by "frequency used in // the wild" for a very small performance bump, but for now we mirror the // order Skia had them in. SkCodecs::Register(SkPngDecoder::Decoder()); SkCodecs::Register(SkJpegDecoder::Decoder()); SkCodecs::Register(SkWebpDecoder::Decoder()); SkCodecs::Register(SkGifDecoder::Decoder()); SkCodecs::Register(SkBmpDecoder::Decoder()); SkCodecs::Register(SkWbmpDecoder::Decoder()); SkCodecs::Register(SkIcoDecoder::Decoder()); } // Though there can be multiple shells, some settings apply to all components in // the process. These have to be set up 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. void PerformInitializationTasks(Settings& settings) { { fml::LogSettings log_settings; log_settings.min_log_level = settings.verbose_logging ? fml::kLogInfo : fml::kLogError; fml::SetLogSettings(log_settings); } static std::once_flag gShellSettingsInitialization = {}; std::call_once(gShellSettingsInitialization, [&settings] { tonic::SetLogHandler( [](const char* message) { FML_LOG(ERROR) << message; }); if (settings.trace_skia) { InitSkiaEventTracer(settings.trace_skia, settings.trace_skia_allowlist); } if (!settings.trace_allowlist.empty()) { fml::tracing::TraceSetAllowlist(settings.trace_allowlist); } if (!settings.skia_deterministic_rendering_on_cpu) { SkGraphics::Init(); } else { FML_DLOG(INFO) << "Skia deterministic rendering is enabled."; } RegisterCodecsWithSkia(); if (settings.icu_initialization_required) { if (!settings.icu_data_path.empty()) { 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."; } } }); #if !SLIMPELLER PersistentCache::SetCacheSkSL(settings.cache_sksl); #endif // !SLIMPELLER } } // namespace std::pair> Shell::InferVmInitDataFromSettings(Settings& settings) { // Always use the `vm_snapshot` and `isolate_snapshot` provided by the // settings to launch the VM. If the VM is already running, the snapshot // arguments are ignored. auto vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings); auto isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings); auto vm = DartVMRef::Create(settings, vm_snapshot, isolate_snapshot); // If the settings did not specify an `isolate_snapshot`, fall back to the // one the VM was launched with. if (!isolate_snapshot) { isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot(); } return {std::move(vm), isolate_snapshot}; } std::unique_ptr Shell::Create( const PlatformData& platform_data, const TaskRunners& task_runners, Settings settings, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer, bool is_gpu_disabled) { // This must come first as it initializes tracing. PerformInitializationTasks(settings); TRACE_EVENT0("flutter", "Shell::Create"); auto [vm, isolate_snapshot] = InferVmInitDataFromSettings(settings); auto resource_cache_limit_calculator = std::make_shared( settings.resource_cache_max_bytes_threshold); return CreateWithSnapshot(platform_data, // task_runners, // /*parent_thread_merger=*/nullptr, // /*parent_io_manager=*/nullptr, // resource_cache_limit_calculator, // settings, // std::move(vm), // std::move(isolate_snapshot), // on_create_platform_view, // on_create_rasterizer, // CreateEngine, is_gpu_disabled); } static impeller::RuntimeStageBackend DetermineRuntimeStageBackend( const std::shared_ptr& impeller_context) { if (!impeller_context) { return impeller::RuntimeStageBackend::kSkSL; } switch (impeller_context->GetBackendType()) { case impeller::Context::BackendType::kMetal: return impeller::RuntimeStageBackend::kMetal; case impeller::Context::BackendType::kOpenGLES: return impeller::RuntimeStageBackend::kOpenGLES; case impeller::Context::BackendType::kVulkan: return impeller::RuntimeStageBackend::kVulkan; } } std::unique_ptr Shell::CreateShellOnPlatformThread( DartVMRef vm, fml::RefPtr parent_merger, std::shared_ptr parent_io_manager, const std::shared_ptr& resource_cache_limit_calculator, const TaskRunners& task_runners, const PlatformData& platform_data, const Settings& settings, fml::RefPtr isolate_snapshot, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer, const Shell::EngineCreateCallback& on_create_engine, bool is_gpu_disabled) { 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, std::move(parent_merger), resource_cache_limit_calculator, settings, is_gpu_disabled)); // 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; } // 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(), // impeller_context = platform_view->GetImpellerContext() // ]() { TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem"); std::unique_ptr rasterizer(on_create_rasterizer(*shell)); rasterizer->SetImpellerContext(impeller_context); snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate()); rasterizer_promise.set_value(std::move(rasterizer)); }); // 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(); // The platform_view will be stored into shell's platform_view_ in // shell->Setup(std::move(platform_view), ...) at the end. PlatformView* platform_view_ptr = platform_view.get(); fml::TaskRunner::RunNowOrPostTask( io_task_runner, [&io_manager_promise, // &weak_io_manager_promise, // &parent_io_manager, // &unref_queue_promise, // platform_view_ptr, // io_task_runner, // is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() // ]() { TRACE_EVENT0("flutter", "ShellSetupIOSubsystem"); std::shared_ptr io_manager; if (parent_io_manager) { io_manager = parent_io_manager; } else { io_manager = std::make_shared( platform_view_ptr->CreateResourceContext(), // resource context is_backgrounded_sync_switch, // sync switch io_task_runner, // unref queue task runner platform_view_ptr->GetImpellerContext() // impeller context ); } weak_io_manager_promise.set_value(io_manager->GetWeakPtr()); unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue()); io_manager_promise.set_value(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, // &platform_data, // isolate_snapshot = std::move(isolate_snapshot), // vsync_waiter = std::move(vsync_waiter), // &weak_io_manager_future, // &snapshot_delegate_future, // &unref_queue_future, // &on_create_engine, runtime_stage_backend = DetermineRuntimeStageBackend( platform_view->GetImpellerContext())]() 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(on_create_engine( *shell, // dispatcher_maker, // *shell->GetDartVM(), // std::move(isolate_snapshot), // task_runners, // platform_data, // shell->GetSettings(), // std::move(animator), // weak_io_manager_future.get(), // unref_queue_future.get(), // snapshot_delegate_future.get(), // shell->is_gpu_disabled_sync_switch_, // runtime_stage_backend // )); })); if (!shell->Setup(std::move(platform_view), // engine_future.get(), // rasterizer_future.get(), // io_manager_future.get()) // ) { return nullptr; } return shell; } std::unique_ptr Shell::CreateWithSnapshot( const PlatformData& platform_data, const TaskRunners& task_runners, const fml::RefPtr& parent_thread_merger, const std::shared_ptr& parent_io_manager, const std::shared_ptr& resource_cache_limit_calculator, Settings settings, DartVMRef vm, fml::RefPtr isolate_snapshot, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer, const Shell::EngineCreateCallback& on_create_engine, bool is_gpu_disabled) { // This must come first as it initializes tracing. PerformInitializationTasks(settings); TRACE_EVENT0("flutter", "Shell::CreateWithSnapshot"); const bool callbacks_valid = on_create_platform_view && on_create_rasterizer && on_create_engine; if (!task_runners.IsValid() || !callbacks_valid) { return nullptr; } fml::AutoResetWaitableEvent latch; std::unique_ptr shell; auto platform_task_runner = task_runners.GetPlatformTaskRunner(); fml::TaskRunner::RunNowOrPostTask( platform_task_runner, fml::MakeCopyable([&latch, // &shell, // parent_thread_merger, // parent_io_manager, // resource_cache_limit_calculator, // task_runners = task_runners, // platform_data = platform_data, // settings = settings, // vm = std::move(vm), // isolate_snapshot = std::move(isolate_snapshot), // on_create_platform_view = on_create_platform_view, // on_create_rasterizer = on_create_rasterizer, // on_create_engine = on_create_engine, is_gpu_disabled]() mutable { shell = CreateShellOnPlatformThread(std::move(vm), // parent_thread_merger, // parent_io_manager, // resource_cache_limit_calculator, // task_runners, // platform_data, // settings, // std::move(isolate_snapshot), // on_create_platform_view, // on_create_rasterizer, // on_create_engine, is_gpu_disabled); latch.Signal(); })); latch.Wait(); return shell; } Shell::Shell(DartVMRef vm, const TaskRunners& task_runners, fml::RefPtr parent_merger, const std::shared_ptr& resource_cache_limit_calculator, const Settings& settings, bool is_gpu_disabled) : task_runners_(task_runners), parent_raster_thread_merger_(std::move(parent_merger)), resource_cache_limit_calculator_(resource_cache_limit_calculator), settings_(settings), vm_(std::move(vm)), is_gpu_disabled_sync_switch_(new fml::SyncSwitch(is_gpu_disabled)), weak_factory_gpu_(nullptr), weak_factory_(this) { FML_CHECK(!settings.enable_software_rendering || !settings.enable_impeller) << "Software rendering is incompatible with Impeller."; if (!settings.enable_impeller && settings.warn_on_impeller_opt_out) { FML_LOG(IMPORTANT) << "[Action Required] The application opted out of Impeller by either " "using the --no-enable-impeller flag or FLTEnableImpeller=false " "plist flag. This option is going to go away in an upcoming Flutter " "release. Remove the explicit opt-out. If you need to opt-out, " "report a bug describing the issue."; } FML_CHECK(vm_) << "Must have access to VM to create a shell."; FML_DCHECK(task_runners_.IsValid()); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); display_manager_ = std::make_unique(); resource_cache_limit_calculator->AddResourceCacheLimitItem( weak_factory_.GetWeakPtr()); // 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)}; service_protocol_handlers_ [ServiceProtocol::kEstimateRasterCacheMemoryExtensionName] = { task_runners_.GetRasterTaskRunner(), std::bind(&Shell::OnServiceProtocolEstimateRasterCacheMemory, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[ServiceProtocol::kReloadAssetFonts] = { task_runners_.GetPlatformTaskRunner(), std::bind(&Shell::OnServiceProtocolReloadAssetFonts, this, std::placeholders::_1, std::placeholders::_2)}; } Shell::~Shell() { #if !SLIMPELLER PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner( task_runners_.GetIOTaskRunner()); #endif // !SLIMPELLER vm_->GetServiceProtocol()->RemoveHandler(this); fml::AutoResetWaitableEvent platiso_latch, ui_latch, gpu_latch, platform_latch, io_latch; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetPlatformTaskRunner(), fml::MakeCopyable([this, &platiso_latch]() mutable { engine_->ShutdownPlatformIsolates(); platiso_latch.Signal(); })); platiso_latch.Wait(); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), fml::MakeCopyable([this, &ui_latch]() mutable { engine_.reset(); ui_latch.Signal(); })); ui_latch.Wait(); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetRasterTaskRunner(), fml::MakeCopyable( [this, rasterizer = std::move(rasterizer_), &gpu_latch]() mutable { rasterizer.reset(); this->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(); } std::unique_ptr Shell::Spawn( RunConfiguration run_configuration, const std::string& initial_route, const CreateCallback& on_create_platform_view, const CreateCallback& on_create_rasterizer) const { FML_DCHECK(task_runners_.IsValid()); // It's safe to store this value since it is set on the platform thread. bool is_gpu_disabled = false; GetIsGpuDisabledSyncSwitch()->Execute( fml::SyncSwitch::Handlers() .SetIfFalse([&is_gpu_disabled] { is_gpu_disabled = false; }) .SetIfTrue([&is_gpu_disabled] { is_gpu_disabled = true; })); std::unique_ptr result = CreateWithSnapshot( PlatformData{}, task_runners_, rasterizer_->GetRasterThreadMerger(), io_manager_, resource_cache_limit_calculator_, GetSettings(), vm_, vm_->GetVMData()->GetIsolateSnapshot(), on_create_platform_view, on_create_rasterizer, [engine = this->engine_.get(), initial_route]( Engine::Delegate& delegate, const PointerDataDispatcherMaker& dispatcher_maker, DartVM& vm, const fml::RefPtr& isolate_snapshot, const TaskRunners& task_runners, const PlatformData& platform_data, const Settings& settings, std::unique_ptr animator, const fml::WeakPtr& io_manager, const fml::RefPtr& unref_queue, fml::TaskRunnerAffineWeakPtr snapshot_delegate, const std::shared_ptr& is_gpu_disabled_sync_switch, impeller::RuntimeStageBackend runtime_stage_backend) { return engine->Spawn( /*delegate=*/delegate, /*dispatcher_maker=*/dispatcher_maker, /*settings=*/settings, /*animator=*/std::move(animator), /*initial_route=*/initial_route, /*io_manager=*/io_manager, /*snapshot_delegate=*/std::move(snapshot_delegate), /*gpu_disabled_switch=*/is_gpu_disabled_sync_switch); }, is_gpu_disabled); result->RunEngine(std::move(run_configuration)); return result; } void Shell::NotifyLowMemoryWarning() const { auto trace_id = fml::tracing::TraceNonce(); TRACE_EVENT_ASYNC_BEGIN0("flutter", "Shell::NotifyLowMemoryWarning", trace_id); // 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(), trace_id = trace_id]() { if (rasterizer) { rasterizer->NotifyLowMemoryWarning(); } TRACE_EVENT_ASYNC_END0("flutter", "Shell::NotifyLowMemoryWarning", trace_id); }); // 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_set_up_); 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_set_up_); 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_set_up_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!weak_engine_) { return false; } return weak_engine_->UIIsolateHasLivePorts(); } bool Shell::IsSetup() const { return is_set_up_; } bool Shell::Setup(std::unique_ptr platform_view, std::unique_ptr engine, std::unique_ptr rasterizer, const std::shared_ptr& io_manager) { if (is_set_up_) { return false; } if (!platform_view || !engine || !rasterizer || !io_manager) { return false; } platform_view_ = std::move(platform_view); platform_message_handler_ = platform_view_->GetPlatformMessageHandler(); route_messages_through_platform_thread_.store(true); task_runners_.GetPlatformTaskRunner()->PostTask( [self = weak_factory_.GetWeakPtr()] { if (self) { self->route_messages_through_platform_thread_.store(false); } }); engine_ = std::move(engine); rasterizer_ = std::move(rasterizer); io_manager_ = io_manager; // Set the external view embedder for the rasterizer. auto view_embedder = platform_view_->CreateExternalViewEmbedder(); rasterizer_->SetExternalViewEmbedder(view_embedder); rasterizer_->SetSnapshotSurfaceProducer( platform_view_->CreateSnapshotSurfaceProducer()); // 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(); // Add the implicit view with empty metrics. engine_->AddView(kFlutterImplicitViewId, ViewportMetrics{}, [](bool added) { FML_DCHECK(added) << "Failed to add the implicit view"; }); // Setup the time-consuming default font manager right after engine created. if (!settings_.prefetched_default_font_manager) { fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = weak_engine_] { if (engine) { engine->SetupDefaultFontManager(); } }); } is_set_up_ = true; #if !SLIMPELLER PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( task_runners_.GetIOTaskRunner()); PersistentCache::GetCacheForProcess()->SetIsDumpingSkp( settings_.dump_skp_on_shader_compilation); if (settings_.purge_persistent_cache) { PersistentCache::GetCacheForProcess()->Purge(); } #endif // !SLIMPELLER return true; } const Settings& Shell::GetSettings() const { return settings_; } const TaskRunners& Shell::GetTaskRunners() const { return task_runners_; } const fml::RefPtr Shell::GetParentRasterThreadMerger() const { return parent_raster_thread_merger_; } fml::TaskRunnerAffineWeakPtr Shell::GetRasterizer() const { FML_DCHECK(is_set_up_); return weak_rasterizer_; } fml::WeakPtr Shell::GetEngine() { FML_DCHECK(is_set_up_); return weak_engine_; } fml::WeakPtr Shell::GetPlatformView() { FML_DCHECK(is_set_up_); return weak_platform_view_; } fml::WeakPtr Shell::GetIOManager() { FML_DCHECK(is_set_up_); return io_manager_->GetWeakPtr(); } DartVM* Shell::GetDartVM() { return &vm_; } // |PlatformView::Delegate| void Shell::OnPlatformViewCreated(std::unique_ptr surface) { TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated"); FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Prevent any request to change the thread configuration for raster and // platform queues while the platform view is being created. // // This prevents false positives such as this method starts assuming that the // raster and platform queues have a given thread configuration, but then the // configuration is changed by a task, and the assumption is no longer true. // // This incorrect assumption can lead to deadlock. // See `should_post_raster_task` for more. rasterizer_->DisableThreadMergerIfNeeded(); // 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 platform/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. const bool should_post_raster_task = !task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread(); auto raster_task = fml::MakeCopyable( [&waiting_for_first_frame = waiting_for_first_frame_, // rasterizer = rasterizer_->GetWeakPtr(), // surface = std::move(surface) // ]() mutable { if (rasterizer) { // Enables the thread merger which may be used by the external view // embedder. rasterizer->EnableThreadMergerIfNeeded(); rasterizer->Setup(std::move(surface)); } waiting_for_first_frame.store(true); }); auto ui_task = [engine = engine_->GetWeakPtr()] { if (engine) { engine->ScheduleFrame(); } }; // 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); fml::AutoResetWaitableEvent latch; auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view, ui_task_runner = task_runners_.GetUITaskRunner(), ui_task, raster_task_runner = task_runners_.GetRasterTaskRunner(), raster_task, should_post_raster_task, &latch] { if (io_manager && !io_manager->GetResourceContext()) { sk_sp resource_context = platform_view->CreateResourceContext(); io_manager->NotifyResourceContextAvailable(resource_context); } // Step 1: 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); // Step 2: 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); } latch.Signal(); }; 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_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Prevent any request to change the thread configuration for raster and // platform queues while the platform view is being destroyed. // // This prevents false positives such as this method starts assuming that the // raster and platform queues have a given thread configuration, but then the // configuration is changed by a task, and the assumption is no longer true. // // This incorrect assumption can lead to deadlock. rasterizer_->DisableThreadMergerIfNeeded(); // Notify the Dart VM that the PlatformView has been destroyed and some // cleanup activity can be done (e.g: garbage collect the Dart heap). fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr()]() { if (engine) { engine->NotifyDestroyed(); } }); // 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. // The UI thread does not need to be serialized here - there is sufficient // guardrailing in the rasterizer to allow the UI thread to post work to it // even after the surface has been torn down. 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 4: 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) { // Enables the thread merger which is required prior tearing down the // rasterizer. If the raster and platform threads are merged, tearing down // the rasterizer unmerges the threads. rasterizer->EnableThreadMergerIfNeeded(); rasterizer->Teardown(); } // Step 2: Tell the IO thread to complete its remaining work. fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task); }; // Step 1: Post a task to the Raster thread (possibly this thread) to tell the // rasterizer the output surface is going away. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetRasterTaskRunner(), raster_task); latch.Wait(); // On Android, the external view embedder may post a task to the platform // thread, and wait until it completes if overlay surfaces must be released. // However, the platform thread might be blocked when Dart is initializing. // In this situation, calling TeardownExternalViewEmbedder is safe because no // platform views have been created before Flutter renders the first frame. // Overall, the longer term plan is to remove this implementation once // https://github.com/flutter/flutter/issues/96679 is fixed. rasterizer_->TeardownExternalViewEmbedder(); } // |PlatformView::Delegate| void Shell::OnPlatformViewScheduleFrame() { TRACE_EVENT0("flutter", "Shell::OnPlatformViewScheduleFrame"); FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr()]() { if (engine) { engine->ScheduleFrame(); } }); } // |PlatformView::Delegate| void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); if (metrics.device_pixel_ratio <= 0 || metrics.physical_width <= 0 || metrics.physical_height <= 0) { // Ignore invalid view-port metrics. return; } // This is the formula Android uses. // https://android.googlesource.com/platform/frameworks/base/+/39ae5bac216757bc201490f4c7b8c0f63006c6cd/libs/hwui/renderthread/CacheManager.cpp#45 resource_cache_limit_ = metrics.physical_width * metrics.physical_height * 12 * 4; size_t resource_cache_max_bytes = resource_cache_limit_calculator_->GetResourceCacheMaxBytes(); task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), resource_cache_max_bytes] { if (rasterizer) { rasterizer->SetResourceCacheMaxBytes(resource_cache_max_bytes, false); } }); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), view_id, metrics]() { if (engine) { engine->SetViewportMetrics(view_id, metrics); } }); { std::scoped_lock lock(resize_mutex_); expected_frame_sizes_[view_id] = SkISize::Make(metrics.physical_width, metrics.physical_height); device_pixel_ratio_ = metrics.device_pixel_ratio; } } // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchPlatformMessage( std::unique_ptr message) { FML_DCHECK(is_set_up_); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG if (!task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()) { std::scoped_lock lock(misbehaving_message_channels_mutex_); auto inserted = misbehaving_message_channels_.insert(message->channel()); if (inserted.second) { FML_LOG(ERROR) << "The '" << message->channel() << "' channel sent a message from native to Flutter on a " "non-platform thread. Platform channel messages must be sent on " "the platform thread. Failure to do so may result in data loss or " "crashes, and must be fixed in the plugin or application code " "creating that channel.\n" "See https://docs.flutter.dev/platform-integration/" "platform-channels#channels-and-platform-threading for more " "information."; } } #endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG // If the root isolate is not yet running this may be the navigation // channel initial route and must be dispatched immediately so that // it can be set before isolate creation. static constexpr char kNavigationChannel[] = "flutter/navigation"; if (!engine_->GetRuntimeController()->IsRootIsolateRunning() && message->channel() == kNavigationChannel) { // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), fml::MakeCopyable([engine = engine_->GetWeakPtr(), message = std::move(message)]() mutable { if (engine) { engine->DispatchPlatformMessage(std::move(message)); } })); } else { // In all other cases, the message must be dispatched via a new task so // that the completion of the platform channel response future is guaranteed // to wake up the Dart event loop, even in cases where the platform and UI // threads are the same. // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) task_runners_.GetUITaskRunner()->PostTask( fml::MakeCopyable([engine = engine_->GetWeakPtr(), message = std::move(message)]() mutable { if (engine) { engine->DispatchPlatformMessage(std::move(message)); } })); } } // |PlatformView::Delegate| void Shell::OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) { TRACE_EVENT0_WITH_FLOW_IDS( "flutter", "Shell::OnPlatformViewDispatchPointerDataPacket", /*flow_id_count=*/1, /*flow_ids=*/&next_pointer_flow_id_); TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), 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 node_id, SemanticsAction action, fml::MallocMapping args) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), fml::MakeCopyable([engine = engine_->GetWeakPtr(), node_id, action, args = std::move(args)]() mutable { if (engine) { engine->DispatchSemanticsAction(node_id, action, std::move(args)); } })); } // |PlatformView::Delegate| void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), enabled] { if (engine) { engine->SetSemanticsEnabled(enabled); } }); } // |PlatformView::Delegate| void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), flags] { if (engine) { engine->SetAccessibilityFeatures(flags); } }); } // |PlatformView::Delegate| void Shell::OnPlatformViewRegisterTexture( std::shared_ptr texture) { FML_DCHECK(is_set_up_); 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_set_up_); 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_set_up_); 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]() { if (!rasterizer) { return; } 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. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr()]() { if (engine) { engine->ScheduleFrame(false); } }); } // |PlatformView::Delegate| void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() { if (rasterizer) { rasterizer->SetNextFrameCallback(closure); } }); } // |PlatformView::Delegate| const Settings& Shell::OnPlatformViewGetSettings() const { return settings_; } // |Animator::Delegate| void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time, uint64_t frame_number) { FML_DCHECK(is_set_up_); 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, frame_number); } } // |Animator::Delegate| void Shell::OnAnimatorNotifyIdle(fml::TimeDelta deadline) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (engine_) { engine_->NotifyIdle(deadline); } } void Shell::OnAnimatorUpdateLatestFrameTargetTime( fml::TimePoint frame_target_time) { FML_DCHECK(is_set_up_); // 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; } } } // |Animator::Delegate| void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { FML_DCHECK(is_set_up_); task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( [&waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), weak_pipeline = std::weak_ptr(pipeline)]() mutable { if (rasterizer) { std::shared_ptr pipeline = weak_pipeline.lock(); if (pipeline) { 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::OnAnimatorDrawLastLayerTrees( std::unique_ptr frame_timings_recorder) { FML_DCHECK(is_set_up_); auto task = fml::MakeCopyable( [rasterizer = rasterizer_->GetWeakPtr(), frame_timings_recorder = std::move(frame_timings_recorder)]() mutable { if (rasterizer) { rasterizer->DrawLastLayerTrees(std::move(frame_timings_recorder)); } }); task_runners_.GetRasterTaskRunner()->PostTask(task); } // |Engine::Delegate| void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update, CustomAccessibilityActionUpdates actions) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetPlatformTaskRunner()->RunNowOrPostTask( task_runners_.GetPlatformTaskRunner(), [view = platform_view_->GetWeakPtr(), update = std::move(update), actions = std::move(actions)] { if (view) { view->UpdateSemantics(update, actions); } }); } // |Engine::Delegate| void Shell::OnEngineHandlePlatformMessage( std::unique_ptr message) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (message->channel() == kSkiaChannel) { HandleEngineSkiaMessage(std::move(message)); return; } if (platform_message_handler_) { if (route_messages_through_platform_thread_ && !platform_message_handler_ ->DoesHandlePlatformMessageOnPlatformThread()) { #if _WIN32 // On Windows capturing a TaskRunner with a TaskRunner will cause an // uncaught exception in process shutdown because of the deletion order of // global variables. See also // https://github.com/flutter/flutter/issues/111575. // This won't be an issue until Windows supports background platform // channels (https://github.com/flutter/flutter/issues/93945). Then this // can potentially be addressed by capturing a weak_ptr to an object that // retains the ui TaskRunner, instead of the TaskRunner directly. FML_DCHECK(false); #endif // We route messages through the platform thread temporarily when the // shell is being initialized to be backwards compatible with setting // message handlers in the same event as starting the isolate, but after // it is started. auto ui_task_runner = task_runners_.GetUITaskRunner(); task_runners_.GetPlatformTaskRunner()->PostTask(fml::MakeCopyable( [weak_platform_message_handler = std::weak_ptr(platform_message_handler_), message = std::move(message), ui_task_runner]() mutable { ui_task_runner->PostTask( fml::MakeCopyable([weak_platform_message_handler, message = std::move(message)]() mutable { auto platform_message_handler = weak_platform_message_handler.lock(); if (platform_message_handler) { platform_message_handler->HandlePlatformMessage( std::move(message)); } })); })); } else { platform_message_handler_->HandlePlatformMessage(std::move(message)); } } else { task_runners_.GetPlatformTaskRunner()->PostTask( fml::MakeCopyable([view = platform_view_->GetWeakPtr(), message = std::move(message)]() mutable { if (view) { view->HandlePlatformMessage(std::move(message)); } })); } } void Shell::OnEngineChannelUpdate(std::string name, bool listening) { FML_DCHECK(is_set_up_); task_runners_.GetPlatformTaskRunner()->PostTask( [view = platform_view_->GetWeakPtr(), name = std::move(name), listening] { if (view) { view->SendChannelUpdate(name, listening); } }); } void Shell::HandleEngineSkiaMessage(std::unique_ptr message) { const auto& data = message->data(); rapidjson::Document document; document.Parse(reinterpret_cast(data.GetMapping()), data.GetSize()); 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 = 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_set_up_); 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::OnRootIsolateCreated() { if (is_added_to_service_protocol_) { return; } auto description = GetServiceProtocolDescription(); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetPlatformTaskRunner(), [self = weak_factory_.GetWeakPtr(), description = std::move(description)]() { if (self) { self->vm_->GetServiceProtocol()->AddHandler(self.get(), description); } }); is_added_to_service_protocol_ = true; } // |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; } // |Engine::Delegate| std::unique_ptr> Shell::ComputePlatformResolvedLocale( const std::vector& supported_locale_data) { return platform_view_->ComputePlatformResolvedLocales(supported_locale_data); } void Shell::LoadDartDeferredLibrary( intptr_t loading_unit_id, std::unique_ptr snapshot_data, std::unique_ptr snapshot_instructions) { task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( [engine = engine_->GetWeakPtr(), loading_unit_id, data = std::move(snapshot_data), instructions = std::move(snapshot_instructions)]() mutable { if (engine) { engine->LoadDartDeferredLibrary(loading_unit_id, std::move(data), std::move(instructions)); } })); } void Shell::LoadDartDeferredLibraryError(intptr_t loading_unit_id, const std::string error_message, bool transient) { fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), [engine = weak_engine_, loading_unit_id, error_message, transient] { if (engine) { engine->LoadDartDeferredLibraryError(loading_unit_id, error_message, transient); } }); } void Shell::UpdateAssetResolverByType( std::unique_ptr updated_asset_resolver, AssetResolver::AssetResolverType type) { fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), fml::MakeCopyable( [engine = weak_engine_, type, asset_resolver = std::move(updated_asset_resolver)]() mutable { if (engine) { engine->GetAssetManager()->UpdateResolverByType( std::move(asset_resolver), type); } })); } // |Engine::Delegate| void Shell::RequestDartDeferredLibrary(intptr_t loading_unit_id) { task_runners_.GetPlatformTaskRunner()->PostTask( [view = platform_view_->GetWeakPtr(), loading_unit_id] { if (view) { view->RequestDartDeferredLibrary(loading_unit_id); } }); } // |Engine::Delegate| double Shell::GetScaledFontSize(double unscaled_font_size, int configuration_id) const { return platform_view_->GetScaledFontSize(unscaled_font_size, configuration_id); } void Shell::ReportTimings() { FML_DCHECK(is_set_up_); 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(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::kStatisticsCount) == 0); return unreported_timings_.size() / (FrameTiming::kStatisticsCount); } void Shell::OnFrameRasterized(const FrameTiming& timing) { FML_DCHECK(is_set_up_); 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; } size_t old_count = unreported_timings_.size(); (void)old_count; for (auto phase : FrameTiming::kPhases) { unreported_timings_.push_back( timing.Get(phase).ToEpochDelta().ToMicroseconds()); } unreported_timings_.push_back(timing.GetLayerCacheCount()); unreported_timings_.push_back(timing.GetLayerCacheBytes()); unreported_timings_.push_back(timing.GetPictureCacheCount()); unreported_timings_.push_back(timing.GetPictureCacheBytes()); unreported_timings_.push_back(timing.GetFrameNumber()); FML_DCHECK(unreported_timings_.size() == old_count + FrameTiming::kStatisticsCount); // 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) { return; } self->frame_timings_report_scheduled_ = false; if (self->UnreportedFramesCount() > 0) { self->ReportTimings(); } }, fml::TimeDelta::FromMilliseconds(kBatchTimeInMilliseconds)); } } fml::Milliseconds Shell::GetFrameBudget() { double display_refresh_rate = display_manager_->GetMainDisplayRefreshRate(); if (display_refresh_rate > 0) { return fml::RefreshRateToFrameBudget(display_refresh_rate); } 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"; // Covered by FML_CHECK(). // NOLINTNEXTLINE(bugprone-unchecked-optional-access) return latest_frame_target_time_.value(); } // |Rasterizer::Delegate| bool Shell::ShouldDiscardLayerTree(int64_t view_id, const flutter::LayerTree& tree) { std::scoped_lock lock(resize_mutex_); auto expected_frame_size = ExpectedFrameSize(view_id); return !expected_frame_size.isEmpty() && tree.frame_size() != expected_frame_size; } // |ServiceProtocol::Handler| fml::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( std::string_view method) const { FML_DCHECK(is_set_up_); 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 { FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!weak_engine_) { return ServiceProtocol::Handler::Description(); } return { weak_engine_->GetUIIsolateMainPort(), weak_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", std::move(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", std::move(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()); if (settings_.enable_impeller) { ServiceProtocolFailureError( response, "Cannot capture SKP screenshot with Impeller enabled."); return false; } 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.SetEntrypointArgs(engine_->GetLastEntrypointArgs()); configuration.AddAssetResolver(std::make_unique( fml::OpenDirectory(asset_directory_path.c_str(), false, fml::FilePermission::kRead), false)); // Preserve any original asset resolvers to avoid syncing unchanged assets // over the DevFS connection. auto old_asset_manager = engine_->GetAssetManager(); if (old_asset_manager != nullptr) { for (auto& old_resolver : old_asset_manager->TakeResolvers()) { if (old_resolver->IsValidAfterAssetManagerChange()) { configuration.AddAssetResolver(std::move(old_resolver)); } } } 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 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", display_manager_->GetMainDisplayRefreshRate(), response->GetAllocator()); return true; } double Shell::GetMainDisplayRefreshRate() { return display_manager_->GetMainDisplayRefreshRate(); } void Shell::RegisterImageDecoder(ImageGeneratorFactory factory, int32_t priority) { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); FML_DCHECK(is_set_up_); fml::TaskRunner::RunNowOrPostTask( task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), factory = std::move(factory), priority]() { if (engine) { engine->GetImageGeneratorRegistry()->AddFactory(factory, priority); } }); } 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); #if !SLIMPELLER PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); std::vector sksls = persistent_cache->LoadSkSLs(); for (const auto& sksl : sksls) { size_t b64_size = Base64::EncodedSize(sksl.value->size()); sk_sp b64_data = SkData::MakeUninitialized(b64_size + 1); char* b64_char = static_cast(b64_data->writable_data()); Base64::Encode(sksl.value->data(), sksl.value->size(), b64_char); b64_char[b64_size] = 0; // make it null terminated for printing rapidjson::Value shader_value(b64_char, response->GetAllocator()); std::string_view key_view(reinterpret_cast(sksl.key->data()), sksl.key->size()); auto encode_result = fml::Base32Encode(key_view); if (!encode_result.first) { continue; } rapidjson::Value shader_key(encode_result.second, response->GetAllocator()); shaders_json.AddMember(shader_key, shader_value, response->GetAllocator()); } #endif // !SLIMPELLER response->AddMember("SkSLs", shaders_json, response->GetAllocator()); return true; } bool Shell::OnServiceProtocolEstimateRasterCacheMemory( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); uint64_t layer_cache_byte_size = 0u; uint64_t picture_cache_byte_size = 0u; #if !SLIMPELLER const auto& raster_cache = rasterizer_->compositor_context()->raster_cache(); layer_cache_byte_size = raster_cache.EstimateLayerCacheByteSize(); picture_cache_byte_size = raster_cache.EstimatePictureCacheByteSize(); #endif // !SLIMPELLER response->SetObject(); response->AddMember("type", "EstimateRasterCacheMemory", response->GetAllocator()); response->AddMember("layerBytes", layer_cache_byte_size, response->GetAllocator()); response->AddMember("pictureBytes", picture_cache_byte_size, 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(); if (!asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").data(), false, fml::FilePermission::kRead), false))) { // The new asset directory path was invalid. FML_DLOG(ERROR) << "Could not update asset directory."; ServiceProtocolFailureError(response, "Could not update asset directory."); return false; } // Preserve any original asset resolvers to avoid syncing unchanged assets // over the DevFS connection. auto old_asset_manager = engine_->GetAssetManager(); if (old_asset_manager != nullptr) { for (auto& old_resolver : old_asset_manager->TakeResolvers()) { if (old_resolver->IsValidAfterAssetManagerChange()) { asset_manager->PushBack(std::move(old_resolver)); } } } if (engine_->UpdateAssetManager(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; } void Shell::SendFontChangeNotification() { // 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(); std::unique_ptr fontsChangeMessage = std::make_unique( kSystemChannel, fml::MallocMapping::Copy(message.c_str(), message.length()), nullptr); OnPlatformViewDispatchPlatformMessage(std::move(fontsChangeMessage)); } bool Shell::OnServiceProtocolReloadAssetFonts( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response) { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); if (!engine_) { return false; } engine_->GetFontCollection().RegisterFonts(engine_->GetAssetManager()); engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache(); SendFontChangeNotification(); auto& allocator = response->GetAllocator(); response->SetObject(); response->AddMember("type", "Success", allocator); return true; } void Shell::OnPlatformViewAddView(int64_t view_id, const ViewportMetrics& viewport_metrics, AddViewCallback callback) { TRACE_EVENT0("flutter", "Shell::AddView"); FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); FML_DCHECK(view_id != kFlutterImplicitViewId) << "Unexpected request to add the implicit view #" << kFlutterImplicitViewId << ". This view should never be added."; task_runners_.GetUITaskRunner()->RunNowOrPostTask( task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), // viewport_metrics, // view_id, // callback = std::move(callback) // ] { if (engine) { engine->AddView(view_id, viewport_metrics, callback); } }); } void Shell::OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) { TRACE_EVENT0("flutter", "Shell::RemoveView"); FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); FML_DCHECK(view_id != kFlutterImplicitViewId) << "Unexpected request to remove the implicit view #" << kFlutterImplicitViewId << ". This view should never be removed."; expected_frame_sizes_.erase(view_id); task_runners_.GetUITaskRunner()->RunNowOrPostTask( task_runners_.GetUITaskRunner(), [&task_runners = task_runners_, // engine = engine_->GetWeakPtr(), // rasterizer = rasterizer_->GetWeakPtr(), // view_id, // callback = std::move(callback) // ] { if (engine) { bool removed = engine->RemoveView(view_id); callback(removed); } // Don't wait for the raster task here, which only cleans up memory and // does not affect functionality. Make sure it is done after Dart // removes the view to avoid receiving another rasterization request // that adds back the view record. task_runners.GetRasterTaskRunner()->PostTask([rasterizer, view_id]() { if (rasterizer) { rasterizer->CollectView(view_id); } }); }); } Rasterizer::Screenshot Shell::Screenshot( Rasterizer::ScreenshotType screenshot_type, bool base64_encode) { if (settings_.enable_impeller) { switch (screenshot_type) { case Rasterizer::ScreenshotType::SkiaPicture: FML_LOG(ERROR) << "Impeller backend cannot produce ScreenshotType::SkiaPicture."; return {}; case Rasterizer::ScreenshotType::UncompressedImage: case Rasterizer::ScreenshotType::CompressedImage: case Rasterizer::ScreenshotType::SurfaceData: break; } } 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_set_up_); 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."); } // Check for overflow. auto now = std::chrono::steady_clock::now(); auto max_duration = std::chrono::steady_clock::time_point::max() - now; auto desired_duration = std::chrono::milliseconds(timeout.ToMilliseconds()); auto duration = now + (desired_duration > max_duration ? max_duration : desired_duration); std::unique_lock lock(waiting_for_first_frame_mutex_); bool success = waiting_for_first_frame_condition_.wait_until( lock, duration, [&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_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); if (!engine_) { return false; } engine_->SetupDefaultFontManager(); engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache(); // After system fonts are reloaded, we send a system channel message // to notify flutter framework. SendFontChangeNotification(); return true; } std::shared_ptr Shell::GetIsGpuDisabledSyncSwitch() const { return is_gpu_disabled_sync_switch_; } void Shell::SetGpuAvailability(GpuAvailability availability) { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); switch (availability) { case GpuAvailability::kAvailable: is_gpu_disabled_sync_switch_->SetSwitch(false); return; case GpuAvailability::kFlushAndMakeUnavailable: { fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetIOTaskRunner(), [io_manager = io_manager_.get(), &latch]() { io_manager->GetSkiaUnrefQueue()->Drain(); latch.Signal(); }); latch.Wait(); } // FALLTHROUGH case GpuAvailability::kUnavailable: is_gpu_disabled_sync_switch_->SetSwitch(true); return; default: FML_DCHECK(false); } } void Shell::OnDisplayUpdates(std::vector> displays) { FML_DCHECK(is_set_up_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); std::vector display_data; display_data.reserve(displays.size()); for (const auto& display : displays) { display_data.push_back(display->GetDisplayData()); } fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), display_data = std::move(display_data)]() { if (engine) { engine->SetDisplays(display_data); } }); display_manager_->HandleDisplayUpdates(std::move(displays)); } fml::TimePoint Shell::GetCurrentTimePoint() { return fml::TimePoint::Now(); } const std::shared_ptr& Shell::GetPlatformMessageHandler() const { return platform_message_handler_; } const std::weak_ptr Shell::GetVsyncWaiter() const { if (!engine_) { return {}; } return engine_->GetVsyncWaiter(); } const std::shared_ptr Shell::GetConcurrentWorkerTaskRunner() const { FML_DCHECK(vm_); if (!vm_) { return nullptr; } return vm_->GetConcurrentWorkerTaskRunner(); } SkISize Shell::ExpectedFrameSize(int64_t view_id) { auto found = expected_frame_sizes_.find(view_id); if (found == expected_frame_sizes_.end()) { return SkISize::MakeEmpty(); } return found->second; } } // namespace flutter