diff --git a/DEPS b/DEPS index a49534aa6b..4634616669 100644 --- a/DEPS +++ b/DEPS @@ -115,7 +115,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '8dddd90bf943a8174913564353b30a3b11ee0f7a', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '3cf97e01cdbd4bb920fa3d40282a56c4b2d62a58', # Fuchsia compatibility # diff --git a/engine/src/flutter/BUILD.gn b/engine/src/flutter/BUILD.gn index 24dda026c6..b37b0b8d40 100644 --- a/engine/src/flutter/BUILD.gn +++ b/engine/src/flutter/BUILD.gn @@ -10,14 +10,12 @@ group("flutter") { public_deps = [ "$flutter_root/lib/snapshot:generate_snapshot_bin", "$flutter_root/lib/snapshot:kernel_platform_files", + "$flutter_root/shell/testing", "$flutter_root/sky", - "$flutter_root/third_party/txt", ] if (flutter_runtime_mode != "debug") { - public_deps += [ - "$flutter_root/lib/snapshot:entry_points_json_files", - ] + public_deps += [ "$flutter_root/lib/snapshot:entry_points_json_files" ] } if (!is_fuchsia && !is_fuchsia_host) { @@ -45,20 +43,24 @@ group("flutter") { "$flutter_root/shell/platform/embedder:flutter_embedder_framework", ] } + + public_deps += [ + "$flutter_root/flow:flow_unittests", + "$flutter_root/fml:fml_unittests", + "$flutter_root/runtime:runtime_unittests", + "$flutter_root/shell/common:shell_unittests", + "$flutter_root/sky/engine/wtf:wtf_unittests", + "$flutter_root/synchronization:synchronization_unittests", + "$flutter_root/third_party/txt:txt_unittests", + "//garnet/public/lib/fxl:fxl_unittests", + ] + if (!is_win) { public_deps += [ "$flutter_root/shell/platform/embedder:embedder_unittests", "$flutter_root/shell/platform/embedder:flutter_engine", ] } - public_deps += [ - "$flutter_root/flow:flow_unittests", - "$flutter_root/fml:fml_unittests", - "$flutter_root/sky/engine/wtf:wtf_unittests", - "$flutter_root/synchronization:synchronization_unittests", - "$flutter_root/third_party/txt:txt_unittests", - "//garnet/public/lib/fxl:fxl_unittests", - ] } } @@ -74,29 +76,23 @@ if (is_fuchsia) { "$flutter_root/content_handler:aot", ] if (flutter_runtime_mode != "release") { - deps += [ - "//third_party/dart/runtime/observatory:embedded_archive_observatory", - ] + deps += [ "//third_party/dart/runtime/observatory:embedded_archive_observatory" ] } binary = "flutter_aot_runner" if (flutter_runtime_mode != "release") { - resources = [ - { - path = rebase_path( - "$root_gen_dir/observatory/embedded_archive_observatory.tar") - dest = "observatory.tar" - }, - ] + resources = [ { + path = rebase_path( + "$root_gen_dir/observatory/embedded_archive_observatory.tar") + dest = "observatory.tar" + } ] } - meta = [ - { - path = rebase_path("content_handler/meta/sandbox") - dest = "sandbox" - }, - ] + meta = [ { + path = rebase_path("content_handler/meta/sandbox") + dest = "sandbox" + } ] } package("flutter_jit_runner") { @@ -104,29 +100,23 @@ if (is_fuchsia) { "$flutter_root/content_handler:jit", ] if (flutter_runtime_mode != "release") { - deps += [ - "//third_party/dart/runtime/observatory:embedded_archive_observatory", - ] + deps += [ "//third_party/dart/runtime/observatory:embedded_archive_observatory" ] } binary = "flutter_jit_runner" if (flutter_runtime_mode != "release") { - resources = [ - { - path = rebase_path( - "$root_gen_dir/observatory/embedded_archive_observatory.tar") - dest = "observatory.tar" - }, - ] + resources = [ { + path = rebase_path( + "$root_gen_dir/observatory/embedded_archive_observatory.tar") + dest = "observatory.tar" + } ] } - meta = [ - { - path = rebase_path("content_handler/meta/sandbox") - dest = "sandbox" - }, - ] + meta = [ { + path = rebase_path("content_handler/meta/sandbox") + dest = "sandbox" + } ] } } else { group("dist") { diff --git a/engine/src/flutter/assets/BUILD.gn b/engine/src/flutter/assets/BUILD.gn index c4901375ad..28d7e56965 100644 --- a/engine/src/flutter/assets/BUILD.gn +++ b/engine/src/flutter/assets/BUILD.gn @@ -4,16 +4,18 @@ source_set("assets") { sources = [ - "asset_provider.h", + "asset_manager.cc", + "asset_manager.h", + "asset_resolver.h", "directory_asset_bundle.cc", "directory_asset_bundle.h", - "unzipper_provider.cc", - "unzipper_provider.h", "zip_asset_store.cc", "zip_asset_store.h", ] deps = [ + "$flutter_root/common", + "$flutter_root/fml", "$flutter_root/glue", "//garnet/public/lib/fxl", "//garnet/public/lib/zip", @@ -23,7 +25,5 @@ source_set("assets") { "//third_party/zlib:minizip", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } diff --git a/engine/src/flutter/assets/asset_manager.cc b/engine/src/flutter/assets/asset_manager.cc new file mode 100644 index 0000000000..9833eee923 --- /dev/null +++ b/engine/src/flutter/assets/asset_manager.cc @@ -0,0 +1,59 @@ +// Copyright 2017 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. + +#include "flutter/assets/asset_manager.h" + +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/assets/zip_asset_store.h" +#include "flutter/glue/trace_event.h" +#include "lib/fxl/files/path.h" + +#ifdef ERROR +#undef ERROR +#endif + +namespace blink { + +AssetManager::AssetManager() = default; + +AssetManager::~AssetManager() = default; + +void AssetManager::PushFront(std::unique_ptr resolver) { + if (resolver == nullptr || !resolver->IsValid()) { + return; + } + + resolvers_.push_front(std::move(resolver)); +} + +void AssetManager::PushBack(std::unique_ptr resolver) { + if (resolver == nullptr || !resolver->IsValid()) { + return; + } + + resolvers_.push_back(std::move(resolver)); +} + +// |blink::AssetResolver| +bool AssetManager::GetAsBuffer(const std::string& asset_name, + std::vector* data) const { + if (asset_name.size() == 0) { + return false; + } + TRACE_EVENT0("flutter", "AssetManager::GetAsBuffer"); + for (const auto& resolver : resolvers_) { + if (resolver->GetAsBuffer(asset_name, data)) { + return true; + } + } + FXL_DLOG(ERROR) << "Could not find asset: " << asset_name; + return false; +} + +// |blink::AssetResolver| +bool AssetManager::IsValid() const { + return resolvers_.size() > 0; +} + +} // namespace blink diff --git a/engine/src/flutter/assets/asset_manager.h b/engine/src/flutter/assets/asset_manager.h new file mode 100644 index 0000000000..fc7f3ef052 --- /dev/null +++ b/engine/src/flutter/assets/asset_manager.h @@ -0,0 +1,47 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_ASSETS_ASSET_MANAGER_H_ +#define FLUTTER_ASSETS_ASSET_MANAGER_H_ + +#include +#include +#include + +#include "flutter/assets/asset_resolver.h" +#include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/ref_counted.h" + +namespace blink { + +class AssetManager final : public AssetResolver, + public fxl::RefCountedThreadSafe { + public: + void PushFront(std::unique_ptr resolver); + + void PushBack(std::unique_ptr resolver); + + // |blink::AssetResolver| + bool IsValid() const override; + + // |blink::AssetResolver| + bool GetAsBuffer(const std::string& asset_name, + std::vector* data) const override; + + private: + std::deque> resolvers_; + + AssetManager(); + + ~AssetManager(); + + FXL_DISALLOW_COPY_AND_ASSIGN(AssetManager); + FRIEND_MAKE_REF_COUNTED(AssetManager); + FRIEND_REF_COUNTED_THREAD_SAFE(AssetManager); +}; + +} // namespace blink + +#endif // FLUTTER_ASSETS_ASSET_MANAGER_H_ diff --git a/engine/src/flutter/assets/asset_provider.h b/engine/src/flutter/assets/asset_provider.h deleted file mode 100644 index 68b7f5c2b7..0000000000 --- a/engine/src/flutter/assets/asset_provider.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_ASSETS_ASSET_PROVIDER_H_ -#define FLUTTER_ASSETS_ASSET_PROVIDER_H_ - -#include -#include - -#include "lib/fxl/memory/ref_counted.h" - -namespace blink { - -class AssetProvider - : public fxl::RefCountedThreadSafe - { - public: - virtual bool GetAsBuffer(const std::string& asset_name, - std::vector* data) = 0; - virtual ~AssetProvider() = default; -}; - -} // namespace blink -#endif // FLUTTER_ASSETS_ASSET_PROVIDER_H diff --git a/engine/src/flutter/assets/asset_resolver.h b/engine/src/flutter/assets/asset_resolver.h new file mode 100644 index 0000000000..6cfe27961a --- /dev/null +++ b/engine/src/flutter/assets/asset_resolver.h @@ -0,0 +1,32 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_ASSETS_ASSET_RESOLVER_H_ +#define FLUTTER_ASSETS_ASSET_RESOLVER_H_ + +#include +#include + +#include "lib/fxl/macros.h" + +namespace blink { + +class AssetResolver { + public: + AssetResolver() = default; + + virtual ~AssetResolver() = default; + + virtual bool IsValid() const = 0; + + virtual bool GetAsBuffer(const std::string& asset_name, + std::vector* data) const = 0; + + private: + FXL_DISALLOW_COPY_AND_ASSIGN(AssetResolver); +}; + +} // namespace blink + +#endif // FLUTTER_ASSETS_ASSET_RESOLVER_H_ diff --git a/engine/src/flutter/assets/directory_asset_bundle.cc b/engine/src/flutter/assets/directory_asset_bundle.cc index 43933079a8..8e5d4df2ab 100644 --- a/engine/src/flutter/assets/directory_asset_bundle.cc +++ b/engine/src/flutter/assets/directory_asset_bundle.cc @@ -3,73 +3,54 @@ // found in the LICENSE file. #include "flutter/assets/directory_asset_bundle.h" -#include "lib/fxl/build_config.h" - -#include #include +#include "flutter/fml/file.h" +#include "flutter/fml/mapping.h" #include "lib/fxl/files/eintr_wrapper.h" -#include "lib/fxl/files/file.h" -#include "lib/fxl/files/path.h" -#include "lib/fxl/files/unique_fd.h" -#include "lib/fxl/portable_unistd.h" namespace blink { -bool DirectoryAssetBundle::GetAsBuffer(const std::string& asset_name, - std::vector* data) { - if (fd_.is_valid()) { -#if defined(OS_WIN) - // This code path is not valid in a Windows environment. - return false; -#else - fxl::UniqueFD asset_file(openat(fd_.get(), asset_name.c_str(), O_RDONLY)); - if (!asset_file.is_valid()) - return false; - - constexpr size_t kBufferSize = 1 << 16; - size_t offset = 0; - ssize_t bytes_read = 0; - do { - offset += bytes_read; - data->resize(offset + kBufferSize); - bytes_read = read(asset_file.get(), &(*data)[offset], kBufferSize); - } while (bytes_read > 0); - - if (bytes_read < 0) { - FXL_LOG(ERROR) << "Reading " << asset_name << " failed"; - data->clear(); - return false; - } - - data->resize(offset + bytes_read); - return true; -#endif +DirectoryAssetBundle::DirectoryAssetBundle(fml::UniqueFD descriptor) + : descriptor_(std::move(descriptor)) { + if (!fml::IsDirectory(descriptor_)) { + return; } - std::string asset_path = GetPathForAsset(asset_name); - if (asset_path.empty()) - return false; - return files::ReadFileToVector(asset_path, data); + is_valid_ = true; } -DirectoryAssetBundle::~DirectoryAssetBundle() {} +DirectoryAssetBundle::~DirectoryAssetBundle() = default; -DirectoryAssetBundle::DirectoryAssetBundle(std::string directory) - : directory_(std::move(directory)), fd_() {} +// |blink::AssetResolver| +bool DirectoryAssetBundle::IsValid() const { + return is_valid_; +} -DirectoryAssetBundle::DirectoryAssetBundle(fxl::UniqueFD fd) - : fd_(std::move(fd)) {} - -std::string DirectoryAssetBundle::GetPathForAsset( - const std::string& asset_name) { - std::string asset_path = files::SimplifyPath(directory_ + "/" + asset_name); - if (asset_path.find(directory_) != 0u) { - FXL_LOG(ERROR) << "Asset name '" << asset_name - << "' attempted to traverse outside asset bundle."; - return std::string(); +// |blink::AssetResolver| +bool DirectoryAssetBundle::GetAsBuffer(const std::string& asset_name, + std::vector* data) const { + if (data == nullptr) { + return false; } - return asset_path; + + if (!is_valid_) { + FXL_DLOG(WARNING) << "Asset bundle was not valid."; + return false; + } + + fml::FileMapping mapping( + fml::OpenFile(descriptor_, asset_name.c_str(), fml::OpenPermission::kRead, + false /* directory */), + false /* executable */); + + if (mapping.GetMapping() == nullptr) { + return false; + } + + data->resize(mapping.GetSize()); + memmove(data->data(), mapping.GetMapping(), mapping.GetSize()); + return true; } } // namespace blink diff --git a/engine/src/flutter/assets/directory_asset_bundle.h b/engine/src/flutter/assets/directory_asset_bundle.h index c710a51379..b594e1357f 100644 --- a/engine/src/flutter/assets/directory_asset_bundle.h +++ b/engine/src/flutter/assets/directory_asset_bundle.h @@ -5,31 +5,31 @@ #ifndef FLUTTER_ASSETS_DIRECTORY_ASSET_BUNDLE_H_ #define FLUTTER_ASSETS_DIRECTORY_ASSET_BUNDLE_H_ -#include -#include - -#include "flutter/assets/asset_provider.h" -#include "lib/fxl/files/unique_fd.h" +#include "flutter/assets/asset_resolver.h" +#include "flutter/fml/unique_fd.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_counted.h" namespace blink { -class DirectoryAssetBundle - : public AssetProvider { +class DirectoryAssetBundle : public AssetResolver { public: - explicit DirectoryAssetBundle(std::string directory); - // Expects fd to be valid, otherwise the file descriptor is ignored. - explicit DirectoryAssetBundle(fxl::UniqueFD fd); - virtual ~DirectoryAssetBundle(); + explicit DirectoryAssetBundle(fml::UniqueFD descriptor); - virtual bool GetAsBuffer(const std::string& asset_name, std::vector* data); - - std::string GetPathForAsset(const std::string& asset_name); + ~DirectoryAssetBundle() override; private: - const std::string directory_; - fxl::UniqueFD fd_; + const fml::UniqueFD descriptor_; + bool is_valid_ = false; + + std::string GetPathForAsset(const std::string& asset_name) const; + + // |blink::AssetResolver| + bool IsValid() const override; + + // |blink::AssetResolver| + bool GetAsBuffer(const std::string& asset_name, + std::vector* data) const override; FXL_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle); }; diff --git a/engine/src/flutter/assets/unzipper_provider.cc b/engine/src/flutter/assets/unzipper_provider.cc deleted file mode 100644 index 8ed023f9a2..0000000000 --- a/engine/src/flutter/assets/unzipper_provider.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/assets/unzipper_provider.h" - -#include "lib/fxl/logging.h" -#include "third_party/zlib/contrib/minizip/unzip.h" - -namespace blink { - -UnzipperProvider GetUnzipperProviderForPath(std::string zip_path) { - return [zip_path]() { - zip::UniqueUnzipper unzipper(unzOpen2(zip_path.c_str(), nullptr)); - if (!unzipper.is_valid()) - FXL_LOG(ERROR) << "Unable to open zip file: " << zip_path; - return unzipper; - }; -} - -} // namespace blink diff --git a/engine/src/flutter/assets/unzipper_provider.h b/engine/src/flutter/assets/unzipper_provider.h deleted file mode 100644 index f0f8d9597d..0000000000 --- a/engine/src/flutter/assets/unzipper_provider.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_ASSETS_UNZIP_PROVIDER_H_ -#define FLUTTER_ASSETS_UNZIP_PROVIDER_H_ - -#include - -#include "lib/zip/unique_unzipper.h" - -namespace blink { - -using UnzipperProvider = std::function; - -UnzipperProvider GetUnzipperProviderForPath(std::string zip_path); - -} // namespace blink - -#endif // FLUTTER_ASSETS_UNZIP_PROVIDER_H_ diff --git a/engine/src/flutter/assets/zip_asset_store.cc b/engine/src/flutter/assets/zip_asset_store.cc index c8534fa10c..1b9216bd34 100644 --- a/engine/src/flutter/assets/zip_asset_store.cc +++ b/engine/src/flutter/assets/zip_asset_store.cc @@ -15,21 +15,28 @@ #include #include "flutter/glue/trace_event.h" -#include "lib/fxl/files/eintr_wrapper.h" -#include "lib/fxl/files/unique_fd.h" -#include "lib/zip/unique_unzipper.h" namespace blink { -ZipAssetStore::ZipAssetStore(UnzipperProvider unzipper_provider) - : unzipper_provider_(std::move(unzipper_provider)) { +ZipAssetStore::ZipAssetStore(std::string file_path) + : file_path_(std::move(file_path)) { BuildStatCache(); } ZipAssetStore::~ZipAssetStore() = default; +zip::UniqueUnzipper ZipAssetStore::CreateUnzipper() const { + return zip::UniqueUnzipper{::unzOpen2(file_path_.c_str(), nullptr)}; +} + +// |blink::AssetResolver| +bool ZipAssetStore::IsValid() const { + return stat_cache_.size() > 0; +} + +// |blink::AssetResolver| bool ZipAssetStore::GetAsBuffer(const std::string& asset_name, - std::vector* data) { + std::vector* data) const { TRACE_EVENT0("flutter", "ZipAssetStore::GetAsBuffer"); auto found = stat_cache_.find(asset_name); @@ -37,7 +44,7 @@ bool ZipAssetStore::GetAsBuffer(const std::string& asset_name, return false; } - auto unzipper = unzipper_provider_(); + auto unzipper = CreateUnzipper(); if (!unzipper.is_valid()) { return false; @@ -73,7 +80,8 @@ bool ZipAssetStore::GetAsBuffer(const std::string& asset_name, void ZipAssetStore::BuildStatCache() { TRACE_EVENT0("flutter", "ZipAssetStore::BuildStatCache"); - auto unzipper = unzipper_provider_(); + + auto unzipper = CreateUnzipper(); if (!unzipper.is_valid()) { return; diff --git a/engine/src/flutter/assets/zip_asset_store.h b/engine/src/flutter/assets/zip_asset_store.h index 1ffda483ba..558678e25b 100644 --- a/engine/src/flutter/assets/zip_asset_store.h +++ b/engine/src/flutter/assets/zip_asset_store.h @@ -6,21 +6,20 @@ #define FLUTTER_ASSETS_ZIP_ASSET_STORE_H_ #include -#include -#include "flutter/assets/unzipper_provider.h" +#include "flutter/assets/asset_resolver.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_counted.h" +#include "lib/zip/unique_unzipper.h" #include "third_party/zlib/contrib/minizip/unzip.h" namespace blink { -class ZipAssetStore : public fxl::RefCountedThreadSafe { +class ZipAssetStore final : public AssetResolver { public: - explicit ZipAssetStore(UnzipperProvider unzipper_provider); - ~ZipAssetStore(); + ZipAssetStore(std::string file_path); - bool GetAsBuffer(const std::string& asset_name, std::vector* data); + ~ZipAssetStore() override; private: struct CacheEntry { @@ -30,11 +29,20 @@ class ZipAssetStore : public fxl::RefCountedThreadSafe { : file_pos(p_file_pos), uncompressed_size(p_uncompressed_size) {} }; - UnzipperProvider unzipper_provider_; - std::map stat_cache_; + std::string file_path_; + mutable std::map stat_cache_; + + // |blink::AssetResolver| + bool IsValid() const override; + + // |blink::AssetResolver| + bool GetAsBuffer(const std::string& asset_name, + std::vector* data) const override; void BuildStatCache(); + zip::UniqueUnzipper CreateUnzipper() const; + FXL_DISALLOW_COPY_AND_ASSIGN(ZipAssetStore); }; diff --git a/engine/src/flutter/common/BUILD.gn b/engine/src/flutter/common/BUILD.gn index 53b71914d4..47cd7d3d62 100644 --- a/engine/src/flutter/common/BUILD.gn +++ b/engine/src/flutter/common/BUILD.gn @@ -12,11 +12,12 @@ source_set("common") { sources = [ "settings.cc", "settings.h", - "threads.cc", - "threads.h", + "task_runners.cc", + "task_runners.h", ] deps = [ + "$flutter_root/fml", "//garnet/public/lib/fxl", ] diff --git a/engine/src/flutter/common/settings.cc b/engine/src/flutter/common/settings.cc index 0198f714cb..85523e1b9a 100644 --- a/engine/src/flutter/common/settings.cc +++ b/engine/src/flutter/common/settings.cc @@ -4,26 +4,50 @@ #include "flutter/common/settings.h" -#include - -#include "lib/fxl/logging.h" +#include namespace blink { -namespace { -Settings* g_settings = nullptr; - -} // namespace - -const Settings& Settings::Get() { - FXL_CHECK(g_settings); - return *g_settings; -} - -void Settings::Set(const Settings& settings) { - FXL_CHECK(!g_settings); - g_settings = new Settings(); - *g_settings = settings; +std::string Settings::ToString() const { + std::stringstream stream; + stream << "Settings: " << std::endl; + stream << "aot_snapshot_path: " << aot_snapshot_path << std::endl; + stream << "script_snapshot_path: " << script_snapshot_path << std::endl; + stream << "aot_vm_snapshot_data_filename: " << aot_vm_snapshot_data_filename + << std::endl; + stream << "aot_vm_snapshot_instr_filename: " << aot_vm_snapshot_instr_filename + << std::endl; + stream << "aot_isolate_snapshot_data_filename: " + << aot_isolate_snapshot_data_filename << std::endl; + stream << "aot_isolate_snapshot_instr_filename: " + << aot_isolate_snapshot_instr_filename << std::endl; + stream << "application_library_path: " << application_library_path + << std::endl; + stream << "main_dart_file_path: " << main_dart_file_path << std::endl; + stream << "packages_file_path: " << packages_file_path << std::endl; + stream << "temp_directory_path: " << temp_directory_path << std::endl; + stream << "dart_flags:" << std::endl; + for (const auto& dart_flag : dart_flags) { + stream << " " << dart_flag << std::endl; + } + stream << "start_paused: " << start_paused << std::endl; + stream << "trace_skia: " << trace_skia << std::endl; + stream << "trace_startup: " << trace_startup << std::endl; + stream << "endless_trace_buffer: " << endless_trace_buffer << std::endl; + stream << "enable_dart_profiling: " << enable_dart_profiling << std::endl; + stream << "dart_non_checked_mode: " << dart_non_checked_mode << std::endl; + stream << "enable_observatory: " << enable_observatory << std::endl; + stream << "observatory_port: " << observatory_port << std::endl; + stream << "ipv6: " << ipv6 << std::endl; + stream << "use_test_fonts: " << use_test_fonts << std::endl; + stream << "enable_software_rendering: " << enable_software_rendering + << std::endl; + stream << "using_blink: " << using_blink << std::endl; + stream << "log_tag: " << log_tag << std::endl; + stream << "icu_data_path: " << icu_data_path << std::endl; + stream << "assets_dir: " << assets_dir << std::endl; + stream << "assets_path: " << assets_path << std::endl; + return stream.str(); } } // namespace blink diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index 5bb5c6cbbe..c6c3159766 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -5,40 +5,82 @@ #ifndef FLUTTER_COMMON_SETTINGS_H_ #define FLUTTER_COMMON_SETTINGS_H_ +#include #include +#include #include #include +#include "flutter/fml/unique_fd.h" +#include "lib/fxl/functional/closure.h" + namespace blink { +using TaskObserverAdd = + std::function; +using TaskObserverRemove = std::function; + struct Settings { - bool enable_observatory = false; - // Port on target will be auto selected by the OS. A message will be printed - // on the target with the port after it has been selected. - uint32_t observatory_port = 0; - bool ipv6 = false; - bool start_paused = false; - bool trace_startup = false; - bool endless_trace_buffer = false; - bool enable_dart_profiling = false; - bool use_test_fonts = false; - bool dart_non_checked_mode = false; - bool enable_software_rendering = false; - bool using_blink = true; - std::string aot_shared_library_path; + // VM settings + std::string script_snapshot_path; + std::string kernel_snapshot_path; + std::string aot_snapshot_path; std::string aot_vm_snapshot_data_filename; std::string aot_vm_snapshot_instr_filename; std::string aot_isolate_snapshot_data_filename; std::string aot_isolate_snapshot_instr_filename; + std::string application_library_path; + std::string application_kernel_asset; + + std::string main_dart_file_path; + std::string packages_file_path; + std::string temp_directory_path; std::vector dart_flags; - std::string log_tag = "flutter"; - static const Settings& Get(); - static void Set(const Settings& settings); + // Isolate settings + bool start_paused = false; + bool trace_skia = false; + bool trace_startup = false; + bool endless_trace_buffer = false; + bool enable_dart_profiling = false; + bool dart_non_checked_mode = false; + + // Observatory settings + bool enable_observatory = false; + // Port on target will be auto selected by the OS. A message will be printed + // on the target with the port after it has been selected. + uint32_t observatory_port = 0; + bool ipv6 = false; + + // Font settings + bool use_test_fonts = false; + + // Engine settings + TaskObserverAdd task_observer_add; + TaskObserverRemove task_observer_remove; + // The main isolate is current when this callback is made. This is a good spot + // to perform native Dart bindings for libraries not built in. + fxl::Closure root_isolate_create_callback; + // The isolate is not current and may have already been destroyed when this + // call is made. + fxl::Closure root_isolate_shutdown_callback; + bool enable_software_rendering = false; + bool using_blink = false; + bool skia_deterministic_rendering_on_cpu = false; + std::string log_tag = "flutter"; + std::string icu_data_path; + + // Assets settings + fml::UniqueFD::element_type assets_dir = + fml::UniqueFD::traits_type::InvalidValue(); + std::string assets_path; + std::string flx_path; + + std::string ToString() const; }; } // namespace blink diff --git a/engine/src/flutter/common/task_runners.cc b/engine/src/flutter/common/task_runners.cc new file mode 100644 index 0000000000..1a09daec18 --- /dev/null +++ b/engine/src/flutter/common/task_runners.cc @@ -0,0 +1,48 @@ +// Copyright 2017 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. + +#include "flutter/common/task_runners.h" + +#include + +namespace blink { + +TaskRunners::TaskRunners(std::string label, + fxl::RefPtr platform, + fxl::RefPtr gpu, + fxl::RefPtr ui, + fxl::RefPtr io) + : label_(std::move(label)), + platform_(std::move(platform)), + gpu_(std::move(gpu)), + ui_(std::move(ui)), + io_(std::move(io)) {} + +TaskRunners::~TaskRunners() = default; + +const std::string& TaskRunners::GetLabel() const { + return label_; +} + +fxl::RefPtr TaskRunners::GetPlatformTaskRunner() const { + return platform_; +} + +fxl::RefPtr TaskRunners::GetUITaskRunner() const { + return ui_; +} + +fxl::RefPtr TaskRunners::GetIOTaskRunner() const { + return io_; +} + +fxl::RefPtr TaskRunners::GetGPUTaskRunner() const { + return gpu_; +} + +bool TaskRunners::IsValid() const { + return platform_ && gpu_ && ui_ && io_; +} + +} // namespace blink diff --git a/engine/src/flutter/common/task_runners.h b/engine/src/flutter/common/task_runners.h new file mode 100644 index 0000000000..f41ae147eb --- /dev/null +++ b/engine/src/flutter/common/task_runners.h @@ -0,0 +1,46 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_COMMON_TASK_RUNNERS_H_ +#define FLUTTER_COMMON_TASK_RUNNERS_H_ + +#include + +#include "lib/fxl/macros.h" +#include "lib/fxl/tasks/task_runner.h" + +namespace blink { + +class TaskRunners { + public: + TaskRunners(std::string label, + fxl::RefPtr platform, + fxl::RefPtr gpu, + fxl::RefPtr ui, + fxl::RefPtr io); + + ~TaskRunners(); + + const std::string& GetLabel() const; + + fxl::RefPtr GetPlatformTaskRunner() const; + + fxl::RefPtr GetUITaskRunner() const; + + fxl::RefPtr GetIOTaskRunner() const; + + fxl::RefPtr GetGPUTaskRunner() const; + + bool IsValid() const; + + private: + const std::string label_; + fxl::RefPtr platform_; + fxl::RefPtr gpu_; + fxl::RefPtr ui_; + fxl::RefPtr io_; +}; +} // namespace blink + +#endif // FLUTTER_COMMON_TASK_RUNNERS_H_ diff --git a/engine/src/flutter/common/threads.cc b/engine/src/flutter/common/threads.cc deleted file mode 100644 index 3634d8d7c8..0000000000 --- a/engine/src/flutter/common/threads.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/common/threads.h" - -#include - -namespace blink { -namespace { - -Threads* g_threads = nullptr; - -} // namespace - -Threads::Threads() {} - -Threads::Threads(fxl::RefPtr platform, - fxl::RefPtr gpu, - fxl::RefPtr ui, - fxl::RefPtr io) - : platform_(std::move(platform)), - gpu_(std::move(gpu)), - ui_(std::move(ui)), - io_(std::move(io)) {} - -Threads::~Threads() {} - -const fxl::RefPtr& Threads::Platform() { - return Get().platform_; -} - -const fxl::RefPtr& Threads::Gpu() { - return Get().gpu_; -} - -const fxl::RefPtr& Threads::UI() { - return Get().ui_; -} - -const fxl::RefPtr& Threads::IO() { - return Get().io_; -} - -const Threads& Threads::Get() { - FXL_CHECK(g_threads); - return *g_threads; -} - -void Threads::Set(const Threads& threads) { - FXL_CHECK(!g_threads); - g_threads = new Threads(); - *g_threads = threads; -} - -} // namespace blink diff --git a/engine/src/flutter/common/threads.h b/engine/src/flutter/common/threads.h deleted file mode 100644 index 456a5eba8a..0000000000 --- a/engine/src/flutter/common/threads.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_COMMON_THREADS_H_ -#define FLUTTER_COMMON_THREADS_H_ - -#include "lib/fxl/tasks/task_runner.h" - -#define ASSERT_IS_PLATFORM_THREAD \ - FXL_DCHECK(::blink::Threads::Platform()->RunsTasksOnCurrentThread()); -#define ASSERT_IS_GPU_THREAD \ - FXL_DCHECK(::blink::Threads::Gpu()->RunsTasksOnCurrentThread()); -#define ASSERT_IS_UI_THREAD \ - FXL_DCHECK(::blink::Threads::UI()->RunsTasksOnCurrentThread()); -#define ASSERT_IS_IO_THREAD \ - FXL_DCHECK(::blink::Threads::IO()->RunsTasksOnCurrentThread()); - -namespace blink { - -class Threads { - public: - Threads(); - Threads(fxl::RefPtr platform, - fxl::RefPtr gpu, - fxl::RefPtr ui, - fxl::RefPtr io); - ~Threads(); - - static const fxl::RefPtr& Platform(); - static const fxl::RefPtr& Gpu(); - static const fxl::RefPtr& UI(); - static const fxl::RefPtr& IO(); - - static void Set(const Threads& settings); - - private: - static const Threads& Get(); - - fxl::RefPtr platform_; - fxl::RefPtr gpu_; - fxl::RefPtr ui_; - fxl::RefPtr io_; -}; - -} // namespace blink - -#endif // FLUTTER_COMMON_THREADS_H_ diff --git a/engine/src/flutter/flow/BUILD.gn b/engine/src/flutter/flow/BUILD.gn index e6a6411a6c..07c5d4e5d4 100644 --- a/engine/src/flutter/flow/BUILD.gn +++ b/engine/src/flutter/flow/BUILD.gn @@ -48,11 +48,12 @@ source_set("flow") { "matrix_decomposition.h", "paint_utils.cc", "paint_utils.h", - "process_info.h", "raster_cache.cc", "raster_cache.h", "raster_cache_key.cc", "raster_cache_key.h", + "skia_gpu_object.cc", + "skia_gpu_object.h", "texture.cc", "texture.h", ] @@ -61,12 +62,11 @@ source_set("flow") { "//garnet/public/lib/fxl", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] deps = [ "$flutter_root/common", + "$flutter_root/fml", "$flutter_root/glue", "$flutter_root/synchronization", "//third_party/skia", @@ -103,8 +103,8 @@ executable("flow_unittests") { deps = [ ":flow", - "//third_party/dart/runtime:libdart_jit", # for tracing "$flutter_root/testing", + "//third_party/dart/runtime:libdart_jit", # for tracing "//third_party/skia", ] } diff --git a/engine/src/flutter/flow/compositor_context.cc b/engine/src/flutter/flow/compositor_context.cc index e0d17229e7..53659103be 100644 --- a/engine/src/flutter/flow/compositor_context.cc +++ b/engine/src/flutter/flow/compositor_context.cc @@ -4,12 +4,12 @@ #include "flutter/flow/compositor_context.h" +#include "flutter/flow/layers/layer_tree.h" #include "third_party/skia/include/core/SkCanvas.h" namespace flow { -CompositorContext::CompositorContext(std::unique_ptr info) - : process_info_(std::move(info)) {} +CompositorContext::CompositorContext() = default; CompositorContext::~CompositorContext() = default; @@ -18,10 +18,6 @@ void CompositorContext::BeginFrame(ScopedFrame& frame, if (enable_instrumentation) { frame_count_.Increment(); frame_time_.Start(); - - if (process_info_ && process_info_->SampleNow()) { - memory_usage_.Add(process_info_->GetResidentMemorySize()); - } } } @@ -33,11 +29,12 @@ void CompositorContext::EndFrame(ScopedFrame& frame, } } -CompositorContext::ScopedFrame CompositorContext::AcquireFrame( +std::unique_ptr CompositorContext::AcquireFrame( GrContext* gr_context, SkCanvas* canvas, bool instrumentation_enabled) { - return ScopedFrame(*this, gr_context, canvas, instrumentation_enabled); + return std::make_unique(*this, gr_context, canvas, + instrumentation_enabled); } CompositorContext::ScopedFrame::ScopedFrame(CompositorContext& context, @@ -51,18 +48,23 @@ CompositorContext::ScopedFrame::ScopedFrame(CompositorContext& context, context_.BeginFrame(*this, instrumentation_enabled_); } -CompositorContext::ScopedFrame::ScopedFrame(ScopedFrame&& frame) = default; - CompositorContext::ScopedFrame::~ScopedFrame() { context_.EndFrame(*this, instrumentation_enabled_); } +bool CompositorContext::ScopedFrame::Raster(flow::LayerTree& layer_tree, + bool ignore_raster_cache) { + layer_tree.Preroll(*this, ignore_raster_cache); + layer_tree.Paint(*this); + return true; +} + void CompositorContext::OnGrContextCreated() { - texture_registry_->OnGrContextCreated(); + texture_registry_.OnGrContextCreated(); } void CompositorContext::OnGrContextDestroyed() { - texture_registry_->OnGrContextDestroyed(); + texture_registry_.OnGrContextDestroyed(); raster_cache_.Clear(); } diff --git a/engine/src/flutter/flow/compositor_context.h b/engine/src/flutter/flow/compositor_context.h index 2d31042274..14c2449dbe 100644 --- a/engine/src/flutter/flow/compositor_context.h +++ b/engine/src/flutter/flow/compositor_context.h @@ -9,7 +9,6 @@ #include #include "flutter/flow/instrumentation.h" -#include "flutter/flow/process_info.h" #include "flutter/flow/raster_cache.h" #include "flutter/flow/texture.h" #include "lib/fxl/macros.h" @@ -18,19 +17,26 @@ namespace flow { +class LayerTree; + class CompositorContext { public: class ScopedFrame { public: + ScopedFrame(CompositorContext& context, + GrContext* gr_context, + SkCanvas* canvas, + bool instrumentation_enabled); + + virtual ~ScopedFrame(); + SkCanvas* canvas() { return canvas_; } CompositorContext& context() const { return context_; } GrContext* gr_context() const { return gr_context_; } - ScopedFrame(ScopedFrame&& frame); - - ~ScopedFrame(); + virtual bool Raster(LayerTree& layer_tree, bool ignore_raster_cache); private: CompositorContext& context_; @@ -38,23 +44,17 @@ class CompositorContext { SkCanvas* canvas_; const bool instrumentation_enabled_; - ScopedFrame(CompositorContext& context, - GrContext* gr_context, - SkCanvas* canvas, - bool instrumentation_enabled); - - friend class CompositorContext; - FXL_DISALLOW_COPY_AND_ASSIGN(ScopedFrame); }; - CompositorContext(std::unique_ptr info); + CompositorContext(); - ~CompositorContext(); + virtual ~CompositorContext(); - ScopedFrame AcquireFrame(GrContext* gr_context, - SkCanvas* canvas, - bool instrumentation_enabled = true); + virtual std::unique_ptr AcquireFrame( + GrContext* gr_context, + SkCanvas* canvas, + bool instrumentation_enabled); void OnGrContextCreated(); @@ -62,7 +62,7 @@ class CompositorContext { RasterCache& raster_cache() { return raster_cache_; } - TextureRegistry& texture_registry() { return *texture_registry_; } + TextureRegistry& texture_registry() { return texture_registry_; } const Counter& frame_count() const { return frame_count_; } @@ -70,20 +70,12 @@ class CompositorContext { Stopwatch& engine_time() { return engine_time_; } - const CounterValues& memory_usage() const { return memory_usage_; } - - void SetTextureRegistry(TextureRegistry* textureRegistry) { - texture_registry_ = textureRegistry; - } - private: RasterCache raster_cache_; - TextureRegistry* texture_registry_; - std::unique_ptr process_info_; + TextureRegistry texture_registry_; Counter frame_count_; Stopwatch frame_time_; Stopwatch engine_time_; - CounterValues memory_usage_; void BeginFrame(ScopedFrame& frame, bool enable_instrumentation); diff --git a/engine/src/flutter/flow/debug_print.cc b/engine/src/flutter/flow/debug_print.cc index 0aa5b4b3b7..3311b9c2b4 100644 --- a/engine/src/flutter/flow/debug_print.cc +++ b/engine/src/flutter/flow/debug_print.cc @@ -80,3 +80,8 @@ std::ostream& operator<<(std::ostream& os, const flow::RasterCacheKey& k) { ; return os; } + +std::ostream& operator<<(std::ostream& os, const SkISize& size) { + os << size.width() << ", " << size.height(); + return os; +} diff --git a/engine/src/flutter/flow/debug_print.h b/engine/src/flutter/flow/debug_print.h index ca5973eeb0..78a28fe385 100644 --- a/engine/src/flutter/flow/debug_print.h +++ b/engine/src/flutter/flow/debug_print.h @@ -15,14 +15,15 @@ #define DEF_PRINTER(x) std::ostream& operator<<(std::ostream&, const x&); -DEF_PRINTER(flow::RasterCacheKey); DEF_PRINTER(flow::MatrixDecomposition); +DEF_PRINTER(flow::RasterCacheKey); +DEF_PRINTER(SkISize); DEF_PRINTER(SkMatrix); DEF_PRINTER(SkMatrix44); -DEF_PRINTER(SkVector3); -DEF_PRINTER(SkVector4); +DEF_PRINTER(SkPoint); DEF_PRINTER(SkRect); DEF_PRINTER(SkRRect); -DEF_PRINTER(SkPoint); +DEF_PRINTER(SkVector3); +DEF_PRINTER(SkVector4); #endif // FLUTTER_FLOW_DEBUG_PRINT_H_ diff --git a/engine/src/flutter/flow/export_node.cc b/engine/src/flutter/flow/export_node.cc index a130c426a7..27ab34030c 100644 --- a/engine/src/flutter/flow/export_node.cc +++ b/engine/src/flutter/flow/export_node.cc @@ -4,28 +4,27 @@ #include "flutter/flow/export_node.h" -#include "flutter/common/threads.h" #include "lib/fxl/functional/make_copyable.h" namespace flow { ExportNodeHolder::ExportNodeHolder( + fxl::RefPtr gpu_task_runner, fxl::RefPtr export_token_handle) - : export_node_(std::make_unique(export_token_handle)) { - ASSERT_IS_UI_THREAD; + : gpu_task_runner_(std::move(gpu_task_runner)), + export_node_(std::make_unique(export_token_handle)) { + FXL_DCHECK(gpu_task_runner_); } void ExportNodeHolder::Bind(SceneUpdateContext& context, scenic_lib::ContainerNode& container, const SkPoint& offset, bool hit_testable) { - ASSERT_IS_GPU_THREAD; export_node_->Bind(context, container, offset, hit_testable); } ExportNodeHolder::~ExportNodeHolder() { - ASSERT_IS_UI_THREAD; - blink::Threads::Gpu()->PostTask( + gpu_task_runner_->PostTask( fxl::MakeCopyable([export_node = std::move(export_node_)]() { export_node->Dispose(true); })); @@ -44,8 +43,6 @@ void ExportNode::Bind(SceneUpdateContext& context, scenic_lib::ContainerNode& container, const SkPoint& offset, bool hit_testable) { - ASSERT_IS_GPU_THREAD; - if (export_token_) { // Happens first time we bind. node_.reset(new scenic_lib::EntityNode(container.session())); @@ -67,8 +64,6 @@ void ExportNode::Bind(SceneUpdateContext& context, } void ExportNode::Dispose(bool remove_from_scene_update_context) { - ASSERT_IS_GPU_THREAD; - // If scene_update_context_ is set, then we should still have a node left to // dereference. // If scene_update_context_ is null, then either: diff --git a/engine/src/flutter/flow/export_node.h b/engine/src/flutter/flow/export_node.h index 24f94fcebf..e6a11175e5 100644 --- a/engine/src/flutter/flow/export_node.h +++ b/engine/src/flutter/flow/export_node.h @@ -15,6 +15,7 @@ #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_counted.h" #include "lib/ui/scenic/client/resources.h" +#include "third_party/flutter/fml/task_runner.h" #include "third_party/skia/include/core/SkPoint.h" namespace flow { @@ -24,7 +25,8 @@ namespace flow { // held by the ExportNode. class ExportNodeHolder : public fxl::RefCountedThreadSafe { public: - ExportNodeHolder(fxl::RefPtr export_token_handle); + ExportNodeHolder(fxl::RefPtr gpu_task_runner, + fxl::RefPtr export_token_handle); ~ExportNodeHolder(); // Calls Bind() on the wrapped ExportNode. @@ -36,6 +38,7 @@ class ExportNodeHolder : public fxl::RefCountedThreadSafe { ExportNode* export_node() { return export_node_.get(); } private: + fxl::RefPtr gpu_task_runner_; std::unique_ptr export_node_; FRIEND_MAKE_REF_COUNTED(ExportNodeHolder); diff --git a/engine/src/flutter/flow/layers/default_layer_builder.cc b/engine/src/flutter/flow/layers/default_layer_builder.cc index a33b0eaa68..ca228a7d58 100644 --- a/engine/src/flutter/flow/layers/default_layer_builder.cc +++ b/engine/src/flutter/flow/layers/default_layer_builder.cc @@ -137,20 +137,20 @@ void DefaultLayerBuilder::PushPerformanceOverlay(uint64_t enabled_options, } void DefaultLayerBuilder::PushPicture(const SkPoint& offset, - sk_sp picture, + SkiaGPUObject picture, bool picture_is_complex, bool picture_will_change) { if (!current_layer_) { return; } - SkRect pictureRect = picture->cullRect(); + SkRect pictureRect = picture.get()->cullRect(); pictureRect.offset(offset.x(), offset.y()); if (!SkRect::Intersects(pictureRect, cull_rects_.top())) { return; } auto layer = std::make_unique(); layer->set_offset(offset); - layer->set_picture(picture); + layer->set_picture(std::move(picture)); layer->set_is_complex(picture_is_complex); layer->set_will_change(picture_will_change); current_layer_->Add(std::move(layer)); diff --git a/engine/src/flutter/flow/layers/default_layer_builder.h b/engine/src/flutter/flow/layers/default_layer_builder.h index a62ec46714..cadd173ab5 100644 --- a/engine/src/flutter/flow/layers/default_layer_builder.h +++ b/engine/src/flutter/flow/layers/default_layer_builder.h @@ -59,7 +59,7 @@ class DefaultLayerBuilder final : public LayerBuilder { // |flow::LayerBuilder| void PushPicture(const SkPoint& offset, - sk_sp picture, + SkiaGPUObject picture, bool picture_is_complex, bool picture_will_change) override; diff --git a/engine/src/flutter/flow/layers/layer.h b/engine/src/flutter/flow/layers/layer.h index d0ef1990ab..cf7ad71c36 100644 --- a/engine/src/flutter/flow/layers/layer.h +++ b/engine/src/flutter/flow/layers/layer.h @@ -44,9 +44,6 @@ class Layer { virtual ~Layer(); struct PrerollContext { -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics = nullptr; -#endif RasterCache* raster_cache; GrContext* gr_context; SkColorSpace* dst_color_space; @@ -59,7 +56,6 @@ class Layer { SkCanvas& canvas; const Stopwatch& frame_time; const Stopwatch& engine_time; - const CounterValues& memory_usage; TextureRegistry& texture_registry; const bool checkerboard_offscreen_layers; }; diff --git a/engine/src/flutter/flow/layers/layer_builder.h b/engine/src/flutter/flow/layers/layer_builder.h index 91ae13974a..a29e959514 100644 --- a/engine/src/flutter/flow/layers/layer_builder.h +++ b/engine/src/flutter/flow/layers/layer_builder.h @@ -8,6 +8,7 @@ #include #include "flutter/flow/layers/layer.h" +#include "flutter/flow/skia_gpu_object.h" #include "garnet/public/lib/fxl/macros.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "third_party/skia/include/core/SkColor.h" @@ -57,7 +58,7 @@ class LayerBuilder { const SkRect& rect) = 0; virtual void PushPicture(const SkPoint& offset, - sk_sp picture, + SkiaGPUObject picture, bool picture_is_complex, bool picture_will_change) = 0; diff --git a/engine/src/flutter/flow/layers/layer_tree.cc b/engine/src/flutter/flow/layers/layer_tree.cc index fde2fc4bf0..d9fb374194 100644 --- a/engine/src/flutter/flow/layers/layer_tree.cc +++ b/engine/src/flutter/flow/layers/layer_tree.cc @@ -17,43 +17,18 @@ LayerTree::LayerTree() LayerTree::~LayerTree() = default; -void LayerTree::Raster(CompositorContext::ScopedFrame& frame, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif - bool ignore_raster_cache) { -#if defined(OS_FUCHSIA) - FXL_DCHECK(metrics); -#endif - Preroll(frame, -#if defined(OS_FUCHSIA) - metrics, -#endif - ignore_raster_cache); - Paint(frame); -} - void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif bool ignore_raster_cache) { -#if defined(OS_FUCHSIA) - FXL_DCHECK(metrics); -#endif TRACE_EVENT0("flutter", "LayerTree::Preroll"); SkColorSpace* color_space = frame.canvas() ? frame.canvas()->imageInfo().colorSpace() : nullptr; frame.context().raster_cache().SetCheckboardCacheImages( checkerboard_raster_cache_images_); Layer::PrerollContext context = { -#if defined(OS_FUCHSIA) - metrics, -#endif - ignore_raster_cache ? nullptr : &frame.context().raster_cache(), - frame.gr_context(), - color_space, - SkRect::MakeEmpty(), + ignore_raster_cache ? nullptr : &frame.context().raster_cache(), + frame.gr_context(), + color_space, + SkRect::MakeEmpty(), }; root_layer_->Preroll(&context, SkMatrix::I()); @@ -63,9 +38,12 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, void LayerTree::UpdateScene(SceneUpdateContext& context, scenic_lib::ContainerNode& container) { TRACE_EVENT0("flutter", "LayerTree::UpdateScene"); - - SceneUpdateContext::Transform transform(context, 1.f / device_pixel_ratio_, - 1.f / device_pixel_ratio_, 1.f); + const auto& metrics = context.metrics(); + SceneUpdateContext::Transform transform(context, // context + 1.0f / metrics->scale_x, // X + 1.0f / metrics->scale_y, // Y + 1.0f / metrics->scale_z // Z + ); SceneUpdateContext::Frame frame( context, SkRRect::MakeRect( @@ -82,12 +60,13 @@ void LayerTree::UpdateScene(SceneUpdateContext& context, #endif void LayerTree::Paint(CompositorContext::ScopedFrame& frame) const { - Layer::PaintContext context = {*frame.canvas(), - frame.context().frame_time(), - frame.context().engine_time(), - frame.context().memory_usage(), - frame.context().texture_registry(), - checkerboard_offscreen_layers_}; + Layer::PaintContext context = { + *frame.canvas(), // + frame.context().frame_time(), // + frame.context().engine_time(), // + frame.context().texture_registry(), // + checkerboard_offscreen_layers_ // + }; TRACE_EVENT0("flutter", "LayerTree::Paint"); if (root_layer_->needs_painting()) diff --git a/engine/src/flutter/flow/layers/layer_tree.h b/engine/src/flutter/flow/layers/layer_tree.h index 5ddebd088d..25a8e072cf 100644 --- a/engine/src/flutter/flow/layers/layer_tree.h +++ b/engine/src/flutter/flow/layers/layer_tree.h @@ -13,9 +13,6 @@ #include "flutter/flow/layers/layer.h" #include "lib/fxl/macros.h" #include "lib/fxl/time/time_delta.h" -#if defined(OS_FUCHSIA) -#include -#endif #include "third_party/skia/include/core/SkSize.h" namespace flow { @@ -26,24 +23,10 @@ class LayerTree { ~LayerTree(); - // Raster includes both Preroll and Paint. - void Raster(CompositorContext::ScopedFrame& frame, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif - bool ignore_raster_cache = false); - void Preroll(CompositorContext::ScopedFrame& frame, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif bool ignore_raster_cache = false); #if defined(OS_FUCHSIA) - void set_device_pixel_ratio(float device_pixel_ratio) { - device_pixel_ratio_ = device_pixel_ratio; - } - void UpdateScene(SceneUpdateContext& context, scenic_lib::ContainerNode& container); #endif @@ -93,10 +76,6 @@ class LayerTree { bool checkerboard_raster_cache_images_; bool checkerboard_offscreen_layers_; -#if defined(OS_FUCHSIA) - float device_pixel_ratio_ = 1.f; -#endif - FXL_DISALLOW_COPY_AND_ASSIGN(LayerTree); }; diff --git a/engine/src/flutter/flow/layers/performance_overlay_layer.cc b/engine/src/flutter/flow/layers/performance_overlay_layer.cc index cc55500115..7d871af33b 100644 --- a/engine/src/flutter/flow/layers/performance_overlay_layer.cc +++ b/engine/src/flutter/flow/layers/performance_overlay_layer.cc @@ -57,34 +57,6 @@ void VisualizeStopWatch(SkCanvas& canvas, } } -void VisualizeCounterValuesBytes(SkCanvas& canvas, - const CounterValues& counter_values, - SkScalar x, - SkScalar y, - SkScalar width, - SkScalar height, - bool show_graph, - bool show_labels, - const std::string& label_prefix) { - const int label_x = 8; // distance from x - const int label_y = -10; // distance from y+height - - if (show_graph) { - SkRect visualization_rect = SkRect::MakeXYWH(x, y, width, height); - counter_values.Visualize(canvas, visualization_rect); - } - - auto current_usage = counter_values.GetCurrentValue(); - - if (show_labels && current_usage > 0) { - std::stringstream stream; - stream.setf(std::ios::fixed | std::ios::showpoint); - stream << std::setprecision(2); - stream << label_prefix << " " << current_usage * 1e-6 << " MB"; - DrawStatisticsText(canvas, stream.str(), x + label_x, y + height + label_y); - } -} - } // namespace PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options) @@ -111,11 +83,6 @@ void PerformanceOverlayLayer::Paint(PaintContext& context) const { VisualizeStopWatch(context.canvas, context.engine_time, x, y + height, width, height - padding, options_ & kVisualizeEngineStatistics, options_ & kDisplayEngineStatistics, "UI"); - - VisualizeCounterValuesBytes( - context.canvas, context.memory_usage, x, y + (2 * height), width, - height - padding, options_ & kVisualizeMemoryStatistics, - options_ & kDisplayMemoryStatistics, "Memory (Resident)"); } } // namespace flow diff --git a/engine/src/flutter/flow/layers/performance_overlay_layer.h b/engine/src/flutter/flow/layers/performance_overlay_layer.h index e7c3ac530a..7744810714 100644 --- a/engine/src/flutter/flow/layers/performance_overlay_layer.h +++ b/engine/src/flutter/flow/layers/performance_overlay_layer.h @@ -14,8 +14,6 @@ const int kDisplayRasterizerStatistics = 1 << 0; const int kVisualizeRasterizerStatistics = 1 << 1; const int kDisplayEngineStatistics = 1 << 2; const int kVisualizeEngineStatistics = 1 << 3; -const int kDisplayMemoryStatistics = 1 << 4; -const int kVisualizeMemoryStatistics = 1 << 5; class PerformanceOverlayLayer : public Layer { public: diff --git a/engine/src/flutter/flow/layers/picture_layer.cc b/engine/src/flutter/flow/layers/picture_layer.cc index 4a99d7d07f..552ca0443b 100644 --- a/engine/src/flutter/flow/layers/picture_layer.cc +++ b/engine/src/flutter/flow/layers/picture_layer.cc @@ -4,39 +4,30 @@ #include "flutter/flow/layers/picture_layer.h" -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" namespace flow { PictureLayer::PictureLayer() = default; -PictureLayer::~PictureLayer() { - // The picture may contain references to textures that are associated - // with the IO thread's context. - SkPicture* picture = picture_.release(); - if (picture) { - blink::Threads::IO()->PostTask([picture]() { picture->unref(); }); - } -} +PictureLayer::~PictureLayer() = default; void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { + SkPicture* sk_picture = picture(); + if (auto cache = context->raster_cache) { raster_cache_result_ = cache->GetPrerolledImage( - context->gr_context, picture_.get(), matrix, context->dst_color_space, -#if defined(OS_FUCHSIA) - context->metrics, -#endif + context->gr_context, sk_picture, matrix, context->dst_color_space, is_complex_, will_change_); } - SkRect bounds = picture_->cullRect().makeOffset(offset_.x(), offset_.y()); + SkRect bounds = sk_picture->cullRect().makeOffset(offset_.x(), offset_.y()); set_paint_bounds(bounds); } void PictureLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "PictureLayer::Paint"); - FXL_DCHECK(picture_); + FXL_DCHECK(picture_.get()); FXL_DCHECK(needs_painting()); SkAutoCanvasRestore save(&context.canvas, true); @@ -53,7 +44,7 @@ void PictureLayer::Paint(PaintContext& context) const { SkCanvas::kStrict_SrcRectConstraint // source constraint ); } else { - context.canvas.drawPicture(picture_.get()); + context.canvas.drawPicture(picture()); } } diff --git a/engine/src/flutter/flow/layers/picture_layer.h b/engine/src/flutter/flow/layers/picture_layer.h index 191ef9d709..c67929b7cb 100644 --- a/engine/src/flutter/flow/layers/picture_layer.h +++ b/engine/src/flutter/flow/layers/picture_layer.h @@ -5,8 +5,11 @@ #ifndef FLUTTER_FLOW_LAYERS_PICTURE_LAYER_H_ #define FLUTTER_FLOW_LAYERS_PICTURE_LAYER_H_ +#include + #include "flutter/flow/layers/layer.h" #include "flutter/flow/raster_cache.h" +#include "flutter/flow/skia_gpu_object.h" namespace flow { @@ -16,12 +19,14 @@ class PictureLayer : public Layer { ~PictureLayer() override; void set_offset(const SkPoint& offset) { offset_ = offset; } - void set_picture(sk_sp picture) { picture_ = std::move(picture); } + void set_picture(SkiaGPUObject picture) { + picture_ = std::move(picture); + } void set_is_complex(bool value) { is_complex_ = value; } void set_will_change(bool value) { will_change_ = value; } - SkPicture* picture() const { return picture_.get(); } + SkPicture* picture() const { return picture_.get().get(); } void Preroll(PrerollContext* frame, const SkMatrix& matrix) override; @@ -29,7 +34,9 @@ class PictureLayer : public Layer { private: SkPoint offset_; - sk_sp picture_; + // Even though pictures themselves are not GPU resources, they may reference + // images that have a reference to a GPU resource. + SkiaGPUObject picture_; bool is_complex_ = false; bool will_change_ = false; RasterCacheResult raster_cache_result_; diff --git a/engine/src/flutter/flow/process_info.h b/engine/src/flutter/flow/process_info.h deleted file mode 100644 index 6623fe7257..0000000000 --- a/engine/src/flutter/flow/process_info.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FLOW_PROCESS_INFO_H_ -#define FLUTTER_FLOW_PROCESS_INFO_H_ - -#include "lib/fxl/macros.h" - -namespace flow { - -/// The CompositorContext attempts to collect information from the process for -/// instrumentation purposes. The compositor does not have the platform -/// specific capabilities to collect this information on its own. The platform -/// can choose to provide this information however. -class ProcessInfo { - public: - virtual ~ProcessInfo() = default; - - virtual bool SampleNow() = 0; - - /// Virtual memory size in bytes. - virtual size_t GetVirtualMemorySize() = 0; - - /// Resident memory size in bytes. - virtual size_t GetResidentMemorySize() = 0; -}; - -} // namespace flow - -#endif // FLUTTER_FLOW_PROCESS_INFO_H_ diff --git a/engine/src/flutter/flow/raster_cache.cc b/engine/src/flutter/flow/raster_cache.cc index aa9fad0e28..11a86729b4 100644 --- a/engine/src/flutter/flow/raster_cache.cc +++ b/engine/src/flutter/flow/raster_cache.cc @@ -6,7 +6,6 @@ #include -#include "flutter/common/threads.h" #include "flutter/flow/paint_utils.h" #include "flutter/glue/trace_event.h" #include "lib/fxl/logging.h" @@ -73,9 +72,6 @@ RasterCacheResult RasterizePicture(SkPicture* picture, GrContext* context, const MatrixDecomposition& matrix, SkColorSpace* dst_color_space, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif bool checkerboard) { TRACE_EVENT0("flutter", "RasterCachePopulate"); @@ -83,17 +79,9 @@ RasterCacheResult RasterizePicture(SkPicture* picture, const SkRect logical_rect = picture->cullRect(); -#if defined(OS_FUCHSIA) - float metrics_scale_x = metrics->scale_x; - float metrics_scale_y = metrics->scale_y; -#else - float metrics_scale_x = 1.f; - float metrics_scale_y = 1.f; -#endif - - const SkRect physical_rect = SkRect::MakeWH( - std::fabs(logical_rect.width() * metrics_scale_x * scale.x()), - std::fabs(logical_rect.height() * metrics_scale_y * scale.y())); + const SkRect physical_rect = + SkRect::MakeWH(std::fabs(logical_rect.width() * scale.x()), + std::fabs(logical_rect.height() * scale.y())); const SkImageInfo image_info = SkImageInfo::MakeN32Premul( std::ceil(physical_rect.width()), // physical width @@ -120,8 +108,7 @@ RasterCacheResult RasterizePicture(SkPicture* picture, } canvas->clear(SK_ColorTRANSPARENT); - canvas->scale(std::abs(scale.x() * metrics_scale_x), - std::abs(scale.y() * metrics_scale_y)); + canvas->scale(std::abs(scale.x()), std::abs(scale.y())); canvas->translate(-logical_rect.left(), -logical_rect.top()); canvas->drawPicture(picture); @@ -153,9 +140,6 @@ RasterCacheResult RasterCache::GetPrerolledImage( SkPicture* picture, const SkMatrix& transformation_matrix, SkColorSpace* dst_color_space, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif bool is_complex, bool will_change) { if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) { @@ -172,11 +156,7 @@ RasterCacheResult RasterCache::GetPrerolledImage( return {}; } - RasterCacheKey cache_key(*picture, -#if defined(OS_FUCHSIA) - metrics->scale_x, metrics->scale_y, -#endif - matrix); + RasterCacheKey cache_key(*picture, matrix); Entry& entry = cache_[cache_key]; entry.access_count = ClampSize(entry.access_count + 1, 0, threshold_); @@ -189,9 +169,6 @@ RasterCacheResult RasterCache::GetPrerolledImage( if (!entry.image.is_valid()) { entry.image = RasterizePicture(picture, context, matrix, dst_color_space, -#if defined(OS_FUCHSIA) - metrics, -#endif checkerboard_images_); } diff --git a/engine/src/flutter/flow/raster_cache.h b/engine/src/flutter/flow/raster_cache.h index 31201049b6..3278eb355b 100644 --- a/engine/src/flutter/flow/raster_cache.h +++ b/engine/src/flutter/flow/raster_cache.h @@ -12,9 +12,6 @@ #include "flutter/flow/raster_cache_key.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/weak_ptr.h" -#if defined(OS_FUCHSIA) -#include -#endif #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSize.h" @@ -57,9 +54,6 @@ class RasterCache { SkPicture* picture, const SkMatrix& transformation_matrix, SkColorSpace* dst_color_space, -#if defined(OS_FUCHSIA) - gfx::Metrics* metrics, -#endif bool is_complex, bool will_change); diff --git a/engine/src/flutter/flow/raster_cache_key.h b/engine/src/flutter/flow/raster_cache_key.h index 83df52658c..1f28ea5320 100644 --- a/engine/src/flutter/flow/raster_cache_key.h +++ b/engine/src/flutter/flow/raster_cache_key.h @@ -15,30 +15,15 @@ namespace flow { class RasterCacheKey { public: - RasterCacheKey(const SkPicture& picture, -#if defined(OS_FUCHSIA) - float metrics_scale_x, - float metrics_scale_y, -#endif - const MatrixDecomposition& matrix) + RasterCacheKey(const SkPicture& picture, const MatrixDecomposition& matrix) : picture_id_(picture.uniqueID()), -#if defined(OS_FUCHSIA) - metrics_scale_x_(metrics_scale_x), - metrics_scale_y_(metrics_scale_y), -#endif - scale_key_( - SkISize::Make(matrix.scale().x() * 1e3, matrix.scale().y() * 1e3)) { - } + scale_key_(SkISize::Make(matrix.scale().x() * 1e3, + matrix.scale().y() * 1e3)) {} uint32_t picture_id() const { return picture_id_; } const SkISize& scale_key() const { return scale_key_; } -#if defined(OS_FUCHSIA) - float metrics_scale_x() const { return metrics_scale_x_; } - float metrics_scale_y() const { return metrics_scale_y_; } -#endif - struct Hash { std::size_t operator()(RasterCacheKey const& key) const { return key.picture_id_; @@ -49,11 +34,6 @@ class RasterCacheKey { constexpr bool operator()(const RasterCacheKey& lhs, const RasterCacheKey& rhs) const { return lhs.picture_id_ == rhs.picture_id_ && -#if defined(OS_FUCHSIA) - lhs.metrics_scale_x_ == rhs.metrics_scale_x_ && - - lhs.metrics_scale_y_ == rhs.metrics_scale_y_ && -#endif lhs.scale_key_ == rhs.scale_key_; } }; @@ -63,10 +43,6 @@ class RasterCacheKey { private: uint32_t picture_id_; -#if defined(OS_FUCHSIA) - float metrics_scale_x_; - float metrics_scale_y_; -#endif SkISize scale_key_; }; diff --git a/engine/src/flutter/flow/scene_update_context.cc b/engine/src/flutter/flow/scene_update_context.cc index 83883c508b..b9d6801cc8 100644 --- a/engine/src/flutter/flow/scene_update_context.cc +++ b/engine/src/flutter/flow/scene_update_context.cc @@ -4,7 +4,6 @@ #include "flutter/flow/scene_update_context.h" -#include "flutter/common/threads.h" #include "flutter/flow/export_node.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/matrix_decomposition.h" @@ -19,9 +18,7 @@ SceneUpdateContext::SceneUpdateContext(scenic_lib::Session* session, } SceneUpdateContext::~SceneUpdateContext() { - ASSERT_IS_GPU_THREAD; - - // Release Scenic session resources for all ExportNodes. + // Release Mozart session resources for all ExportNodes. for (auto export_node : export_nodes_) { export_node->Dispose(false); } @@ -30,21 +27,16 @@ SceneUpdateContext::~SceneUpdateContext() { void SceneUpdateContext::AddChildScene(ExportNode* export_node, SkPoint offset, bool hit_testable) { - ASSERT_IS_GPU_THREAD; FXL_DCHECK(top_entity_); export_node->Bind(*this, top_entity_->entity_node(), offset, hit_testable); } void SceneUpdateContext::AddExportNode(ExportNode* export_node) { - ASSERT_IS_GPU_THREAD; - export_nodes_.insert(export_node); // Might already have been added. } void SceneUpdateContext::RemoveExportNode(ExportNode* export_node) { - ASSERT_IS_GPU_THREAD; - export_nodes_.erase(export_node); } @@ -195,12 +187,9 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { for (auto& task : paint_tasks_) { FXL_DCHECK(task.surface); SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas(); - Layer::PaintContext context = {*canvas, - frame.context().frame_time(), + Layer::PaintContext context = {*canvas, frame.context().frame_time(), frame.context().engine_time(), - frame.context().memory_usage(), - frame.context().texture_registry(), - false}; + frame.context().texture_registry(), false}; canvas->restoreToCount(1); canvas->save(); canvas->clear(task.background_color); diff --git a/engine/src/flutter/flow/skia_gpu_object.cc b/engine/src/flutter/flow/skia_gpu_object.cc new file mode 100644 index 0000000000..ce2312f921 --- /dev/null +++ b/engine/src/flutter/flow/skia_gpu_object.cc @@ -0,0 +1,44 @@ +// Copyright 2017 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. + +#include "flutter/flow/skia_gpu_object.h" + +#include "flutter/fml/message_loop.h" + +namespace flow { + +SkiaUnrefQueue::SkiaUnrefQueue(fxl::RefPtr task_runner, + fxl::TimeDelta delay) + : task_runner_(std::move(task_runner)), + drain_delay_(delay), + drain_pending_(false) {} + +SkiaUnrefQueue::~SkiaUnrefQueue() { + Drain(); +} + +void SkiaUnrefQueue::Unref(SkRefCnt* object) { + std::lock_guard lock(mutex_); + objects_.push_back(object); + if (!drain_pending_) { + drain_pending_ = true; + task_runner_->PostDelayedTask( + [strong = fxl::Ref(this)]() { strong->Drain(); }, drain_delay_); + } +} + +void SkiaUnrefQueue::Drain() { + std::deque skia_objects; + { + std::lock_guard lock(mutex_); + objects_.swap(skia_objects); + drain_pending_ = false; + } + + for (SkRefCnt* skia_object : skia_objects) { + skia_object->unref(); + } +} + +} // namespace flow diff --git a/engine/src/flutter/flow/skia_gpu_object.h b/engine/src/flutter/flow/skia_gpu_object.h new file mode 100644 index 0000000000..4711f80c6f --- /dev/null +++ b/engine/src/flutter/flow/skia_gpu_object.h @@ -0,0 +1,88 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_FLOW_SKIA_GPU_OBJECT_H_ +#define FLUTTER_FLOW_SKIA_GPU_OBJECT_H_ + +#include +#include + +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/task_runner.h" +#include "lib/fxl/memory/ref_ptr.h" +#include "third_party/skia/include/core/SkRefCnt.h" + +namespace flow { + +// A queue that holds Skia objects that must be destructed on the the given task +// runner. +class SkiaUnrefQueue : public fxl::RefCountedThreadSafe { + public: + void Unref(SkRefCnt* object); + + // Usually, the drain is called automatically. However, during IO manager + // shutdown (when the platform side reference to the OpenGL context is about + // to go away), we may need to pre-emptively drain the unref queue. It is the + // responsibility of the caller to ensure that no further unrefs are queued + // after this call. + void Drain(); + + private: + const fxl::RefPtr task_runner_; + const fxl::TimeDelta drain_delay_; + std::mutex mutex_; + std::deque objects_; + bool drain_pending_; + + SkiaUnrefQueue(fxl::RefPtr task_runner, + fxl::TimeDelta delay); + + ~SkiaUnrefQueue(); + + FRIEND_REF_COUNTED_THREAD_SAFE(SkiaUnrefQueue); + FRIEND_MAKE_REF_COUNTED(SkiaUnrefQueue); + FXL_DISALLOW_COPY_AND_ASSIGN(SkiaUnrefQueue); +}; + +/// An object whose deallocation needs to be performed on an specific unref +/// queue. The template argument U need to have a call operator that returns +/// that unref queue. +template +class SkiaGPUObject { + public: + using SkiaObjectType = T; + + SkiaGPUObject() = default; + + SkiaGPUObject(sk_sp object, fxl::RefPtr queue) + : object_(std::move(object)), queue_(std::move(queue)) { + FXL_DCHECK(queue_ && object_); + } + + SkiaGPUObject(SkiaGPUObject&&) = default; + + ~SkiaGPUObject() { reset(); } + + SkiaGPUObject& operator=(SkiaGPUObject&&) = default; + + sk_sp get() const { return object_; } + + void reset() { + if (object_) { + queue_->Unref(object_.release()); + } + queue_ = nullptr; + FXL_DCHECK(object_ == nullptr); + } + + private: + sk_sp object_; + fxl::RefPtr queue_; + + FXL_DISALLOW_COPY_AND_ASSIGN(SkiaGPUObject); +}; + +} // namespace flow + +#endif // FLUTTER_FLOW_SKIA_GPU_OBJECT_H_ diff --git a/engine/src/flutter/flow/texture.cc b/engine/src/flutter/flow/texture.cc index 33a48af989..d5cc3ae228 100644 --- a/engine/src/flutter/flow/texture.cc +++ b/engine/src/flutter/flow/texture.cc @@ -11,36 +11,32 @@ TextureRegistry::TextureRegistry() = default; TextureRegistry::~TextureRegistry() = default; void TextureRegistry::RegisterTexture(std::shared_ptr texture) { - ASSERT_IS_GPU_THREAD mapping_[texture->Id()] = texture; } void TextureRegistry::UnregisterTexture(int64_t id) { - ASSERT_IS_GPU_THREAD mapping_.erase(id); } void TextureRegistry::OnGrContextCreated() { - ASSERT_IS_GPU_THREAD; for (auto& it : mapping_) { it.second->OnGrContextCreated(); } } void TextureRegistry::OnGrContextDestroyed() { - ASSERT_IS_GPU_THREAD; for (auto& it : mapping_) { it.second->OnGrContextDestroyed(); } } std::shared_ptr TextureRegistry::GetTexture(int64_t id) { - ASSERT_IS_GPU_THREAD auto it = mapping_.find(id); return it != mapping_.end() ? it->second : nullptr; } Texture::Texture(int64_t id) : id_(id) {} + Texture::~Texture() = default; } // namespace flow diff --git a/engine/src/flutter/flow/texture.h b/engine/src/flutter/flow/texture.h index a602a6b45b..f5e4430834 100644 --- a/engine/src/flutter/flow/texture.h +++ b/engine/src/flutter/flow/texture.h @@ -6,7 +6,7 @@ #define FLUTTER_FLOW_TEXTURE_H_ #include -#include "flutter/common/threads.h" + #include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -29,6 +29,9 @@ class Texture { // Called from GPU thread. virtual void OnGrContextDestroyed() = 0; + // Called on GPU thread. + virtual void MarkNewFrameAvailable() = 0; + int64_t Id() { return id_; } private: diff --git a/engine/src/flutter/fml/BUILD.gn b/engine/src/flutter/fml/BUILD.gn index 6dabcb446c..ffbf949cc0 100644 --- a/engine/src/flutter/fml/BUILD.gn +++ b/engine/src/flutter/fml/BUILD.gn @@ -4,9 +4,12 @@ source_set("fml") { sources = [ + "file.h", "icu_util.cc", "icu_util.h", + "mapping.cc", "mapping.h", + "memory/thread_checker.h", "memory/weak_ptr.h", "memory/weak_ptr_internal.cc", "memory/weak_ptr_internal.h", @@ -14,8 +17,9 @@ source_set("fml") { "message_loop.h", "message_loop_impl.cc", "message_loop_impl.h", + "native_library.h", + "paths.cc", "paths.h", - "task_observer.h", "task_runner.cc", "task_runner.h", "thread.cc", @@ -23,6 +27,9 @@ source_set("fml") { "thread_local.h", "trace_event.cc", "trace_event.h", + "unique_fd.cc", + "unique_fd.h", + "unique_object.h", ] deps = [ @@ -95,16 +102,25 @@ source_set("fml") { ] } + if (is_fuchsia) { + sources += [ "platform/fuchsia/paths_fuchsia.cc" ] + } + if (is_win) { sources += [ + "platform/win/file_win.cc", "platform/win/mapping_win.cc", "platform/win/message_loop_win.cc", "platform/win/message_loop_win.h", + "platform/win/native_library_win.cc", "platform/win/paths_win.cc", + "platform/win/wstring_conversion.h", ] } else { sources += [ + "platform/posix/file_posix.cc", "platform/posix/mapping_posix.cc", + "platform/posix/native_library_posix.cc", ] } } diff --git a/engine/src/flutter/fml/file.h b/engine/src/flutter/fml/file.h new file mode 100644 index 0000000000..2327b2eef8 --- /dev/null +++ b/engine/src/flutter/fml/file.h @@ -0,0 +1,35 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_FML_FILE_H_ +#define FLUTTER_FML_FILE_H_ + +#include "flutter/fml/unique_fd.h" +#include "lib/fxl/macros.h" + +namespace fml { + +enum class OpenPermission { + kRead = 1, + kWrite = 1 << 1, + kReadWrite = kRead | kWrite, + kExecute, +}; + +fml::UniqueFD OpenFile(const char* path, + OpenPermission permission, + bool is_directory = false); + +fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, + const char* path, + OpenPermission permission, + bool is_directory = false); + +fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor); + +bool IsDirectory(const fml::UniqueFD& directory); + +} // namespace fml + +#endif // FLUTTER_FML_FILE_H_ diff --git a/engine/src/flutter/fml/icu_util.cc b/engine/src/flutter/fml/icu_util.cc index 093dc3d02b..0c0acd06db 100644 --- a/engine/src/flutter/fml/icu_util.cc +++ b/engine/src/flutter/fml/icu_util.cc @@ -22,8 +22,6 @@ static constexpr char kPathSeparator = '\\'; static constexpr char kPathSeparator = '/'; #endif -static constexpr char kIcuDataFileName[] = "icudtl.dat"; - class ICUContext { public: ICUContext(const std::string& icu_data_path) : valid_(false) { @@ -34,15 +32,15 @@ class ICUContext { bool SetupMapping(const std::string& icu_data_path) { // Check if the explicit path specified exists. - auto overriden_path_mapping = std::make_unique(icu_data_path); - if (overriden_path_mapping->GetSize() != 0) { - mapping_ = std::move(overriden_path_mapping); + auto path_mapping = std::make_unique(icu_data_path, false); + if (path_mapping->GetSize() != 0) { + mapping_ = std::move(path_mapping); return true; } // Check to see if the mapping is in the resources bundle. if (PlatformHasResourcesBundle()) { - auto resource = GetResourceMapping(kIcuDataFileName); + auto resource = GetResourceMapping(icu_data_path); if (resource != nullptr && resource->GetSize() != 0) { mapping_ = std::move(resource); return true; @@ -57,10 +55,8 @@ class ICUContext { return false; } - // FIXME(chinmaygarde): There is no Path::Join in FXL. So a non-portable - // version is used here. Patch FXL and update. auto file = std::make_unique( - directory.second + kPathSeparator + kIcuDataFileName); + directory.second + kPathSeparator + icu_data_path, false); if (file->GetSize() != 0) { mapping_ = std::move(file); return true; @@ -96,7 +92,8 @@ class ICUContext { void InitializeICUOnce(const std::string& icu_data_path) { static ICUContext* context = new ICUContext(icu_data_path); - FXL_CHECK(context->IsValid()) << "Must be able to initialize the ICU context"; + FXL_CHECK(context->IsValid()) + << "Must be able to initialize the ICU context. Tried: " << icu_data_path; } std::once_flag g_icu_init_flag; diff --git a/engine/src/flutter/fml/macros.h b/engine/src/flutter/fml/macros.h new file mode 100644 index 0000000000..ba46b9dda4 --- /dev/null +++ b/engine/src/flutter/fml/macros.h @@ -0,0 +1,20 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_FML_MACROS_H_ +#define FLUTTER_FML_MACROS_H_ + +#include "lib/fxl/macros.h" + +#ifndef FML_USED_ON_EMBEDDER + +#define FML_EMBEDDER_ONLY [[deprecated]] + +#else // FML_USED_ON_EMBEDDER + +#define FML_EMBEDDER_ONLY + +#endif // FML_USED_ON_EMBEDDER + +#endif // FLUTTER_FML_MACROS_H_ diff --git a/engine/src/flutter/fml/mapping.cc b/engine/src/flutter/fml/mapping.cc new file mode 100644 index 0000000000..97d7905015 --- /dev/null +++ b/engine/src/flutter/fml/mapping.cc @@ -0,0 +1,20 @@ +// Copyright 2018 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. + +#include "flutter/fml/mapping.h" + +namespace fml { + +DataMapping::DataMapping(std::vector data) : data_(std::move(data)) {} + +DataMapping::~DataMapping() = default; + +size_t DataMapping::GetSize() const { + return data_.size(); +} + +const uint8_t* DataMapping::GetMapping() const { + return data_.data(); +} +} // namespace fml diff --git a/engine/src/flutter/fml/mapping.h b/engine/src/flutter/fml/mapping.h index 8963b22a9c..00e9685dd6 100644 --- a/engine/src/flutter/fml/mapping.h +++ b/engine/src/flutter/fml/mapping.h @@ -7,14 +7,10 @@ #include #include +#include +#include "flutter/fml//unique_fd.h" #include "lib/fxl/build_config.h" - -#if OS_WIN -#include -#endif - -#include "lib/fxl/files/unique_fd.h" #include "lib/fxl/macros.h" namespace fml { @@ -39,12 +35,9 @@ std::unique_ptr GetResourceMapping(const std::string& resource_name); class FileMapping : public Mapping { public: - FileMapping(const std::string& path); + FileMapping(const std::string& path, bool executable = false); -// fxl::UniqueFD isn't supported for Windows handles. -#if !OS_WIN - FileMapping(const fxl::UniqueFD& fd); -#endif + FileMapping(const fml::UniqueFD& fd, bool executable = false); ~FileMapping() override; @@ -53,16 +46,32 @@ class FileMapping : public Mapping { const uint8_t* GetMapping() const override; private: - size_t size_; - uint8_t* mapping_; + size_t size_ = 0; + uint8_t* mapping_ = nullptr; #if OS_WIN - HANDLE mapping_handle_; + fml::UniqueFD mapping_handle_; #endif FXL_DISALLOW_COPY_AND_ASSIGN(FileMapping); }; +class DataMapping : public Mapping { + public: + DataMapping(std::vector data); + + ~DataMapping() override; + + size_t GetSize() const override; + + const uint8_t* GetMapping() const override; + + private: + std::vector data_; + + FXL_DISALLOW_COPY_AND_ASSIGN(DataMapping); +}; + } // namespace fml #endif // FLUTTER_FML_MAPPING_H_ diff --git a/engine/src/flutter/fml/memory/thread_checker.h b/engine/src/flutter/fml/memory/thread_checker.h new file mode 100644 index 0000000000..f22e9d1cfa --- /dev/null +++ b/engine/src/flutter/fml/memory/thread_checker.h @@ -0,0 +1,69 @@ +// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A class for checking that the current thread is/isn't the same as an initial +// thread. + +#ifndef FLUTTER_FML_MEMORY_THREAD_CHECKER_H_ +#define FLUTTER_FML_MEMORY_THREAD_CHECKER_H_ + +#include "lib/fxl/build_config.h" + +#if defined(OS_WIN) +#include +#else +#include +#endif + +#include "lib/fxl/logging.h" +#include "lib/fxl/macros.h" + +namespace fml { + +// A simple class that records the identity of the thread that it was created +// on, and at later points can tell if the current thread is the same as its +// creation thread. This class is thread-safe. +// +// Note: Unlike Chromium's |base::ThreadChecker|, this is *not* Debug-only (so +// #ifdef it out if you want something Debug-only). (Rationale: Having a +// |CalledOnValidThread()| that lies in Release builds seems bad. Moreover, +// there's a small space cost to having even an empty class. ) +class ThreadChecker final { + public: +#if defined(OS_WIN) + ThreadChecker() : self_(GetCurrentThreadId()) {} + ~ThreadChecker() {} + + bool IsCreationThreadCurrent() const { return GetCurrentThreadId() == self_; } + + private: + DWORD self_; + +#else + ThreadChecker() : self_(pthread_self()) {} + ~ThreadChecker() {} + + // Returns true if the current thread is the thread this object was created + // on and false otherwise. + bool IsCreationThreadCurrent() const { + return !!pthread_equal(pthread_self(), self_); + } + + private: + pthread_t self_; +#endif +}; + +#ifndef NDEBUG +#define FML_DECLARE_THREAD_CHECKER(c) fml::ThreadChecker c +#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c) \ + FXL_DCHECK((c).IsCreationThreadCurrent()) +#else +#define FML_DECLARE_THREAD_CHECKER(c) +#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c) ((void)0) +#endif + +} // namespace fml + +#endif // FLUTTER_FML_MEMORY_THREAD_CHECKER_H_ diff --git a/engine/src/flutter/fml/memory/weak_ptr.h b/engine/src/flutter/fml/memory/weak_ptr.h index 5b85c53196..2b369952d7 100644 --- a/engine/src/flutter/fml/memory/weak_ptr.h +++ b/engine/src/flutter/fml/memory/weak_ptr.h @@ -10,12 +10,17 @@ #include +#include "flutter/fml/memory/thread_checker.h" #include "flutter/fml/memory/weak_ptr_internal.h" #include "lib/fxl/logging.h" #include "lib/fxl/memory/ref_counted.h" namespace fml { +struct DebugThreadChecker { + FML_DECLARE_THREAD_CHECKER(checker); +}; + // Forward declaration, so |WeakPtr| can friend it. template class WeakPtrFactory; @@ -41,13 +46,17 @@ class WeakPtr { WeakPtr(const WeakPtr& r) = default; template - WeakPtr(const WeakPtr& r) : ptr_(r.ptr_), flag_(r.flag_) {} + WeakPtr(const WeakPtr& r) + : ptr_(static_cast(r.ptr_)), flag_(r.flag_), checker_(r.checker_) {} // Move constructor. WeakPtr(WeakPtr&& r) = default; template - WeakPtr(WeakPtr&& r) : ptr_(r.ptr_), flag_(std::move(r.flag_)) {} + WeakPtr(WeakPtr&& r) + : ptr_(static_cast(r.ptr_)), + flag_(std::move(r.flag_)), + checker_(r.checker_) {} ~WeakPtr() = default; @@ -65,16 +74,24 @@ class WeakPtr { // The following methods should only be called on the same thread as the // "originating" |WeakPtrFactory|. - explicit operator bool() const { return flag_ && flag_->is_valid(); } + explicit operator bool() const { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); + return flag_ && flag_->is_valid(); + } - T* get() const { return *this ? ptr_ : nullptr; } + T* get() const { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); + return *this ? ptr_ : nullptr; + } T& operator*() const { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); FXL_DCHECK(*this); return *get(); } T* operator->() const { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); FXL_DCHECK(*this); return get(); } @@ -85,11 +102,14 @@ class WeakPtr { friend class WeakPtrFactory; - explicit WeakPtr(T* ptr, fxl::RefPtr&& flag) - : ptr_(ptr), flag_(std::move(flag)) {} + explicit WeakPtr(T* ptr, + fxl::RefPtr&& flag, + DebugThreadChecker checker) + : ptr_(ptr), flag_(std::move(flag)), checker_(checker) {} T* ptr_; fxl::RefPtr flag_; + DebugThreadChecker checker_; // Copy/move construction/assignment supported. }; @@ -140,19 +160,22 @@ template class WeakPtrFactory { public: explicit WeakPtrFactory(T* ptr) : ptr_(ptr) { FXL_DCHECK(ptr_); } + ~WeakPtrFactory() { InvalidateWeakPtrs(); } // Gets a new weak pointer, which will be valid until either // |InvalidateWeakPtrs()| is called or this object is destroyed. WeakPtr GetWeakPtr() { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); if (!flag_) flag_ = fxl::MakeRefCounted(); - return WeakPtr(ptr_, flag_.Clone()); + return WeakPtr(ptr_, flag_.Clone(), checker_); } // Call this method to invalidate all existing weak pointers. (Note that // additional weak pointers can be produced even after this is called.) void InvalidateWeakPtrs() { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); if (!flag_) return; flag_->Invalidate(); @@ -162,13 +185,17 @@ class WeakPtrFactory { // Call this method to determine if any weak pointers exist. (Note that a // "false" result is definitive, but a "true" result may not be if weak // pointers are held/reset/destroyed/reassigned on other threads.) - bool HasWeakPtrs() const { return flag_ && !flag_->HasOneRef(); } + bool HasWeakPtrs() const { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker_.checker); + return flag_ && !flag_->HasOneRef(); + } private: // Note: See weak_ptr_internal.h for an explanation of why we store the // pointer here, instead of in the "flag". T* const ptr_; fxl::RefPtr flag_; + DebugThreadChecker checker_; FXL_DISALLOW_COPY_AND_ASSIGN(WeakPtrFactory); }; diff --git a/engine/src/flutter/fml/message_loop.cc b/engine/src/flutter/fml/message_loop.cc index 4765cfa765..44a0e307c1 100644 --- a/engine/src/flutter/fml/message_loop.cc +++ b/engine/src/flutter/fml/message_loop.cc @@ -55,7 +55,7 @@ void MessageLoop::Terminate() { loop_->DoTerminate(); } -fxl::RefPtr MessageLoop::GetTaskRunner() const { +fxl::RefPtr MessageLoop::GetTaskRunner() const { return task_runner_; } @@ -63,12 +63,12 @@ fxl::RefPtr MessageLoop::GetLoopImpl() const { return loop_; } -void MessageLoop::AddTaskObserver(TaskObserver* observer) { - loop_->AddTaskObserver(observer); +void MessageLoop::AddTaskObserver(intptr_t key, fxl::Closure callback) { + loop_->AddTaskObserver(key, callback); } -void MessageLoop::RemoveTaskObserver(TaskObserver* observer) { - loop_->RemoveTaskObserver(observer); +void MessageLoop::RemoveTaskObserver(intptr_t key) { + loop_->RemoveTaskObserver(key); } void MessageLoop::RunExpiredTasksNow() { diff --git a/engine/src/flutter/fml/message_loop.h b/engine/src/flutter/fml/message_loop.h index 87773619cc..3bfb1c40c6 100644 --- a/engine/src/flutter/fml/message_loop.h +++ b/engine/src/flutter/fml/message_loop.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_FML_MESSAGE_LOOP_H_ #define FLUTTER_FML_MESSAGE_LOOP_H_ -#include "flutter/fml/task_observer.h" +#include "flutter/fml/macros.h" #include "lib/fxl/macros.h" #include "lib/fxl/tasks/task_runner.h" @@ -16,6 +16,7 @@ class MessageLoopImpl; class MessageLoop { public: + FML_EMBEDDER_ONLY static MessageLoop& GetCurrent(); bool IsValid() const; @@ -24,11 +25,11 @@ class MessageLoop { void Terminate(); - void AddTaskObserver(TaskObserver* observer); + void AddTaskObserver(intptr_t key, fxl::Closure callback); - void RemoveTaskObserver(TaskObserver* observer); + void RemoveTaskObserver(intptr_t key); - fxl::RefPtr GetTaskRunner() const; + fxl::RefPtr GetTaskRunner() const; // Exposed for the embedder shell which allows clients to poll for events // instead of dedicating a thread to the message loop. diff --git a/engine/src/flutter/fml/message_loop_impl.cc b/engine/src/flutter/fml/message_loop_impl.cc index cec9b7e448..df885f3792 100644 --- a/engine/src/flutter/fml/message_loop_impl.cc +++ b/engine/src/flutter/fml/message_loop_impl.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/fml/message_loop_impl.h" #include @@ -11,35 +13,29 @@ #include "lib/fxl/build_config.h" #if OS_MACOSX - #include "flutter/fml/platform/darwin/message_loop_darwin.h" -using PlatformMessageLoopImpl = fml::MessageLoopDarwin; - #elif OS_ANDROID - #include "flutter/fml/platform/android/message_loop_android.h" -using PlatformMessageLoopImpl = fml::MessageLoopAndroid; - #elif OS_LINUX - #include "flutter/fml/platform/linux/message_loop_linux.h" -using PlatformMessageLoopImpl = fml::MessageLoopLinux; - #elif OS_WIN - #include "flutter/fml/platform/win/message_loop_win.h" -using PlatformMessageLoopImpl = fml::MessageLoopWin; - -#else - -#error This platform does not have a message loop implementation. - #endif namespace fml { fxl::RefPtr MessageLoopImpl::Create() { - return fxl::MakeRefCounted<::PlatformMessageLoopImpl>(); +#if OS_MACOSX + return fxl::MakeRefCounted(); +#elif OS_ANDROID + return fxl::MakeRefCounted(); +#elif OS_LINUX + return fxl::MakeRefCounted(); +#elif OS_WIN + return fxl::MakeRefCounted(); +#else + return nullptr; +#endif } MessageLoopImpl::MessageLoopImpl() : order_(0), terminated_(false) {} @@ -55,20 +51,19 @@ void MessageLoopImpl::RunExpiredTasksNow() { RunExpiredTasks(); } -void MessageLoopImpl::AddTaskObserver(TaskObserver* observer) { - FXL_DCHECK(observer != nullptr); +void MessageLoopImpl::AddTaskObserver(intptr_t key, fxl::Closure callback) { + FXL_DCHECK(callback != nullptr); FXL_DCHECK(MessageLoop::GetCurrent().GetLoopImpl().get() == this) << "Message loop task observer must be added on the same thread as the " "loop."; - task_observers_.insert(observer); + task_observers_[key] = std::move(callback); } -void MessageLoopImpl::RemoveTaskObserver(TaskObserver* observer) { - FXL_DCHECK(observer != nullptr); +void MessageLoopImpl::RemoveTaskObserver(intptr_t key) { FXL_DCHECK(MessageLoop::GetCurrent().GetLoopImpl().get() == this) << "Message loop task observer must be removed from the same thread as " "the loop."; - task_observers_.erase(observer); + task_observers_.erase(key); } void MessageLoopImpl::DoRun() { @@ -144,7 +139,7 @@ void MessageLoopImpl::RunExpiredTasks() { for (const auto& invocation : invocations) { invocation(); for (const auto& observer : task_observers_) { - observer->DidProcessTask(); + observer.second(); } } } diff --git a/engine/src/flutter/fml/message_loop_impl.h b/engine/src/flutter/fml/message_loop_impl.h index bfdb2064cb..478cbd1f1a 100644 --- a/engine/src/flutter/fml/message_loop_impl.h +++ b/engine/src/flutter/fml/message_loop_impl.h @@ -7,9 +7,9 @@ #include #include +#include #include #include -#include #include #include "flutter/fml/message_loop.h" @@ -34,9 +34,9 @@ class MessageLoopImpl : public fxl::RefCountedThreadSafe { void PostTask(fxl::Closure task, fxl::TimePoint target_time); - void AddTaskObserver(TaskObserver* observer); + void AddTaskObserver(intptr_t key, fxl::Closure callback); - void RemoveTaskObserver(TaskObserver* observer); + void RemoveTaskObserver(intptr_t key); void DoRun(); @@ -71,7 +71,7 @@ class MessageLoopImpl : public fxl::RefCountedThreadSafe { using DelayedTaskQueue = std:: priority_queue, DelayedTaskCompare>; - std::set task_observers_; + std::map task_observers_; std::mutex delayed_tasks_mutex_; DelayedTaskQueue delayed_tasks_; size_t order_; diff --git a/engine/src/flutter/fml/message_loop_unittests.cc b/engine/src/flutter/fml/message_loop_unittests.cc index 11ea72c390..cd2acd308b 100644 --- a/engine/src/flutter/fml/message_loop_unittests.cc +++ b/engine/src/flutter/fml/message_loop_unittests.cc @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include #include "flutter/fml/message_loop.h" +#include "flutter/fml/task_runner.h" #include "gtest/gtest.h" #include "lib/fxl/synchronization/waitable_event.h" @@ -244,22 +247,6 @@ TEST(MessageLoop, TIME_SENSITIVE(MultipleDelayedTasksWithDecreasingDeltas)) { ASSERT_EQ(checked, count); } -class CustomTaskObserver : public fml::TaskObserver { - public: - CustomTaskObserver(std::function lambda) : lambda_(lambda){}; - - ~CustomTaskObserver() override = default; - - void DidProcessTask() override { - if (lambda_) { - lambda_(); - } - }; - - private: - std::function lambda_; -}; - TEST(MessageLoop, TaskObserverFire) { bool started = false; bool terminated = false; @@ -269,8 +256,7 @@ TEST(MessageLoop, TaskObserverFire) { auto& loop = fml::MessageLoop::GetCurrent(); size_t task_count = 0; size_t obs_count = 0; - CustomTaskObserver obs( - PLATFORM_SPECIFIC_CAPTURE(&obs_count)() { obs_count++; }); + auto obs = PLATFORM_SPECIFIC_CAPTURE(&obs_count)() { obs_count++; }; for (size_t i = 0; i < count; i++) { loop.GetTaskRunner()->PostTask( PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &task_count)() { @@ -282,7 +268,7 @@ TEST(MessageLoop, TaskObserverFire) { } }); } - loop.AddTaskObserver(&obs); + loop.AddTaskObserver(0, obs); loop.Run(); ASSERT_EQ(task_count, count); ASSERT_EQ(obs_count, count); diff --git a/engine/src/flutter/fml/native_library.h b/engine/src/flutter/fml/native_library.h new file mode 100644 index 0000000000..7e48ed6d3a --- /dev/null +++ b/engine/src/flutter/fml/native_library.h @@ -0,0 +1,52 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_FML_NATIVE_LIBRARY_H_ +#define FLUTTER_FML_NATIVE_LIBRARY_H_ + +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/ref_counted.h" +#include "lib/fxl/memory/ref_ptr.h" + +#if OS_WIN + +#include + +#endif // OS_WIN + +namespace fml { +class NativeLibrary : public fxl::RefCountedThreadSafe { + public: +#if OS_WIN + using Handle = HMODULE; +#else // OS_WIN + using Handle = void*; +#endif // OS_WIN + + static fxl::RefPtr Create(const char* path); + + static fxl::RefPtr CreateForCurrentProcess(); + + const uint8_t* ResolveSymbol(const char* symbol); + + private: + Handle handle_ = nullptr; + bool close_handle_ = true; + + NativeLibrary(const char* path); + + NativeLibrary(Handle handle, bool close_handle); + + ~NativeLibrary(); + + Handle GetHandle() const; + + FXL_DISALLOW_COPY_AND_ASSIGN(NativeLibrary); + FRIEND_REF_COUNTED_THREAD_SAFE(NativeLibrary); + FRIEND_MAKE_REF_COUNTED(NativeLibrary); +}; + +} // namespace fml + +#endif // FLUTTER_FML_NATIVE_LIBRARY_H_ diff --git a/engine/src/flutter/fml/paths.cc b/engine/src/flutter/fml/paths.cc new file mode 100644 index 0000000000..9b06b6847f --- /dev/null +++ b/engine/src/flutter/fml/paths.cc @@ -0,0 +1,31 @@ +// Copyright 2018 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. + +#include "flutter/fml/paths.h" + +#include "lib/fxl/build_config.h" + +namespace fml { +namespace paths { + +std::string JoinPaths(std::initializer_list components) { + std::stringstream stream; + size_t i = 0; + const size_t size = components.size(); + for (const auto& component : components) { + i++; + stream << component; + if (i != size) { +#if OS_WIN + stream << "\\"; +#else // OS_WIN + stream << "/"; +#endif // OS_WIN + } + } + return stream.str(); +} + +} // namespace paths +} // namespace fml diff --git a/engine/src/flutter/fml/paths.h b/engine/src/flutter/fml/paths.h index 8ec3b4b580..0d4367654d 100644 --- a/engine/src/flutter/fml/paths.h +++ b/engine/src/flutter/fml/paths.h @@ -8,11 +8,15 @@ #include #include +#include "lib/fxl/strings/string_view.h" + namespace fml { namespace paths { std::pair GetExecutableDirectoryPath(); +std::string JoinPaths(std::initializer_list components); + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/platform/android/message_loop_android.h b/engine/src/flutter/fml/platform/android/message_loop_android.h index e6d04e8253..19c1eafe79 100644 --- a/engine/src/flutter/fml/platform/android/message_loop_android.h +++ b/engine/src/flutter/fml/platform/android/message_loop_android.h @@ -10,7 +10,7 @@ #include #include "flutter/fml/message_loop_impl.h" -#include "lib/fxl/files/unique_fd.h" +#include "flutter/fml/unique_fd.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/unique_object.h" @@ -24,8 +24,8 @@ struct UniqueLooperTraits { class MessageLoopAndroid : public MessageLoopImpl { private: - fxl::UniqueObject looper_; - fxl::UniqueFD timer_fd_; + fml::UniqueObject looper_; + fml::UniqueFD timer_fd_; bool running_; MessageLoopAndroid(); diff --git a/engine/src/flutter/fml/platform/darwin/resource_mapping_darwin.mm b/engine/src/flutter/fml/platform/darwin/resource_mapping_darwin.mm index 5d1b9664e2..3ee100121a 100644 --- a/engine/src/flutter/fml/platform/darwin/resource_mapping_darwin.mm +++ b/engine/src/flutter/fml/platform/darwin/resource_mapping_darwin.mm @@ -9,8 +9,8 @@ namespace fml { ResourceMappingDarwin::ResourceMappingDarwin(const std::string& resource) - : actual_([[[NSBundle mainBundle] pathForResource:@(resource.c_str()) ofType:nil] UTF8String]) { -} + : actual_([[[NSBundle mainBundle] pathForResource:@(resource.c_str()) ofType:nil] UTF8String], + false) {} ResourceMappingDarwin::~ResourceMappingDarwin() = default; diff --git a/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc b/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc new file mode 100644 index 0000000000..20def9f34f --- /dev/null +++ b/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc @@ -0,0 +1,15 @@ +// Copyright 2018 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. + +#include "flutter/fml/paths.h" + +namespace fml { +namespace paths { + +std::pair GetExecutableDirectoryPath() { + return {false, ""}; +} + +} // namespace paths +} // namespace fml diff --git a/engine/src/flutter/fml/platform/linux/message_loop_linux.h b/engine/src/flutter/fml/platform/linux/message_loop_linux.h index 51dc690ce8..ba5e902d4a 100644 --- a/engine/src/flutter/fml/platform/linux/message_loop_linux.h +++ b/engine/src/flutter/fml/platform/linux/message_loop_linux.h @@ -8,15 +8,15 @@ #include #include "flutter/fml/message_loop_impl.h" -#include "lib/fxl/files/unique_fd.h" +#include "flutter/fml/unique_fd.h" #include "lib/fxl/macros.h" namespace fml { class MessageLoopLinux : public MessageLoopImpl { private: - fxl::UniqueFD epoll_fd_; - fxl::UniqueFD timer_fd_; + fml::UniqueFD epoll_fd_; + fml::UniqueFD timer_fd_; bool running_; MessageLoopLinux(); diff --git a/engine/src/flutter/fml/platform/posix/file_posix.cc b/engine/src/flutter/fml/platform/posix/file_posix.cc new file mode 100644 index 0000000000..028f49f4d1 --- /dev/null +++ b/engine/src/flutter/fml/platform/posix/file_posix.cc @@ -0,0 +1,71 @@ +// Copyright 2018 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. + +#include "flutter/fml/file.h" + +#include +#include +#include + +#include "lib/fxl/files/eintr_wrapper.h" + +namespace fml { + +fml::UniqueFD OpenFile(const char* path, + OpenPermission permission, + bool is_directory) { + return OpenFile(fml::UniqueFD{AT_FDCWD}, path, permission, is_directory); +} + +fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, + const char* path, + OpenPermission permission, + bool is_directory) { + if (path == nullptr) { + return fml::UniqueFD{}; + } + + int flags = 0; + switch (permission) { + case OpenPermission::kRead: + flags = O_RDONLY; + break; + case OpenPermission::kWrite: + flags = O_WRONLY; + break; + case OpenPermission::kReadWrite: + flags = O_RDWR; + break; + case OpenPermission::kExecute: + flags = O_RDONLY; + break; + } + + if (is_directory) { + flags |= O_DIRECTORY; + } + + return fml::UniqueFD{ + HANDLE_EINTR(::openat(base_directory.get(), path, flags))}; +} + +fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) { + return fml::UniqueFD{HANDLE_EINTR(::dup(descriptor))}; +} + +bool IsDirectory(const fml::UniqueFD& directory) { + if (!directory.is_valid()) { + return false; + } + + struct stat stat_result = {}; + + if (::fstat(directory.get(), &stat_result) != 0) { + return false; + } + + return S_ISDIR(stat_result.st_mode); +} + +} // namespace fml diff --git a/engine/src/flutter/fml/platform/posix/mapping_posix.cc b/engine/src/flutter/fml/platform/posix/mapping_posix.cc index 07f7edb074..fc53d6a4f7 100644 --- a/engine/src/flutter/fml/platform/posix/mapping_posix.cc +++ b/engine/src/flutter/fml/platform/posix/mapping_posix.cc @@ -11,6 +11,7 @@ #include +#include "flutter/fml/unique_fd.h" #include "lib/fxl/build_config.h" #include "lib/fxl/files/eintr_wrapper.h" @@ -39,11 +40,11 @@ std::unique_ptr GetResourceMapping(const std::string& resource_name) { return std::make_unique(resource_name); } -FileMapping::FileMapping(const std::string& path) - : FileMapping(fxl::UniqueFD{HANDLE_EINTR(::open(path.c_str(), O_RDONLY))}) { -} +FileMapping::FileMapping(const std::string& path, bool executable) + : FileMapping(fml::UniqueFD{HANDLE_EINTR(::open(path.c_str(), O_RDONLY))}, + executable) {} -FileMapping::FileMapping(const fxl::UniqueFD& handle) +FileMapping::FileMapping(const fml::UniqueFD& handle, bool executable) : size_(0), mapping_(nullptr) { if (!handle.is_valid()) { return; @@ -59,8 +60,13 @@ FileMapping::FileMapping(const fxl::UniqueFD& handle) return; } - auto mapping = ::mmap(nullptr, stat_buffer.st_size, PROT_READ, MAP_PRIVATE, - handle.get(), 0); + int flags = PROT_READ; + if (executable) { + flags |= PROT_EXEC; + } + + auto mapping = + ::mmap(nullptr, stat_buffer.st_size, flags, MAP_PRIVATE, handle.get(), 0); if (mapping == MAP_FAILED) { return; diff --git a/engine/src/flutter/fml/platform/posix/native_library_posix.cc b/engine/src/flutter/fml/platform/posix/native_library_posix.cc new file mode 100644 index 0000000000..1255c38fc0 --- /dev/null +++ b/engine/src/flutter/fml/platform/posix/native_library_posix.cc @@ -0,0 +1,60 @@ +// Copyright 2018 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. + +#include "flutter/fml/native_library.h" + +#include +#include + +namespace fml { + +NativeLibrary::NativeLibrary(const char* path) { + ::dlerror(); + handle_ = ::dlopen(path, RTLD_NOW); + if (handle_ == nullptr) { + FXL_LOG(ERROR) << "Could not open library '" << path << "' due to error '" + << ::dlerror() << "'."; + } +} + +NativeLibrary::NativeLibrary(Handle handle, bool close_handle) + : handle_(handle), close_handle_(close_handle) {} + +NativeLibrary::~NativeLibrary() { + if (handle_ == nullptr) { + return; + } + + if (close_handle_) { + ::dlerror(); + if (::dlclose(handle_) != 0) { + handle_ = nullptr; + FXL_LOG(ERROR) << "Could not close library due to error '" << ::dlerror() + << "'."; + } + } +} + +NativeLibrary::Handle NativeLibrary::GetHandle() const { + return handle_; +} + +fxl::RefPtr NativeLibrary::Create(const char* path) { + auto library = fxl::AdoptRef(new NativeLibrary(path)); + return library->GetHandle() != nullptr ? library : nullptr; +} + +fxl::RefPtr NativeLibrary::CreateForCurrentProcess() { + return fxl::AdoptRef(new NativeLibrary(RTLD_DEFAULT, false)); +} + +const uint8_t* NativeLibrary::ResolveSymbol(const char* symbol) { + auto resolved_symbol = static_cast(::dlsym(handle_, symbol)); + if (resolved_symbol == nullptr) { + FXL_DLOG(ERROR) << "Could not resolve symbol in library: " << symbol; + } + return resolved_symbol; +} + +} // namespace fml diff --git a/engine/src/flutter/fml/platform/win/file_win.cc b/engine/src/flutter/fml/platform/win/file_win.cc new file mode 100644 index 0000000000..3f5e90494b --- /dev/null +++ b/engine/src/flutter/fml/platform/win/file_win.cc @@ -0,0 +1,119 @@ +// Copyright 2018 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. + +#include "flutter/fml/file.h" + +#include + +#include + +#include "flutter/fml/platform/win/wstring_conversion.h" + +namespace fml { + +fml::UniqueFD OpenFile(const std::wstring& path, + OpenPermission permission, + bool is_directory) { + if (path.size() == 0) { + return fml::UniqueFD{}; + } + + DWORD desired_access = 0; + + switch (permission) { + case OpenPermission::kRead: + desired_access = GENERIC_READ; + break; + case OpenPermission::kWrite: + desired_access = GENERIC_WRITE; + break; + case OpenPermission::kReadWrite: + desired_access = GENERIC_WRITE | GENERIC_READ; + break; + case OpenPermission::kExecute: + desired_access = GENERIC_READ | GENERIC_EXECUTE; + break; + } + + return fml::UniqueFD{::CreateFile( + path.c_str(), // lpFileName + desired_access, // dwDesiredAccess + FILE_SHARE_READ, // dwShareMode + 0, // lpSecurityAttributes + OPEN_EXISTING, // dwCreationDisposition + FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes + 0 // hTemplateFile + )}; +} + +fml::UniqueFD OpenFile(const char* path, + OpenPermission permission, + bool is_directory) { + return OpenFile(ConvertToWString(path), permission, is_directory); +} + +static std::wstring GetFullHandlePath(const fml::UniqueFD& handle) { + wchar_t buffer[MAX_PATH]; + + DWORD returned = ::GetFinalPathNameByHandle(handle.get(), buffer, MAX_PATH, + FILE_NAME_NORMALIZED); + if (returned == 0 || returned > MAX_PATH) { + return {}; + } + + return {buffer}; +} + +fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, + const char* path, + OpenPermission permission, + bool is_directory) { + // If the base directory is invalid or the path is absolute, use the generic + // open file variant. + if (!base_directory.is_valid()) { + return OpenFile(path, permission, is_directory); + } + + const auto wpath = ConvertToWString(path); + + if (!::PathIsRelative(wpath.c_str())) { + return OpenFile(path, permission, is_directory); + } + + std::wstringstream stream; + stream << GetFullHandlePath(base_directory) << "\\" << path; + return OpenFile(stream.str(), permission, is_directory); +} + +fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) { + if (descriptor == INVALID_HANDLE_VALUE) { + return fml::UniqueFD{}; + } + + HANDLE duplicated = INVALID_HANDLE_VALUE; + + if (!::DuplicateHandle( + GetCurrentProcess(), // source process + descriptor, // source handle + GetCurrentProcess(), // target process + &duplicated, // target handle + 0, // desired access (ignored because DUPLICATE_SAME_ACCESS) + FALSE, // inheritable + DUPLICATE_SAME_ACCESS) // options + ) { + return fml::UniqueFD{}; + } + + return fml::UniqueFD{duplicated}; +} + +bool IsDirectory(const fml::UniqueFD& directory) { + BY_HANDLE_FILE_INFORMATION info; + if (!::GetFileInformationByHandle(directory.get(), &info)) { + return false; + } + return info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; +} + +} // namespace fml diff --git a/engine/src/flutter/fml/platform/win/mapping_win.cc b/engine/src/flutter/fml/platform/win/mapping_win.cc index fd404a14f3..9dd5dcf6f2 100644 --- a/engine/src/flutter/fml/platform/win/mapping_win.cc +++ b/engine/src/flutter/fml/platform/win/mapping_win.cc @@ -5,13 +5,13 @@ #include "flutter/fml/mapping.h" #include +#include +#include #include -#include "lib/fxl/build_config.h" - -#include -#include +#include "flutter/fml/file.h" +#include "flutter/fml/platform/win/wstring_conversion.h" using PlatformResourceMapping = fml::FileMapping; @@ -29,47 +29,50 @@ std::unique_ptr GetResourceMapping(const std::string& resource_name) { return std::make_unique(resource_name); } -FileMapping::FileMapping(const std::string& path) +FileMapping::FileMapping(const std::string& path, bool executable) + : FileMapping(OpenFile(path.c_str(), + executable ? OpenPermission::kExecute + : OpenPermission::kRead, + false), + executable) {} + +FileMapping::FileMapping(const fml::UniqueFD& fd, bool executable) : size_(0), mapping_(nullptr) { - HANDLE file_handle_ = - CreateFileA(reinterpret_cast(path.c_str()), GENERIC_READ, - FILE_SHARE_READ, nullptr, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr); - - if (file_handle_ == INVALID_HANDLE_VALUE) { + if (!fd.is_valid()) { return; } - size_ = GetFileSize(file_handle_, nullptr); - if (size_ == INVALID_FILE_SIZE) { - size_ = 0; + if (auto size = ::GetFileSize(fd.get(), nullptr)) { + if (size > 0) { + size_ = size; + } else { + return; + } + } + + const DWORD protect = executable ? PAGE_EXECUTE_READ : PAGE_READONLY; + + mapping_handle_.reset(::CreateFileMapping(fd.get(), // hFile + nullptr, // lpAttributes + protect, // flProtect + 0, // dwMaximumSizeHigh + 0, // dwMaximumSizeLow + nullptr // lpName + )); + + if (!mapping_handle_.is_valid()) { return; } - mapping_handle_ = CreateFileMapping(file_handle_, nullptr, PAGE_READONLY, 0, - size_, nullptr); + const DWORD desired_access = executable ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ; - CloseHandle(file_handle_); - - if (mapping_handle_ == INVALID_HANDLE_VALUE) { - return; - } - - auto mapping = MapViewOfFile(mapping_handle_, FILE_MAP_READ, 0, 0, size_); - - if (mapping == INVALID_HANDLE_VALUE) { - CloseHandle(mapping_handle_); - mapping_handle_ = INVALID_HANDLE_VALUE; - return; - } - - mapping_ = static_cast(mapping); + mapping_ = reinterpret_cast( + MapViewOfFile(mapping_handle_.get(), desired_access, 0, 0, size_)); } FileMapping::~FileMapping() { if (mapping_ != nullptr) { UnmapViewOfFile(mapping_); - CloseHandle(mapping_handle_); } } diff --git a/engine/src/flutter/fml/platform/win/native_library_win.cc b/engine/src/flutter/fml/platform/win/native_library_win.cc new file mode 100644 index 0000000000..6992f06f3e --- /dev/null +++ b/engine/src/flutter/fml/platform/win/native_library_win.cc @@ -0,0 +1,51 @@ +// Copyright 2018 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. + +#include "flutter/fml/native_library.h" + +#include + +#include "flutter/fml/platform/win/wstring_conversion.h" + +namespace fml { + +NativeLibrary::NativeLibrary(const char* path) + : handle_(nullptr), close_handle_(true) { + if (path == nullptr) { + return; + } + + handle_ = ::LoadLibrary(ConvertToWString(path).c_str()); +} + +NativeLibrary::NativeLibrary(Handle handle, bool close_handle) + : handle_(handle), close_handle_(close_handle) {} + +NativeLibrary::~NativeLibrary() { + if (handle_ != nullptr && close_handle_) { + ::FreeLibrary(handle_); + } +} + +NativeLibrary::Handle NativeLibrary::GetHandle() const { + return handle_; +} + +fxl::RefPtr NativeLibrary::Create(const char* path) { + auto library = fxl::AdoptRef(new NativeLibrary(path)); + return library->GetHandle() != nullptr ? library : nullptr; +} + +fxl::RefPtr NativeLibrary::CreateForCurrentProcess() { + return fxl::AdoptRef(new NativeLibrary(::GetModuleHandle(nullptr), false)); +} + +const uint8_t* NativeLibrary::ResolveSymbol(const char* symbol) { + if (symbol == nullptr || handle_ == nullptr) { + return nullptr; + } + return reinterpret_cast(::GetProcAddress(handle_, symbol)); +} + +} // namespace fml diff --git a/engine/src/flutter/fml/platform/win/wstring_conversion.h b/engine/src/flutter/fml/platform/win/wstring_conversion.h new file mode 100644 index 0000000000..0682dd8406 --- /dev/null +++ b/engine/src/flutter/fml/platform/win/wstring_conversion.h @@ -0,0 +1,25 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_FML_PLATFORM_WIN_WSTRING_CONVERSION_H_ +#define FLUTTER_FML_PLATFORM_WIN_WSTRING_CONVERSION_H_ + +#include +#include +#include + +namespace fml { + +inline std::wstring ConvertToWString(const char* path) { + if (path == nullptr) { + return {}; + } + std::string path8(path); + std::wstring_convert, wchar_t> wchar_conv; + return wchar_conv.from_bytes(path8); +} + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_WIN_WSTRING_CONVERSION_H_ diff --git a/engine/src/flutter/fml/task_observer.h b/engine/src/flutter/fml/task_observer.h deleted file mode 100644 index 21697cf022..0000000000 --- a/engine/src/flutter/fml/task_observer.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FML_TASK_OBSERVER_H_ -#define FLUTTER_FML_TASK_OBSERVER_H_ - -#include "lib/fxl/macros.h" - -namespace fml { - -class TaskObserver { - public: - virtual ~TaskObserver() = default; - - virtual void DidProcessTask() = 0; -}; - -} // namespace fml - -#endif // FLUTTER_FML_TASK_OBSERVER_H_ diff --git a/engine/src/flutter/fml/task_runner.cc b/engine/src/flutter/fml/task_runner.cc index 3d13674d78..95f91de8e9 100644 --- a/engine/src/flutter/fml/task_runner.cc +++ b/engine/src/flutter/fml/task_runner.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/fml/task_runner.h" #include @@ -38,4 +40,14 @@ bool TaskRunner::RunsTasksOnCurrentThread() { return MessageLoop::GetCurrent().GetLoopImpl() == loop_; } +void TaskRunner::RunNowOrPostTask(fxl::RefPtr runner, + fxl::Closure task) { + FXL_DCHECK(runner); + if (runner->RunsTasksOnCurrentThread()) { + task(); + } else { + runner->PostTask(std::move(task)); + } +} + } // namespace fml diff --git a/engine/src/flutter/fml/task_runner.h b/engine/src/flutter/fml/task_runner.h index 20ea85e4e5..3b3d2de016 100644 --- a/engine/src/flutter/fml/task_runner.h +++ b/engine/src/flutter/fml/task_runner.h @@ -13,7 +13,7 @@ namespace fml { class MessageLoopImpl; -class TaskRunner : public fxl::TaskRunner { +class TaskRunner final : public fxl::TaskRunner { public: void PostTask(fxl::Closure task) override; @@ -23,12 +23,15 @@ class TaskRunner : public fxl::TaskRunner { bool RunsTasksOnCurrentThread() override; + static void RunNowOrPostTask(fxl::RefPtr runner, + fxl::Closure task); + private: fxl::RefPtr loop_; TaskRunner(fxl::RefPtr loop); - ~TaskRunner(); + ~TaskRunner() override; FRIEND_MAKE_REF_COUNTED(TaskRunner); FRIEND_REF_COUNTED_THREAD_SAFE(TaskRunner); diff --git a/engine/src/flutter/fml/thread.cc b/engine/src/flutter/fml/thread.cc index a1500bfef4..de4ee6dd36 100644 --- a/engine/src/flutter/fml/thread.cc +++ b/engine/src/flutter/fml/thread.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/fml/thread.h" #include "lib/fxl/build_config.h" @@ -22,7 +24,7 @@ namespace fml { Thread::Thread(const std::string& name) : joined_(false) { fxl::AutoResetWaitableEvent latch; - fxl::RefPtr runner; + fxl::RefPtr runner; thread_ = std::make_unique([&latch, &runner, name]() -> void { SetCurrentThreadName(name); fml::MessageLoop::EnsureInitializedForCurrentThread(); @@ -39,7 +41,7 @@ Thread::~Thread() { Join(); } -fxl::RefPtr Thread::GetTaskRunner() const { +fxl::RefPtr Thread::GetTaskRunner() const { return task_runner_; } @@ -84,7 +86,8 @@ void Thread::SetCurrentThreadName(const std::string& name) { } __except (EXCEPTION_CONTINUE_EXECUTION) { } #else -#error Unsupported Platform + FXL_DLOG(INFO) << "Could not set the thread name to '" << name + << "' on this platform."; #endif } diff --git a/engine/src/flutter/fml/thread.h b/engine/src/flutter/fml/thread.h index 44062f1032..542871f788 100644 --- a/engine/src/flutter/fml/thread.h +++ b/engine/src/flutter/fml/thread.h @@ -9,8 +9,8 @@ #include #include +#include "flutter/fml/task_runner.h" #include "lib/fxl/macros.h" -#include "lib/fxl/tasks/task_runner.h" namespace fml { @@ -20,13 +20,13 @@ class Thread { ~Thread(); - fxl::RefPtr GetTaskRunner() const; + fxl::RefPtr GetTaskRunner() const; void Join(); private: std::unique_ptr thread_; - fxl::RefPtr task_runner_; + fxl::RefPtr task_runner_; std::atomic_bool joined_; static void SetCurrentThreadName(const std::string& name); diff --git a/engine/src/flutter/fml/unique_fd.cc b/engine/src/flutter/fml/unique_fd.cc new file mode 100644 index 0000000000..bab79f7a9b --- /dev/null +++ b/engine/src/flutter/fml/unique_fd.cc @@ -0,0 +1,35 @@ +// Copyright 2018 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. + +#include "flutter/fml/unique_fd.h" + +#include "lib/fxl/files/eintr_wrapper.h" + +namespace fml { +namespace internal { + +#if OS_WIN + +namespace win { + +void UniqueFDTraits::Free(HANDLE fd) { + CloseHandle(fd); +} + +} // namespace win + +#else // OS_WIN + +namespace unix { + +void UniqueFDTraits::Free(int fd) { + IGNORE_EINTR(fd); +} + +} // namespace unix + +#endif // OS_WIN + +} // namespace internal +} // namespace fml diff --git a/engine/src/flutter/fml/unique_fd.h b/engine/src/flutter/fml/unique_fd.h new file mode 100644 index 0000000000..9f2d796579 --- /dev/null +++ b/engine/src/flutter/fml/unique_fd.h @@ -0,0 +1,60 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_FML_UNIQUE_FD_H_ +#define FLUTTER_FML_UNIQUE_FD_H_ + +#include "flutter/fml/unique_object.h" +#include "lib/fxl/build_config.h" + +#if OS_WIN + +#include + +#endif // OS_WIN + +namespace fml { +namespace internal { + +#if OS_WIN + +namespace win { + +struct UniqueFDTraits { + static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; } + static bool IsValid(HANDLE value) { return value != InvalidValue(); } + static void Free(HANDLE fd); +}; + +} // namespace win + +#else // OS_WIN + +namespace unix { + +struct UniqueFDTraits { + static int InvalidValue() { return -1; } + static bool IsValid(int value) { return value >= 0; } + static void Free(int fd); +}; + +} // namespace unix + +#endif // OS_WIN + +} // namespace internal + +#if OS_WIN + +using UniqueFD = UniqueObject; + +#else // OS_WIN + +using UniqueFD = UniqueObject; + +#endif // OS_WIN + +} // namespace fml + +#endif // FLUTTER_FML_UNIQUE_FD_H_ diff --git a/engine/src/flutter/fml/unique_object.h b/engine/src/flutter/fml/unique_object.h new file mode 100644 index 0000000000..e2487a5388 --- /dev/null +++ b/engine/src/flutter/fml/unique_object.h @@ -0,0 +1,137 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_FML_UNIQUE_OBJECT_H_ +#define FLUTTER_FML_UNIQUE_OBJECT_H_ + +#include + +#include "lib/fxl/compiler_specific.h" +#include "lib/fxl/logging.h" +#include "lib/fxl/macros.h" + +namespace fml { + +// struct UniqueFooTraits { +// // This function should be fast an inline. +// static int InvalidValue() { return 0; } +// +// // This function should be fast an inline. +// static bool IsValid(const T& value) { return value != InvalidValue(); } +// +// // This free function will not be called if f == InvalidValue()! +// static void Free(int f) { ::FreeFoo(f); } +// }; + +template +class UniqueObject { + private: + // This must be first since it's used inline below. + // + // Use the empty base class optimization to allow us to have a Traits + // member, while avoiding any space overhead for it when Traits is an + // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good + // discussion of this technique. + struct Data : public Traits { + explicit Data(const T& in) : generic(in) {} + Data(const T& in, const Traits& other) : Traits(other), generic(in) {} + + T generic; + }; + + public: + using element_type = T; + using traits_type = Traits; + + UniqueObject() : data_(Traits::InvalidValue()) {} + explicit UniqueObject(const T& value) : data_(value) {} + + UniqueObject(const T& value, const Traits& traits) : data_(value, traits) {} + + UniqueObject(UniqueObject&& other) + : data_(other.release(), other.get_traits()) {} + + ~UniqueObject() { FreeIfNecessary(); } + + UniqueObject& operator=(UniqueObject&& other) { + reset(other.release()); + return *this; + } + + void reset(const T& value = Traits::InvalidValue()) { + FXL_CHECK(data_.generic == Traits::InvalidValue() || + data_.generic != value); + FreeIfNecessary(); + data_.generic = value; + } + + void swap(UniqueObject& other) { + // Standard swap idiom: 'using std::swap' ensures that std::swap is + // present in the overload set, but we call swap unqualified so that + // any more-specific overloads can be used, if available. + using std::swap; + swap(static_cast(data_), static_cast(other.data_)); + swap(data_.generic, other.data_.generic); + } + + // Release the object. The return value is the current object held by this + // object. After this operation, this object will hold an invalid value, and + // will not own the object any more. + T release() FXL_WARN_UNUSED_RESULT { + T old_generic = data_.generic; + data_.generic = Traits::InvalidValue(); + return old_generic; + } + + const T& get() const { return data_.generic; } + + bool is_valid() const { return Traits::IsValid(data_.generic); } + + bool operator==(const T& value) const { return data_.generic == value; } + + bool operator!=(const T& value) const { return data_.generic != value; } + + Traits& get_traits() { return data_; } + const Traits& get_traits() const { return data_; } + + private: + void FreeIfNecessary() { + if (data_.generic != Traits::InvalidValue()) { + data_.Free(data_.generic); + data_.generic = Traits::InvalidValue(); + } + } + + // Forbid comparison. If U != T, it totally doesn't make sense, and if U == + // T, it still doesn't make sense because you should never have the same + // object owned by two different UniqueObject. + template + bool operator==(const UniqueObject& p2) const = delete; + + template + bool operator!=(const UniqueObject& p2) const = delete; + + Data data_; + + FXL_DISALLOW_COPY_AND_ASSIGN(UniqueObject); +}; + +template +void swap(const UniqueObject& a, const UniqueObject& b) { + a.swap(b); +} + +template +bool operator==(const T& value, const UniqueObject& object) { + return value == object.get(); +} + +template +bool operator!=(const T& value, const UniqueObject& object) { + return !(value == object.get()); +} + +} // namespace fml + +#endif // FLUTTER_FML_UNIQUE_OBJECT_H_ diff --git a/engine/src/flutter/lib/snapshot/BUILD.gn b/engine/src/flutter/lib/snapshot/BUILD.gn index 2b53ffc4d4..8c568f4773 100644 --- a/engine/src/flutter/lib/snapshot/BUILD.gn +++ b/engine/src/flutter/lib/snapshot/BUILD.gn @@ -29,20 +29,40 @@ if (is_fuchsia) { # The sole purpose of this target is to generate a .packages file. sources = [] - infer_package_name = true + dot_packages_file = "$target_gen_dir/snapshot.packages" + outputs = [ + dot_packages_file, + ] + deps = [] + foreach(dep, dart_deps) { + deps += [ "$dep($dart_toolchain)" ] + } disable_analysis = true - deps = [ - "//topaz/public/dart/fuchsia", - "//topaz/public/dart/zircon", - ] + script = "//build/dart/gen_dot_packages.py" + args = [ + "--out", + rebase_path(dot_packages_file, root_build_dir), + "--source-dir", + rebase_path("."), + "--root-build-dir", + rebase_path(root_build_dir), + "--root-gen-dir", + rebase_path(dart_root_gen_dir), + "--package-name", + "snapshot_root", + "--depfile", + rebase_path(depfile), + "--deps", + ] + dart_deps } } action("generate_snapshot_bin") { if (is_fuchsia) { snapshot_dart = "snapshot_fuchsia.dart" + # TODO(rmacnak): Fuchsia cross builds use the wrong Dart target # architecture, and have added steps that depend on this error for # reasonable build times (e.g., invoking the analyzer). @@ -85,9 +105,9 @@ action("generate_snapshot_bin") { rebased_dart_ui_path = rebase_path(dart_ui_path) - gen_snapshot_dir = - get_label_info("//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)", - "root_out_dir") + gen_snapshot_dir = get_label_info( + "//third_party/dart/runtime/bin:gen_snapshot($host_toolchain)", + "root_out_dir") script = "//third_party/dart/runtime/tools/create_snapshot_bin.py" args = [ @@ -209,7 +229,7 @@ bin_to_assembly("isolate_snapshot_data_assembly") { ] input = "$target_gen_dir/isolate_snapshot.bin" output = "$target_gen_dir/isolate_snapshot_data.S" - symbol = "kDartIsolateCoreSnapshotData" + symbol = "kDartIsolateSnapshotData" executable = false } @@ -219,7 +239,7 @@ bin_to_assembly("isolate_snapshot_instructions_assembly") { ] input = "$target_gen_dir/isolate_snapshot_instructions.bin" output = "$target_gen_dir/isolate_snapshot_instructions.S" - symbol = "kDartIsolateCoreSnapshotInstructions" + symbol = "kDartIsolateSnapshotInstructions" executable = true } @@ -249,9 +269,9 @@ compile_platform("non_strong_platform") { ] args = [ - "--target=flutter", - "dart:core", - ] + "--target=flutter", + "dart:core", + ] } compile_platform("strong_platform") { @@ -265,10 +285,10 @@ compile_platform("strong_platform") { ] args = [ - "--target=flutter", - "--strong", - "dart:core" - ] + "--target=flutter", + "--strong", + "dart:core", + ] } group("kernel_platform_files") { diff --git a/engine/src/flutter/lib/ui/BUILD.gn b/engine/src/flutter/lib/ui/BUILD.gn index 8b3be72d59..6a75cd22c8 100644 --- a/engine/src/flutter/lib/ui/BUILD.gn +++ b/engine/src/flutter/lib/ui/BUILD.gn @@ -40,14 +40,10 @@ source_set("ui") { "painting/picture.h", "painting/picture_recorder.cc", "painting/picture_recorder.h", - "painting/resource_context.cc", - "painting/resource_context.h", "painting/rrect.cc", "painting/rrect.h", "painting/shader.cc", "painting/shader.h", - "painting/utils.cc", - "painting/utils.h", "painting/vertices.cc", "painting/vertices.h", "semantics/semantics_node.cc", @@ -87,19 +83,18 @@ source_set("ui") { "window/window.h", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] deps = [ - "//third_party/dart/runtime/bin:embedded_dart_io", "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", + "$flutter_root/fml", "$flutter_root/glue", "$flutter_root/runtime:test_font", "$flutter_root/sky/engine", "$flutter_root/third_party/txt", + "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/rapidjson", "//third_party/skia", "//third_party/skia:gpu", diff --git a/engine/src/flutter/lib/ui/compositing/scene_builder.cc b/engine/src/flutter/lib/ui/compositing/scene_builder.cc index 006826abbf..60b911cb4d 100644 --- a/engine/src/flutter/lib/ui/compositing/scene_builder.cc +++ b/engine/src/flutter/lib/ui/compositing/scene_builder.cc @@ -120,10 +120,11 @@ void SceneBuilder::addPicture(double dx, double dy, Picture* picture, int hints) { - layer_builder_->PushPicture(SkPoint::Make(dx, dy), // - picture->picture(), // - !!(hints & 1), // picture is complex - !!(hints & 2) // picture will change + layer_builder_->PushPicture( + SkPoint::Make(dx, dy), // + UIDartState::CreateGPUObject(picture->picture()), // + !!(hints & 1), // picture is complex + !!(hints & 2) // picture will change ); } diff --git a/engine/src/flutter/lib/ui/compositing/scene_host.cc b/engine/src/flutter/lib/ui/compositing/scene_host.cc index c0e4c53325..f5e0cfb354 100644 --- a/engine/src/flutter/lib/ui/compositing/scene_host.cc +++ b/engine/src/flutter/lib/ui/compositing/scene_host.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/compositing/scene_host.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" #include "lib/tonic/dart_library_natives.h" @@ -37,8 +38,9 @@ fxl::RefPtr SceneHost::create( } SceneHost::SceneHost(fxl::RefPtr export_token_handle) { - export_node_holder_ = - fxl::MakeRefCounted(export_token_handle); + export_node_holder_ = fxl::MakeRefCounted( + blink::UIDartState::Current()->GetTaskRunners().GetGPUTaskRunner(), + export_token_handle); } #else fxl::RefPtr SceneHost::create(Dart_Handle export_token_handle) { diff --git a/engine/src/flutter/lib/ui/dart_runtime_hooks.cc b/engine/src/flutter/lib/ui/dart_runtime_hooks.cc index edd51245b0..32a244c9cb 100644 --- a/engine/src/flutter/lib/ui/dart_runtime_hooks.cc +++ b/engine/src/flutter/lib/ui/dart_runtime_hooks.cc @@ -8,7 +8,11 @@ #include #include +#include +#include + #include "flutter/common/settings.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/fxl/build_config.h" #include "lib/fxl/logging.h" #include "lib/tonic/converter/dart_converter.h" @@ -141,17 +145,43 @@ void DartRuntimeHooks::Install(IsolateType isolate_type, // Implementation of native functions which are used for some // test/debug functionality in standalone dart mode. void Logger_PrintString(Dart_NativeArguments args) { - intptr_t length = 0; - uint8_t* chars = nullptr; - Dart_Handle str = Dart_GetNativeArgument(args, 0); - Dart_Handle result = Dart_StringToUTF8(str, &chars, &length); - if (Dart_IsError(result)) { - Dart_PropagateError(result); - } else { + std::stringstream stream; + const auto& logger_prefix = UIDartState::Current()->logger_prefix(); + +#if !OS(ANDROID) + // Prepend all logs with the isolate debug name except on Android where that + // prefix is specified in the log tag. + if (logger_prefix.size() > 0) { + stream << logger_prefix << ": "; + } +#endif // !OS(ANDROID) + + // Append the log buffer obtained from Dart code. + { + Dart_Handle str = Dart_GetNativeArgument(args, 0); + uint8_t* chars = nullptr; + intptr_t length = 0; + Dart_Handle result = Dart_StringToUTF8(str, &chars, &length); + if (Dart_IsError(result)) { + Dart_PropagateError(result); + return; + } + if (length > 0) { + stream << std::string{reinterpret_cast(chars), + static_cast(length)}; + } + } + + const auto log_string = stream.str(); + const char* chars = log_string.c_str(); + const size_t length = log_string.size(); + + // Log using platform specific mechanisms + { #if defined(OS_ANDROID) // Write to the logcat on Android. - const char* tag = Settings::Get().log_tag.c_str(); - __android_log_print(ANDROID_LOG_INFO, tag, "%.*s", (int)length, chars); + __android_log_print(ANDROID_LOG_INFO, logger_prefix.c_str(), "%.*s", + (int)length, chars); #elif defined(OS_IOS) // Write to syslog on iOS. // @@ -159,26 +189,22 @@ void Logger_PrintString(Dart_NativeArguments args) { // iOS logging APIs altogether. syslog(1 /* LOG_ALERT */, "%.*s", (int)length, chars); #else - // On Fuchsia and in flutter_tester (on both macOS and Linux), write - // directly to stdout. - fwrite(chars, 1, length, stdout); - fputs("\n", stdout); - fflush(stdout); + std::cout << log_string << std::endl; #endif } + if (dart::bin::ShouldCaptureStdout()) { // For now we report print output on the Stdout stream. uint8_t newline[] = {'\n'}; - Dart_ServiceSendDataEvent("Stdout", "WriteEvent", chars, length); + Dart_ServiceSendDataEvent("Stdout", "WriteEvent", + reinterpret_cast(chars), length); Dart_ServiceSendDataEvent("Stdout", "WriteEvent", newline, sizeof(newline)); } } void ScheduleMicrotask(Dart_NativeArguments args) { Dart_Handle closure = Dart_GetNativeArgument(args, 0); - if (LogIfError(closure) || !Dart_IsClosure(closure)) - return; - tonic::DartMicrotaskQueue::GetForCurrentThread()->ScheduleMicrotask(closure); + UIDartState::Current()->ScheduleMicrotask(closure); } } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/codec.cc b/engine/src/flutter/lib/ui/painting/codec.cc index 3e47ba86d5..758a722b40 100644 --- a/engine/src/flutter/lib/ui/painting/codec.cc +++ b/engine/src/flutter/lib/ui/painting/codec.cc @@ -4,11 +4,11 @@ #include "flutter/lib/ui/painting/codec.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/glue/trace_event.h" #include "flutter/lib/ui/painting/frame_info.h" -#include "flutter/lib/ui/painting/resource_context.h" #include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/logging.h" #include "lib/tonic/dart_binding_macros.h" #include "lib/tonic/dart_library_natives.h" #include "lib/tonic/dart_state.h" @@ -17,6 +17,10 @@ #include "third_party/skia/include/codec/SkCodec.h" #include "third_party/skia/include/core/SkPixelRef.h" +#ifdef ERROR +#undef ERROR +#endif + using tonic::DartInvoke; using tonic::DartPersistentValue; using tonic::ToDart; @@ -28,9 +32,9 @@ namespace { static constexpr const char* kInitCodecTraceTag = "InitCodec"; static constexpr const char* kCodecNextFrameTraceTag = "CodecNextFrame"; -void InvokeCodecCallback(fxl::RefPtr codec, - std::unique_ptr callback, - size_t trace_id) { +static void InvokeCodecCallback(fxl::RefPtr codec, + std::unique_ptr callback, + size_t trace_id) { tonic::DartState* dart_state = callback->dart_state().get(); if (!dart_state) { TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id); @@ -45,7 +49,9 @@ void InvokeCodecCallback(fxl::RefPtr codec, TRACE_FLOW_END("flutter", kInitCodecTraceTag, trace_id); } -sk_sp DecodeImage(sk_sp buffer, size_t trace_id) { +static sk_sp DecodeImage(fml::WeakPtr context, + sk_sp buffer, + size_t trace_id) { TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id); TRACE_EVENT0("flutter", "DecodeImage"); @@ -53,13 +59,11 @@ sk_sp DecodeImage(sk_sp buffer, size_t trace_id) { return nullptr; } - std::unique_ptr resourceContext = ResourceContext::Acquire(); - GrContext* context = resourceContext->Get(); if (context) { // This indicates that we do not want a "linear blending" decode. sk_sp dstColorSpace = nullptr; - return SkImage::MakeCrossContextFromEncoded(context, std::move(buffer), - false, dstColorSpace.get()); + return SkImage::MakeCrossContextFromEncoded( + context.get(), std::move(buffer), false, dstColorSpace.get()); } else { // Defer decoding until time of draw later on the GPU thread. Can happen // when GL operations are currently forbidden such as in the background @@ -68,7 +72,10 @@ sk_sp DecodeImage(sk_sp buffer, size_t trace_id) { } } -fxl::RefPtr InitCodec(sk_sp buffer, size_t trace_id) { +fxl::RefPtr InitCodec(fml::WeakPtr context, + sk_sp buffer, + fxl::RefPtr unref_queue, + size_t trace_id) { TRACE_FLOW_STEP("flutter", kInitCodecTraceTag, trace_id); TRACE_EVENT0("blink", "InitCodec"); @@ -86,27 +93,31 @@ fxl::RefPtr InitCodec(sk_sp buffer, size_t trace_id) { if (skCodec->getFrameCount() > 1) { return fxl::MakeRefCounted(std::move(skCodec)); } - auto skImage = DecodeImage(buffer, trace_id); + auto skImage = DecodeImage(context, buffer, trace_id); if (!skImage) { FXL_LOG(ERROR) << "DecodeImage failed"; return nullptr; } auto image = CanvasImage::Create(); - image->set_image(skImage); + image->set_image({skImage, unref_queue}); auto frameInfo = fxl::MakeRefCounted(std::move(image), 0); return fxl::MakeRefCounted(std::move(frameInfo)); } void InitCodecAndInvokeCodecCallback( + fxl::RefPtr ui_task_runner, + fml::WeakPtr context, + fxl::RefPtr unref_queue, std::unique_ptr callback, sk_sp buffer, size_t trace_id) { - auto codec = InitCodec(std::move(buffer), trace_id); - Threads::UI()->PostTask(fxl::MakeCopyable([ - callback = std::move(callback), codec = std::move(codec), trace_id - ]() mutable { - InvokeCodecCallback(std::move(codec), std::move(callback), trace_id); - })); + auto codec = + InitCodec(context, std::move(buffer), std::move(unref_queue), trace_id); + ui_task_runner->PostTask( + fxl::MakeCopyable([callback = std::move(callback), + codec = std::move(codec), trace_id]() mutable { + InvokeCodecCallback(std::move(codec), std::move(callback), trace_id); + })); } void InstantiateImageCodec(Dart_NativeArguments args) { @@ -133,14 +144,20 @@ void InstantiateImageCodec(Dart_NativeArguments args) { auto buffer = SkData::MakeWithCopy(list.data(), list.num_elements()); - Threads::IO()->PostTask(fxl::MakeCopyable([ - callback = std::make_unique( - tonic::DartState::Current(), callback_handle), - buffer = std::move(buffer), trace_id - ]() mutable { - InitCodecAndInvokeCodecCallback(std::move(callback), std::move(buffer), - trace_id); - })); + auto dart_state = UIDartState::Current(); + + const auto& task_runners = dart_state->GetTaskRunners(); + task_runners.GetIOTaskRunner()->PostTask(fxl::MakeCopyable( + [callback = std::make_unique( + tonic::DartState::Current(), callback_handle), + buffer = std::move(buffer), trace_id, + ui_task_runner = task_runners.GetUITaskRunner(), + context = dart_state->GetResourceContext(), + queue = UIDartState::Current()->GetSkiaUnrefQueue()]() mutable { + InitCodecAndInvokeCodecCallback(std::move(ui_task_runner), context, + std::move(queue), std::move(callback), + std::move(buffer), trace_id); + })); } bool copy_to(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src) { @@ -213,7 +230,8 @@ MultiFrameCodec::MultiFrameCodec(std::unique_ptr codec) nextFrameIndex_ = 0; } -sk_sp MultiFrameCodec::GetNextFrameImage() { +sk_sp MultiFrameCodec::GetNextFrameImage( + fml::WeakPtr resourceContext) { SkBitmap& bitmap = frameBitmaps_[nextFrameIndex_]; if (!bitmap.getPixels()) { // We haven't decoded this frame yet const SkImageInfo info = codec_->getInfo().makeColorType(kN32_SkColorType); @@ -245,15 +263,13 @@ sk_sp MultiFrameCodec::GetNextFrameImage() { } } - std::unique_ptr resourceContext = ResourceContext::Acquire(); - GrContext* context = resourceContext->Get(); - if (context) { + if (resourceContext) { SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(), bitmap.pixelRef()->rowBytes()); // This indicates that we do not want a "linear blending" decode. sk_sp dstColorSpace = nullptr; - return SkImage::MakeCrossContextFromPixmap(context, pixmap, false, - dstColorSpace.get()); + return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap, + false, dstColorSpace.get()); } else { // Defer decoding until time of draw later on the GPU thread. Can happen // when GL operations are currently forbidden such as in the background @@ -264,19 +280,22 @@ sk_sp MultiFrameCodec::GetNextFrameImage() { void MultiFrameCodec::GetNextFrameAndInvokeCallback( std::unique_ptr callback, + fxl::RefPtr ui_task_runner, + fml::WeakPtr resourceContext, + fxl::RefPtr unref_queue, size_t trace_id) { fxl::RefPtr frameInfo = NULL; - sk_sp skImage = GetNextFrameImage(); + sk_sp skImage = GetNextFrameImage(resourceContext); if (skImage) { fxl::RefPtr image = CanvasImage::Create(); - image->set_image(skImage); + image->set_image({skImage, std::move(unref_queue)}); frameInfo = fxl::MakeRefCounted( std::move(image), frameInfos_[nextFrameIndex_].fDuration); } nextFrameIndex_ = (nextFrameIndex_ + 1) % frameInfos_.size(); - Threads::UI()->PostTask(fxl::MakeCopyable( - [ callback = std::move(callback), frameInfo, trace_id ]() mutable { + ui_task_runner->PostTask(fxl::MakeCopyable( + [callback = std::move(callback), frameInfo, trace_id]() mutable { InvokeNextFrameCallback(frameInfo, std::move(callback), trace_id); })); @@ -293,13 +312,20 @@ Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) { return ToDart("Callback must be a function"); } - Threads::IO()->PostTask(fxl::MakeCopyable([ - callback = std::make_unique( - tonic::DartState::Current(), callback_handle), - this, trace_id - ]() mutable { - GetNextFrameAndInvokeCallback(std::move(callback), trace_id); - })); + auto dart_state = UIDartState::Current(); + + const auto& task_runners = dart_state->GetTaskRunners(); + + task_runners.GetIOTaskRunner()->PostTask(fxl::MakeCopyable( + [callback = std::make_unique( + tonic::DartState::Current(), callback_handle), + this, trace_id, ui_task_runner = task_runners.GetUITaskRunner(), + queue = UIDartState::Current()->GetSkiaUnrefQueue(), + context = dart_state->GetResourceContext()]() mutable { + GetNextFrameAndInvokeCallback(std::move(callback), + std::move(ui_task_runner), context, + std::move(queue), trace_id); + })); return Dart_Null(); } diff --git a/engine/src/flutter/lib/ui/painting/codec.h b/engine/src/flutter/lib/ui/painting/codec.h index 98e5bc56df..9fdce1a8c1 100644 --- a/engine/src/flutter/lib/ui/painting/codec.h +++ b/engine/src/flutter/lib/ui/painting/codec.h @@ -43,11 +43,16 @@ class MultiFrameCodec : public Codec { private: MultiFrameCodec(std::unique_ptr codec); + ~MultiFrameCodec() {} - sk_sp GetNextFrameImage(); + sk_sp GetNextFrameImage(fml::WeakPtr resourceContext); + void GetNextFrameAndInvokeCallback( std::unique_ptr callback, + fxl::RefPtr ui_task_runner, + fml::WeakPtr resourceContext, + fxl::RefPtr unref_queue, size_t trace_id); const std::unique_ptr codec_; diff --git a/engine/src/flutter/lib/ui/painting/gradient.cc b/engine/src/flutter/lib/ui/painting/gradient.cc index f6bb864627..52ccd63f3f 100644 --- a/engine/src/flutter/lib/ui/painting/gradient.cc +++ b/engine/src/flutter/lib/ui/painting/gradient.cc @@ -48,10 +48,10 @@ void CanvasGradient::initLinear(const tonic::Float32List& end_points, static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor doesn't use int32_t."); - set_shader(SkGradientShader::MakeLinear( + set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeLinear( reinterpret_cast(end_points.data()), reinterpret_cast(colors.data()), color_stops.data(), - colors.num_elements(), tile_mode)); + colors.num_elements(), tile_mode))); } void CanvasGradient::initRadial(double center_x, @@ -73,14 +73,14 @@ void CanvasGradient::initRadial(double center_x, sk_matrix = ToSkMatrix(matrix4); } - set_shader(SkGradientShader::MakeRadial( + set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeRadial( SkPoint::Make(center_x, center_y), radius, reinterpret_cast(colors.data()), color_stops.data(), - colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)); + colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr))); } -CanvasGradient::CanvasGradient() : Shader(nullptr) {} +CanvasGradient::CanvasGradient() = default; -CanvasGradient::~CanvasGradient() {} +CanvasGradient::~CanvasGradient() = default; } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/image.cc b/engine/src/flutter/lib/ui/painting/image.cc index 42e733241c..a1b9b45cdc 100644 --- a/engine/src/flutter/lib/ui/painting/image.cc +++ b/engine/src/flutter/lib/ui/painting/image.cc @@ -4,9 +4,7 @@ #include "flutter/lib/ui/painting/image.h" -#include "flutter/common/threads.h" #include "flutter/lib/ui/painting/image_encoding.h" -#include "flutter/lib/ui/painting/utils.h" #include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" @@ -30,13 +28,9 @@ void CanvasImage::RegisterNatives(tonic::DartLibraryNatives* natives) { natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); } -CanvasImage::CanvasImage() {} +CanvasImage::CanvasImage() = default; -CanvasImage::~CanvasImage() { - // Skia objects must be deleted on the IO thread so that any associated GL - // objects will be cleaned up through the IO thread's GL context. - SkiaUnrefOnIOThread(&image_); -} +CanvasImage::~CanvasImage() = default; Dart_Handle CanvasImage::toByteData(int format, int quality, @@ -49,8 +43,8 @@ void CanvasImage::dispose() { } size_t CanvasImage::GetAllocationSize() { - if (image_) { - return image_->width() * image_->height() * 4; + if (auto image = image_.get()) { + return image->width() * image->height() * 4; } else { return sizeof(CanvasImage); } diff --git a/engine/src/flutter/lib/ui/painting/image.h b/engine/src/flutter/lib/ui/painting/image.h index a7ed429850..aeec2a0149 100644 --- a/engine/src/flutter/lib/ui/painting/image.h +++ b/engine/src/flutter/lib/ui/painting/image.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_LIB_UI_PAINTING_IMAGE_H_ #define FLUTTER_LIB_UI_PAINTING_IMAGE_H_ +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/tonic/dart_wrappable.h" #include "third_party/skia/include/core/SkImage.h" @@ -25,13 +27,18 @@ class CanvasImage final : public fxl::RefCountedThreadSafe, return fxl::MakeRefCounted(); } - int width() { return image_->width(); } - int height() { return image_->height(); } + int width() { return image_.get()->width(); } + + int height() { return image_.get()->height(); } + Dart_Handle toByteData(int format, int quality, Dart_Handle callback); + void dispose(); - const sk_sp& image() const { return image_; } - void set_image(sk_sp image) { image_ = std::move(image); } + sk_sp image() const { return image_.get(); } + void set_image(flow::SkiaGPUObject image) { + image_ = std::move(image); + } virtual size_t GetAllocationSize() override; @@ -40,7 +47,7 @@ class CanvasImage final : public fxl::RefCountedThreadSafe, private: CanvasImage(); - sk_sp image_; + flow::SkiaGPUObject image_; }; } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/image_encoding.cc b/engine/src/flutter/lib/ui/painting/image_encoding.cc index f356e955f6..f010fce893 100644 --- a/engine/src/flutter/lib/ui/painting/image_encoding.cc +++ b/engine/src/flutter/lib/ui/painting/image_encoding.cc @@ -4,13 +4,15 @@ #include "flutter/lib/ui/painting/image_encoding.h" -#include "flutter/common/threads.h" +#include +#include + +#include "flutter/common/task_runners.h" #include "flutter/lib/ui/painting/image.h" -#include "flutter/lib/ui/painting/resource_context.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/fxl/build_config.h" #include "lib/fxl/functional/make_copyable.h" #include "lib/tonic/dart_persistent_value.h" -#include "lib/tonic/dart_state.h" #include "lib/tonic/logging/dart_invoke.h" #include "lib/tonic/typed_data/uint8_list.h" #include "third_party/skia/include/core/SkEncodedImageFormat.h" @@ -52,10 +54,11 @@ void EncodeImageAndInvokeDataCallback( std::unique_ptr callback, sk_sp image, SkEncodedImageFormat format, - int quality) { + int quality, + fxl::RefPtr ui_task_runner) { sk_sp encoded = EncodeImage(std::move(image), format, quality); - Threads::UI()->PostTask( + ui_task_runner->PostTask( fxl::MakeCopyable([callback = std::move(callback), encoded]() mutable { InvokeDataCallback(std::move(callback), std::move(encoded)); })); @@ -101,10 +104,14 @@ Dart_Handle EncodeImage(CanvasImage* canvas_image, tonic::DartState::Current(), callback_handle); sk_sp image = canvas_image->image(); - Threads::IO()->PostTask(fxl::MakeCopyable( - [callback = std::move(callback), image, image_format, quality]() mutable { + const auto& task_runners = UIDartState::Current()->GetTaskRunners(); + + task_runners.GetIOTaskRunner()->PostTask(fxl::MakeCopyable( + [callback = std::move(callback), image, image_format, quality, + ui_task_runner = task_runners.GetUITaskRunner()]() mutable { EncodeImageAndInvokeDataCallback(std::move(callback), std::move(image), - image_format, quality); + image_format, quality, + std::move(ui_task_runner)); })); return Dart_Null(); diff --git a/engine/src/flutter/lib/ui/painting/image_shader.cc b/engine/src/flutter/lib/ui/painting/image_shader.cc index 0fabd134c7..a4ddbba0ea 100644 --- a/engine/src/flutter/lib/ui/painting/image_shader.cc +++ b/engine/src/flutter/lib/ui/painting/image_shader.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/image_shader.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" @@ -37,15 +38,17 @@ void ImageShader::initWithImage(CanvasImage* image, SkShader::TileMode tmx, SkShader::TileMode tmy, const tonic::Float64List& matrix4) { - if (!image) + if (!image) { Dart_ThrowException( ToDart("ImageShader constructor called with non-genuine Image.")); + } SkMatrix sk_matrix = ToSkMatrix(matrix4); - set_shader(image->image()->makeShader(tmx, tmy, &sk_matrix)); + set_shader(UIDartState::CreateGPUObject( + image->image()->makeShader(tmx, tmy, &sk_matrix))); } -ImageShader::ImageShader() : Shader(nullptr) {} +ImageShader::ImageShader() = default; -ImageShader::~ImageShader() {} +ImageShader::~ImageShader() = default; } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/picture.cc b/engine/src/flutter/lib/ui/painting/picture.cc index 89ddbf5f41..34f92b8c31 100644 --- a/engine/src/flutter/lib/ui/painting/picture.cc +++ b/engine/src/flutter/lib/ui/painting/picture.cc @@ -4,9 +4,8 @@ #include "flutter/lib/ui/painting/picture.h" -#include "flutter/common/threads.h" #include "flutter/lib/ui/painting/canvas.h" -#include "flutter/lib/ui/painting/utils.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" @@ -23,24 +22,20 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Picture); DART_BIND_ALL(Picture, FOR_EACH_BINDING) -fxl::RefPtr Picture::Create(sk_sp picture) { +fxl::RefPtr Picture::Create(flow::SkiaGPUObject picture) { return fxl::MakeRefCounted(std::move(picture)); } -Picture::Picture(sk_sp picture) : picture_(std::move(picture)) {} +Picture::Picture(flow::SkiaGPUObject picture) + : picture_(std::move(picture)) {} -Picture::~Picture() { - // Skia objects must be deleted on the IO thread so that any associated GL - // objects will be cleaned up through the IO thread's GL context. - SkiaUnrefOnIOThread(&picture_); -} +Picture::~Picture() = default; fxl::RefPtr Picture::toImage(int width, int height) { fxl::RefPtr image = CanvasImage::Create(); - // TODO(abarth): We should pass in an SkColorSpace at some point. - image->set_image(SkImage::MakeFromPicture( - picture_, SkISize::Make(width, height), nullptr, nullptr, - SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB())); + image->set_image(UIDartState::CreateGPUObject(SkImage::MakeFromPicture( + picture_.get(), SkISize::Make(width, height), nullptr, nullptr, + SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()))); return image; } @@ -49,8 +44,8 @@ void Picture::dispose() { } size_t Picture::GetAllocationSize() { - if (picture_) { - return picture_->approximateBytesUsed(); + if (auto picture = picture_.get()) { + return picture->approximateBytesUsed(); } else { return sizeof(Picture); } diff --git a/engine/src/flutter/lib/ui/painting/picture.h b/engine/src/flutter/lib/ui/painting/picture.h index 4a26d01c08..d916086b02 100644 --- a/engine/src/flutter/lib/ui/painting/picture.h +++ b/engine/src/flutter/lib/ui/painting/picture.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_H_ +#include "flutter/flow/skia_gpu_object.h" #include "flutter/lib/ui/painting/image.h" #include "lib/tonic/dart_wrappable.h" #include "third_party/skia/include/core/SkPicture.h" @@ -23,9 +24,9 @@ class Picture : public fxl::RefCountedThreadSafe, public: ~Picture() override; - static fxl::RefPtr Create(sk_sp picture); + static fxl::RefPtr Create(flow::SkiaGPUObject picture); - const sk_sp& picture() const { return picture_; } + sk_sp picture() const { return picture_.get(); } fxl::RefPtr toImage(int width, int height); @@ -36,9 +37,9 @@ class Picture : public fxl::RefCountedThreadSafe, static void RegisterNatives(tonic::DartLibraryNatives* natives); private: - explicit Picture(sk_sp picture); + explicit Picture(flow::SkiaGPUObject picture); - sk_sp picture_; + flow::SkiaGPUObject picture_; }; } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/picture_recorder.cc b/engine/src/flutter/lib/ui/painting/picture_recorder.cc index adc73c0043..e870e400a7 100644 --- a/engine/src/flutter/lib/ui/painting/picture_recorder.cc +++ b/engine/src/flutter/lib/ui/painting/picture_recorder.cc @@ -50,8 +50,9 @@ SkCanvas* PictureRecorder::BeginRecording(SkRect bounds) { fxl::RefPtr PictureRecorder::endRecording() { if (!isRecording()) return nullptr; - fxl::RefPtr picture = - Picture::Create(picture_recorder_.finishRecordingAsPicture()); + + fxl::RefPtr picture = Picture::Create(UIDartState::CreateGPUObject( + picture_recorder_.finishRecordingAsPicture())); canvas_->Clear(); canvas_->ClearDartWrapper(); canvas_ = nullptr; diff --git a/engine/src/flutter/lib/ui/painting/resource_context.cc b/engine/src/flutter/lib/ui/painting/resource_context.cc deleted file mode 100644 index 1b9f71548c..0000000000 --- a/engine/src/flutter/lib/ui/painting/resource_context.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/lib/ui/painting/resource_context.h" - -#include - -#include "lib/fxl/logging.h" - -namespace blink { -namespace { - -static GrContext* g_context = nullptr; -static std::mutex g_mutex; -static volatile bool g_freeze = false; - -} // namespace - -ResourceContext::ResourceContext() { - g_mutex.lock(); -} - -ResourceContext::~ResourceContext() { - g_mutex.unlock(); -} - -void ResourceContext::Set(sk_sp context) { - FXL_DCHECK(!g_context); - g_context = context.release(); -} - -GrContext* ResourceContext::Get() { - return g_freeze ? nullptr : g_context; -} - -std::unique_ptr ResourceContext::Acquire() { - return std::make_unique(); -} - -void ResourceContext::Freeze() { - std::lock_guard lock(g_mutex); - g_freeze = true; -} - -void ResourceContext::Unfreeze() { - std::lock_guard lock(g_mutex); - g_freeze = false; -} - -} // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/resource_context.h b/engine/src/flutter/lib/ui/painting/resource_context.h deleted file mode 100644 index 627b2053c2..0000000000 --- a/engine/src/flutter/lib/ui/painting/resource_context.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_LIB_UI_PAINTING_RESOURCE_CONTEXT_H_ -#define FLUTTER_LIB_UI_PAINTING_RESOURCE_CONTEXT_H_ - -#include "lib/fxl/macros.h" -#include "third_party/skia/include/gpu/GrContext.h" - -namespace blink { - -class ResourceContext { - public: - /** - * Globally set the GrContext singleton instance. - */ - static void Set(sk_sp context); - - /** - * Acquire a GrContext wrapping ResourceContext that's also an exclusive mutex - * on GrContext operations. - * - * Destructing the ResourceContext frees the mutex. - */ - static std::unique_ptr Acquire(); - - /** - * Synchronously signal a freeze on GrContext operations. - * - * ResourceContext instances will return nullptr on GrContext Get until - * unfrozen. - */ - static void Freeze(); - - /** - * Synchronously unfreeze GrContext operations. - * - * ResourceContext instances will continue to return the global GrContext - * instance on Get. - */ - static void Unfreeze(); - - ResourceContext(); - ~ResourceContext(); - - /** - * Returns global GrContext instance. May return null when operations are - * frozen. - * - * Happens on iOS when background operations on GrContext are forbidden. - */ - GrContext* Get(); - - FXL_DISALLOW_COPY_AND_ASSIGN(ResourceContext); -}; - -} // namespace blink - -#endif // FLUTTER_LIB_UI_PAINTING_RESOURCE_CONTEXT_H_ diff --git a/engine/src/flutter/lib/ui/painting/shader.cc b/engine/src/flutter/lib/ui/painting/shader.cc index d7d8ccf20a..7999b84dfc 100644 --- a/engine/src/flutter/lib/ui/painting/shader.cc +++ b/engine/src/flutter/lib/ui/painting/shader.cc @@ -4,19 +4,15 @@ #include "flutter/lib/ui/painting/shader.h" -#include "flutter/common/threads.h" -#include "flutter/lib/ui/painting/utils.h" +#include "flutter/lib/ui/ui_dart_state.h" namespace blink { IMPLEMENT_WRAPPERTYPEINFO(ui, Shader); -Shader::Shader(sk_sp shader) : shader_(shader) {} +Shader::Shader(flow::SkiaGPUObject shader) + : shader_(std::move(shader)) {} -Shader::~Shader() { - // Skia objects must be deleted on the IO thread so that any associated GL - // objects will be cleaned up through the IO thread's GL context. - SkiaUnrefOnIOThread(&shader_); -} +Shader::~Shader() = default; } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/shader.h b/engine/src/flutter/lib/ui/painting/shader.h index 9c0f3c601d..205197250e 100644 --- a/engine/src/flutter/lib/ui/painting/shader.h +++ b/engine/src/flutter/lib/ui/painting/shader.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_LIB_UI_PAINTING_SHADER_H_ #define FLUTTER_LIB_UI_PAINTING_SHADER_H_ +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "lib/tonic/dart_wrappable.h" #include "third_party/skia/include/core/SkShader.h" @@ -18,14 +20,17 @@ class Shader : public fxl::RefCountedThreadSafe, public: ~Shader() override; - const sk_sp& shader() { return shader_; } - void set_shader(sk_sp shader) { shader_ = std::move(shader); } + sk_sp shader() { return shader_.get(); } + + void set_shader(flow::SkiaGPUObject shader) { + shader_ = std::move(shader); + } protected: - Shader(sk_sp shader); + Shader(flow::SkiaGPUObject shader = {}); private: - sk_sp shader_; + flow::SkiaGPUObject shader_; }; } // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/utils.cc b/engine/src/flutter/lib/ui/painting/utils.cc deleted file mode 100644 index b3f87135ac..0000000000 --- a/engine/src/flutter/lib/ui/painting/utils.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/lib/ui/painting/utils.h" -#include "flutter/common/threads.h" - -namespace blink { - -namespace { - -constexpr fxl::TimeDelta kDrainDelay = fxl::TimeDelta::FromMilliseconds(250); - -} // anonymous namespace - -SkiaUnrefQueue::SkiaUnrefQueue() : drain_pending_(false) {} - -SkiaUnrefQueue SkiaUnrefQueue::instance_; - -SkiaUnrefQueue& SkiaUnrefQueue::Get() { - return instance_; -} - -void SkiaUnrefQueue::Unref(SkRefCnt* object) { - std::lock_guard lock(mutex_); - objects_.push_back(object); - if (!drain_pending_) { - drain_pending_ = true; - Threads::IO()->PostDelayedTask([this] { Drain(); }, kDrainDelay); - } -} - -void SkiaUnrefQueue::Drain() { - std::deque skia_objects; - { - std::lock_guard lock(mutex_); - objects_.swap(skia_objects); - drain_pending_ = false; - } - - for (SkRefCnt* skia_object : skia_objects) { - skia_object->unref(); - } -} - -} // namespace blink diff --git a/engine/src/flutter/lib/ui/painting/utils.h b/engine/src/flutter/lib/ui/painting/utils.h deleted file mode 100644 index 02782c6796..0000000000 --- a/engine/src/flutter/lib/ui/painting/utils.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/skia/include/core/SkRefCnt.h" - -#include -#include - -namespace blink { - -// A queue that holds Skia objects that must be destructed on the IO thread. -class SkiaUnrefQueue { - public: - static SkiaUnrefQueue& Get(); - - void Unref(SkRefCnt* object); - - private: - SkiaUnrefQueue(); - void Drain(); - - static SkiaUnrefQueue instance_; - - std::mutex mutex_; - std::deque objects_; - bool drain_pending_; -}; - -template -void SkiaUnrefOnIOThread(sk_sp* sp) { - T* object = sp->release(); - if (object) { - SkiaUnrefQueue::Get().Unref(object); - } -} - -} // namespace blink diff --git a/engine/src/flutter/lib/ui/text/font_collection.cc b/engine/src/flutter/lib/ui/text/font_collection.cc index d6407f9ed4..248468cc97 100644 --- a/engine/src/flutter/lib/ui/text/font_collection.cc +++ b/engine/src/flutter/lib/ui/text/font_collection.cc @@ -34,15 +34,9 @@ std::shared_ptr FontCollection::GetFontCollection() const { return collection_; } -void FontCollection::RegisterFontsFromAssetProvider( - fxl::RefPtr asset_provider) { - - if (!asset_provider){ - return; - } - +void FontCollection::RegisterFonts(const AssetManager& asset_manager) { std::vector manifest_data; - if (!asset_provider->GetAsBuffer("FontManifest.json", &manifest_data)) { + if (!asset_manager.GetAsBuffer("FontManifest.json", &manifest_data)) { FXL_DLOG(WARNING) << "Could not find the font manifest in the asset store."; return; } @@ -92,8 +86,8 @@ void FontCollection::RegisterFontsFromAssetProvider( // TODO: Handle weights and styles. std::vector font_data; - if (asset_provider->GetAsBuffer(font_asset->value.GetString(), - &font_data)) { + if (asset_manager.GetAsBuffer(font_asset->value.GetString(), + &font_data)) { // The data must be copied because it needs to be moved into the // typeface as a stream. auto data = diff --git a/engine/src/flutter/lib/ui/text/font_collection.h b/engine/src/flutter/lib/ui/text/font_collection.h index fb393b1582..ea9c2f46a9 100644 --- a/engine/src/flutter/lib/ui/text/font_collection.h +++ b/engine/src/flutter/lib/ui/text/font_collection.h @@ -7,10 +7,10 @@ #include #include -#include "flutter/assets/asset_provider.h" + +#include "flutter/assets/asset_manager.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_ptr.h" -#include "txt/asset_data_provider.h" #include "txt/font_collection.h" namespace blink { @@ -21,7 +21,8 @@ class FontCollection { std::shared_ptr GetFontCollection() const; - void RegisterFontsFromAssetProvider(fxl::RefPtr asset_provider); + void RegisterFonts(const AssetManager& asset_manager); + void RegisterTestFonts(); private: diff --git a/engine/src/flutter/lib/ui/text/paragraph.cc b/engine/src/flutter/lib/ui/text/paragraph.cc index 858ccd01d4..fd00f6f51b 100644 --- a/engine/src/flutter/lib/ui/text/paragraph.cc +++ b/engine/src/flutter/lib/ui/text/paragraph.cc @@ -5,7 +5,7 @@ #include "flutter/lib/ui/text/paragraph.h" #include "flutter/common/settings.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/sky/engine/core/rendering/PaintInfo.h" #include "flutter/sky/engine/core/rendering/RenderParagraph.h" #include "flutter/sky/engine/core/rendering/RenderText.h" @@ -53,7 +53,8 @@ Paragraph::Paragraph(std::unique_ptr paragraph) Paragraph::~Paragraph() { if (m_renderView) { RenderView* renderView = m_renderView.leakPtr(); - Threads::UI()->PostTask([renderView]() { renderView->destroy(); }); + destruction_task_runner_->PostTask( + [renderView]() { renderView->destroy(); }); } } diff --git a/engine/src/flutter/lib/ui/text/paragraph.h b/engine/src/flutter/lib/ui/text/paragraph.h index 1b20193735..b3a943209a 100644 --- a/engine/src/flutter/lib/ui/text/paragraph.h +++ b/engine/src/flutter/lib/ui/text/paragraph.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_TEXT_PARAGRAPH_H_ #define FLUTTER_LIB_UI_TEXT_PARAGRAPH_H_ +#include "flutter/fml/message_loop.h" #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/text/paragraph_impl.h" #include "flutter/lib/ui/text/paragraph_impl_blink.h" @@ -66,6 +67,10 @@ class Paragraph : public fxl::RefCountedThreadSafe, explicit Paragraph(std::unique_ptr paragraph); + // TODO: This can be removed when the render view association for the legacy + // runtime is removed. + fxl::RefPtr destruction_task_runner_ = + UIDartState::Current()->GetTaskRunners().GetUITaskRunner(); OwnPtr m_renderView; }; diff --git a/engine/src/flutter/lib/ui/text/paragraph_builder.cc b/engine/src/flutter/lib/ui/text/paragraph_builder.cc index 44d05053ba..b987e3fde6 100644 --- a/engine/src/flutter/lib/ui/text/paragraph_builder.cc +++ b/engine/src/flutter/lib/ui/text/paragraph_builder.cc @@ -5,7 +5,7 @@ #include "flutter/lib/ui/text/paragraph_builder.h" #include "flutter/common/settings.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/sky/engine/core/rendering/RenderInline.h" @@ -205,9 +205,11 @@ fxl::RefPtr ParagraphBuilder::create( double fontSize, double lineHeight, const std::u16string& ellipsis, - const std::string& locale) { - return fxl::MakeRefCounted(encoded, fontFamily, fontSize, - lineHeight, ellipsis, locale); + const std::string& locale, + bool use_blink) { + return fxl::MakeRefCounted( + encoded, fontFamily, fontSize, lineHeight, ellipsis, locale, + UIDartState::Current()->use_blink()); } ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded, @@ -215,8 +217,10 @@ ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded, double fontSize, double lineHeight, const std::u16string& ellipsis, - const std::string& locale) { - if (!Settings::Get().using_blink) { + const std::string& locale, + bool use_blink) + : m_useBlink(use_blink) { + if (!m_useBlink) { int32_t mask = encoded[0]; txt::ParagraphStyle style; if (mask & psTextAlignMask) @@ -275,7 +279,8 @@ ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded, ParagraphBuilder::~ParagraphBuilder() { if (m_renderView) { RenderView* renderView = m_renderView.leakPtr(); - Threads::UI()->PostTask([renderView]() { renderView->destroy(); }); + destruction_task_runner_->PostTask( + [renderView]() { renderView->destroy(); }); } } @@ -290,7 +295,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, int32_t mask = encoded[0]; - if (!Settings::Get().using_blink) { + if (!m_useBlink) { // Set to use the properties of the previous style if the property is not // explicitly given. txt::TextStyle style = m_paragraphBuilder->PeekStyle(); @@ -423,7 +428,7 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, } void ParagraphBuilder::pop() { - if (!Settings::Get().using_blink) { + if (!m_useBlink) { m_paragraphBuilder->Pop(); } else { // Blink Version. @@ -445,7 +450,7 @@ Dart_Handle ParagraphBuilder::addText(const std::u16string& text) { if (error_code != U_BUFFER_OVERFLOW_ERROR) return tonic::ToDart("string is not well-formed UTF-16"); - if (!Settings::Get().using_blink) { + if (!m_useBlink) { m_paragraphBuilder->AddText(text); } else { // Blink Version. @@ -464,7 +469,7 @@ Dart_Handle ParagraphBuilder::addText(const std::u16string& text) { fxl::RefPtr ParagraphBuilder::build() { m_currentRenderObject = nullptr; - if (!Settings::Get().using_blink) { + if (!m_useBlink) { return Paragraph::Create(m_paragraphBuilder->Build()); } else { return Paragraph::Create(m_renderView.release()); diff --git a/engine/src/flutter/lib/ui/text/paragraph_builder.h b/engine/src/flutter/lib/ui/text/paragraph_builder.h index 37a1e9a3e7..1c38d98696 100644 --- a/engine/src/flutter/lib/ui/text/paragraph_builder.h +++ b/engine/src/flutter/lib/ui/text/paragraph_builder.h @@ -32,7 +32,8 @@ class ParagraphBuilder : public fxl::RefCountedThreadSafe, double fontSize, double lineHeight, const std::u16string& ellipsis, - const std::string& locale); + const std::string& locale, + bool use_blink); ~ParagraphBuilder() override; @@ -58,14 +59,20 @@ class ParagraphBuilder : public fxl::RefCountedThreadSafe, double fontSize, double lineHeight, const std::u16string& ellipsis, - const std::string& locale); + const std::string& locale, + bool use_blink); void createRenderView(); + // TODO: This can be removed when the render view association for the legacy + // runtime is removed. + fxl::RefPtr destruction_task_runner_ = + UIDartState::Current()->GetTaskRunners().GetUITaskRunner(); OwnPtr m_renderView; RenderObject* m_renderParagraph; RenderObject* m_currentRenderObject; std::unique_ptr m_paragraphBuilder; + bool m_useBlink; }; } // namespace blink diff --git a/engine/src/flutter/lib/ui/text/paragraph_impl_blink.cc b/engine/src/flutter/lib/ui/text/paragraph_impl_blink.cc index c7752c0a7c..5c50f047af 100644 --- a/engine/src/flutter/lib/ui/text/paragraph_impl_blink.cc +++ b/engine/src/flutter/lib/ui/text/paragraph_impl_blink.cc @@ -4,7 +4,6 @@ #include "flutter/lib/ui/text/paragraph_impl_blink.h" -#include "flutter/common/threads.h" #include "flutter/lib/ui/text/paragraph.h" #include "flutter/lib/ui/text/paragraph_impl.h" #include "flutter/sky/engine/core/rendering/PaintInfo.h" @@ -30,7 +29,8 @@ ParagraphImplBlink::ParagraphImplBlink(PassOwnPtr renderView) ParagraphImplBlink::~ParagraphImplBlink() { if (m_renderView) { RenderView* renderView = m_renderView.leakPtr(); - Threads::UI()->PostTask([renderView]() { renderView->destroy(); }); + destruction_task_runner_->PostTask( + [renderView]() { renderView->destroy(); }); } } diff --git a/engine/src/flutter/lib/ui/text/paragraph_impl_blink.h b/engine/src/flutter/lib/ui/text/paragraph_impl_blink.h index 79d89ad471..ebd80f8dd5 100644 --- a/engine/src/flutter/lib/ui/text/paragraph_impl_blink.h +++ b/engine/src/flutter/lib/ui/text/paragraph_impl_blink.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_TEXT_PARAGRAPH_IMPL_BLINK_H_ #define FLUTTER_LIB_UI_TEXT_PARAGRAPH_IMPL_BLINK_H_ +#include "flutter/fml/message_loop.h" #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/text/paragraph_impl.h" #include "flutter/lib/ui/text/text_box.h" @@ -41,6 +42,10 @@ class ParagraphImplBlink : public ParagraphImpl { int absoluteOffsetForPosition(const PositionWithAffinity& position); + // TODO: This can be removed when the render view association for the legacy + // runtime is removed. + fxl::RefPtr destruction_task_runner_ = + UIDartState::Current()->GetTaskRunners().GetUITaskRunner(); OwnPtr m_renderView; }; diff --git a/engine/src/flutter/lib/ui/text/paragraph_impl_txt.cc b/engine/src/flutter/lib/ui/text/paragraph_impl_txt.cc index de8d8ff0b2..28c6ea19a8 100644 --- a/engine/src/flutter/lib/ui/text/paragraph_impl_txt.cc +++ b/engine/src/flutter/lib/ui/text/paragraph_impl_txt.cc @@ -4,7 +4,7 @@ #include "flutter/lib/ui/text/paragraph_impl_txt.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/lib/ui/text/paragraph.h" #include "flutter/lib/ui/text/paragraph_impl.h" #include "lib/fxl/logging.h" diff --git a/engine/src/flutter/lib/ui/ui_dart_state.cc b/engine/src/flutter/lib/ui/ui_dart_state.cc index 3bdf21e6a1..8ef7ca98fd 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.cc +++ b/engine/src/flutter/lib/ui/ui_dart_state.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/fml/message_loop.h" #include "flutter/lib/ui/window/window.h" #include "flutter/sky/engine/platform/fonts/FontSelector.h" #include "lib/tonic/converter/dart_converter.h" @@ -12,34 +13,44 @@ using tonic::ToDart; namespace blink { -IsolateClient::~IsolateClient() {} - -UIDartState::UIDartState(IsolateClient* isolate_client, - std::unique_ptr window, - int dirfd) - : tonic::DartState(dirfd), - isolate_client_(isolate_client), - main_port_(ILLEGAL_PORT), - window_(std::move(window)) {} - -UIDartState::~UIDartState() { - main_port_ = ILLEGAL_PORT; - // We've already destroyed the isolate. Revoke any weak ptrs held by - // DartPersistentValues so they don't try to enter the destroyed isolate to - // clean themselves up. - // TODO(abarth): Can we do this work in the base class? - weak_factory_.InvalidateWeakPtrs(); +UIDartState::UIDartState(TaskRunners task_runners, + TaskObserverAdd add_callback, + TaskObserverRemove remove_callback, + fml::WeakPtr resource_context, + fxl::RefPtr skia_unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + std::string logger_prefix) + : task_runners_(std::move(task_runners)), + add_callback_(std::move(add_callback)), + remove_callback_(std::move(remove_callback)), + resource_context_(std::move(resource_context)), + advisory_script_uri_(std::move(advisory_script_uri)), + advisory_script_entrypoint_(std::move(advisory_script_entrypoint)), + logger_prefix_(std::move(logger_prefix)), + skia_unref_queue_(std::move(skia_unref_queue)), + weak_factory_(this) { + AddOrRemoveTaskObserver(true /* add */); } -UIDartState* UIDartState::CreateForChildIsolate() { - return new UIDartState(isolate_client_, nullptr); +UIDartState::~UIDartState() { + AddOrRemoveTaskObserver(false /* remove */); +} + +const std::string& UIDartState::GetAdvisoryScriptURI() const { + return advisory_script_uri_; +} + +const std::string& UIDartState::GetAdvisoryScriptEntrypoint() const { + return advisory_script_entrypoint_; } void UIDartState::DidSetIsolate() { - FXL_DCHECK(!debug_name_prefix_.empty()); main_port_ = Dart_GetMainPortId(); std::ostringstream debug_name; - debug_name << debug_name_prefix_ << "$main-" << main_port_; + // main.dart$main-1234 + debug_name << advisory_script_uri_ << "$" << advisory_script_entrypoint_ + << "-" << main_port_; debug_name_ = debug_name.str(); } @@ -55,8 +66,48 @@ PassRefPtr UIDartState::font_selector() { return font_selector_; } -void UIDartState::set_debug_name_prefix(const std::string& debug_name_prefix) { - debug_name_prefix_ = debug_name_prefix; +void UIDartState::SetWindow(std::unique_ptr window) { + window_ = std::move(window); +} + +const TaskRunners& UIDartState::GetTaskRunners() const { + return task_runners_; +} + +fxl::RefPtr UIDartState::GetSkiaUnrefQueue() const { + return skia_unref_queue_; +} + +void UIDartState::ScheduleMicrotask(Dart_Handle closure) { + if (tonic::LogIfError(closure) || !Dart_IsClosure(closure)) { + return; + } + + microtask_queue_.ScheduleMicrotask(closure); +} + +void UIDartState::FlushMicrotasksNow() { + microtask_queue_.RunMicrotasks(); +} + +void UIDartState::AddOrRemoveTaskObserver(bool add) { + auto task_runner = task_runners_.GetUITaskRunner(); + if (!task_runner) { + // This may happen in case the isolate has no thread affinity (for example, + // the service isolate). + return; + } + FXL_DCHECK(add_callback_ && remove_callback_); + if (add) { + add_callback_(reinterpret_cast(this), + [this]() { this->FlushMicrotasksNow(); }); + } else { + remove_callback_(reinterpret_cast(this)); + } +} + +fml::WeakPtr UIDartState::GetResourceContext() const { + return resource_context_; } } // namespace blink diff --git a/engine/src/flutter/lib/ui/ui_dart_state.h b/engine/src/flutter/lib/ui/ui_dart_state.h index 0f7f9343d4..a5c78c1671 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.h +++ b/engine/src/flutter/lib/ui/ui_dart_state.h @@ -5,65 +5,106 @@ #ifndef FLUTTER_LIB_UI_UI_DART_STATE_H_ #define FLUTTER_LIB_UI_UI_DART_STATE_H_ +#include +#include #include +#include "flutter/common/settings.h" +#include "flutter/common/task_runners.h" +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/fml/memory/weak_ptr.h" #include "flutter/sky/engine/wtf/RefPtr.h" #include "lib/fxl/build_config.h" +#include "lib/tonic/dart_microtask_queue.h" #include "lib/tonic/dart_persistent_value.h" #include "lib/tonic/dart_state.h" #include "third_party/dart/runtime/include/dart_api.h" +#include "third_party/skia/include/gpu/GrContext.h" namespace blink { class FontSelector; class Window; -class IsolateClient { - public: - virtual void DidCreateSecondaryIsolate(Dart_Isolate isolate) = 0; - virtual void DidShutdownMainIsolate() = 0; - - protected: - virtual ~IsolateClient(); -}; - class UIDartState : public tonic::DartState { public: - UIDartState(IsolateClient* isolate_client, - std::unique_ptr window, - int dirfd = -1); - ~UIDartState() override; - static UIDartState* Current(); - UIDartState* CreateForChildIsolate(); - - IsolateClient* isolate_client() const { return isolate_client_; } - void set_isolate_client(IsolateClient* isolate_client) { - isolate_client_ = isolate_client; - } Dart_Port main_port() const { return main_port_; } + const std::string& debug_name() const { return debug_name_; } + + const std::string& logger_prefix() const { return logger_prefix_; } + Window* window() const { return window_.get(); } - void set_debug_name_prefix(const std::string& debug_name_prefix); void set_font_selector(PassRefPtr selector); + PassRefPtr font_selector(); - bool is_controller_state() const { return is_controller_state_; } - void set_is_controller_state(bool value) { is_controller_state_ = value; } - bool shutting_down() const { return shutting_down_; } - void set_shutting_down(bool value) { shutting_down_ = value; } + + bool use_blink() const { return use_blink_; } + + const TaskRunners& GetTaskRunners() const; + + void ScheduleMicrotask(Dart_Handle handle); + + void FlushMicrotasksNow(); + + fxl::RefPtr GetSkiaUnrefQueue() const; + + fml::WeakPtr GetResourceContext() const; + + template + static flow::SkiaGPUObject CreateGPUObject(sk_sp object) { + if (!object) { + return {}; + } + auto state = UIDartState::Current(); + FXL_DCHECK(state); + auto queue = state->GetSkiaUnrefQueue(); + return {std::move(object), std::move(queue)}; + }; + + protected: + UIDartState(TaskRunners task_runners, + TaskObserverAdd add_callback, + TaskObserverRemove remove_callback, + fml::WeakPtr resource_context, + fxl::RefPtr skia_unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + std::string logger_prefix); + + ~UIDartState() override; + + void SetWindow(std::unique_ptr window); + + void set_use_blink(bool use_blink) { use_blink_ = use_blink; } + + const std::string& GetAdvisoryScriptURI() const; + + const std::string& GetAdvisoryScriptEntrypoint() const; private: void DidSetIsolate() override; - IsolateClient* isolate_client_; - Dart_Port main_port_; - std::string debug_name_prefix_; + const TaskRunners task_runners_; + const TaskObserverAdd add_callback_; + const TaskObserverRemove remove_callback_; + fml::WeakPtr resource_context_; + const std::string advisory_script_uri_; + const std::string advisory_script_entrypoint_; + const std::string logger_prefix_; + Dart_Port main_port_ = ILLEGAL_PORT; std::string debug_name_; std::unique_ptr window_; RefPtr font_selector_; - bool is_controller_state_; - bool shutting_down_ = false; + fxl::RefPtr skia_unref_queue_; + tonic::DartMicrotaskQueue microtask_queue_; + fml::WeakPtrFactory weak_factory_; + + void AddOrRemoveTaskObserver(bool add); + + bool use_blink_ = false; }; } // namespace blink diff --git a/engine/src/flutter/lib/ui/window/platform_message_response_dart.cc b/engine/src/flutter/lib/ui/window/platform_message_response_dart.cc index 065159b797..2cca2fd179 100644 --- a/engine/src/flutter/lib/ui/window/platform_message_response_dart.cc +++ b/engine/src/flutter/lib/ui/window/platform_message_response_dart.cc @@ -6,7 +6,7 @@ #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/lib/ui/window/window.h" #include "lib/fxl/functional/make_copyable.h" #include "lib/tonic/dart_state.h" @@ -43,12 +43,14 @@ Dart_Handle WrapByteData(std::vector data) { } // anonymous namespace PlatformMessageResponseDart::PlatformMessageResponseDart( - tonic::DartPersistentValue callback) - : callback_(std::move(callback)) {} + tonic::DartPersistentValue callback, + fxl::RefPtr ui_task_runner) + : callback_(std::move(callback)), + ui_task_runner_(std::move(ui_task_runner)) {} PlatformMessageResponseDart::~PlatformMessageResponseDart() { if (!callback_.is_empty()) { - Threads::UI()->PostTask( + ui_task_runner_->PostTask( fxl::MakeCopyable([callback = std::move(callback_)]() mutable { callback.Clear(); })); @@ -60,7 +62,7 @@ void PlatformMessageResponseDart::Complete(std::vector data) { return; FXL_DCHECK(!is_complete_); is_complete_ = true; - Threads::UI()->PostTask(fxl::MakeCopyable( + ui_task_runner_->PostTask(fxl::MakeCopyable( [ callback = std::move(callback_), data = std::move(data) ]() mutable { tonic::DartState* dart_state = callback.dart_state().get(); if (!dart_state) @@ -77,7 +79,7 @@ void PlatformMessageResponseDart::CompleteEmpty() { return; FXL_DCHECK(!is_complete_); is_complete_ = true; - Threads::UI()->PostTask( + ui_task_runner_->PostTask( fxl::MakeCopyable([callback = std::move(callback_)]() mutable { tonic::DartState* dart_state = callback.dart_state().get(); if (!dart_state) diff --git a/engine/src/flutter/lib/ui/window/platform_message_response_dart.h b/engine/src/flutter/lib/ui/window/platform_message_response_dart.h index 51c55a9300..67bfb3d359 100644 --- a/engine/src/flutter/lib/ui/window/platform_message_response_dart.h +++ b/engine/src/flutter/lib/ui/window/platform_message_response_dart.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PLATFORM_PLATFORM_MESSAGE_RESPONSE_DART_H_ #define FLUTTER_LIB_UI_PLATFORM_PLATFORM_MESSAGE_RESPONSE_DART_H_ +#include "flutter/fml/message_loop.h" #include "flutter/lib/ui/window/platform_message_response.h" #include "lib/tonic/dart_persistent_value.h" @@ -19,10 +20,13 @@ class PlatformMessageResponseDart : public PlatformMessageResponse { void CompleteEmpty() override; protected: - explicit PlatformMessageResponseDart(tonic::DartPersistentValue callback); + explicit PlatformMessageResponseDart( + tonic::DartPersistentValue callback, + fxl::RefPtr ui_task_runner); ~PlatformMessageResponseDart() override; tonic::DartPersistentValue callback_; + fxl::RefPtr ui_task_runner_; }; } // namespace blink diff --git a/engine/src/flutter/lib/ui/window/viewport_metrics.h b/engine/src/flutter/lib/ui/window/viewport_metrics.h index 1e443ae8c1..5085d06f42 100644 --- a/engine/src/flutter/lib/ui/window/viewport_metrics.h +++ b/engine/src/flutter/lib/ui/window/viewport_metrics.h @@ -23,6 +23,25 @@ struct ViewportMetrics { int32_t physical_view_inset_left = 0; }; +struct LogicalSize { + double width = 0.0; + double height = 0.0; +}; + +struct LogicalInset { + double left = 0.0; + double top = 0.0; + double right = 0.0; + double bottom = 0.0; +}; + +struct LogicalMetrics { + LogicalSize size; + double scale = 1.0; + LogicalInset padding; + LogicalInset view_inset; +}; + } // namespace blink #endif // FLUTTER_LIB_UI_WINDOW_VIEWPORT_METRICS_H_ diff --git a/engine/src/flutter/lib/ui/window/window.cc b/engine/src/flutter/lib/ui/window/window.cc index e12e03f10d..4cce1cc590 100644 --- a/engine/src/flutter/lib/ui/window/window.cc +++ b/engine/src/flutter/lib/ui/window/window.cc @@ -63,7 +63,8 @@ void SendPlatformMessage(Dart_Handle window, fxl::RefPtr response; if (!Dart_IsNull(callback)) { response = fxl::MakeRefCounted( - tonic::DartPersistentValue(dart_state, callback)); + tonic::DartPersistentValue(dart_state, callback), + dart_state->GetTaskRunners().GetUITaskRunner()); } if (Dart_IsNull(data.dart_handle())) { UIDartState::Current()->window()->client()->HandlePlatformMessage( @@ -254,7 +255,7 @@ void Window::BeginFrame(fxl::TimePoint frameTime) { Dart_NewInteger(microseconds), }); - tonic::DartMicrotaskQueue::GetForCurrentThread()->RunMicrotasks(); + UIDartState::Current()->FlushMicrotasksNow(); DartInvokeField(library_.value(), "_drawFrame", {}); } diff --git a/engine/src/flutter/lib/ui/window/window.h b/engine/src/flutter/lib/ui/window/window.h index ef0d7ef863..2feeccb65f 100644 --- a/engine/src/flutter/lib/ui/window/window.h +++ b/engine/src/flutter/lib/ui/window/window.h @@ -13,6 +13,7 @@ #include "flutter/lib/ui/window/viewport_metrics.h" #include "lib/fxl/time/time_point.h" #include "lib/tonic/dart_persistent_value.h" +#include "third_party/skia/include/gpu/GrContext.h" namespace tonic { class DartLibraryNatives; @@ -35,12 +36,14 @@ class WindowClient { virtual ~WindowClient(); }; -class Window { +class Window final { public: explicit Window(WindowClient* client); + ~Window(); WindowClient* client() const { return client_; } + const ViewportMetrics& viewport_metrics() { return viewport_metrics_; } void DidCreateIsolate(); diff --git a/engine/src/flutter/runtime/BUILD.gn b/engine/src/flutter/runtime/BUILD.gn index dea8a8906d..455d7e2281 100644 --- a/engine/src/flutter/runtime/BUILD.gn +++ b/engine/src/flutter/runtime/BUILD.gn @@ -4,6 +4,7 @@ import("//third_party/dart/runtime/bin/vmservice/vmservice_sources.gni") import("$flutter_root/common/config.gni") +import("$flutter_root/testing/testing.gni") action("gen_embedded_resources_cc") { script = "//third_party/dart/runtime/tools/create_resources.py" @@ -37,9 +38,7 @@ source_set("embedded_resources_cc") { deps = [ ":gen_embedded_resources_cc", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } source_set("test_font") { @@ -50,9 +49,7 @@ source_set("test_font") { deps = [ "//third_party/skia", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] defines = [] if (flutter_runtime_mode == "debug" || current_toolchain == host_toolchain) { # Though the test font data is small, we dont want to add to the binary size @@ -63,16 +60,19 @@ source_set("test_font") { } source_set("runtime") { - sources = [ "asset_font_selector.cc", "asset_font_selector.h", - "dart_controller.cc", - "dart_controller.h", - "dart_init.cc", - "dart_init.h", + "dart_isolate.cc", + "dart_isolate.h", "dart_service_isolate.cc", "dart_service_isolate.h", + "dart_snapshot.cc", + "dart_snapshot.h", + "dart_snapshot_buffer.cc", + "dart_snapshot_buffer.h", + "dart_vm.cc", + "dart_vm.h", "embedder_resources.cc", "embedder_resources.h", "platform_impl.cc", @@ -81,8 +81,8 @@ source_set("runtime") { "runtime_controller.h", "runtime_delegate.cc", "runtime_delegate.h", - "runtime_init.cc", - "runtime_init.h", + "service_protocol.cc", + "service_protocol.h", "start_up.cc", "start_up.h", "test_font_selector.cc", @@ -92,25 +92,24 @@ source_set("runtime") { deps = [ ":embedded_resources_cc", ":test_font", - "//third_party/dart/runtime:dart_api", - "//third_party/dart/runtime/bin:embedded_dart_io", "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", + "$flutter_root/fml", "$flutter_root/glue", "$flutter_root/lib/io", "$flutter_root/lib/ui", "$flutter_root/sky/engine/platform", "$flutter_root/third_party/txt", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:dart_api", + "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/rapidjson", "//third_party/skia", "//topaz/lib/tonic", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] # In AOT mode, precompiled snapshots contain the instruction buffer. # Generation of the same requires all application specific script code to be @@ -119,3 +118,28 @@ source_set("runtime") { deps += [ "$flutter_root/lib/snapshot" ] } } + +test_fixtures("runtime_fixtures") { + fixtures = [ "fixtures/simple_main.dart" ] +} + +executable("runtime_unittests") { + testonly = true + + sources = [ + "dart_isolate_unittests.cc", + "dart_vm_unittests.cc", + ] + + deps = [ + ":runtime", + ":runtime_fixtures", + "$flutter_root/fml", + "$flutter_root/lib/snapshot", + "$flutter_root/testing", + "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", + "//third_party/skia", + "//topaz/lib/tonic", + ] +} diff --git a/engine/src/flutter/runtime/asset_font_selector.cc b/engine/src/flutter/runtime/asset_font_selector.cc index abf4bf9874..2a43f39e3a 100644 --- a/engine/src/flutter/runtime/asset_font_selector.cc +++ b/engine/src/flutter/runtime/asset_font_selector.cc @@ -80,27 +80,15 @@ struct FontMatcher { } // namespace -void AssetFontSelector::Install( - fxl::RefPtr asset_provider) { +void AssetFontSelector::Install(fxl::RefPtr asset_manager) { RefPtr font_selector = - adoptRef(new AssetFontSelector(std::move(asset_provider))); + adoptRef(new AssetFontSelector(std::move(asset_manager))); font_selector->parseFontManifest(); UIDartState::Current()->set_font_selector(font_selector); } -void AssetFontSelector::Install(fxl::RefPtr asset_store) { - RefPtr font_selector = - adoptRef(new AssetFontSelector(std::move(asset_store))); - font_selector->parseFontManifest(); - UIDartState::Current()->set_font_selector(font_selector); -} - -AssetFontSelector::AssetFontSelector( - fxl::RefPtr asset_provider) - : asset_provider_(std::move(asset_provider)) {} - -AssetFontSelector::AssetFontSelector(fxl::RefPtr asset_store) - : asset_store_(std::move(asset_store)) {} +AssetFontSelector::AssetFontSelector(fxl::RefPtr asset_manager) + : asset_manager_(std::move(asset_manager)) {} AssetFontSelector::~AssetFontSelector() {} @@ -118,12 +106,9 @@ AssetFontSelector::FlutterFontAttributes::~FlutterFontAttributes() {} void AssetFontSelector::parseFontManifest() { std::vector font_manifest_data; - if (!asset_provider_ || - !asset_provider_->GetAsBuffer(kFontManifestAssetPath, - &font_manifest_data)) { - if (!asset_store_ || - !asset_store_->GetAsBuffer(kFontManifestAssetPath, &font_manifest_data)) - return; + if (!asset_manager_->GetAsBuffer(kFontManifestAssetPath, + &font_manifest_data)) { + return; } rapidjson::Document document; @@ -239,13 +224,8 @@ sk_sp AssetFontSelector::getTypefaceAsset( } std::unique_ptr typeface_asset(new TypefaceAsset); - if (!asset_provider_ || !asset_provider_->GetAsBuffer( - asset_path, &typeface_asset->data)) { - if (!asset_store_ || - !asset_store_->GetAsBuffer(asset_path, &typeface_asset->data)) { - typeface_cache_.insert(std::make_pair(asset_path, nullptr)); - return nullptr; - } + if (!asset_manager_->GetAsBuffer(asset_path, &typeface_asset->data)) { + return nullptr; } sk_sp font_mgr(SkFontMgr::RefDefault()); diff --git a/engine/src/flutter/runtime/asset_font_selector.h b/engine/src/flutter/runtime/asset_font_selector.h index 921c0472db..8d7e946d89 100644 --- a/engine/src/flutter/runtime/asset_font_selector.h +++ b/engine/src/flutter/runtime/asset_font_selector.h @@ -8,7 +8,7 @@ #include #include -#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/assets/asset_manager.h" #include "flutter/assets/zip_asset_store.h" #include "flutter/sky/engine/platform/fonts/FontCacheKey.h" #include "flutter/sky/engine/platform/fonts/FontSelector.h" @@ -24,11 +24,7 @@ class AssetFontSelector : public FontSelector { ~AssetFontSelector() override; - static void Install(fxl::RefPtr asset_provider); - - // TODO(zarah): Remove this and related code using asset_store once flx is - // removed. - static void Install(fxl::RefPtr asset_store); + static void Install(fxl::RefPtr asset_manager); PassRefPtr getFontData(const FontDescription& font_description, const AtomicString& family_name) override; @@ -44,19 +40,14 @@ class AssetFontSelector : public FontSelector { private: struct TypefaceAsset; - explicit AssetFontSelector( - fxl::RefPtr asset_provider); - - explicit AssetFontSelector(fxl::RefPtr asset_store); + explicit AssetFontSelector(fxl::RefPtr asset_manager); void parseFontManifest(); sk_sp getTypefaceAsset(const FontDescription& font_description, const AtomicString& family_name); - fxl::RefPtr asset_provider_; - - fxl::RefPtr asset_store_; + fxl::RefPtr asset_manager_; HashMap> font_family_map_; diff --git a/engine/src/flutter/runtime/dart_controller.cc b/engine/src/flutter/runtime/dart_controller.cc deleted file mode 100644 index 84cfceeaec..0000000000 --- a/engine/src/flutter/runtime/dart_controller.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/runtime/dart_controller.h" -#include "lib/fxl/build_config.h" - -#if defined(OS_WIN) -#include -#undef GetCurrentDirectory -#endif - -#include - -#include "flutter/common/settings.h" -#include "flutter/common/threads.h" -#include "flutter/glue/trace_event.h" -#include "flutter/lib/io/dart_io.h" -#include "flutter/lib/ui/dart_runtime_hooks.h" -#include "flutter/lib/ui/dart_ui.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/runtime/dart_service_isolate.h" -#include "lib/fxl/files/directory.h" -#include "lib/fxl/files/path.h" -#include "lib/tonic/dart_class_library.h" -#include "lib/tonic/dart_message_handler.h" -#include "lib/tonic/dart_state.h" -#include "lib/tonic/dart_wrappable.h" -#include "lib/tonic/file_loader/file_loader.h" -#include "lib/tonic/logging/dart_error.h" -#include "lib/tonic/logging/dart_invoke.h" -#include "lib/tonic/scopes/dart_api_scope.h" -#include "lib/tonic/scopes/dart_isolate_scope.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" - -using tonic::LogIfError; -using tonic::ToDart; - -namespace blink { -namespace { -#if defined(OS_WIN) - -std::string FindAndReplace(const std::string& str, - const std::string& findStr, - const std::string& replaceStr) { - std::string rStr = str; - size_t pos = 0; - while ((pos = rStr.find(findStr, pos)) != std::string::npos) { - rStr.replace(pos, findStr.length(), replaceStr); - pos += replaceStr.length(); - } - return rStr; -} - -std::string SanitizePath(const std::string& path) { - return FindAndReplace(path, "\\\\", "/"); -} - -std::string ResolvePath(std::string path) { - std::string sanitized = SanitizePath(path); - if ((sanitized.length() > 2) && (sanitized[1] == ':')) { - return sanitized; - } - return files::SimplifyPath(files::GetCurrentDirectory() + "/" + sanitized); -} - -#else // defined(OS_WIN) - -std::string SanitizePath(const std::string& path) { - return path; -} - -// TODO(abarth): Consider adding this to //garnet/public/lib/fxl. -std::string ResolvePath(std::string path) { - if (!path.empty() && path[0] == '/') - return path; - return files::SimplifyPath(files::GetCurrentDirectory() + "/" + path); -} - -#endif - -} // namespace - -DartController::DartController() : ui_dart_state_(nullptr) {} - -DartController::~DartController() { - if (ui_dart_state_) { - ui_dart_state_->set_isolate_client(nullptr); - - if (!ui_dart_state_->shutting_down()) { - // Don't use a tonic::DartIsolateScope here since we never exit the - // isolate. - Dart_EnterIsolate(ui_dart_state_->isolate()); - // Clear the message notify callback. - Dart_SetMessageNotifyCallback(nullptr); - Dart_ShutdownIsolate(); - } - } -} - -const std::string DartController::main_entrypoint_ = "main"; - -bool DartController::SendStartMessage(Dart_Handle root_library, - const std::string& entrypoint) { - if (LogIfError(root_library)) - return true; - - { - // Temporarily exit the isolate while we make it runnable. - Dart_Isolate isolate = dart_state()->isolate(); - FXL_DCHECK(Dart_CurrentIsolate() == isolate); - Dart_ExitIsolate(); - Dart_IsolateMakeRunnable(isolate); - Dart_EnterIsolate(isolate); - } - - // In order to support pausing the isolate at start, we indirectly invoke - // main by sending a message to the isolate. - - // Get the closure of main(). - Dart_Handle main_closure = Dart_GetClosure( - root_library, Dart_NewStringFromCString(entrypoint.c_str())); - if (LogIfError(main_closure)) - return true; - - // Grab the 'dart:isolate' library. - Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); - DART_CHECK_VALID(isolate_lib); - - // Send the start message containing the entry point by calling - // _startMainIsolate in dart:isolate. - const intptr_t kNumIsolateArgs = 2; - Dart_Handle isolate_args[kNumIsolateArgs]; - isolate_args[0] = main_closure; - isolate_args[1] = Dart_Null(); - Dart_Handle result = Dart_Invoke(isolate_lib, ToDart("_startMainIsolate"), - kNumIsolateArgs, isolate_args); - return LogIfError(result); -} - -tonic::DartErrorHandleType DartController::RunFromKernel( - const std::vector& kernel, - const std::string& entrypoint) { - tonic::DartState::Scope scope(dart_state()); - tonic::DartErrorHandleType error = tonic::kNoError; - if (Dart_IsNull(Dart_RootLibrary())) { - Dart_Handle result = Dart_LoadScriptFromKernel(kernel.data(), kernel.size()); - LogIfError(result); - error = tonic::GetErrorHandleType(result); - } - if (SendStartMessage(Dart_RootLibrary(), entrypoint)) { - return tonic::kUnknownErrorType; - } - return error; -} - -tonic::DartErrorHandleType DartController::RunFromPrecompiledSnapshot( - const std::string& entrypoint) { - TRACE_EVENT0("flutter", "DartController::RunFromPrecompiledSnapshot"); - FXL_DCHECK(Dart_CurrentIsolate() == nullptr); - tonic::DartState::Scope scope(dart_state()); - if (SendStartMessage(Dart_RootLibrary(), entrypoint)) { - return tonic::kUnknownErrorType; - } - return tonic::kNoError; -} - -tonic::DartErrorHandleType DartController::RunFromScriptSnapshot( - const uint8_t* buffer, - size_t size, - const std::string& entrypoint) { - tonic::DartState::Scope scope(dart_state()); - tonic::DartErrorHandleType error = tonic::kNoError; - if (Dart_IsNull(Dart_RootLibrary())) { - Dart_Handle result = Dart_LoadScriptFromSnapshot(buffer, size); - LogIfError(result); - error = tonic::GetErrorHandleType(result); - } - if (SendStartMessage(Dart_RootLibrary(), entrypoint)) { - return tonic::kUnknownErrorType; - } - return error; -} - -tonic::DartErrorHandleType DartController::RunFromSource( - const std::string& main, - const std::string& packages) { - tonic::DartState::Scope scope(dart_state()); - tonic::DartErrorHandleType error = tonic::kNoError; - if (Dart_IsNull(Dart_RootLibrary())) { - tonic::FileLoader& loader = dart_state()->file_loader(); - if (!packages.empty() && !loader.LoadPackagesMap(ResolvePath(packages))) - FXL_LOG(WARNING) << "Failed to load package map: " << packages; - Dart_Handle result = loader.LoadScript(SanitizePath(main)); - LogIfError(result); - error = tonic::GetErrorHandleType(result); - } - if (SendStartMessage(Dart_RootLibrary())) { - return tonic::kCompilationErrorType; - } - return error; -} - -void DartController::CreateIsolateFor(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - std::unique_ptr state) { - char* error = nullptr; - - void* platform_kernel = GetKernelPlatformBinary(); - - Dart_Isolate isolate; - if (platform_kernel != nullptr) { - isolate = Dart_CreateIsolateFromKernel( - script_uri.c_str(), "main", platform_kernel, nullptr /* flags */, - static_cast(state.get()), &error); - } else { - isolate = - Dart_CreateIsolate(script_uri.c_str(), "main", isolate_snapshot_data, - isolate_snapshot_instr, nullptr, - static_cast(state.get()), &error); - } - FXL_CHECK(isolate) << error; - ui_dart_state_ = state.release(); - ui_dart_state_->set_is_controller_state(true); - dart_state()->message_handler().Initialize(blink::Threads::UI()); - - Dart_SetShouldPauseOnStart(Settings::Get().start_paused); - - ui_dart_state_->set_debug_name_prefix(script_uri); - ui_dart_state_->SetIsolate(isolate); - FXL_CHECK(!LogIfError( - Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); - - { - tonic::DartApiScope dart_api_scope; - DartIO::InitForIsolate(); - DartUI::InitForIsolate(); - DartRuntimeHooks::Install(DartRuntimeHooks::MainIsolate, script_uri); - - std::unique_ptr ui_class_provider( - new tonic::DartClassProvider(dart_state(), "dart:ui")); - dart_state()->class_library().add_provider("ui", - std::move(ui_class_provider)); - } - Dart_ExitIsolate(); -} - -} // namespace blink diff --git a/engine/src/flutter/runtime/dart_controller.h b/engine/src/flutter/runtime/dart_controller.h deleted file mode 100644 index 4b11a8660b..0000000000 --- a/engine/src/flutter/runtime/dart_controller.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_RUNTIME_DART_CONTROLLER_H_ -#define FLUTTER_RUNTIME_DART_CONTROLLER_H_ - -#include -#include - -#include "lib/fxl/macros.h" -#include "lib/tonic/logging/dart_error.h" -#include "third_party/dart/runtime/include/dart_api.h" - -namespace blink { -class UIDartState; - -class DartController { - public: - DartController(); - ~DartController(); - - tonic::DartErrorHandleType RunFromKernel( - const std::vector& kernel, - const std::string& entrypoint = main_entrypoint_); - tonic::DartErrorHandleType RunFromPrecompiledSnapshot( - const std::string& entrypoint = main_entrypoint_); - tonic::DartErrorHandleType RunFromScriptSnapshot( - const uint8_t* buffer, - size_t size, - const std::string& entrypoint = main_entrypoint_); - tonic::DartErrorHandleType RunFromSource(const std::string& main, - const std::string& packages); - - void CreateIsolateFor(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - std::unique_ptr ui_dart_state); - - UIDartState* dart_state() const { return ui_dart_state_; } - - private: - bool SendStartMessage(Dart_Handle root_library, - const std::string& entrypoint = main_entrypoint_); - - static const std::string main_entrypoint_; - - // The DartState associated with the main isolate. - UIDartState* ui_dart_state_; - - FXL_DISALLOW_COPY_AND_ASSIGN(DartController); -}; -} // namespace blink - -#endif // FLUTTER_RUNTIME_DART_CONTROLLER_H_ diff --git a/engine/src/flutter/runtime/dart_init.cc b/engine/src/flutter/runtime/dart_init.cc deleted file mode 100644 index e3d88322a4..0000000000 --- a/engine/src/flutter/runtime/dart_init.cc +++ /dev/null @@ -1,725 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/runtime/dart_init.h" -#include "flutter/sky/engine/wtf/OperatingSystem.h" - -#include -#include -#include - -#if defined(OS_WIN) -#include -#include -#undef ERROR - -#define access _access -#define R_OK 0x4 - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif - -#else -#include -#endif - -#include -#include -#include -#include - -#include "flutter/assets/directory_asset_bundle.h" -#include "flutter/assets/unzipper_provider.h" -#include "flutter/assets/zip_asset_store.h" -#include "flutter/common/settings.h" -#include "flutter/glue/trace_event.h" -#include "flutter/lib/io/dart_io.h" -#include "flutter/lib/ui/dart_runtime_hooks.h" -#include "flutter/lib/ui/dart_ui.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/window.h" -#include "flutter/runtime/dart_service_isolate.h" -#include "flutter/runtime/start_up.h" -#include "lib/fxl/arraysize.h" -#include "lib/fxl/build_config.h" -#include "lib/fxl/files/path.h" -#include "lib/fxl/files/file.h" -#include "lib/fxl/logging.h" -#include "lib/fxl/time/time_delta.h" -#include "lib/tonic/converter/dart_converter.h" -#include "lib/tonic/dart_class_library.h" -#include "lib/tonic/dart_state.h" -#include "lib/tonic/dart_sticky_error.h" -#include "lib/tonic/dart_wrappable.h" -#include "lib/tonic/file_loader/file_loader.h" -#include "lib/tonic/logging/dart_error.h" -#include "lib/tonic/logging/dart_invoke.h" -#include "lib/tonic/scopes/dart_api_scope.h" -#include "lib/tonic/scopes/dart_isolate_scope.h" -#include "lib/tonic/typed_data/uint8_list.h" -#include "third_party/dart/runtime/bin/embedded_dart_io.h" -#include "third_party/dart/runtime/include/dart_mirrors_api.h" - -using tonic::DartClassProvider; -using tonic::LogIfError; -using tonic::ToDart; - -namespace dart { -namespace observatory { - -#if !OS(FUCHSIA) && FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - -// These two symbols are defined in |observatory_archive.cc| which is generated -// by the |//third_party/dart/runtime/observatory:archive_observatory| rule. -// Both of these symbols will be part of the data segment and therefore are read -// only. -extern unsigned int observatory_assets_archive_len; -extern const uint8_t* observatory_assets_archive; - -#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - -} // namespace observatory -} // namespace dart - -namespace blink { - -const char kKernelAssetKey[] = "kernel_blob.bin"; -const char kSnapshotAssetKey[] = "snapshot_blob.bin"; -const char kPlatformKernelAssetKey[] = "platform.dill"; - -namespace { - -// Arguments passed to the Dart VM in all configurations. -static const char* kDartLanguageArgs[] = { - "--enable_mirrors=false", "--background_compilation", "--await_is_keyword", - "--causal_async_stacks", "--limit-ints-to-64-bits", -}; - -static const char* kDartPrecompilationArgs[] = { - "--precompilation", -}; - -static const char* kDartWriteProtectCodeArgs[] FXL_ALLOW_UNUSED_TYPE = { - "--no_write_protect_code", -}; - -static const char* kDartAssertArgs[] = { - // clang-format off - "--enable_asserts", - // clang-format on -}; - -static const char* kDartCheckedModeArgs[] = { - // clang-format off - "--enable_type_checks", - "--error_on_bad_type", - "--error_on_bad_override", - // clang-format on -}; - -static const char* kDartStrongModeArgs[] = { - // clang-format off - "--limit_ints_to_64_bits", - "--reify_generic_functions", - "--strong", - "--sync_async", - // clang-format on -}; - -static const char* kDartStartPausedArgs[]{ - "--pause_isolates_on_start", -}; - -static const char* kDartTraceStartupArgs[]{ - "--timeline_streams=Compiler,Dart,Embedder,GC", -}; - -static const char* kDartEndlessTraceBufferArgs[]{ - "--timeline_recorder=endless", -}; - -static const char* kDartFuchsiaTraceArgs[] FXL_ALLOW_UNUSED_TYPE = { - "--systrace_timeline", - "--timeline_streams=VM,Isolate,Compiler,Dart,GC", -}; - -constexpr char kFileUriPrefix[] = "file://"; -constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1; - -static const uint8_t* g_default_isolate_snapshot_data = nullptr; -static const uint8_t* g_default_isolate_snapshot_instructions = nullptr; -static bool g_service_isolate_initialized = false; -static ServiceIsolateHook g_service_isolate_hook = nullptr; -static RegisterNativeServiceProtocolExtensionHook - g_register_native_service_protocol_extensions_hook = nullptr; - -// Kernel representation of core dart libraries(loaded from platform.dill). -// TODO(aam): This (and platform_data below) have to be released when engine -// gets torn down. At that point we could also call Dart_Cleanup to complete -// Dart VM cleanup. -static void* kernel_platform = nullptr; -// Bytes actually read from platform.dill that are referenced by kernel_platform -static std::vector platform_data; - -void IsolateShutdownCallback(void* callback_data) { - if (tonic::DartStickyError::IsSet()) { - tonic::DartApiScope api_scope; - FXL_LOG(ERROR) << "Isolate " << tonic::StdStringFromDart(Dart_DebugName()) - << " exited with an error"; - Dart_Handle sticky_error = Dart_GetStickyError(); - FXL_CHECK(LogIfError(sticky_error)); - } - - UIDartState* dart_state = static_cast(callback_data); - // If the isolate that's shutting down is the main one, tell the higher layers - // of the stack. - if ((dart_state != NULL) && dart_state->is_controller_state()) { - dart_state->set_shutting_down(true); - if (dart_state->isolate_client()) { - dart_state->isolate_client()->DidShutdownMainIsolate(); - } - } -} - -// The cleanup callback frees the DartState object. -void IsolateCleanupCallback(void* callback_data) { - UIDartState* dart_state = static_cast(callback_data); - delete dart_state; -} - -bool DartFileModifiedCallback(const char* source_url, int64_t since_ms) { - if (strncmp(source_url, kFileUriPrefix, kFileUriPrefixLength) != 0u) { - // Assume modified. - return true; - } - - const char* path = source_url + kFileUriPrefixLength; - struct stat info; - if (stat(path, &info) < 0) - return true; - - // If st_mtime is zero, it's more likely that the file system doesn't support - // mtime than that the file was actually modified in the 1970s. - if (!info.st_mtime) - return true; - - // It's very unclear what time bases we're with here. The Dart API doesn't - // document the time base for since_ms. Reading the code, the value varies by - // platform, with a typical source being something like gettimeofday. - // - // We add one to st_mtime because st_mtime has less precision than since_ms - // and we want to treat the file as modified if the since time is between - // ticks of the mtime. - fxl::TimeDelta mtime = fxl::TimeDelta::FromSeconds(info.st_mtime + 1); - fxl::TimeDelta since = fxl::TimeDelta::FromMilliseconds(since_ms); - - return mtime > since; -} - -void ThreadExitCallback() {} - -bool IsServiceIsolateURL(const char* url_name) { - return url_name != nullptr && - std::string(url_name) == DART_VM_SERVICE_ISOLATE_NAME; -} - -static bool StringEndsWith(const std::string& string, - const std::string& ending) { - if (ending.size() > string.size()) - return false; - - return string.compare(string.size() - ending.size(), ending.size(), ending) == - 0; -} - -static void ReleaseFetchedBytes(uint8_t* buffer) { - free(buffer); -} - -Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, - Dart_IsolateFlags* flags, - char** error) { -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - // No VM-service in release mode. - return nullptr; -#else // FLUTTER_RUNTIME_MODE - UIDartState* dart_state = new UIDartState(nullptr, nullptr); - - bool is_running_from_kernel = GetKernelPlatformBinary() != nullptr; - - flags->load_vmservice_library = true; - Dart_Isolate isolate = - is_running_from_kernel - ? Dart_CreateIsolateFromKernel( - script_uri, "main", kernel_platform, flags, - static_cast(dart_state), error) - : Dart_CreateIsolate( - script_uri, "main", g_default_isolate_snapshot_data, - g_default_isolate_snapshot_instructions, flags, - static_cast(dart_state), error); - - FXL_CHECK(isolate) << error; - dart_state->set_debug_name_prefix(script_uri); - dart_state->SetIsolate(isolate); - FXL_CHECK(Dart_IsServiceIsolate(isolate)); - FXL_CHECK(!LogIfError( - Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); - { - tonic::DartApiScope dart_api_scope; - DartIO::InitForIsolate(); - DartUI::InitForIsolate(); - DartRuntimeHooks::Install(DartRuntimeHooks::SecondaryIsolate, script_uri); - const Settings& settings = Settings::Get(); - if (settings.enable_observatory) { - std::string ip = settings.ipv6 ? "::1" : "127.0.0.1"; - const intptr_t port = settings.observatory_port; - const bool disable_websocket_origin_check = false; - const bool service_isolate_booted = DartServiceIsolate::Startup( - ip, port, tonic::DartState::HandleLibraryTag, - !IsRunningPrecompiledCode() && !is_running_from_kernel, - disable_websocket_origin_check, error); - FXL_CHECK(service_isolate_booted) << error; - } - - if (g_service_isolate_hook) - g_service_isolate_hook(IsRunningPrecompiledCode()); - } - Dart_ExitIsolate(); - - g_service_isolate_initialized = true; - // Register any native service protocol extensions. - if (g_register_native_service_protocol_extensions_hook) { - g_register_native_service_protocol_extensions_hook( - IsRunningPrecompiledCode()); - } - return isolate; -#endif // FLUTTER_RUNTIME_MODE -} - -static bool GetAssetAsBuffer( - const std::string& name, - std::vector* data, - fxl::RefPtr& directory_asset_bundle, - fxl::RefPtr& asset_store) { - return (directory_asset_bundle && - directory_asset_bundle->GetAsBuffer(name, data)) || - (asset_store && asset_store->GetAsBuffer(name, data)); -} - -Dart_Isolate IsolateCreateCallback(const char* script_uri, - const char* main, - const char* package_root, - const char* package_config, - Dart_IsolateFlags* flags, - void* callback_data, - char** error) { - TRACE_EVENT0("flutter", __func__); - - if (IsServiceIsolateURL(script_uri)) { - return ServiceIsolateCreateCallback(script_uri, flags, error); - } - - std::string entry_uri = script_uri; - // Are we running from a Dart source file? - const bool running_from_source = StringEndsWith(entry_uri, ".dart"); - - std::vector kernel_data; - std::vector snapshot_data; - std::string entry_path; - if (!IsRunningPrecompiledCode()) { - // Check that the entry script URI starts with file:// - if (entry_uri.find(kFileUriPrefix) != 0u) { - *error = strdup("Isolates must use file:// URIs"); - return nullptr; - } - // Entry script path (file:// is stripped). - entry_path = std::string(script_uri + strlen(kFileUriPrefix)); - if (StringEndsWith(entry_path, ".dill")) { - // Load the kernel from the script URI. - if (!files::ReadFileToVector(entry_path, &kernel_data)) { - FXL_LOG(ERROR) << "Failed to load kernel"; - } - } else if (!running_from_source) { - // Attempt to copy the snapshot or kernel from the asset bundle. - const std::string& bundle_path = entry_path; - - struct stat stat_result = {}; - if (::stat(bundle_path.c_str(), &stat_result) == 0) { - fxl::RefPtr directory_asset_bundle; - // TODO(zarah): Remove usage of zip_asset_store once app.flx is removed. - fxl::RefPtr zip_asset_store; - // bundle_path is either the path to app.flx or the flutter assets - // directory. - std::string flx_path = bundle_path; - if (S_ISDIR(stat_result.st_mode)) { - directory_asset_bundle = - fxl::MakeRefCounted(bundle_path); - flx_path = files::GetDirectoryName(bundle_path) + "/app.flx"; - } - - if (access(flx_path.c_str(), R_OK) == 0) { - zip_asset_store = fxl::MakeRefCounted( - GetUnzipperProviderForPath(flx_path)); - } - GetAssetAsBuffer(kKernelAssetKey, &kernel_data, directory_asset_bundle, - zip_asset_store); - GetAssetAsBuffer(kSnapshotAssetKey, &snapshot_data, - directory_asset_bundle, zip_asset_store); - } - } - } - - UIDartState* parent_dart_state = static_cast(callback_data); - UIDartState* dart_state = parent_dart_state->CreateForChildIsolate(); - - Dart_Isolate isolate = - kernel_platform != nullptr - ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_platform, - nullptr /* flags */, dart_state, error) - : Dart_CreateIsolate(script_uri, main, - g_default_isolate_snapshot_data, - g_default_isolate_snapshot_instructions, nullptr, - dart_state, error); - FXL_CHECK(isolate) << error; - dart_state->set_debug_name_prefix(script_uri); - dart_state->SetIsolate(isolate); - FXL_CHECK(!LogIfError( - Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); - - { - tonic::DartApiScope dart_api_scope; - DartIO::InitForIsolate(); - DartUI::InitForIsolate(); - DartRuntimeHooks::Install(DartRuntimeHooks::SecondaryIsolate, script_uri); - - std::unique_ptr ui_class_provider( - new DartClassProvider(dart_state, "dart:ui")); - dart_state->class_library().add_provider("ui", - std::move(ui_class_provider)); - - if (!kernel_data.empty()) { - // We are running kernel code. - FXL_CHECK(!LogIfError(Dart_LoadScriptFromKernel(kernel_data.data(), - kernel_data.size()))); - } else if (!snapshot_data.empty()) { - // We are running from a script snapshot. - FXL_CHECK(!LogIfError(Dart_LoadScriptFromSnapshot(snapshot_data.data(), - snapshot_data.size()))); - } else if (running_from_source) { - // We are running from source. - // Forward the .packages configuration from the parent isolate to the - // child isolate. - tonic::FileLoader& parent_loader = parent_dart_state->file_loader(); - const std::string& packages = parent_loader.packages(); - tonic::FileLoader& loader = dart_state->file_loader(); - if (!packages.empty() && !loader.LoadPackagesMap(packages)) { - FXL_LOG(WARNING) << "Failed to load package map: " << packages; - } - // Load the script. - FXL_CHECK(!LogIfError(loader.LoadScript(entry_path))); - } - - dart_state->isolate_client()->DidCreateSecondaryIsolate(isolate); - } - - Dart_ExitIsolate(); - - FXL_CHECK(Dart_IsolateMakeRunnable(isolate)); - return isolate; -} - -Dart_Handle GetVMServiceAssetsArchiveCallback() { -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - return nullptr; -#elif OS(FUCHSIA) - std::vector observatory_assets_archive; - if (!files::ReadFileToVector("pkg/data/observatory.tar", - &observatory_assets_archive)) { - FXL_LOG(ERROR) << "Fail to load Observatory archive"; - return nullptr; - } - return tonic::DartConverter::ToDart( - observatory_assets_archive.data(), - observatory_assets_archive.size()); -#else - return tonic::DartConverter::ToDart( - ::dart::observatory::observatory_assets_archive, - ::dart::observatory::observatory_assets_archive_len); -#endif -} - -static const char kStdoutStreamId[] = "Stdout"; -static const char kStderrStreamId[] = "Stderr"; - -static bool ServiceStreamListenCallback(const char* stream_id) { - if (strcmp(stream_id, kStdoutStreamId) == 0) { - dart::bin::SetCaptureStdout(true); - return true; - } else if (strcmp(stream_id, kStderrStreamId) == 0) { - dart::bin::SetCaptureStderr(true); - return true; - } - return false; -} - -static void ServiceStreamCancelCallback(const char* stream_id) { - if (strcmp(stream_id, kStdoutStreamId) == 0) { - dart::bin::SetCaptureStdout(false); - } else if (strcmp(stream_id, kStderrStreamId) == 0) { - dart::bin::SetCaptureStderr(false); - } -} - -} // namespace - -bool IsRunningPrecompiledCode() { - return Dart_IsPrecompiledRuntime(); -} - -EmbedderTracingCallbacks* g_tracing_callbacks = nullptr; - -EmbedderTracingCallbacks::EmbedderTracingCallbacks( - EmbedderTracingCallback start, - EmbedderTracingCallback stop) - : start_tracing_callback(start), stop_tracing_callback(stop) {} - -void SetEmbedderTracingCallbacks( - std::unique_ptr callbacks) { - g_tracing_callbacks = callbacks.release(); -} - -static void EmbedderTimelineStartRecording() { - if (g_tracing_callbacks) - g_tracing_callbacks->start_tracing_callback(); -} - -static void EmbedderTimelineStopRecording() { - if (g_tracing_callbacks) - g_tracing_callbacks->stop_tracing_callback(); -} - -static std::vector ProfilingFlags(bool enable_profiling) { -// Disable Dart's built in profiler when building a debug build. This -// works around a race condition that would sometimes stop a crash's -// stack trace from being printed on Android. -#ifndef NDEBUG - enable_profiling = false; -#endif - - // We want to disable profiling by default because it overwhelms LLDB. But - // the VM enables the same by default. In either case, we have some profiling - // flags. - if (enable_profiling) { - return { - // This is the default. But just be explicit. - "--profiler", - // This instructs the profiler to walk C++ frames, and to include - // them in the profile. - "--profile-vm"}; - } else { - return {"--no-profiler"}; - } -} - -void SetServiceIsolateHook(ServiceIsolateHook hook) { - FXL_CHECK(!g_service_isolate_initialized); - g_service_isolate_hook = hook; -} - -void SetRegisterNativeServiceProtocolExtensionHook( - RegisterNativeServiceProtocolExtensionHook hook) { - FXL_CHECK(!g_service_isolate_initialized); - g_register_native_service_protocol_extensions_hook = hook; -} - -void PushBackAll(std::vector* args, - const char** argv, - size_t argc) { - for (size_t i = 0; i < argc; ++i) { - args->push_back(argv[i]); - } -} - -static void EmbedderInformationCallback(Dart_EmbedderInformation* info) { - info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION; - dart::bin::GetIOEmbedderInformation(info); - info->name = "Flutter"; -} - -void* GetKernelPlatformBinary() { - return kernel_platform; -} - -void InitDartVM(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path) { - TRACE_EVENT0("flutter", __func__); - - g_default_isolate_snapshot_data = default_isolate_snapshot_data; - g_default_isolate_snapshot_instructions = - default_isolate_snapshot_instructions; - - const Settings& settings = Settings::Get(); - - { - TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo"); - dart::bin::BootstrapDartIo(); - - if (!settings.temp_directory_path.empty()) { - dart::bin::SetSystemTempDirectory(settings.temp_directory_path.c_str()); - } - } - - std::vector args; - - // Instruct the VM to ignore unrecognized flags. - // There is a lot of diversity in a lot of combinations when it - // comes to the arguments the VM supports. And, if the VM comes across a flag - // it does not recognize, it exits immediately. - args.push_back("--ignore-unrecognized-flags"); - - for (const auto& profiler_flag : - ProfilingFlags(settings.enable_dart_profiling)) { - args.push_back(profiler_flag); - } - - PushBackAll(&args, kDartLanguageArgs, arraysize(kDartLanguageArgs)); - - if (IsRunningPrecompiledCode()) { - PushBackAll(&args, kDartPrecompilationArgs, - arraysize(kDartPrecompilationArgs)); - } - -#if defined(OS_FUCHSIA) -#if defined(NDEBUG) - // Do not enable checked mode for Fuchsia release builds - // TODO(mikejurka): remove this once precompiled code is working on Fuchsia - const bool use_checked_mode = false; -#else // !defined(NDEBUG) - const bool use_checked_mode = true; -#endif // !defined(NDEBUG) -#else // !defined(OS_FUCHSIA) - // Enable checked mode if we are not running precompiled code. We run non- - // precompiled code only in the debug product mode. - const bool use_checked_mode = - !IsRunningPrecompiledCode() && !settings.dart_non_checked_mode; -#endif // !defined(OS_FUCHSIA) - -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG - // Debug mode uses the JIT, disable code page write protection to avoid - // memory page protection changes before and after every compilation. - PushBackAll(&args, kDartWriteProtectCodeArgs, - arraysize(kDartWriteProtectCodeArgs)); -#endif - - if (settings.start_paused) - PushBackAll(&args, kDartStartPausedArgs, arraysize(kDartStartPausedArgs)); - - if (settings.endless_trace_buffer || settings.trace_startup) { - // If we are tracing startup, make sure the trace buffer is endless so we - // don't lose early traces. - PushBackAll(&args, kDartEndlessTraceBufferArgs, - arraysize(kDartEndlessTraceBufferArgs)); - } - - if (settings.trace_startup) { - PushBackAll(&args, kDartTraceStartupArgs, arraysize(kDartTraceStartupArgs)); - } - -#if defined(OS_FUCHSIA) - PushBackAll(&args, kDartFuchsiaTraceArgs, arraysize(kDartFuchsiaTraceArgs)); -#endif - - if (!bundle_path.empty()) { - fxl::RefPtr directory_asset_bundle = - fxl::MakeRefCounted( - std::move(bundle_path)); - directory_asset_bundle->GetAsBuffer(kPlatformKernelAssetKey, - &platform_data); - if (!platform_data.empty()) { - uint8_t* kernel_buf = static_cast(malloc(platform_data.size())); - memcpy(kernel_buf, platform_data.data(), platform_data.size()); - kernel_platform = Dart_ReadKernelBinary(kernel_buf, platform_data.size(), - ReleaseFetchedBytes); - FXL_DCHECK(kernel_platform != nullptr); - } - } - if ((kernel_platform != nullptr) || - Dart_IsDart2Snapshot(g_default_isolate_snapshot_data)) { - // The presence of the kernel platform file or a snapshot that was generated - // for Dart2 indicates we are running in preview-dart-2 mode and in this - // mode enable strong mode options by default. - // Note: When we start using core snapshots instead of the platform file - // in the engine just sniffing the snapshot file should be sufficient. - PushBackAll(&args, kDartStrongModeArgs, arraysize(kDartStrongModeArgs)); - // In addition if we are running in debug mode we also enable asserts. - if (use_checked_mode) { - PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); - } - } else if (use_checked_mode) { - // In non preview-dart-2 mode we enable checked mode and asserts if - // we are running in debug mode. - PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); - PushBackAll(&args, kDartCheckedModeArgs, arraysize(kDartCheckedModeArgs)); - } - - for (size_t i = 0; i < settings.dart_flags.size(); i++) - args.push_back(settings.dart_flags[i].c_str()); - - FXL_CHECK(Dart_SetVMFlags(args.size(), args.data())); - - DartUI::InitForGlobal(); - - // Setup embedder tracing hooks. To avoid data races, it is recommended that - // these hooks be installed before the DartInitialize, so do that setup now. - Dart_SetEmbedderTimelineCallbacks(&EmbedderTimelineStartRecording, - &EmbedderTimelineStopRecording); - - Dart_SetFileModifiedCallback(&DartFileModifiedCallback); - - { - TRACE_EVENT0("flutter", "Dart_Initialize"); - Dart_InitializeParams params = {}; - params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; - params.vm_snapshot_data = vm_snapshot_data; - params.vm_snapshot_instructions = vm_snapshot_instructions; - params.create = IsolateCreateCallback; - params.shutdown = IsolateShutdownCallback; - params.cleanup = IsolateCleanupCallback; - params.thread_exit = ThreadExitCallback; - params.get_service_assets = GetVMServiceAssetsArchiveCallback; - params.entropy_source = DartIO::EntropySource; - char* init_error = Dart_Initialize(¶ms); - if (init_error != nullptr) - FXL_LOG(FATAL) << "Error while initializing the Dart VM: " << init_error; - free(init_error); - - // Send the earliest available timestamp in the application lifecycle to - // timeline. The difference between this timestamp and the time we render - // the very first frame gives us a good idea about Flutter's startup time. - // Use a duration event so about:tracing will consider this event when - // deciding the earliest event to use as time 0. - if (blink::engine_main_enter_ts != 0) { - Dart_TimelineEvent("FlutterEngineMainEnter", // label - blink::engine_main_enter_ts, // timestamp0 - blink::engine_main_enter_ts, // timestamp1_or_async_id - Dart_Timeline_Event_Duration, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values - ); - } - } - - // Allow streaming of stdout and stderr by the Dart vm. - Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback, - &ServiceStreamCancelCallback); - - Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); -} - -} // namespace blink diff --git a/engine/src/flutter/runtime/dart_init.h b/engine/src/flutter/runtime/dart_init.h deleted file mode 100644 index 99c8fe8913..0000000000 --- a/engine/src/flutter/runtime/dart_init.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_RUNTIME_DART_INIT_H_ -#define FLUTTER_RUNTIME_DART_INIT_H_ - -#include "lib/fxl/build_config.h" -#include "lib/fxl/functional/closure.h" -#include "third_party/dart/runtime/include/dart_api.h" - -#include -#include -#include - -namespace blink { - -// Name of the kernel blob asset within the asset directory. -extern const char kKernelAssetKey[]; - -// Name of the snapshot blob asset within the asset directory. -extern const char kSnapshotAssetKey[]; - -// Name of the platform kernel blob asset within the asset directory. -extern const char kPlatformKernelAssetKey[]; - -bool IsRunningPrecompiledCode(); - -using EmbedderTracingCallback = fxl::Closure; - -typedef void (*ServiceIsolateHook)(bool); -typedef void (*RegisterNativeServiceProtocolExtensionHook)(bool); - -struct EmbedderTracingCallbacks { - EmbedderTracingCallback start_tracing_callback; - EmbedderTracingCallback stop_tracing_callback; - - EmbedderTracingCallbacks(EmbedderTracingCallback start, - EmbedderTracingCallback stop); -}; - -void InitDartVM(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path); - -void* GetKernelPlatformBinary(); - -void SetEmbedderTracingCallbacks( - std::unique_ptr callbacks); - -// Provide a function that will be called during initialization of the -// service isolate. -void SetServiceIsolateHook(ServiceIsolateHook hook); - -// Provide a function that will be called to register native service protocol -// extensions. -void SetRegisterNativeServiceProtocolExtensionHook( - RegisterNativeServiceProtocolExtensionHook hook); - -} // namespace blink - -#endif // FLUTTER_RUNTIME_DART_INIT_H_ diff --git a/engine/src/flutter/runtime/dart_isolate.cc b/engine/src/flutter/runtime/dart_isolate.cc new file mode 100644 index 0000000000..6d69ab7fc5 --- /dev/null +++ b/engine/src/flutter/runtime/dart_isolate.cc @@ -0,0 +1,690 @@ +// Copyright 2017 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. + +#include "flutter/runtime/dart_isolate.h" + +#include +#include + +#include "flutter/fml/trace_event.h" +#include "flutter/lib/io/dart_io.h" +#include "flutter/lib/ui/dart_runtime_hooks.h" +#include "flutter/lib/ui/dart_ui.h" +#include "flutter/runtime/dart_service_isolate.h" +#include "flutter/runtime/dart_vm.h" +#include "lib/fxl/files/path.h" +#include "lib/tonic/converter/dart_converter.h" +#include "lib/tonic/dart_class_library.h" +#include "lib/tonic/dart_class_provider.h" +#include "lib/tonic/dart_message_handler.h" +#include "lib/tonic/dart_state.h" +#include "lib/tonic/dart_sticky_error.h" +#include "lib/tonic/file_loader/file_loader.h" +#include "lib/tonic/scopes/dart_api_scope.h" +#include "lib/tonic/scopes/dart_isolate_scope.h" +#include "third_party/dart/runtime/include/dart_tools_api.h" + +#ifdef ERROR +#undef ERROR +#endif + +namespace blink { + +fml::WeakPtr DartIsolate::CreateRootIsolate( + const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + std::unique_ptr window, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + Dart_IsolateFlags* flags) { + TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); + Dart_Isolate vm_isolate = nullptr; + fml::WeakPtr embedder_isolate; + + char* error = nullptr; + + // Since this is the root isolate, we fake a parent embedder data object. We + // cannot use unique_ptr here because the destructor is private (since the + // isolate lifecycle is entirely managed by the VM). + auto root_embedder_data = std::make_unique( + vm, // VM + std::move(isolate_snapshot), // isolate snapshot + task_runners, // task runners + std::move(resource_context), // resource context + std::move(unref_queue), // skia unref queue + advisory_script_uri, // advisory URI + advisory_script_entrypoint // advisory entrypoint + ); + + std::tie(vm_isolate, embedder_isolate) = CreateDartVMAndEmbedderObjectPair( + advisory_script_uri.c_str(), // advisory script URI + advisory_script_entrypoint.c_str(), // advisory script entrypoint + nullptr, // package root + nullptr, // package config + flags, // flags + root_embedder_data.get(), // parent embedder data + true, // is root isolate + &error // error (out) + ); + + if (error != nullptr) { + free(error); + } + + if (vm_isolate == nullptr) { + return {}; + } + + if (embedder_isolate) { + // Only root isolates can interact with windows. + embedder_isolate->SetWindow(std::move(window)); + embedder_isolate->set_use_blink(vm->GetSettings().using_blink); + } + + return embedder_isolate; +} + +DartIsolate::DartIsolate(const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint) + : UIDartState(std::move(task_runners), + vm->GetSettings().task_observer_add, + vm->GetSettings().task_observer_remove, + std::move(resource_context), + std::move(unref_queue), + advisory_script_uri, + advisory_script_entrypoint, + vm->GetSettings().log_tag), + vm_(vm), + isolate_snapshot_(std::move(isolate_snapshot)), + weak_factory_(this) { + FXL_DCHECK(isolate_snapshot_) << "Must contain a valid isolate snapshot."; + weak_prototype_ = weak_factory_.GetWeakPtr(); + + if (vm_ == nullptr) { + return; + } + + phase_ = Phase::Uninitialized; +} + +DartIsolate::~DartIsolate() = default; + +DartIsolate::Phase DartIsolate::GetPhase() const { + return phase_; +} + +const DartVM* DartIsolate::GetDartVM() const { + return vm_; +} + +bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { + TRACE_EVENT0("flutter", "DartIsolate::Initialize"); + if (phase_ != Phase::Uninitialized) { + return false; + } + + if (dart_isolate == nullptr) { + return false; + } + + if (Dart_CurrentIsolate() != dart_isolate) { + return false; + } + + if (Dart_IsolateData(dart_isolate) != this) { + return false; + } + + // After this point, isolate scopes can be safely used. + SetIsolate(dart_isolate); + + // We are entering a new scope (for the first time since initialization) and + // we want to restore the current scope to null when we exit out of this + // method. This balances the implicit Dart_EnterIsolate call made by + // Dart_CreateIsolate (which calls the Initialize). + Dart_ExitIsolate(); + + tonic::DartIsolateScope scope(isolate()); + + if (is_root_isolate) { + if (auto task_runner = GetTaskRunners().GetUITaskRunner()) { + // Isolates may not have any particular thread affinity. Only initialize + // the message handler if a task runner is explicitly specified. + message_handler().Initialize(task_runner); + } + } + + if (tonic::LogIfError( + Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))) { + return false; + } + + if (!UpdateThreadPoolNames()) { + return false; + } + + phase_ = Phase::Initialized; + return true; +} + +// Updating thread names here does not change the underlying OS thread names. +// Instead, this is just additional metadata for the Observatory to show the +// thread name of the isolate. +bool DartIsolate::UpdateThreadPoolNames() const { + // TODO(chinmaygarde): This implementation does not account for multiple + // shells sharing the same (or subset of) threads. + const auto& task_runners = GetTaskRunners(); + + if (auto task_runner = task_runners.GetGPUTaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".gpu"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + if (auto task_runner = task_runners.GetUITaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".ui"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + if (auto task_runner = task_runners.GetIOTaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".io"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + if (auto task_runner = task_runners.GetPlatformTaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".platform"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + return true; +} + +bool DartIsolate::LoadLibraries() { + TRACE_EVENT0("flutter", "DartIsolate::LoadLibraries"); + if (phase_ != Phase::Initialized) { + return false; + } + + tonic::DartState::Scope scope(this); + + DartIO::InitForIsolate(); + + DartUI::InitForIsolate(); + + const bool is_service_isolate = Dart_IsServiceIsolate(isolate()); + + DartRuntimeHooks::Install(is_service_isolate + ? DartRuntimeHooks::SecondaryIsolate + : DartRuntimeHooks::MainIsolate, + GetAdvisoryScriptURI()); + + if (!is_service_isolate) { + class_library().add_provider( + "ui", std::make_unique(this, "dart:ui")); + } + + phase_ = Phase::LibrariesSetup; + return true; +} + +bool DartIsolate::PrepareForRunningFromPrecompiledCode() { + TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromPrecompiledCode"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + if (!DartVM::IsRunningPrecompiledCode()) { + return false; + } + + tonic::DartState::Scope scope(this); + + if (Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!MarkIsolateRunnable()) { + return false; + } + + phase_ = Phase::Ready; + return true; +} + +static bool LoadScriptSnapshot(std::unique_ptr mapping) { + if (tonic::LogIfError(Dart_LoadScriptFromSnapshot(mapping->GetMapping(), + mapping->GetSize()))) { + return false; + } + return true; +} + +static bool LoadKernelSnapshot(std::unique_ptr mapping) { + if (tonic::LogIfError(Dart_LoadScriptFromKernel(mapping->GetMapping(), + mapping->GetSize()))) { + return false; + } + + return true; +} + +static bool LoadSnapshot(std::unique_ptr mapping, + bool is_kernel) { + if (is_kernel) { + return LoadKernelSnapshot(std::move(mapping)); + } else { + return LoadScriptSnapshot(std::move(mapping)); + } + return false; +} + +FXL_WARN_UNUSED_RESULT +bool DartIsolate::PrepareForRunningFromSnapshot( + std::unique_ptr mapping) { + TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromSnapshot"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + if (DartVM::IsRunningPrecompiledCode()) { + return false; + } + + if (!mapping || mapping->GetSize() == 0) { + return false; + } + + tonic::DartState::Scope scope(this); + + if (!Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!LoadSnapshot(std::move(mapping), vm_->GetPlatformKernel() != nullptr)) { + return false; + } + + if (Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!MarkIsolateRunnable()) { + return false; + } + + phase_ = Phase::Ready; + return true; +} + +bool DartIsolate::PrepareForRunningFromSource( + const std::string& main_source_file, + const std::string& packages) { + TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromSource"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + if (DartVM::IsRunningPrecompiledCode()) { + return false; + } + + if (main_source_file.empty()) { + return false; + } + + tonic::DartState::Scope scope(this); + + if (!Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + auto& loader = file_loader(); + + if (!packages.empty()) { + auto packages_absolute_path = files::AbsolutePath(packages); + FXL_DLOG(INFO) << "Loading from packages: " << packages_absolute_path; + if (!loader.LoadPackagesMap(packages_absolute_path)) { + return false; + } + } + + auto main_source_absolute_path = files::AbsolutePath(main_source_file); + FXL_DLOG(INFO) << "Loading from source: " << main_source_absolute_path; + + if (tonic::LogIfError(loader.LoadScript(main_source_absolute_path))) { + return false; + } + + if (Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!MarkIsolateRunnable()) { + return false; + } + + phase_ = Phase::Ready; + return true; +} + +bool DartIsolate::MarkIsolateRunnable() { + TRACE_EVENT0("flutter", "DartIsolate::MarkIsolateRunnable"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + // This function may only be called from an active isolate scope. + if (Dart_CurrentIsolate() != isolate()) { + return false; + } + + // There must be no current isolate to mark an isolate as being runnable. + Dart_ExitIsolate(); + + if (!Dart_IsolateMakeRunnable(isolate())) { + // Failed. Restore the isolate. + Dart_EnterIsolate(isolate()); + return false; + } + // Success. Restore the isolate. + Dart_EnterIsolate(isolate()); + return true; +} + +FXL_WARN_UNUSED_RESULT +bool DartIsolate::Run(const std::string& entrypoint_name) { + TRACE_EVENT0("flutter", "DartIsolate::Run"); + if (phase_ != Phase::Ready) { + return false; + } + + tonic::DartState::Scope scope(this); + + Dart_Handle entrypoint = Dart_GetClosure( + Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str())); + if (tonic::LogIfError(entrypoint)) { + return false; + } + + Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate")); + if (tonic::LogIfError(isolate_lib)) { + return false; + } + + Dart_Handle isolate_args[] = { + entrypoint, + Dart_Null(), + }; + + if (tonic::LogIfError(Dart_Invoke( + isolate_lib, tonic::ToDart("_startMainIsolate"), + sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) { + return false; + } + + phase_ = Phase::Running; + return true; +} + +bool DartIsolate::Shutdown() { + TRACE_EVENT0("flutter", "DartIsolate::Shutdown"); + // This call may be re-entrant since Dart_ShutdownIsolate can invoke the + // cleanup callback which deletes the embedder side object of the dart isolate + // (a.k.a. this). + if (phase_ == Phase::Shutdown) { + return false; + } + phase_ = Phase::Shutdown; + Dart_Isolate vm_isolate = isolate(); + // The isolate can be nullptr if this instance is the stub isolate data used + // during root isolate creation. + if (vm_isolate != nullptr) { + // We need to enter the isolate because Dart_ShutdownIsolate does not take + // the isolate to shutdown as a parameter. + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); + Dart_EnterIsolate(vm_isolate); + Dart_ShutdownIsolate(); + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); + } + return true; +} + +static Dart_Isolate DartCreateAndStartServiceIsolate( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + char** error) { + auto vm = DartVM::ForProcessIfInitialized(); + + if (!vm) { + *error = strdup( + "Could not resolve the VM when attempting to create the service " + "isolate."); + return nullptr; + } + + const auto& settings = vm->GetSettings(); + + if (!settings.enable_observatory) { + FXL_DLOG(INFO) << "Observatory is disabled."; + return nullptr; + } + + blink::TaskRunners null_task_runners( + "io.flutter." DART_VM_SERVICE_ISOLATE_NAME, nullptr, nullptr, nullptr, + nullptr); + + flags->load_vmservice_library = true; + + auto service_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + null_task_runners, // task runners + nullptr, // window + {}, // resource context + {}, // unref queue + advisory_script_uri == nullptr ? "" : advisory_script_uri, // script uri + advisory_script_entrypoint == nullptr + ? "" + : advisory_script_entrypoint, // script entrypoint + flags // flags + ); + + if (!service_isolate) { + *error = strdup("Could not create the service isolate."); + FXL_DLOG(ERROR) << *error; + return nullptr; + } + + // The engine never holds a strong reference to the VM service isolate. Since + // we are about to lose our last weak reference to it, start the VM service + // while we have this reference. + + const bool running_from_sources = + !DartVM::IsRunningPrecompiledCode() && vm->GetPlatformKernel() == nullptr; + + tonic::DartState::Scope scope(service_isolate.get()); + if (!DartServiceIsolate::Startup( + settings.ipv6 ? "::1" : "127.0.0.1", // server IP address + settings.observatory_port, // server observatory port + tonic::DartState::HandleLibraryTag, // embedder library tag handler + running_from_sources, // running from source code + false, // disable websocket origin check + error // error (out) + )) { + // Error is populated by call to startup. + FXL_DLOG(ERROR) << *error; + return nullptr; + } + + vm->GetServiceProtocol().ToggleHooks(true); + + return service_isolate->isolate(); +} + +// |Dart_IsolateCreateCallback| +Dart_Isolate DartIsolate::DartIsolateCreateCallback( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* parent_embedder_isolate, + char** error) { + if (parent_embedder_isolate == nullptr && + strcmp(advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { + // The VM attempts to start the VM service for us on |Dart_Initialize|. In + // such a case, the callback data will be null and the script URI will be + // DART_VM_SERVICE_ISOLATE_NAME. In such cases, we just create the service + // isolate like normal but dont hold a reference to it at all. We also start + // this isolate since we will never again reference it from the engine. + return DartCreateAndStartServiceIsolate(advisory_script_uri, // + advisory_script_entrypoint, // + package_root, // + package_config, // + flags, // + error // + ); + } + + return CreateDartVMAndEmbedderObjectPair( + advisory_script_uri, // URI + advisory_script_entrypoint, // entrypoint + package_root, // package root + package_config, // package config + flags, // isolate flags + parent_embedder_isolate, // embedder data + false, // is root isolate + error // error + ) + .first; +} + +std::pair> +DartIsolate::CreateDartVMAndEmbedderObjectPair( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* parent_embedder_isolate, + bool is_root_isolate, + char** error) { + TRACE_EVENT0("flutter", "DartIsolate::CreateDartVMAndEmbedderObjectPair"); + if (parent_embedder_isolate == nullptr || + parent_embedder_isolate->GetDartVM() == nullptr) { + *error = + strdup("Parent isolate did not have embedder specific callback data."); + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + const DartVM* vm = parent_embedder_isolate->GetDartVM(); + + // Create the native object on the embedder side. This object is deleted in + // the cleanup callback. + auto embedder_isolate = std::make_unique( + vm, // + parent_embedder_isolate->GetIsolateSnapshot(), // + parent_embedder_isolate->GetTaskRunners(), // + parent_embedder_isolate->GetResourceContext(), // + parent_embedder_isolate->GetSkiaUnrefQueue(), // + advisory_script_uri, // + advisory_script_entrypoint // + ); + + // Create the Dart VM isolate and give it the embedder object as the baton. + Dart_Isolate isolate = + vm->GetPlatformKernel() != nullptr + ? Dart_CreateIsolateFromKernel(advisory_script_uri, // + advisory_script_entrypoint, // + vm->GetPlatformKernel(), // + flags, // + embedder_isolate.get(), // + error // + ) + : Dart_CreateIsolate(advisory_script_uri, // + advisory_script_entrypoint, // + embedder_isolate->GetIsolateSnapshot() + ->GetData() + ->GetSnapshotPointer(), // + embedder_isolate->GetIsolateSnapshot() + ->GetInstructionsIfPresent(), // + flags, // + embedder_isolate.get(), // + error // + ); + + if (isolate == nullptr) { + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + if (!embedder_isolate->Initialize(isolate, is_root_isolate)) { + *error = strdup("Embedder could not initialize the Dart isolate."); + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + if (!embedder_isolate->LoadLibraries()) { + *error = + strdup("Embedder could not load libraries in the new Dart isolate."); + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + // The ownership of the embedder object is controlled by the Dart VM. So the + // only reference returned to the caller is weak. + return {isolate, embedder_isolate.release()->GetWeakIsolatePtr()}; +} + +// |Dart_IsolateShutdownCallback| +void DartIsolate::DartIsolateShutdownCallback(DartIsolate* embedder_isolate) { + if (!tonic::DartStickyError::IsSet()) { + return; + } + + tonic::DartApiScope api_scope; + FXL_LOG(ERROR) << "Isolate " << tonic::StdStringFromDart(Dart_DebugName()) + << " exited with an error"; + Dart_Handle sticky_error = Dart_GetStickyError(); + FXL_CHECK(tonic::LogIfError(sticky_error)); +} + +// |Dart_IsolateCleanupCallback| +void DartIsolate::DartIsolateCleanupCallback(DartIsolate* embedder_isolate) { + delete embedder_isolate; +} + +fxl::RefPtr DartIsolate::GetIsolateSnapshot() const { + return isolate_snapshot_; +} + +fml::WeakPtr DartIsolate::GetWeakIsolatePtr() const { + return weak_prototype_; +} + +void DartIsolate::AddIsolateShutdownCallback(fxl::Closure closure) { + shutdown_callbacks_.emplace_back( + std::make_unique(std::move(closure))); +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_isolate.h b/engine/src/flutter/runtime/dart_isolate.h new file mode 100644 index 0000000000..13d573ebe3 --- /dev/null +++ b/engine/src/flutter/runtime/dart_isolate.h @@ -0,0 +1,152 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_ISOLATE_H_ +#define FLUTTER_RUNTIME_DART_ISOLATE_H_ + +#include +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/mapping.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/window.h" +#include "flutter/runtime/dart_snapshot.h" +#include "lib/fxl/compiler_specific.h" +#include "lib/fxl/macros.h" +#include "lib/tonic/dart_state.h" +#include "third_party/dart/runtime/include/dart_api.h" + +namespace blink { +class DartVM; + +class DartIsolate : public UIDartState { + public: + enum class Phase { + Unknown, + Uninitialized, + Initialized, + LibrariesSetup, + Ready, + Running, + Shutdown, + }; + + // The root isolate of a Flutter application is special because it gets Window + // bindings. From the VM's perspective, this isolate is not special in any + // way. + static fml::WeakPtr CreateRootIsolate( + const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + std::unique_ptr window, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri = "main.dart", + std::string advisory_script_entrypoint = "main", + Dart_IsolateFlags* flags = nullptr); + + DartIsolate(const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint); + + ~DartIsolate() override; + + Phase GetPhase() const; + + FXL_WARN_UNUSED_RESULT + bool PrepareForRunningFromPrecompiledCode(); + + FXL_WARN_UNUSED_RESULT + bool PrepareForRunningFromSnapshot(std::unique_ptr snapshot); + + FXL_WARN_UNUSED_RESULT + bool PrepareForRunningFromSource(const std::string& main_source_file, + const std::string& packages); + + FXL_WARN_UNUSED_RESULT + bool Run(const std::string& entrypoint); + + FXL_WARN_UNUSED_RESULT + bool Shutdown(); + + void AddIsolateShutdownCallback(fxl::Closure closure); + + const DartVM* GetDartVM() const; + + fxl::RefPtr GetIsolateSnapshot() const; + + fml::WeakPtr GetWeakIsolatePtr() const; + + private: + class AutoFireClosure { + public: + AutoFireClosure(fxl::Closure closure) : closure_(std::move(closure)) {} + ~AutoFireClosure() { + if (closure_) { + closure_(); + } + } + + private: + fxl::Closure closure_; + FXL_DISALLOW_COPY_AND_ASSIGN(AutoFireClosure); + }; + friend class DartVM; + + const DartVM* vm_ = nullptr; + Phase phase_ = Phase::Unknown; + const fxl::RefPtr isolate_snapshot_; + std::vector> shutdown_callbacks_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + FXL_WARN_UNUSED_RESULT + bool Initialize(Dart_Isolate isolate, bool is_root_isolate); + + FXL_WARN_UNUSED_RESULT + bool LoadLibraries(); + + bool UpdateThreadPoolNames() const; + + FXL_WARN_UNUSED_RESULT + bool MarkIsolateRunnable(); + + // |Dart_IsolateCreateCallback| + static Dart_Isolate DartIsolateCreateCallback( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* embedder_isolate, + char** error); + + static std::pair /* embedder */> + CreateDartVMAndEmbedderObjectPair(const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* parent_embedder_isolate, + bool is_root_isolate, + char** error); + + // |Dart_IsolateShutdownCallback| + static void DartIsolateShutdownCallback(DartIsolate* embedder_isolate); + + // |Dart_IsolateCleanupCallback| + static void DartIsolateCleanupCallback(DartIsolate* embedder_isolate); + + FXL_DISALLOW_COPY_AND_ASSIGN(DartIsolate); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_ISOLATE_H_ diff --git a/engine/src/flutter/runtime/dart_isolate_unittests.cc b/engine/src/flutter/runtime/dart_isolate_unittests.cc new file mode 100644 index 0000000000..d8f2e02382 --- /dev/null +++ b/engine/src/flutter/runtime/dart_isolate_unittests.cc @@ -0,0 +1,103 @@ +// Copyright 2017 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. + +#include "flutter/fml/thread.h" +#include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/testing/testing.h" +#include "flutter/testing/thread_test.h" + +#define CURRENT_TEST_NAME \ + std::string { \ + ::testing::UnitTest::GetInstance()->current_test_info()->name() \ + } + +namespace blink { + +using DartIsolateTest = ::testing::ThreadTest; + +TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto root_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // resource context + nullptr // unref qeueue + ); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->Shutdown()); +} + +TEST_F(DartIsolateTest, IsolateCanAssociateSnapshot) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto root_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // resource context + nullptr // unref qeueue + ); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->PrepareForRunningFromSource( + testing::GetFixturesPath() + std::string{"/simple_main.dart"}, "")); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Ready); + ASSERT_TRUE(root_isolate->Shutdown()); +} + +TEST_F(DartIsolateTest, CanResolveAndInvokeMethod) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto root_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // resource context + nullptr // unref qeueue + ); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->PrepareForRunningFromSource( + testing::GetFixturesPath() + std::string{"/simple_main.dart"}, "")); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Ready); + ASSERT_TRUE(root_isolate->Run("simple_main")); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Running); + ASSERT_TRUE(root_isolate->Shutdown()); +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_service_isolate.h b/engine/src/flutter/runtime/dart_service_isolate.h index 9edd663feb..59a02e2e9d 100644 --- a/engine/src/flutter/runtime/dart_service_isolate.h +++ b/engine/src/flutter/runtime/dart_service_isolate.h @@ -13,8 +13,6 @@ namespace blink { class DartServiceIsolate { public: - static bool Bootstrap(); - static bool Startup(std::string server_ip, intptr_t server_port, Dart_LibraryTagHandler embedder_tag_handler, diff --git a/engine/src/flutter/runtime/dart_snapshot.cc b/engine/src/flutter/runtime/dart_snapshot.cc new file mode 100644 index 0000000000..5d759257d1 --- /dev/null +++ b/engine/src/flutter/runtime/dart_snapshot.cc @@ -0,0 +1,159 @@ +// Copyright 2017 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. + +#include "flutter/runtime/dart_snapshot.h" + +#include + +#include "flutter/fml/native_library.h" +#include "flutter/fml/paths.h" +#include "flutter/fml/trace_event.h" +#include "flutter/runtime/dart_snapshot_buffer.h" +#include "flutter/runtime/dart_vm.h" + +namespace blink { + +const char* DartSnapshot::kVMDataSymbol = "kDartVmSnapshotData"; +const char* DartSnapshot::kVMInstructionsSymbol = "kDartVmSnapshotInstructions"; +const char* DartSnapshot::kIsolateDataSymbol = "kDartIsolateSnapshotData"; +const char* DartSnapshot::kIsolateInstructionsSymbol = + "kDartIsolateSnapshotInstructions"; + +static std::unique_ptr ResolveVMData( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = fml::paths::JoinPaths( + {settings.aot_snapshot_path, settings.aot_vm_snapshot_data_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), false /* executable */)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary( + loaded_process, DartSnapshot::kVMDataSymbol); +} + +static std::unique_ptr ResolveVMInstructions( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = fml::paths::JoinPaths( + {settings.aot_snapshot_path, settings.aot_vm_snapshot_instr_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), true /* executable */)) { + return source; + } + } + + if (settings.application_library_path.size() > 0) { + auto library = + fml::NativeLibrary::Create(settings.application_library_path.c_str()); + if (auto source = DartSnapshotBuffer::CreateWithSymbolInLibrary( + library, DartSnapshot::kVMInstructionsSymbol)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary( + loaded_process, DartSnapshot::kVMInstructionsSymbol); +} + +static std::unique_ptr ResolveIsolateData( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = + fml::paths::JoinPaths({settings.aot_snapshot_path, + settings.aot_isolate_snapshot_data_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), false /* executable */)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary( + loaded_process, DartSnapshot::kIsolateDataSymbol); +} + +static std::unique_ptr ResolveIsolateInstructions( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = + fml::paths::JoinPaths({settings.aot_snapshot_path, + settings.aot_isolate_snapshot_instr_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), true /* executable */)) { + return source; + } + } + + if (settings.application_library_path.size() > 0) { + auto library = + fml::NativeLibrary::Create(settings.application_library_path.c_str()); + if (auto source = DartSnapshotBuffer::CreateWithSymbolInLibrary( + library, DartSnapshot::kIsolateInstructionsSymbol)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary( + loaded_process, DartSnapshot::kIsolateInstructionsSymbol); +} + +fxl::RefPtr DartSnapshot::VMSnapshotFromSettings( + const Settings& settings) { + TRACE_EVENT0("flutter", "DartSnapshot::VMSnapshotFromSettings"); + auto snapshot = + fxl::MakeRefCounted(ResolveVMData(settings), // + ResolveVMInstructions(settings) // + ); + if (snapshot->IsValid()) { + return snapshot; + } + return nullptr; +} + +fxl::RefPtr DartSnapshot::IsolateSnapshotFromSettings( + const Settings& settings) { + TRACE_EVENT0("flutter", "DartSnapshot::IsolateSnapshotFromSettings"); + auto snapshot = + fxl::MakeRefCounted(ResolveIsolateData(settings), // + ResolveIsolateInstructions(settings) // + ); + if (snapshot->IsValid()) { + return snapshot; + } + return nullptr; +} + +DartSnapshot::DartSnapshot(std::unique_ptr data, + std::unique_ptr instructions) + : data_(std::move(data)), instructions_(std::move(instructions)) {} + +DartSnapshot::~DartSnapshot() = default; + +bool DartSnapshot::IsValid() const { + return static_cast(data_); +} + +bool DartSnapshot::IsValidForAOT() const { + return data_ && instructions_; +} + +const DartSnapshotBuffer* DartSnapshot::GetData() const { + return data_.get(); +} + +const DartSnapshotBuffer* DartSnapshot::GetInstructions() const { + return instructions_.get(); +} + +const uint8_t* DartSnapshot::GetInstructionsIfPresent() const { + return instructions_ ? instructions_->GetSnapshotPointer() : nullptr; +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_snapshot.h b/engine/src/flutter/runtime/dart_snapshot.h new file mode 100644 index 0000000000..4f04765b4f --- /dev/null +++ b/engine/src/flutter/runtime/dart_snapshot.h @@ -0,0 +1,57 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_SNAPSHOT_H_ +#define FLUTTER_RUNTIME_DART_SNAPSHOT_H_ + +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/runtime/dart_snapshot_buffer.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/ref_counted.h" + +namespace blink { + +class DartSnapshot : public fxl::RefCountedThreadSafe { + public: + static const char* kVMDataSymbol; + static const char* kVMInstructionsSymbol; + static const char* kIsolateDataSymbol; + static const char* kIsolateInstructionsSymbol; + + static fxl::RefPtr VMSnapshotFromSettings( + const Settings& settings); + + static fxl::RefPtr IsolateSnapshotFromSettings( + const Settings& settings); + + bool IsValid() const; + + bool IsValidForAOT() const; + + const DartSnapshotBuffer* GetData() const; + + const DartSnapshotBuffer* GetInstructions() const; + + const uint8_t* GetInstructionsIfPresent() const; + + private: + std::unique_ptr data_; + std::unique_ptr instructions_; + + DartSnapshot(std::unique_ptr data, + std::unique_ptr instructions); + + ~DartSnapshot(); + + FRIEND_REF_COUNTED_THREAD_SAFE(DartSnapshot); + FRIEND_MAKE_REF_COUNTED(DartSnapshot); + FXL_DISALLOW_COPY_AND_ASSIGN(DartSnapshot); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_SNAPSHOT_H_ diff --git a/engine/src/flutter/runtime/dart_snapshot_buffer.cc b/engine/src/flutter/runtime/dart_snapshot_buffer.cc new file mode 100644 index 0000000000..c39233ac83 --- /dev/null +++ b/engine/src/flutter/runtime/dart_snapshot_buffer.cc @@ -0,0 +1,72 @@ +// Copyright 2017 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. + +#include "flutter/runtime/dart_snapshot_buffer.h" + +#include + +#include "flutter/fml/mapping.h" + +namespace blink { + +class NativeLibrarySnapshotBuffer final : public DartSnapshotBuffer { + public: + NativeLibrarySnapshotBuffer(fxl::RefPtr library, + const char* symbol_name) + : library_(std::move(library)) { + if (library_) { + symbol_ = library_->ResolveSymbol(symbol_name); + } + } + + const uint8_t* GetSnapshotPointer() const override { return symbol_; } + + size_t GetSnapshotSize() const override { return 0; } + + private: + fxl::RefPtr library_; + const uint8_t* symbol_ = nullptr; + + FXL_DISALLOW_COPY_AND_ASSIGN(NativeLibrarySnapshotBuffer); +}; + +class FileSnapshotBuffer final : public DartSnapshotBuffer { + public: + FileSnapshotBuffer(const char* path, bool executable) + : mapping_(path, executable) { + if (mapping_.GetSize() > 0) { + symbol_ = mapping_.GetMapping(); + } + } + + const uint8_t* GetSnapshotPointer() const override { return symbol_; } + + size_t GetSnapshotSize() const override { return mapping_.GetSize(); } + + private: + fml::FileMapping mapping_; + const uint8_t* symbol_ = nullptr; + + FXL_DISALLOW_COPY_AND_ASSIGN(FileSnapshotBuffer); +}; + +std::unique_ptr +DartSnapshotBuffer::CreateWithSymbolInLibrary( + fxl::RefPtr library, + const char* symbol_name) { + auto source = std::make_unique( + std::move(library), symbol_name); + return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); +} + +std::unique_ptr +DartSnapshotBuffer::CreateWithContentsOfFile(const char* file_path, + bool executable) { + auto source = std::make_unique(file_path, executable); + return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); +} + +DartSnapshotBuffer::~DartSnapshotBuffer() = default; + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_snapshot_buffer.h b/engine/src/flutter/runtime/dart_snapshot_buffer.h new file mode 100644 index 0000000000..675946c0ca --- /dev/null +++ b/engine/src/flutter/runtime/dart_snapshot_buffer.h @@ -0,0 +1,34 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ +#define FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ + +#include + +#include "flutter/fml/native_library.h" +#include "lib/fxl/macros.h" + +namespace blink { + +class DartSnapshotBuffer { + public: + static std::unique_ptr CreateWithSymbolInLibrary( + fxl::RefPtr library, + const char* symbol_name); + + static std::unique_ptr CreateWithContentsOfFile( + const char* file_path, + bool executable); + + virtual ~DartSnapshotBuffer(); + + virtual const uint8_t* GetSnapshotPointer() const = 0; + + virtual size_t GetSnapshotSize() const = 0; +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ diff --git a/engine/src/flutter/runtime/dart_vm.cc b/engine/src/flutter/runtime/dart_vm.cc new file mode 100644 index 0000000000..bb46779078 --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm.cc @@ -0,0 +1,472 @@ +// Copyright 2017 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. + +#include "flutter/runtime/dart_vm.h" + +#include + +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/trace_event.h" +#include "flutter/lib/io/dart_io.h" +#include "flutter/lib/ui/dart_runtime_hooks.h" +#include "flutter/lib/ui/dart_ui.h" +#include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_service_isolate.h" +#include "flutter/runtime/start_up.h" +#include "lib/fxl/arraysize.h" +#include "lib/fxl/compiler_specific.h" +#include "lib/fxl/files/file.h" +#include "lib/fxl/logging.h" +#include "lib/fxl/time/time_delta.h" +#include "lib/tonic/converter/dart_converter.h" +#include "lib/tonic/dart_class_library.h" +#include "lib/tonic/dart_class_provider.h" +#include "lib/tonic/dart_sticky_error.h" +#include "lib/tonic/file_loader/file_loader.h" +#include "lib/tonic/logging/dart_error.h" +#include "lib/tonic/scopes/dart_api_scope.h" +#include "lib/tonic/typed_data/uint8_list.h" +#include "third_party/dart/runtime/bin/embedded_dart_io.h" + +#ifdef ERROR +#undef ERROR +#endif + +namespace dart { +namespace observatory { + +#if !OS(FUCHSIA) && (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE) + +// These two symbols are defined in |observatory_archive.cc| which is generated +// by the |//third_party/dart/runtime/observatory:archive_observatory| rule. +// Both of these symbols will be part of the data segment and therefore are read +// only. +extern unsigned int observatory_assets_archive_len; +extern const uint8_t* observatory_assets_archive; + +#endif // !OS(FUCHSIA) && (FLUTTER_RUNTIME_MODE != + // FLUTTER_RUNTIME_MODE_RELEASE) + +} // namespace observatory +} // namespace dart + +namespace blink { + +// Arguments passed to the Dart VM in all configurations. +static const char* kDartLanguageArgs[] = { + "--enable_mirrors=false", "--background_compilation", "--await_is_keyword", + "--causal_async_stacks", "--limit-ints-to-64-bits", +}; + +static const char* kDartPrecompilationArgs[] = { + "--precompilation", +}; + +FXL_ALLOW_UNUSED_TYPE +static const char* kDartWriteProtectCodeArgs[] = { + "--no_write_protect_code", +}; + +static const char* kDartAssertArgs[] = { + // clang-format off + "--enable_asserts", + // clang-format on +}; + +static const char* kDartCheckedModeArgs[] = { + // clang-format off + "--enable_type_checks", + "--error_on_bad_type", + "--error_on_bad_override", + // clang-format on +}; + +static const char* kDartStrongModeArgs[] = { + // clang-format off + "--strong", + "--reify_generic_functions", + "--limit_ints_to_64_bits", + "--sync_async", + // clang-format on +}; + +static const char* kDartStartPausedArgs[]{ + "--pause_isolates_on_start", +}; + +static const char* kDartTraceStartupArgs[]{ + "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", +}; + +static const char* kDartEndlessTraceBufferArgs[]{ + "--timeline_recorder=endless", +}; + +static const char* kDartFuchsiaTraceArgs[] FXL_ALLOW_UNUSED_TYPE = { + "--systrace_timeline", + "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", +}; + +constexpr char kFileUriPrefix[] = "file://"; +constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1; + +bool DartFileModifiedCallback(const char* source_url, int64_t since_ms) { + if (strncmp(source_url, kFileUriPrefix, kFileUriPrefixLength) != 0u) { + // Assume modified. + return true; + } + + const char* path = source_url + kFileUriPrefixLength; + struct stat info; + if (stat(path, &info) < 0) + return true; + + // If st_mtime is zero, it's more likely that the file system doesn't support + // mtime than that the file was actually modified in the 1970s. + if (!info.st_mtime) + return true; + + // It's very unclear what time bases we're with here. The Dart API doesn't + // document the time base for since_ms. Reading the code, the value varies by + // platform, with a typical source being something like gettimeofday. + // + // We add one to st_mtime because st_mtime has less precision than since_ms + // and we want to treat the file as modified if the since time is between + // ticks of the mtime. + fxl::TimeDelta mtime = fxl::TimeDelta::FromSeconds(info.st_mtime + 1); + fxl::TimeDelta since = fxl::TimeDelta::FromMilliseconds(since_ms); + + return mtime > since; +} + +void ThreadExitCallback() {} + +Dart_Handle GetVMServiceAssetsArchiveCallback() { +#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE) + return nullptr; +#elif OS(FUCHSIA) + std::vector observatory_assets_archive; + if (!files::ReadFileToVector("pkg/data/observatory.tar", + &observatory_assets_archive)) { + FXL_LOG(ERROR) << "Fail to load Observatory archive"; + return nullptr; + } + return tonic::DartConverter::ToDart( + observatory_assets_archive.data(), observatory_assets_archive.size()); +#else + return tonic::DartConverter::ToDart( + ::dart::observatory::observatory_assets_archive, + ::dart::observatory::observatory_assets_archive_len); +#endif +} + +static const char kStdoutStreamId[] = "Stdout"; +static const char kStderrStreamId[] = "Stderr"; + +static bool ServiceStreamListenCallback(const char* stream_id) { + if (strcmp(stream_id, kStdoutStreamId) == 0) { + dart::bin::SetCaptureStdout(true); + return true; + } else if (strcmp(stream_id, kStderrStreamId) == 0) { + dart::bin::SetCaptureStderr(true); + return true; + } + return false; +} + +static void ServiceStreamCancelCallback(const char* stream_id) { + if (strcmp(stream_id, kStdoutStreamId) == 0) { + dart::bin::SetCaptureStdout(false); + } else if (strcmp(stream_id, kStderrStreamId) == 0) { + dart::bin::SetCaptureStderr(false); + } +} + +bool DartVM::IsRunningPrecompiledCode() { + return Dart_IsPrecompiledRuntime(); +} + +static std::vector ProfilingFlags(bool enable_profiling) { +// Disable Dart's built in profiler when building a debug build. This +// works around a race condition that would sometimes stop a crash's +// stack trace from being printed on Android. +#ifndef NDEBUG + enable_profiling = false; +#endif + + // We want to disable profiling by default because it overwhelms LLDB. But + // the VM enables the same by default. In either case, we have some profiling + // flags. + if (enable_profiling) { + return {// This is the default. But just be explicit. + "--profiler", + // This instructs the profiler to walk C++ frames, and to include + // them in the profile. + "--profile-vm"}; + } else { + return {"--no-profiler"}; + } +} + +void PushBackAll(std::vector* args, + const char** argv, + size_t argc) { + for (size_t i = 0; i < argc; ++i) { + args->push_back(argv[i]); + } +} + +static void EmbedderInformationCallback(Dart_EmbedderInformation* info) { + info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION; + dart::bin::GetIOEmbedderInformation(info); + info->name = "Flutter"; +} + +fxl::RefPtr DartVM::ForProcess(Settings settings) { + return ForProcess(settings, nullptr, nullptr); +} + +static std::once_flag gVMInitialization; +static fxl::RefPtr gVM; + +fxl::RefPtr DartVM::ForProcess( + Settings settings, + fxl::RefPtr vm_snapshot, + fxl::RefPtr isolate_snapshot) { + std::call_once(gVMInitialization, [settings, // + vm_snapshot, // + isolate_snapshot // + ]() mutable { + if (!vm_snapshot) { + vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings); + } + if (!isolate_snapshot) { + isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings); + } + gVM = fxl::MakeRefCounted(settings, // + std::move(vm_snapshot), // + std::move(isolate_snapshot) // + ); + }); + return gVM; +} + +fxl::RefPtr DartVM::ForProcessIfInitialized() { + return gVM; +} + +DartVM::DartVM(const Settings& settings, + fxl::RefPtr vm_snapshot, + fxl::RefPtr isolate_snapshot) + : settings_(settings), + vm_snapshot_(std::move(vm_snapshot)), + isolate_snapshot_(std::move(isolate_snapshot)), + platform_kernel_mapping_( + std::make_unique(settings.kernel_snapshot_path)), + weak_factory_(this) { + TRACE_EVENT0("flutter", "DartVMInitializer"); + FXL_DLOG(INFO) << "Attempting Dart VM launch for mode: " + << (IsRunningPrecompiledCode() ? "AOT" : "Interpreter"); + + FXL_DCHECK(vm_snapshot_ && vm_snapshot_->IsValid()) + << "VM snapshot must be valid."; + + FXL_DCHECK(isolate_snapshot_ && isolate_snapshot_->IsValid()) + << "Isolate snapshot must be valid."; + + if (platform_kernel_mapping_->GetSize() > 0) { + // The platform kernel mapping lifetime is managed by this instance of the + // DartVM and hence will exceed that of the PlatformKernel. So provide an + // empty release callback. + Dart_ReleaseBufferCallback empty = [](auto arg) {}; + platform_kernel_ = reinterpret_cast(Dart_ReadKernelBinary( + platform_kernel_mapping_->GetMapping(), // buffer + platform_kernel_mapping_->GetSize(), // buffer size + empty // buffer deleter + )); + } + + { + TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo"); + dart::bin::BootstrapDartIo(); + + if (!settings.temp_directory_path.empty()) { + dart::bin::SetSystemTempDirectory(settings.temp_directory_path.c_str()); + } + } + + std::vector args; + + // Instruct the VM to ignore unrecognized flags. + // There is a lot of diversity in a lot of combinations when it + // comes to the arguments the VM supports. And, if the VM comes across a flag + // it does not recognize, it exits immediately. + args.push_back("--ignore-unrecognized-flags"); + + for (const auto& profiler_flag : + ProfilingFlags(settings.enable_dart_profiling)) { + args.push_back(profiler_flag); + } + + PushBackAll(&args, kDartLanguageArgs, arraysize(kDartLanguageArgs)); + + if (IsRunningPrecompiledCode()) { + PushBackAll(&args, kDartPrecompilationArgs, + arraysize(kDartPrecompilationArgs)); + } + + // Enable checked mode if we are not running precompiled code. We run non- + // precompiled code only in the debug product mode. + bool use_checked_mode = !settings.dart_non_checked_mode; + +#if !OS(FUCHSIA) + if (IsRunningPrecompiledCode()) { + use_checked_mode = false; + } +#endif // !OS(FUCHSIA) + +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + // Debug mode uses the JIT, disable code page write protection to avoid + // memory page protection changes before and after every compilation. + PushBackAll(&args, kDartWriteProtectCodeArgs, + arraysize(kDartWriteProtectCodeArgs)); +#endif + + const bool isolate_snapshot_is_dart_2 = + Dart_IsDart2Snapshot(isolate_snapshot_->GetData()->GetSnapshotPointer()); + + const bool is_preview_dart2 = + platform_kernel_ != nullptr || isolate_snapshot_is_dart_2; + + if (is_preview_dart2) { + FXL_DLOG(INFO) << "Dart 2 is enabled."; + } else { + FXL_DLOG(INFO) << "Dart 2 is NOT enabled. Platform kernel: " + << static_cast(platform_kernel_) + << " Isolate Snapshot is Dart 2: " + << isolate_snapshot_is_dart_2; + } + if (is_preview_dart2) { + PushBackAll(&args, kDartStrongModeArgs, arraysize(kDartStrongModeArgs)); + if (use_checked_mode) { + PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); + } + } else if (use_checked_mode) { + FXL_DLOG(INFO) << "Checked mode is ON"; + PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); + PushBackAll(&args, kDartCheckedModeArgs, arraysize(kDartCheckedModeArgs)); + } else { + FXL_DLOG(INFO) << "Is not Dart 2 and Checked mode is OFF"; + } + + if (settings.start_paused) { + PushBackAll(&args, kDartStartPausedArgs, arraysize(kDartStartPausedArgs)); + } + + if (settings.endless_trace_buffer || settings.trace_startup) { + // If we are tracing startup, make sure the trace buffer is endless so we + // don't lose early traces. + PushBackAll(&args, kDartEndlessTraceBufferArgs, + arraysize(kDartEndlessTraceBufferArgs)); + } + + if (settings.trace_startup) { + PushBackAll(&args, kDartTraceStartupArgs, arraysize(kDartTraceStartupArgs)); + } + +#if defined(OS_FUCHSIA) + PushBackAll(&args, kDartFuchsiaTraceArgs, arraysize(kDartFuchsiaTraceArgs)); +#endif + + for (size_t i = 0; i < settings.dart_flags.size(); i++) + args.push_back(settings.dart_flags[i].c_str()); + + FXL_CHECK(Dart_SetVMFlags(args.size(), args.data())); + + DartUI::InitForGlobal(); + + { + TRACE_EVENT0("flutter", "Dart_Initialize"); + Dart_InitializeParams params = {}; + params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; + params.vm_snapshot_data = vm_snapshot_->GetData()->GetSnapshotPointer(); + params.vm_snapshot_instructions = vm_snapshot_->GetInstructionsIfPresent(); + params.create = reinterpret_cast( + DartIsolate::DartIsolateCreateCallback); + params.shutdown = reinterpret_cast( + DartIsolate::DartIsolateShutdownCallback); + params.cleanup = reinterpret_cast( + DartIsolate::DartIsolateCleanupCallback); + params.thread_exit = ThreadExitCallback; + params.get_service_assets = GetVMServiceAssetsArchiveCallback; + params.entropy_source = DartIO::EntropySource; + char* init_error = Dart_Initialize(¶ms); + if (init_error) { + FXL_LOG(FATAL) << "Error while initializing the Dart VM: " << init_error; + ::free(init_error); + } + // Send the earliest available timestamp in the application lifecycle to + // timeline. The difference between this timestamp and the time we render + // the very first frame gives us a good idea about Flutter's startup time. + // Use a duration event so about:tracing will consider this event when + // deciding the earliest event to use as time 0. + if (blink::engine_main_enter_ts != 0) { + Dart_TimelineEvent("FlutterEngineMainEnter", // label + blink::engine_main_enter_ts, // timestamp0 + blink::engine_main_enter_ts, // timestamp1_or_async_id + Dart_Timeline_Event_Duration, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); + } + } + + // Allow streaming of stdout and stderr by the Dart vm. + Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback, + &ServiceStreamCancelCallback); + + Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); +} + +DartVM::~DartVM() { + if (Dart_CurrentIsolate() != nullptr) { + Dart_ExitIsolate(); + } + char* result = Dart_Cleanup(); + if (result != nullptr) { + FXL_LOG(ERROR) << "Could not cleanly shut down the Dart VM. Message: \"" + << result << "\"."; + free(result); + } +} + +const Settings& DartVM::GetSettings() const { + return settings_; +} + +DartVM::PlatformKernel* DartVM::GetPlatformKernel() const { + return platform_kernel_; +} + +const DartSnapshot& DartVM::GetVMSnapshot() const { + return *vm_snapshot_.get(); +} + +fxl::RefPtr DartVM::GetIsolateSnapshot() const { + return isolate_snapshot_; +} + +ServiceProtocol& DartVM::GetServiceProtocol() { + return service_protocol_; +} + +fxl::WeakPtr DartVM::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_vm.h b/engine/src/flutter/runtime/dart_vm.h new file mode 100644 index 0000000000..e8feb9ba1d --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm.h @@ -0,0 +1,75 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_VM_H_ +#define FLUTTER_RUNTIME_DART_VM_H_ + +#include +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_snapshot.h" +#include "flutter/runtime/service_protocol.h" +#include "lib/fxl/build_config.h" +#include "lib/fxl/functional/closure.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/ref_counted.h" +#include "lib/fxl/memory/ref_ptr.h" +#include "lib/fxl/memory/weak_ptr.h" +#include "third_party/dart/runtime/include/dart_api.h" + +namespace blink { + +class DartVM : public fxl::RefCountedThreadSafe { + public: + class PlatformKernel; + + static fxl::RefPtr ForProcess(Settings settings); + + static fxl::RefPtr ForProcess( + Settings settings, + fxl::RefPtr vm_snapshot, + fxl::RefPtr isolate_snapshot); + + static fxl::RefPtr ForProcessIfInitialized(); + + static bool IsRunningPrecompiledCode(); + + const Settings& GetSettings() const; + + PlatformKernel* GetPlatformKernel() const; + + const DartSnapshot& GetVMSnapshot() const; + + fxl::RefPtr GetIsolateSnapshot() const; + + fxl::WeakPtr GetWeakPtr(); + + ServiceProtocol& GetServiceProtocol(); + + private: + const Settings settings_; + const fxl::RefPtr vm_snapshot_; + const fxl::RefPtr isolate_snapshot_; + std::unique_ptr platform_kernel_mapping_; + PlatformKernel* platform_kernel_ = nullptr; + ServiceProtocol service_protocol_; + fxl::WeakPtrFactory weak_factory_; + + DartVM(const Settings& settings, + fxl::RefPtr vm_snapshot, + fxl::RefPtr isolate_snapshot); + + ~DartVM(); + + FRIEND_REF_COUNTED_THREAD_SAFE(DartVM); + FRIEND_MAKE_REF_COUNTED(DartVM); + FXL_DISALLOW_COPY_AND_ASSIGN(DartVM); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_VM_H_ diff --git a/engine/src/flutter/runtime/dart_vm_unittests.cc b/engine/src/flutter/runtime/dart_vm_unittests.cc new file mode 100644 index 0000000000..5b2f5e6ee8 --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm_unittests.cc @@ -0,0 +1,21 @@ +// Copyright 2017 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. + +#include "flutter/runtime/dart_vm.h" +#include "gtest/gtest.h" + +namespace blink { + +TEST(DartVM, SimpleInitialization) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + ASSERT_EQ(vm, DartVM::ForProcess(settings)); + ASSERT_FALSE(DartVM::IsRunningPrecompiledCode()); + ASSERT_EQ(vm->GetPlatformKernel(), nullptr); +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/fixtures/simple_main.dart b/engine/src/flutter/runtime/fixtures/simple_main.dart new file mode 100644 index 0000000000..552dfbe344 --- /dev/null +++ b/engine/src/flutter/runtime/fixtures/simple_main.dart @@ -0,0 +1,7 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +void simple_main() { + print("Hello"); +} diff --git a/engine/src/flutter/runtime/runtime_controller.cc b/engine/src/flutter/runtime/runtime_controller.cc index 75796cd686..e7a8ab5253 100644 --- a/engine/src/flutter/runtime/runtime_controller.cc +++ b/engine/src/flutter/runtime/runtime_controller.cc @@ -4,192 +4,248 @@ #include "flutter/runtime/runtime_controller.h" +#include "flutter/fml/message_loop.h" #include "flutter/glue/trace_event.h" #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/window.h" -#include "flutter/runtime/dart_controller.h" #include "flutter/runtime/runtime_delegate.h" #include "lib/tonic/dart_message_handler.h" -using tonic::DartState; +#ifdef ERROR +#undef ERROR +#endif namespace blink { -std::unique_ptr RuntimeController::Create( - RuntimeDelegate* client) { - return std::unique_ptr(new RuntimeController(client)); -} +RuntimeController::RuntimeController( + RuntimeDelegate& p_client, + const DartVM* p_vm, + TaskRunners p_task_runners, + fml::WeakPtr p_resource_context, + fxl::RefPtr p_unref_queue) + : RuntimeController(p_client, + p_vm, + std::move(p_task_runners), + std::move(p_resource_context), + std::move(p_unref_queue), + WindowData{/* default window data */}) {} -RuntimeController::RuntimeController(RuntimeDelegate* client) - : client_(client) {} - -RuntimeController::~RuntimeController() {} - -void RuntimeController::CreateDartController( - const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - int dirfd) { - FXL_DCHECK(!dart_controller_); - - dart_controller_.reset(new DartController()); - dart_controller_->CreateIsolateFor( - script_uri, isolate_snapshot_data, isolate_snapshot_instr, - std::make_unique(this, std::make_unique(this), - dirfd)); - - UIDartState* dart_state = dart_controller_->dart_state(); - DartState::Scope scope(dart_state); - dart_state->window()->DidCreateIsolate(); - client_->DidCreateMainIsolate(dart_state->isolate()); - - Window* window = GetWindow(); - - window->UpdateLocale(language_code_, country_code_); - - if (semantics_enabled_) - window->UpdateSemanticsEnabled(semantics_enabled_); -} - -void RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { - GetWindow()->UpdateWindowMetrics(metrics); -} - -void RuntimeController::SetLocale(const std::string& language_code, - const std::string& country_code) { - if (language_code_ == language_code && country_code_ == country_code) - return; - - language_code_ = language_code; - country_code_ = country_code; - GetWindow()->UpdateLocale(language_code_, country_code_); -} - -void RuntimeController::SetUserSettingsData(const std::string& data) { - if (user_settings_data_ == data) - return; - user_settings_data_ = data; - GetWindow()->UpdateUserSettingsData(user_settings_data_); -} - -void RuntimeController::SetSemanticsEnabled(bool enabled) { - if (semantics_enabled_ == enabled) - return; - semantics_enabled_ = enabled; - GetWindow()->UpdateSemanticsEnabled(semantics_enabled_); -} - -void RuntimeController::BeginFrame(fxl::TimePoint frame_time) { - GetWindow()->BeginFrame(frame_time); -} - -void RuntimeController::NotifyIdle(int64_t deadline) { - UIDartState* dart_state = dart_controller_->dart_state(); - if (!dart_state) { - return; +RuntimeController::RuntimeController( + RuntimeDelegate& p_client, + const DartVM* p_vm, + TaskRunners p_task_runners, + fml::WeakPtr p_resource_context, + fxl::RefPtr p_unref_queue, + WindowData p_window_data) + : client_(p_client), + vm_(p_vm), + task_runners_(p_task_runners), + resource_context_(p_resource_context), + unref_queue_(p_unref_queue), + window_data_(std::move(p_window_data)), + root_isolate_( + DartIsolate::CreateRootIsolate(vm_, + vm_->GetIsolateSnapshot(), + task_runners_, + std::make_unique(this), + resource_context_, + unref_queue_)) { + root_isolate_->SetReturnCodeCallback([this](uint32_t code) { + root_isolate_return_code_ = {true, code}; + }); + if (auto window = GetWindowIfAvailable()) { + tonic::DartState::Scope scope(root_isolate_.get()); + window->DidCreateIsolate(); + if (!FlushRuntimeStateToIsolate()) { + FXL_DLOG(ERROR) << "Could not setup intial isolate state."; + } + } else { + FXL_DCHECK(false) << "RuntimeController created without window binding."; } - DartState::Scope scope(dart_state); + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); +} + +RuntimeController::~RuntimeController() { + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); + if (root_isolate_) { + root_isolate_->SetReturnCodeCallback(nullptr); + auto result = root_isolate_->Shutdown(); + if (!result) { + FXL_DLOG(ERROR) << "Could not shutdown the root isolate."; + } + root_isolate_ = {}; + } +} + +std::unique_ptr RuntimeController::Clone() const { + return std::unique_ptr(new RuntimeController( + client_, // + vm_, // + task_runners_, // + resource_context_, // + unref_queue_, // + window_data_ // + )); +} + +bool RuntimeController::FlushRuntimeStateToIsolate() { + return SetViewportMetrics(window_data_.viewport_metrics) && + SetLocale(window_data_.language_code, window_data_.country_code) && + SetSemanticsEnabled(window_data_.semantics_enabled); +} + +bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { + window_data_.viewport_metrics = metrics; + + if (auto window = GetWindowIfAvailable()) { + window->UpdateWindowMetrics(metrics); + return true; + } + return false; +} + +bool RuntimeController::SetLocale(const std::string& language_code, + const std::string& country_code) { + window_data_.language_code = language_code; + window_data_.country_code = country_code; + + if (auto window = GetWindowIfAvailable()) { + window->UpdateLocale(window_data_.language_code, window_data_.country_code); + return true; + } + + return false; +} + +bool RuntimeController::SetUserSettingsData(const std::string& data) { + window_data_.user_settings_data = data; + + if (auto window = GetWindowIfAvailable()) { + window->UpdateUserSettingsData(window_data_.user_settings_data); + return true; + } + + return false; +} + +bool RuntimeController::SetSemanticsEnabled(bool enabled) { + window_data_.semantics_enabled = enabled; + + if (auto window = GetWindowIfAvailable()) { + window->UpdateSemanticsEnabled(window_data_.semantics_enabled); + return true; + } + + return false; +} + +bool RuntimeController::BeginFrame(fxl::TimePoint frame_time) { + if (auto window = GetWindowIfAvailable()) { + window->BeginFrame(frame_time); + return true; + } + return false; +} + +bool RuntimeController::NotifyIdle(int64_t deadline) { + if (!root_isolate_) { + return false; + } + + tonic::DartState::Scope scope(root_isolate_.get()); Dart_NotifyIdle(deadline); + return true; } -void RuntimeController::DispatchPlatformMessage( +bool RuntimeController::DispatchPlatformMessage( fxl::RefPtr message) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", "mode", - "basic"); - GetWindow()->DispatchPlatformMessage(std::move(message)); + if (auto window = GetWindowIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", + "mode", "basic"); + window->DispatchPlatformMessage(std::move(message)); + return true; + } + return false; } -void RuntimeController::DispatchPointerDataPacket( +bool RuntimeController::DispatchPointerDataPacket( const PointerDataPacket& packet) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", - "mode", "basic"); - GetWindow()->DispatchPointerDataPacket(packet); + if (auto window = GetWindowIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", + "mode", "basic"); + window->DispatchPointerDataPacket(packet); + return true; + } + return false; } -void RuntimeController::DispatchSemanticsAction(int32_t id, +bool RuntimeController::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { TRACE_EVENT1("flutter", "RuntimeController::DispatchSemanticsAction", "mode", "basic"); - GetWindow()->DispatchSemanticsAction(id, action, std::move(args)); + if (auto window = GetWindowIfAvailable()) { + window->DispatchSemanticsAction(id, action, std::move(args)); + return true; + } + return false; } -Window* RuntimeController::GetWindow() { - return dart_controller_->dart_state()->window(); +Window* RuntimeController::GetWindowIfAvailable() { + return root_isolate_ ? root_isolate_->window() : nullptr; } std::string RuntimeController::DefaultRouteName() { - return client_->DefaultRouteName(); + return client_.DefaultRouteName(); } void RuntimeController::ScheduleFrame() { - client_->ScheduleFrame(); + client_.ScheduleFrame(); } void RuntimeController::Render(Scene* scene) { - client_->Render(scene->takeLayerTree()); + client_.Render(scene->takeLayerTree()); } void RuntimeController::UpdateSemantics(SemanticsUpdate* update) { - if (semantics_enabled_) - client_->UpdateSemantics(update->takeNodes()); + if (window_data_.semantics_enabled) { + client_.UpdateSemantics(update->takeNodes()); + } } void RuntimeController::HandlePlatformMessage( fxl::RefPtr message) { - client_->HandlePlatformMessage(std::move(message)); -} - -void RuntimeController::DidCreateSecondaryIsolate(Dart_Isolate isolate) { - client_->DidCreateSecondaryIsolate(isolate); -} - -void RuntimeController::DidShutdownMainIsolate() { - client_->DidShutdownMainIsolate(); + client_.HandlePlatformMessage(std::move(message)); } Dart_Port RuntimeController::GetMainPort() { - if (!dart_controller_) { - return ILLEGAL_PORT; - } - if (!dart_controller_->dart_state()) { - return ILLEGAL_PORT; - } - return dart_controller_->dart_state()->main_port(); + return root_isolate_ ? root_isolate_->main_port() : ILLEGAL_PORT; } std::string RuntimeController::GetIsolateName() { - if (!dart_controller_) { - return ""; - } - if (!dart_controller_->dart_state()) { - return ""; - } - return dart_controller_->dart_state()->debug_name(); + return root_isolate_ ? root_isolate_->debug_name() : ""; } bool RuntimeController::HasLivePorts() { - if (!dart_controller_) { + if (!root_isolate_) { return false; } - UIDartState* dart_state = dart_controller_->dart_state(); - if (!dart_state) { - return false; - } - DartState::Scope scope(dart_state); + tonic::DartState::Scope scope(root_isolate_.get()); return Dart_HasLivePorts(); } tonic::DartErrorHandleType RuntimeController::GetLastError() { - if (!dart_controller_) { - return tonic::kNoError; - } - UIDartState* dart_state = dart_controller_->dart_state(); - if (!dart_state) { - return tonic::kNoError; - } - return dart_state->message_handler().isolate_last_error(); + return root_isolate_ ? root_isolate_->message_handler().isolate_last_error() + : tonic::kNoError; +} + +fml::WeakPtr RuntimeController::GetRootIsolate() { + return root_isolate_; +} + +std::pair RuntimeController::GetRootIsolateReturnCode() { + return root_isolate_return_code_; } } // namespace blink diff --git a/engine/src/flutter/runtime/runtime_controller.h b/engine/src/flutter/runtime/runtime_controller.h index 628bc69939..326c517f31 100644 --- a/engine/src/flutter/runtime/runtime_controller.h +++ b/engine/src/flutter/runtime/runtime_controller.h @@ -7,72 +7,109 @@ #include +#include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/window.h" +#include "flutter/runtime/dart_vm.h" #include "lib/fxl/macros.h" namespace blink { -class DartController; -class DartLibraryProvider; class Scene; class RuntimeDelegate; class View; class Window; -class RuntimeController : public WindowClient, public IsolateClient { +class RuntimeController final : public WindowClient { public: - static std::unique_ptr Create(RuntimeDelegate* client); + RuntimeController(RuntimeDelegate& client, + const DartVM* vm, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue); + ~RuntimeController(); - void CreateDartController(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - int dirfd = -1); - DartController* dart_controller() const { return dart_controller_.get(); } + std::unique_ptr Clone() const; - void SetViewportMetrics(const ViewportMetrics& metrics); - void SetLocale(const std::string& language_code, + bool SetViewportMetrics(const ViewportMetrics& metrics); + + bool SetLocale(const std::string& language_code, const std::string& country_code); - void SetUserSettingsData(const std::string& data); - void SetSemanticsEnabled(bool enabled); - void BeginFrame(fxl::TimePoint frame_time); - void NotifyIdle(int64_t deadline); + bool SetUserSettingsData(const std::string& data); - void DispatchPlatformMessage(fxl::RefPtr message); - void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchSemanticsAction(int32_t id, + bool SetSemanticsEnabled(bool enabled); + + bool BeginFrame(fxl::TimePoint frame_time); + + bool NotifyIdle(int64_t deadline); + + bool DispatchPlatformMessage(fxl::RefPtr message); + + bool DispatchPointerDataPacket(const PointerDataPacket& packet); + + bool DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args); Dart_Port GetMainPort(); + std::string GetIsolateName(); + bool HasLivePorts(); + tonic::DartErrorHandleType GetLastError(); + fml::WeakPtr GetRootIsolate(); + + std::pair GetRootIsolateReturnCode(); + private: - explicit RuntimeController(RuntimeDelegate* client); + struct WindowData { + ViewportMetrics viewport_metrics; + std::string language_code; + std::string country_code; + std::string user_settings_data = "{}"; + bool semantics_enabled = false; + }; - Window* GetWindow(); + RuntimeDelegate& client_; + const DartVM* vm_; + TaskRunners task_runners_; + fml::WeakPtr resource_context_; + fxl::RefPtr unref_queue_; + WindowData window_data_; + fml::WeakPtr root_isolate_; + std::pair root_isolate_return_code_ = {false, 0}; + RuntimeController(RuntimeDelegate& client, + const DartVM* vm, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + WindowData data); + + Window* GetWindowIfAvailable(); + + bool FlushRuntimeStateToIsolate(); + + // |blink::WindowClient| std::string DefaultRouteName() override; + + // |blink::WindowClient| void ScheduleFrame() override; + + // |blink::WindowClient| void Render(Scene* scene) override; + + // |blink::WindowClient| void UpdateSemantics(SemanticsUpdate* update) override; + + // |blink::WindowClient| void HandlePlatformMessage(fxl::RefPtr message) override; - void DidCreateSecondaryIsolate(Dart_Isolate isolate) override; - void DidShutdownMainIsolate() override; - - RuntimeDelegate* client_; - std::string language_code_; - std::string country_code_; - std::string user_settings_data_ = "{}"; - bool semantics_enabled_ = false; - std::unique_ptr dart_controller_; - FXL_DISALLOW_COPY_AND_ASSIGN(RuntimeController); }; diff --git a/engine/src/flutter/runtime/runtime_delegate.cc b/engine/src/flutter/runtime/runtime_delegate.cc index 6ec55c4c2e..902672be06 100644 --- a/engine/src/flutter/runtime/runtime_delegate.cc +++ b/engine/src/flutter/runtime/runtime_delegate.cc @@ -6,12 +6,6 @@ namespace blink { -RuntimeDelegate::~RuntimeDelegate() {} - -void RuntimeDelegate::DidCreateMainIsolate(Dart_Isolate isolate) {} - -void RuntimeDelegate::DidCreateSecondaryIsolate(Dart_Isolate isolate) {} - -void RuntimeDelegate::DidShutdownMainIsolate() {} +RuntimeDelegate::~RuntimeDelegate() = default; } // namespace blink diff --git a/engine/src/flutter/runtime/runtime_delegate.h b/engine/src/flutter/runtime/runtime_delegate.h index 36650fe7fb..c6d6c0a92b 100644 --- a/engine/src/flutter/runtime/runtime_delegate.h +++ b/engine/src/flutter/runtime/runtime_delegate.h @@ -18,14 +18,14 @@ namespace blink { class RuntimeDelegate { public: virtual std::string DefaultRouteName() = 0; - virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; - virtual void Render(std::unique_ptr layer_tree) = 0; - virtual void UpdateSemantics(blink::SemanticsNodeUpdates update) = 0; - virtual void HandlePlatformMessage(fxl::RefPtr message) = 0; - virtual void DidCreateMainIsolate(Dart_Isolate isolate); - virtual void DidCreateSecondaryIsolate(Dart_Isolate isolate); - virtual void DidShutdownMainIsolate(); + virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; + + virtual void Render(std::unique_ptr layer_tree) = 0; + + virtual void UpdateSemantics(blink::SemanticsNodeUpdates update) = 0; + + virtual void HandlePlatformMessage(fxl::RefPtr message) = 0; protected: virtual ~RuntimeDelegate(); diff --git a/engine/src/flutter/runtime/runtime_init.cc b/engine/src/flutter/runtime/runtime_init.cc deleted file mode 100644 index eda66e5495..0000000000 --- a/engine/src/flutter/runtime/runtime_init.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/runtime/runtime_init.h" - -#include "flutter/glue/trace_event.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/runtime/platform_impl.h" -#include "flutter/sky/engine/public/web/Sky.h" -#include "lib/fxl/logging.h" - -namespace blink { -namespace { - -PlatformImpl* g_platform_impl = nullptr; - -} // namespace - -void InitRuntime(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path) { - TRACE_EVENT0("flutter", "InitRuntime"); - - FXL_CHECK(!g_platform_impl); - g_platform_impl = new PlatformImpl(); - InitEngine(g_platform_impl); - InitDartVM(vm_snapshot_data, vm_snapshot_instructions, - default_isolate_snapshot_data, - default_isolate_snapshot_instructions, bundle_path); -} - -} // namespace blink diff --git a/engine/src/flutter/runtime/runtime_init.h b/engine/src/flutter/runtime/runtime_init.h deleted file mode 100644 index 515ae284e3..0000000000 --- a/engine/src/flutter/runtime/runtime_init.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_RUNTIME_RUNTIME_INIT_H_ -#define FLUTTER_RUNTIME_RUNTIME_INIT_H_ - -#include -#include - -namespace blink { - -void InitRuntime(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path); - -} // namespace blink - -#endif // FLUTTER_RUNTIME_RUNTIME_INIT_H_ diff --git a/engine/src/flutter/runtime/service_protocol.cc b/engine/src/flutter/runtime/service_protocol.cc new file mode 100644 index 0000000000..030901e542 --- /dev/null +++ b/engine/src/flutter/runtime/service_protocol.cc @@ -0,0 +1,279 @@ +// Copyright 2017 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/runtime/service_protocol.h" + +#include + +#include +#include +#include +#include + +#include "lib/fxl/synchronization/waitable_event.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" +#include "third_party/dart/runtime/include/dart_tools_api.h" + +namespace blink { + +const fxl::StringView ServiceProtocol::kScreenshotExtensionName = + "_flutter.screenshot"; +const fxl::StringView ServiceProtocol::kScreenshotSkpExtensionName = + "_flutter.screenshotSkp"; +const fxl::StringView ServiceProtocol::kRunInViewExtensionName = + "_flutter.runInView"; +const fxl::StringView ServiceProtocol::kFlushUIThreadTasksExtensionName = + "_flutter.flushUIThreadTasks"; +const fxl::StringView ServiceProtocol::kSetAssetBundlePathExtensionName = + "_flutter.setAssetBundlePath"; + +static constexpr fxl::StringView kViewIdPrefx = "_flutterView/"; +static constexpr fxl::StringView kListViewsExtensionName = "_flutter.listViews"; + +ServiceProtocol::ServiceProtocol() + : endpoints_({ + // Private + kListViewsExtensionName, + + // Public + kScreenshotExtensionName, + kScreenshotSkpExtensionName, + kRunInViewExtensionName, + kFlushUIThreadTasksExtensionName, + kSetAssetBundlePathExtensionName, + }) {} + +ServiceProtocol::~ServiceProtocol() { + ToggleHooks(false); +} + +void ServiceProtocol::AddHandler(Handler* handler) { + std::lock_guard lock(handlers_mutex_); + handlers_.emplace(handler); +} + +void ServiceProtocol::RemoveHandler(Handler* handler) { + std::lock_guard lock(handlers_mutex_); + handlers_.erase(handler); +} + +void ServiceProtocol::ToggleHooks(bool set) { + for (const auto& endpoint : endpoints_) { + Dart_RegisterRootServiceRequestCallback( + endpoint.data(), // method + &ServiceProtocol::HandleMessage, // callback + set ? this : nullptr // user data + ); + } +} + +static void WriteServerErrorResponse(rapidjson::Document& document, + const char* message) { + document.SetObject(); + document.AddMember("code", -32000, document.GetAllocator()); + rapidjson::Value message_value; + message_value.SetString(message, document.GetAllocator()); + document.AddMember("message", message_value, document.GetAllocator()); +} + +bool ServiceProtocol::HandleMessage(const char* method, + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object) { + Handler::ServiceProtocolMap params; + for (intptr_t i = 0; i < num_params; i++) { + params[fxl::StringView{param_keys[i]}] = fxl::StringView{param_values[i]}; + } + +#ifndef NDEBUG + FXL_DLOG(INFO) << "Service protcol method: " << method; + FXL_DLOG(INFO) << "Arguments: " << params.size(); + for (intptr_t i = 0; i < num_params; i++) { + FXL_DLOG(INFO) << " " << i + 1 << ": " << param_keys[i] << " = " + << param_values[i]; + } +#endif // NDEBUG + + rapidjson::Document document; + bool result = HandleMessage(fxl::StringView{method}, // + params, // + static_cast(user_data), // + document // + ); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + *json_object = strdup(buffer.GetString()); + +#ifndef NDEBUG + FXL_DLOG(INFO) << "Response: " << *json_object; + FXL_DLOG(INFO) << "RPC Result: " << result; +#endif // NDEBUG + + return result; +} + +bool ServiceProtocol::HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + ServiceProtocol* service_protocol, + rapidjson::Document& response) { + if (service_protocol == nullptr) { + WriteServerErrorResponse(response, "Service protocol unavailable."); + return false; + } + + return service_protocol->HandleMessage(method, params, response); +} + +FXL_WARN_UNUSED_RESULT +static bool HandleMessageOnHandler( + ServiceProtocol::Handler* handler, + fxl::StringView method, + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& document) { + FXL_DCHECK(handler); + fxl::AutoResetWaitableEvent latch; + bool result = false; + fml::TaskRunner::RunNowOrPostTask( + handler->GetServiceProtocolHandlerTaskRunner(method), + [&latch, // + &result, // + &handler, // + &method, // + ¶ms, // + &document // + ]() { + result = + handler->HandleServiceProtocolMessage(method, params, document); + latch.Signal(); + }); + latch.Wait(); + return result; +} + +bool ServiceProtocol::HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + rapidjson::Document& response) const { + if (method == kListViewsExtensionName) { + // So far, this is the only built-in method that does not forward to the + // dynamic set of handlers. + return HandleListViewsMethod(response); + } + + std::lock_guard lock(handlers_mutex_); + + if (handlers_.size() == 0) { + WriteServerErrorResponse(response, + "There are no running service protocol handlers."); + return false; + } + + // Find the handler by its "viewId" in the params. + auto view_id_param_found = params.find(fxl::StringView{"viewId"}); + if (view_id_param_found != params.end()) { + auto handler = reinterpret_cast(std::stoull( + view_id_param_found->second.data() + kViewIdPrefx.size(), nullptr, 16)); + auto handler_found = handlers_.find(handler); + if (handler_found != handlers_.end()) { + return HandleMessageOnHandler(handler, method, params, response); + } + } + + // Handle legacy calls that do not specify a handler in their args. + // TODO(chinmaygarde): Deprecate these calls in the tools and remove these + // fallbacks. + if (method == kScreenshotExtensionName || + method == kScreenshotSkpExtensionName) { + return HandleMessageOnHandler(*handlers_.begin(), method, params, response); + } + + WriteServerErrorResponse( + response, + "Service protocol could not handle or find a handler for the " + "requested method."); + return false; +} + +static std::string CreateFlutterViewID(intptr_t handler) { + std::stringstream stream; + stream << kViewIdPrefx << "0x" << std::hex << handler; + return stream.str(); +} + +static std::string CreateIsolateID(int64_t isolate) { + std::stringstream stream; + stream << "isolates/" << isolate; + return stream.str(); +} + +void ServiceProtocol::Handler::Description::Write( + Handler* handler, + rapidjson::Value& view, + rapidjson::MemoryPoolAllocator<>& allocator) const { + view.SetObject(); + view.AddMember("type", "FlutterView", allocator); + view.AddMember("id", CreateFlutterViewID(reinterpret_cast(handler)), + allocator); + if (isolate_port != 0) { + rapidjson::Value isolate(rapidjson::Type::kObjectType); + { + isolate.AddMember("type", "@Isolate", allocator); + isolate.AddMember("fixedId", true, allocator); + isolate.AddMember("id", CreateIsolateID(isolate_port), allocator); + isolate.AddMember("name", isolate_name, allocator); + isolate.AddMember("number", isolate_port, allocator); + } + view.AddMember("isolate", isolate, allocator); + } +} + +bool ServiceProtocol::HandleListViewsMethod( + rapidjson::Document& response) const { + // Collect handler descriptions on their respective task runners. + std::lock_guard lock(handlers_mutex_); + std::vector> descriptions; + for (const auto& handler : handlers_) { + fxl::AutoResetWaitableEvent latch; + Handler::Description description; + + fml::TaskRunner::RunNowOrPostTask( + handler->GetServiceProtocolHandlerTaskRunner( + kListViewsExtensionName), // task runner + [&latch, // + &description, // + &handler // + ]() { + description = handler->GetServiceProtocolDescription(); + latch.Signal(); + }); + latch.Wait(); + descriptions.emplace_back(std::make_pair( + reinterpret_cast(handler), std::move(description))); + } + + auto& allocator = response.GetAllocator(); + + // Construct the response objects. + response.SetObject(); + response.AddMember("type", "FlutterViewList", allocator); + + rapidjson::Value viewsList(rapidjson::Type::kArrayType); + for (const auto& description : descriptions) { + rapidjson::Value view(rapidjson::Type::kObjectType); + description.second.Write(reinterpret_cast(description.first), + view, allocator); + viewsList.PushBack(view, allocator); + } + + response.AddMember("views", viewsList, allocator); + + return true; +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/service_protocol.h b/engine/src/flutter/runtime/service_protocol.h new file mode 100644 index 0000000000..056f7389ea --- /dev/null +++ b/engine/src/flutter/runtime/service_protocol.h @@ -0,0 +1,99 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_ +#define FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_ + +#include +#include +#include +#include + +#include "flutter/fml/task_runner.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/strings/string_view.h" +#include "lib/fxl/synchronization/thread_annotations.h" +#include "third_party/rapidjson/rapidjson/document.h" + +namespace blink { + +class ServiceProtocol { + public: + static const fxl::StringView kScreenshotExtensionName; + static const fxl::StringView kScreenshotSkpExtensionName; + static const fxl::StringView kRunInViewExtensionName; + static const fxl::StringView kFlushUIThreadTasksExtensionName; + static const fxl::StringView kSetAssetBundlePathExtensionName; + + class Handler { + public: + struct Description { + int64_t isolate_port = 0 /* illegal port by default. */; + std::string isolate_name; + + Description() {} + + Description(int64_t p_isolate_port, std::string p_isolate_name) + : isolate_port(p_isolate_port), + isolate_name(std::move(p_isolate_name)) {} + + void Write(Handler* handler, + rapidjson::Value& value, + rapidjson::MemoryPoolAllocator<>& allocator) const; + }; + + using ServiceProtocolMap = std::map; + + virtual fxl::RefPtr GetServiceProtocolHandlerTaskRunner( + fxl::StringView method) const = 0; + + virtual Description GetServiceProtocolDescription() const = 0; + + virtual bool HandleServiceProtocolMessage( + fxl::StringView method, // one if the extension names specified above. + const ServiceProtocolMap& params, + rapidjson::Document& response) = 0; + }; + + ServiceProtocol(); + + ~ServiceProtocol(); + + void ToggleHooks(bool set); + + void AddHandler(Handler* handler); + + void RemoveHandler(Handler* handler); + + private: + const std::set endpoints_; + mutable std::mutex handlers_mutex_; + std::set handlers_; + + FXL_WARN_UNUSED_RESULT + static bool HandleMessage(const char* method, + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object); + FXL_WARN_UNUSED_RESULT + static bool HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + ServiceProtocol* service_protocol, + rapidjson::Document& response); + FXL_WARN_UNUSED_RESULT + bool HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + rapidjson::Document& response) const; + + FXL_WARN_UNUSED_RESULT + bool HandleListViewsMethod(rapidjson::Document& response) const; + + FXL_DISALLOW_COPY_AND_ASSIGN(ServiceProtocol); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_ diff --git a/engine/src/flutter/shell/common/BUILD.gn b/engine/src/flutter/shell/common/BUILD.gn index f9e25a3c8d..25edcfa23b 100644 --- a/engine/src/flutter/shell/common/BUILD.gn +++ b/engine/src/flutter/shell/common/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("$flutter_root/testing/testing.gni") + # Template to generate a dart embedder resource.cc file. # Required invoker inputs: # String output (name of output file) @@ -61,18 +63,18 @@ source_set("common") { "animator.h", "engine.cc", "engine.h", - "null_platform_view.cc", - "null_platform_view.h", - "null_rasterizer.cc", - "null_rasterizer.h", + "io_manager.cc", + "io_manager.h", + "isolate_configuration.cc", + "isolate_configuration.h", "picture_serializer.cc", "picture_serializer.h", "platform_view.cc", "platform_view.h", - "platform_view_service_protocol.cc", - "platform_view_service_protocol.h", "rasterizer.cc", "rasterizer.h", + "run_configuration.cc", + "run_configuration.h", "shell.cc", "shell.h", "skia_event_tracer_impl.cc", @@ -81,8 +83,8 @@ source_set("common") { "surface.h", "switches.cc", "switches.h", - "tracing_controller.cc", - "tracing_controller.h", + "thread_host.cc", + "thread_host.h", "vsync_waiter.cc", "vsync_waiter.h", "vsync_waiter_fallback.cc", @@ -90,8 +92,6 @@ source_set("common") { ] deps = [ - "//third_party/dart/runtime:dart_api", - "//third_party/dart/runtime/platform:libdart_platform", "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", @@ -99,10 +99,13 @@ source_set("common") { "$flutter_root/glue", "$flutter_root/lib/ui", "$flutter_root/runtime", + "$flutter_root/sky/engine/platform", "$flutter_root/sky/engine/wtf", "$flutter_root/synchronization", "$flutter_root/third_party/txt", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:dart_api", + "//third_party/dart/runtime/platform:libdart_platform", "//third_party/rapidjson", "//third_party/skia", "//third_party/skia:gpu", @@ -112,7 +115,23 @@ source_set("common") { "//topaz/lib/tonic", ] - public_configs = [ - "$flutter_root:config", + public_configs = [ "$flutter_root:config" ] +} + +executable("shell_unittests") { + testonly = true + + sources = [ + "shell_unittests.cc", + ] + deps = [ + ":common", + "$flutter_root/fml", + "$flutter_root/lib/snapshot", + "$flutter_root/testing", + "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", + "//third_party/skia", + "//topaz/lib/tonic", ] } diff --git a/engine/src/flutter/shell/common/animator.cc b/engine/src/flutter/shell/common/animator.cc index d5679a0160..60177a18f8 100644 --- a/engine/src/flutter/shell/common/animator.cc +++ b/engine/src/flutter/shell/common/animator.cc @@ -4,19 +4,18 @@ #include "flutter/shell/common/animator.h" -#include "flutter/common/threads.h" -#include "flutter/fml/trace_event.h" +#include "flutter/glue/trace_event.h" #include "lib/fxl/time/stopwatch.h" #include "third_party/dart/runtime/include/dart_tools_api.h" namespace shell { -Animator::Animator(fml::WeakPtr rasterizer, - VsyncWaiter* waiter, - Engine* engine) - : rasterizer_(rasterizer), - waiter_(waiter), - engine_(engine), +Animator::Animator(Delegate& delegate, + blink::TaskRunners task_runners, + std::unique_ptr waiter) + : delegate_(delegate), + task_runners_(std::move(task_runners)), + waiter_(std::move(waiter)), last_begin_frame_time_(), dart_frame_deadline_(0), layer_tree_pipeline_(fxl::MakeRefCounted(2)), @@ -79,7 +78,6 @@ void Animator::BeginFrame(fxl::TimePoint frame_start_time, // If we still don't have valid continuation, the pipeline is currently // full because the consumer is being too slow. Try again at the next // frame interval. - TRACE_EVENT_INSTANT0("flutter", "ConsumerSlowDefer"); RequestFrame(); return; } @@ -94,13 +92,13 @@ void Animator::BeginFrame(fxl::TimePoint frame_start_time, { TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame", FrameParity()); - engine_->BeginFrame(last_begin_frame_time_); + delegate_.OnAnimatorBeginFrame(*this, last_begin_frame_time_); } if (!frame_scheduled_) { // We don't have another frame pending, so we're waiting on user input // or I/O. Allow the Dart VM 100 ms. - engine_->NotifyIdle(dart_frame_deadline_ + 100000); + delegate_.OnAnimatorNotifyIdle(*this, dart_frame_deadline_ + 100000); } } @@ -120,15 +118,7 @@ void Animator::Render(std::unique_ptr layer_tree) { // Commit the pending continuation. producer_continuation_.Complete(std::move(layer_tree)); - blink::Threads::Gpu()->PostTask([ - rasterizer = rasterizer_, pipeline = layer_tree_pipeline_, - frame_id = FrameParity() - ]() { - if (!rasterizer.get()) - return; - TRACE_EVENT2("flutter", "GPU Workload", "mode", "basic", "frame", frame_id); - rasterizer->Draw(pipeline); - }); + delegate_.OnAnimatorDraw(*this, layer_tree_pipeline_); } bool Animator::CanReuseLastLayerTree() { @@ -137,10 +127,7 @@ bool Animator::CanReuseLastLayerTree() { void Animator::DrawLastLayerTree() { pending_frame_semaphore_.Signal(); - blink::Threads::Gpu()->PostTask([rasterizer = rasterizer_]() { - if (rasterizer.get()) - rasterizer->DrawLastLayerTree(); - }); + delegate_.OnAnimatorDrawLastLayerTree(*this); } void Animator::RequestFrame(bool regenerate_layer_tree) { @@ -164,31 +151,31 @@ void Animator::RequestFrame(bool regenerate_layer_tree) { // started an expensive operation right after posting this message however. // To support that, we need edge triggered wakes on VSync. - blink::Threads::UI()->PostTask( - [ self = weak_factory_.GetWeakPtr(), frame_number = frame_number_ ]() { - if (!self.get()) { - return; - } - TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", - frame_number); - self->AwaitVSync(); - }); + task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(), + frame_number = frame_number_]() { + if (!self.get()) { + return; + } + TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number); + self->AwaitVSync(); + }); frame_scheduled_ = true; } void Animator::AwaitVSync() { - waiter_->AsyncWaitForVsync([self = weak_factory_.GetWeakPtr()]( - fxl::TimePoint frame_start_time, fxl::TimePoint frame_target_time) { - if (self) { - if (self->CanReuseLastLayerTree()) { - self->DrawLastLayerTree(); - } else { - self->BeginFrame(frame_start_time, frame_target_time); - } - } - }); + waiter_->AsyncWaitForVsync( + [self = weak_factory_.GetWeakPtr()](fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time) { + if (self) { + if (self->CanReuseLastLayerTree()) { + self->DrawLastLayerTree(); + } else { + self->BeginFrame(frame_start_time, frame_target_time); + } + } + }); - engine_->NotifyIdle(dart_frame_deadline_); + delegate_.OnAnimatorNotifyIdle(*this, dart_frame_deadline_); } } // namespace shell diff --git a/engine/src/flutter/shell/common/animator.h b/engine/src/flutter/shell/common/animator.h index bc2ee21441..53b2ac6884 100644 --- a/engine/src/flutter/shell/common/animator.h +++ b/engine/src/flutter/shell/common/animator.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_ANIMATOR_H_ #define FLUTTER_SHELL_COMMON_ANIMATOR_H_ -#include "flutter/shell/common/engine.h" +#include "flutter/common/task_runners.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/vsync_waiter.h" #include "flutter/synchronization/pipeline.h" @@ -16,18 +16,29 @@ namespace shell { -class Animator { +class Animator final { public: - Animator(fml::WeakPtr rasterizer, - VsyncWaiter* waiter, - Engine* engine); + class Delegate { + public: + virtual void OnAnimatorBeginFrame(const Animator& animator, + fxl::TimePoint frame_time) = 0; + + virtual void OnAnimatorNotifyIdle(const Animator& animator, + int64_t deadline) = 0; + + virtual void OnAnimatorDraw( + const Animator& animator, + fxl::RefPtr> pipeline) = 0; + + virtual void OnAnimatorDrawLastLayerTree(const Animator& animator) = 0; + }; + + Animator(Delegate& delegate, + blink::TaskRunners task_runners, + std::unique_ptr waiter); ~Animator(); - void set_rasterizer(fml::WeakPtr rasterizer) { - rasterizer_ = rasterizer; - } - void RequestFrame(bool regenerate_layer_tree = true); void Render(std::unique_ptr layer_tree); @@ -51,9 +62,9 @@ class Animator { const char* FrameParity(); - fml::WeakPtr rasterizer_; - VsyncWaiter* waiter_; - Engine* engine_; + Delegate& delegate_; + blink::TaskRunners task_runners_; + std::unique_ptr waiter_; fxl::TimePoint last_begin_frame_time_; int64_t dart_frame_deadline_; @@ -67,7 +78,7 @@ class Animator { bool dimension_change_pending_; SkISize last_layer_tree_size_; - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; FXL_DISALLOW_COPY_AND_ASSIGN(Animator); }; diff --git a/engine/src/flutter/shell/common/engine.cc b/engine/src/flutter/shell/common/engine.cc index 3629b729c2..3d6fd496d3 100644 --- a/engine/src/flutter/shell/common/engine.cc +++ b/engine/src/flutter/shell/common/engine.cc @@ -4,47 +4,20 @@ #include "flutter/shell/common/engine.h" -#if OS(WIN) -#include -#include -#define access _access -#define R_OK 0x4 - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif - -#ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif - -#else -#include -#include -#include -#endif // OS(WIN) - -#include -#include #include #include -#include "flutter/assets/directory_asset_bundle.h" -#include "flutter/assets/unzipper_provider.h" -#include "flutter/assets/zip_asset_store.h" -#include "flutter/assets/asset_provider.h" #include "flutter/common/settings.h" -#include "flutter/common/threads.h" #include "flutter/glue/trace_event.h" #include "flutter/lib/snapshot/snapshot.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/runtime/asset_font_selector.h" -#include "flutter/runtime/dart_controller.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/runtime/runtime_init.h" +#include "flutter/runtime/platform_impl.h" #include "flutter/runtime/test_font_selector.h" #include "flutter/shell/common/animator.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell.h" +#include "flutter/sky/engine/platform/fonts/FontFallbackList.h" #include "flutter/sky/engine/public/web/Sky.h" #include "lib/fxl/files/eintr_wrapper.h" #include "lib/fxl/files/file.h" @@ -55,380 +28,208 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +#ifdef ERROR +#undef ERROR +#endif + namespace shell { -namespace { -constexpr char kAssetChannel[] = "flutter/assets"; -constexpr char kLifecycleChannel[] = "flutter/lifecycle"; -constexpr char kNavigationChannel[] = "flutter/navigation"; -constexpr char kLocalizationChannel[] = "flutter/localization"; -constexpr char kSettingsChannel[] = "flutter/settings"; +static constexpr char kAssetChannel[] = "flutter/assets"; +static constexpr char kLifecycleChannel[] = "flutter/lifecycle"; +static constexpr char kNavigationChannel[] = "flutter/navigation"; +static constexpr char kLocalizationChannel[] = "flutter/localization"; +static constexpr char kSettingsChannel[] = "flutter/settings"; -#if OS(WIN) -void FindAndReplaceInPlace(std::string& str, - const std::string& findStr, - const std::string& replaceStr) { - size_t pos = 0; - while ((pos = str.find(findStr, pos)) != std::string::npos) { - str.replace(pos, findStr.length(), replaceStr); - pos += replaceStr.length(); - } -} -#endif - -std::string SanitizePath(const std::string& path) { -#if OS(WIN) - std::string sanitized = path; - FindAndReplaceInPlace(sanitized, "\\\\", "/"); - if ((sanitized.length() > 2) && (sanitized[1] == ':')) { - // Path begins with a drive letter. - sanitized = '/' + sanitized; - } - return sanitized; -#else - return path; -#endif -} - -bool PathExists(const std::string& path) { - return access(path.c_str(), R_OK) == 0; -} - -std::string FindPackagesPath(const std::string& main_dart) { - std::string directory = files::GetDirectoryName(main_dart); - std::string packages_path = directory + "/.packages"; - if (!PathExists(packages_path)) { - directory = files::GetDirectoryName(directory); - packages_path = directory + "/.packages"; - if (!PathExists(packages_path)) - packages_path = std::string(); - } - return packages_path; -} - -std::string GetScriptUriFromPath(const std::string& path) { - return "file://" + SanitizePath(path); -} - -} // namespace - -Engine::Engine(PlatformView* platform_view) - : platform_view_(platform_view->GetWeakPtr()), - animator_(std::make_unique( - platform_view->rasterizer().GetWeakRasterizerPtr(), - platform_view->GetVsyncWaiter(), - this)), +Engine::Engine(Delegate& delegate, + const blink::DartVM& vm, + blink::TaskRunners task_runners, + blink::Settings settings, + std::unique_ptr animator, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue) + : delegate_(delegate), + settings_(std::move(settings)), + animator_(std::move(animator)), + legacy_sky_platform_(settings_.using_blink ? new blink::PlatformImpl() + : nullptr), load_script_error_(tonic::kNoError), - user_settings_data_("{}"), activity_running_(false), have_surface_(false), - weak_factory_(this) {} + weak_factory_(this) { + weak_prototype_ = weak_factory_.GetWeakPtr(); -Engine::~Engine() {} + if (legacy_sky_platform_) { + // TODO: Remove this legacy call along with the platform. This is what makes + // the engine unable to run from multiple threads in the legacy + // configuration. + blink::InitEngine(legacy_sky_platform_.get()); + } -void Engine::set_rasterizer(fml::WeakPtr rasterizer) { - animator_->set_rasterizer(rasterizer); + // Runtime controller is initialized here because it takes a reference to this + // object as its delegate. The delegate may be called in the constructor and + // we want to be fully initilazed by that point. + runtime_controller_ = std::make_unique( + *this, // runtime delegate + &vm, // VM + std::move(task_runners), // task runners + std::move(resource_context), // resource context + std::move(unref_queue) // skia unref queue + ); } -fml::WeakPtr Engine::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); +Engine::~Engine() { + if (legacy_sky_platform_) { + blink::ShutdownEngine(/* legacy_sky_platform_ */); + } } -#if !FLUTTER_AOT -#elif OS(IOS) -#elif OS(ANDROID) -// TODO(bkonyi): do we even get here for Windows? -static const uint8_t* MemMapSnapshot(const std::string& aot_snapshot_path, - const std::string& default_file_name, - const std::string& settings_file_name, - bool executable) { - std::string asset_path; - if (settings_file_name.empty()) { - asset_path = aot_snapshot_path + "/" + default_file_name; +fml::WeakPtr Engine::GetWeakPtr() const { + return weak_prototype_; +} + +bool Engine::UpdateAssetManager( + fxl::RefPtr new_asset_manager) { + if (asset_manager_ == new_asset_manager) { + return false; + } + + asset_manager_ = new_asset_manager; + + if (!asset_manager_) { + return false; + } + + if (settings_.using_blink) { + // Using blink as the text engine. + blink::FontFallbackList::SetUseTestFonts(settings_.use_test_fonts); } else { - asset_path = aot_snapshot_path + "/" + settings_file_name; - } - -#if OS(WIN) - HANDLE file_handle_ = - CreateFileA(reinterpret_cast(path.c_str()), GENERIC_READ, - FILE_SHARE_READ, nullptr, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr); - - if (file_handle_ == INVALID_HANDLE_VALUE) { - return; - } - - size_ = GetFileSize(file_handle_, nullptr); - if (size_ == INVALID_FILE_SIZE) { - size_ = 0; - return; - } - - int mapping_flags = executable ? PAGE_EXECUTE_READ : PAGE_READONLY; - mapping_handle_ = CreateFileMapping(file_handle_, nullptr, mapping_flags, 0, - size_, nullptr); - - CloseHandle(file_handle_); - - if (mapping_handle_ == INVALID_HANDLE_VALUE) { - return; - } - - int access_flags = FILE_MAP_READ; - if (executable) { - access_flags |= FILE_MAP_EXECUTE; - } - auto mapping = MapViewOfFile(mapping_handle_, access_flags, 0, 0, size_); - - if (mapping == INVALID_HANDLE_VALUE) { - CloseHandle(mapping_handle_); - mapping_handle_ = INVALID_HANDLE_VALUE; - return; - } - - void* symbol = static_cast(mapping); - if (symbol == NULL) { - return nullptr; - } -#else - struct stat info; - if (stat(asset_path.c_str(), &info) < 0) { - return nullptr; - } - int64_t asset_size = info.st_size; - - fxl::UniqueFD fd(HANDLE_EINTR(open(asset_path.c_str(), O_RDONLY))); - if (fd.get() == -1) { - return nullptr; - } - - int mmap_flags = PROT_READ; - if (executable) - mmap_flags |= PROT_EXEC; - - void* symbol = mmap(NULL, asset_size, mmap_flags, MAP_PRIVATE, fd.get(), 0); - if (symbol == MAP_FAILED) { - return nullptr; - } -#endif - return reinterpret_cast(symbol); -} -#endif - -static const uint8_t* default_isolate_snapshot_data = nullptr; -static const uint8_t* default_isolate_snapshot_instr = nullptr; - -void Engine::Init(const std::string& bundle_path) { - const uint8_t* vm_snapshot_data; - const uint8_t* vm_snapshot_instr; -#if !FLUTTER_AOT - vm_snapshot_data = ::kDartVmSnapshotData; - vm_snapshot_instr = ::kDartVmSnapshotInstructions; - default_isolate_snapshot_data = ::kDartIsolateCoreSnapshotData; - default_isolate_snapshot_instr = ::kDartIsolateCoreSnapshotInstructions; -#elif OS(IOS) - const char* kDartApplicationLibraryPath = "App.framework/App"; - const char* application_library_path = kDartApplicationLibraryPath; - const blink::Settings& settings = blink::Settings::Get(); - const std::string& application_library_path_setting = - settings.application_library_path; - if (!application_library_path_setting.empty()) { - application_library_path = application_library_path_setting.c_str(); - } - dlerror(); // clear previous errors on thread - void* library_handle = dlopen(application_library_path, RTLD_NOW); - const char* err = dlerror(); - if (err != nullptr) { - FXL_LOG(FATAL) << "dlopen failed: " << err; - } - vm_snapshot_data = reinterpret_cast( - dlsym(library_handle, "kDartVmSnapshotData")); - vm_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "kDartVmSnapshotInstructions")); - default_isolate_snapshot_data = reinterpret_cast( - dlsym(library_handle, "kDartIsolateSnapshotData")); - default_isolate_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "kDartIsolateSnapshotInstructions")); -#elif OS(ANDROID) || OS(WIN) - const blink::Settings& settings = blink::Settings::Get(); - const std::string& aot_shared_library_path = settings.aot_shared_library_path; - const std::string& aot_snapshot_path = settings.aot_snapshot_path; - - if (!aot_shared_library_path.empty()) { - FXL_CHECK(aot_snapshot_path.empty()); - dlerror(); // clear previous errors on thread - void* library_handle = dlopen(aot_shared_library_path.c_str(), RTLD_NOW); - const char* err = dlerror(); - if (err != nullptr) { - FXL_LOG(FATAL) << "dlopen failed: " << err; + // Using libTXT as the text engine. + if (settings_.use_test_fonts) { + blink::FontCollection::ForProcess().RegisterTestFonts(); + } else { + blink::FontCollection::ForProcess().RegisterFonts(*asset_manager_.get()); } - vm_snapshot_data = reinterpret_cast( - dlsym(library_handle, "_kDartVmSnapshotData")); - vm_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "_kDartVmSnapshotInstructions")); - default_isolate_snapshot_data = reinterpret_cast( - dlsym(library_handle, "_kDartIsolateSnapshotData")); - default_isolate_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "_kDartIsolateSnapshotInstructions")); - } else { - FXL_CHECK(!aot_snapshot_path.empty()); - vm_snapshot_data = - MemMapSnapshot(aot_snapshot_path, "vm_snapshot_data", - settings.aot_vm_snapshot_data_filename, false); - vm_snapshot_instr = - MemMapSnapshot(aot_snapshot_path, "vm_snapshot_instr", - settings.aot_vm_snapshot_instr_filename, true); - default_isolate_snapshot_data = - MemMapSnapshot(aot_snapshot_path, "isolate_snapshot_data", - settings.aot_isolate_snapshot_data_filename, false); - default_isolate_snapshot_instr = - MemMapSnapshot(aot_snapshot_path, "isolate_snapshot_instr", - settings.aot_isolate_snapshot_instr_filename, true); } -#else -#error Unknown OS -#endif - blink::InitRuntime(vm_snapshot_data, vm_snapshot_instr, - default_isolate_snapshot_data, - default_isolate_snapshot_instr, bundle_path); + + return true; } -const std::string Engine::main_entrypoint_ = "main"; - -void Engine::RunBundle(const std::string& bundle_path, - const std::string& entrypoint, - bool reuse_runtime_controller) { - TRACE_EVENT0("flutter", "Engine::RunBundle"); - ConfigureAssetBundle(bundle_path); - DoRunBundle(GetScriptUriFromPath(bundle_path), entrypoint, - reuse_runtime_controller); +bool Engine::Restart(RunConfiguration configuration) { + TRACE_EVENT0("flutter", "Engine::Restart"); + if (!configuration.IsValid()) { + FXL_LOG(ERROR) << "Engine run configuration was invalid."; + return false; + } + runtime_controller_ = runtime_controller_->Clone(); + UpdateAssetManager(nullptr); + return Run(std::move(configuration)); } -void Engine::DoRunBundle(const std::string& script_uri, - const std::string& entrypoint, - bool reuse_runtime_controller) { - ConfigureRuntime(script_uri, reuse_runtime_controller); - if (blink::IsRunningPrecompiledCode()) { - runtime_->dart_controller()->RunFromPrecompiledSnapshot(entrypoint); - } else { - std::vector kernel; - if (GetAssetAsBuffer(blink::kKernelAssetKey, &kernel)) { - runtime_->dart_controller()->RunFromKernel(kernel, entrypoint); - return; +bool Engine::Run(RunConfiguration configuration) { + if (!configuration.IsValid()) { + FXL_LOG(ERROR) << "Engine run configuration was invalid."; + return false; + } + + if (!PrepareAndLaunchIsolate(std::move(configuration))) { + return false; + } + + auto isolate = runtime_controller_->GetRootIsolate(); + + bool isolate_running = + isolate && isolate->GetPhase() == blink::DartIsolate::Phase::Running; + + if (isolate_running) { + tonic::DartState::Scope scope(isolate.get()); + + if (settings_.root_isolate_create_callback) { + settings_.root_isolate_create_callback(); } - std::vector snapshot; - if (!GetAssetAsBuffer(blink::kSnapshotAssetKey, &snapshot)) - return; - runtime_->dart_controller()->RunFromScriptSnapshot( - snapshot.data(), snapshot.size(), entrypoint); - } -} -// TODO(jsimmons): merge this with RunBundle -void Engine::RunBundleWithAssets( - fxl::RefPtr asset_provider, - const std::string& bundle_path, - const std::string& entrypoint, - bool reuse_runtime_controller) { - TRACE_EVENT0("flutter", "Engine::RunBundleWithAssets"); - asset_provider_ = asset_provider; - DoRunBundle(GetScriptUriFromPath(bundle_path), entrypoint, - reuse_runtime_controller); -} - -void Engine::RunBundleAndSource(const std::string& bundle_path, - const std::string& main, - const std::string& packages, - bool reuse_runtime_controller) { - TRACE_EVENT0("flutter", "Engine::RunBundleAndSource"); - FXL_CHECK(!blink::IsRunningPrecompiledCode()) - << "Cannot run from source in a precompiled build."; - std::string packages_path = packages; - if (packages_path.empty()) - packages_path = FindPackagesPath(main); - - if (!bundle_path.empty()) - ConfigureAssetBundle(bundle_path); - - ConfigureRuntime(main, reuse_runtime_controller); - - if (blink::GetKernelPlatformBinary() != nullptr) { - std::vector kernel; - if (!files::ReadFileToVector(main, &kernel)) { - load_script_error_ = tonic::kUnknownErrorType; + if (settings_.root_isolate_shutdown_callback) { + isolate->AddIsolateShutdownCallback( + settings_.root_isolate_shutdown_callback); + } + + // Blink uses a per isolate font selector. + if (settings_.using_blink) { + if (settings_.use_test_fonts) { + blink::TestFontSelector::Install(); + } else { + blink::AssetFontSelector::Install(asset_manager_); + } } - load_script_error_ = runtime_->dart_controller()->RunFromKernel(kernel); - } else { - load_script_error_ = - runtime_->dart_controller()->RunFromSource(main, packages_path); } + + return isolate_running; +} + +bool Engine::PrepareAndLaunchIsolate(RunConfiguration configuration) { + TRACE_EVENT0("flutter", "Engine::PrepareAndLaunchIsolate"); + + UpdateAssetManager(configuration.GetAssetManager()); + + auto isolate_configuration = configuration.TakeIsolateConfiguration(); + + auto isolate = runtime_controller_->GetRootIsolate(); + + if (!isolate_configuration->PrepareIsolate(isolate)) { + FXL_DLOG(ERROR) << "Could not prepare to run the isolate."; + return false; + } + + if (!isolate->Run(configuration.GetEntrypoint())) { + FXL_DLOG(ERROR) << "Could not run the isolate."; + return false; + } + + return true; } void Engine::BeginFrame(fxl::TimePoint frame_time) { TRACE_EVENT0("flutter", "Engine::BeginFrame"); - if (runtime_) - runtime_->BeginFrame(frame_time); + runtime_controller_->BeginFrame(frame_time); } void Engine::NotifyIdle(int64_t deadline) { TRACE_EVENT0("flutter", "Engine::NotifyIdle"); - if (runtime_) - runtime_->NotifyIdle(deadline); + runtime_controller_->NotifyIdle(deadline); } -void Engine::RunFromSource(const std::string& main, - const std::string& packages, - const std::string& bundle_path) { - RunBundleAndSource(bundle_path, main, packages); -} - -void Engine::SetAssetBundlePath(const std::string& bundle_path) { - TRACE_EVENT0("flutter", "Engine::SetAssetBundlePath"); - ConfigureAssetBundle(bundle_path); +std::pair Engine::GetUIIsolateReturnCode() { + return runtime_controller_->GetRootIsolateReturnCode(); } Dart_Port Engine::GetUIIsolateMainPort() { - if (!runtime_) - return ILLEGAL_PORT; - return runtime_->GetMainPort(); + return runtime_controller_->GetMainPort(); } std::string Engine::GetUIIsolateName() { - if (!runtime_) { - return ""; - } - return runtime_->GetIsolateName(); + return runtime_controller_->GetIsolateName(); } bool Engine::UIIsolateHasLivePorts() { - if (!runtime_) - return false; - return runtime_->HasLivePorts(); + return runtime_controller_->HasLivePorts(); } tonic::DartErrorHandleType Engine::GetUIIsolateLastError() { - if (!runtime_) - return tonic::kNoError; - return runtime_->GetLastError(); + return runtime_controller_->GetLastError(); } tonic::DartErrorHandleType Engine::GetLoadScriptError() { return load_script_error_; } -void Engine::OnOutputSurfaceCreated(const fxl::Closure& gpu_continuation) { - blink::Threads::Gpu()->PostTask(gpu_continuation); +void Engine::OnOutputSurfaceCreated() { have_surface_ = true; StartAnimatorIfPossible(); - if (runtime_) - ScheduleFrame(); + ScheduleFrame(); } -void Engine::OnOutputSurfaceDestroyed(const fxl::Closure& gpu_continuation) { +void Engine::OnOutputSurfaceDestroyed() { have_surface_ = false; StopAnimator(); - blink::Threads::Gpu()->PostTask(gpu_continuation); } void Engine::SetViewportMetrics(const blink::ViewportMetrics& metrics) { @@ -436,8 +237,7 @@ void Engine::SetViewportMetrics(const blink::ViewportMetrics& metrics) { viewport_metrics_.physical_height != metrics.physical_height || viewport_metrics_.physical_width != metrics.physical_width; viewport_metrics_ = metrics; - if (runtime_) - runtime_->SetViewportMetrics(viewport_metrics_); + runtime_controller_->SetViewportMetrics(viewport_metrics_); if (animator_) { if (dimensions_changed) animator_->SetDimensionChangePending(); @@ -459,8 +259,7 @@ void Engine::DispatchPlatformMessage( return; } - if (runtime_) { - runtime_->DispatchPlatformMessage(std::move(message)); + if (runtime_controller_->DispatchPlatformMessage(std::move(message))) { return; } @@ -493,7 +292,6 @@ bool Engine::HandleLifecyclePlatformMessage(blink::PlatformMessage* message) { bool Engine::HandleNavigationPlatformMessage( fxl::RefPtr message) { - FXL_DCHECK(!runtime_); const auto& data = message->data(); rapidjson::Document document; @@ -532,99 +330,33 @@ bool Engine::HandleLocalizationPlatformMessage( if (!language.IsString() || !country.IsString()) return false; - language_code_ = language.GetString(); - country_code_ = country.GetString(); - if (runtime_) - runtime_->SetLocale(language_code_, country_code_); - return true; + return runtime_controller_->SetLocale(language.GetString(), + country.GetString()); } void Engine::HandleSettingsPlatformMessage(blink::PlatformMessage* message) { const auto& data = message->data(); std::string jsonData(reinterpret_cast(data.data()), data.size()); - user_settings_data_ = jsonData; - if (runtime_) { - runtime_->SetUserSettingsData(user_settings_data_); - if (have_surface_) - ScheduleFrame(); + if (runtime_controller_->SetUserSettingsData(std::move(jsonData)) && + have_surface_) { + ScheduleFrame(); } } -void Engine::DispatchPointerDataPacket(const PointerDataPacket& packet) { - if (runtime_) - runtime_->DispatchPointerDataPacket(packet); +void Engine::DispatchPointerDataPacket(const blink::PointerDataPacket& packet) { + runtime_controller_->DispatchPointerDataPacket(packet); } void Engine::DispatchSemanticsAction(int id, blink::SemanticsAction action, std::vector args) { - if (runtime_) - runtime_->DispatchSemanticsAction(id, action, std::move(args)); + runtime_controller_->DispatchSemanticsAction(id, action, std::move(args)); } void Engine::SetSemanticsEnabled(bool enabled) { - semantics_enabled_ = enabled; - if (runtime_) - runtime_->SetSemanticsEnabled(semantics_enabled_); + runtime_controller_->SetSemanticsEnabled(enabled); } -void Engine::ConfigureAssetBundle(const std::string& path) { - asset_provider_ = fxl::MakeRefCounted(path); - - struct stat stat_result = {}; - - // TODO(abarth): We should reset directory_asset_bundle_, but that might break - // custom font loading in hot reload. - - if (::stat(path.c_str(), &stat_result) != 0) { - FXL_LOG(INFO) << "Could not configure asset bundle at path: " << path; - return; - } - - std::string flx_path; - if (S_ISDIR(stat_result.st_mode)) { - flx_path = files::GetDirectoryName(path) + "/app.flx"; - } else if (S_ISREG(stat_result.st_mode)) { - flx_path = path; - } - - if (PathExists(flx_path)) { - asset_store_ = fxl::MakeRefCounted( - blink::GetUnzipperProviderForPath(flx_path)); - } -} - -void Engine::ConfigureRuntime(const std::string& script_uri, - bool reuse_runtime_controller) { - if (runtime_ && reuse_runtime_controller) { - return; - } - runtime_ = blink::RuntimeController::Create(this); - runtime_->CreateDartController(std::move(script_uri), - default_isolate_snapshot_data, - default_isolate_snapshot_instr); - runtime_->SetViewportMetrics(viewport_metrics_); - runtime_->SetLocale(language_code_, country_code_); - runtime_->SetUserSettingsData(user_settings_data_); - runtime_->SetSemanticsEnabled(semantics_enabled_); -} - -void Engine::DidCreateMainIsolate(Dart_Isolate isolate) { - if (blink::Settings::Get().use_test_fonts) { - blink::TestFontSelector::Install(); - if (!blink::Settings::Get().using_blink) - blink::FontCollection::ForProcess().RegisterTestFonts(); - } else if (asset_provider_) { - blink::AssetFontSelector::Install(asset_provider_); - if (!blink::Settings::Get().using_blink) { - blink::FontCollection::ForProcess().RegisterFontsFromAssetProvider( - asset_provider_); - } - } -} - -void Engine::DidCreateSecondaryIsolate(Dart_Isolate isolate) {} - void Engine::StopAnimator() { animator_->Stop(); } @@ -659,49 +391,34 @@ void Engine::Render(std::unique_ptr layer_tree) { } void Engine::UpdateSemantics(blink::SemanticsNodeUpdates update) { - blink::Threads::Platform()->PostTask(fxl::MakeCopyable([ - platform_view = platform_view_.lock(), update = std::move(update) - ]() mutable { - if (platform_view) - platform_view->UpdateSemantics(std::move(update)); - })); + delegate_.OnEngineUpdateSemantics(*this, std::move(update)); } void Engine::HandlePlatformMessage( fxl::RefPtr message) { if (message->channel() == kAssetChannel) { HandleAssetPlatformMessage(std::move(message)); - return; + } else { + delegate_.OnEngineHandlePlatformMessage(*this, std::move(message)); } - blink::Threads::Platform()->PostTask([ - platform_view = platform_view_.lock(), message = std::move(message) - ]() mutable { - if (platform_view) - platform_view->HandlePlatformMessage(std::move(message)); - }); } void Engine::HandleAssetPlatformMessage( fxl::RefPtr message) { fxl::RefPtr response = message->response(); - if (!response) + if (!response) { return; + } const auto& data = message->data(); std::string asset_name(reinterpret_cast(data.data()), data.size()); + std::vector asset_data; - if (GetAssetAsBuffer(asset_name, &asset_data)) { + if (asset_manager_ && asset_manager_->GetAsBuffer(asset_name, &asset_data)) { response->Complete(std::move(asset_data)); } else { response->CompleteEmpty(); } } -bool Engine::GetAssetAsBuffer(const std::string& name, - std::vector* data) { - return ((asset_provider_ && - asset_provider_->GetAsBuffer(name, data)) || - (asset_store_ && asset_store_->GetAsBuffer(name, data))); -} - } // namespace shell diff --git a/engine/src/flutter/shell/common/engine.h b/engine/src/flutter/shell/common/engine.h index a0c5183338..ede442ee3e 100644 --- a/engine/src/flutter/shell/common/engine.h +++ b/engine/src/flutter/shell/common/engine.h @@ -5,132 +5,145 @@ #ifndef SHELL_COMMON_ENGINE_H_ #define SHELL_COMMON_ENGINE_H_ -#include "flutter/assets/zip_asset_store.h" -#include "flutter/assets/asset_provider.h" +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/common/task_runners.h" +#include "flutter/lib/ui/semantics/semantics_node.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/platform_impl.h" #include "flutter/runtime/runtime_controller.h" #include "flutter/runtime/runtime_delegate.h" +#include "flutter/shell/common/animator.h" #include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/run_configuration.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/weak_ptr.h" #include "third_party/skia/include/core/SkPicture.h" -namespace blink { -class DirectoryAssetBundle; -class ZipAssetBundle; -} // namespace blink - namespace shell { -class PlatformView; -class Animator; -using PointerDataPacket = blink::PointerDataPacket; -class Engine : public blink::RuntimeDelegate { +class Engine final : public blink::RuntimeDelegate { public: - explicit Engine(PlatformView* platform_view); + class Delegate { + public: + virtual void OnEngineUpdateSemantics( + const Engine& engine, + blink::SemanticsNodeUpdates update) = 0; + + virtual void OnEngineHandlePlatformMessage( + const Engine& engine, + fxl::RefPtr message) = 0; + }; + + Engine(Delegate& delegate, + const blink::DartVM& vm, + blink::TaskRunners task_runners, + blink::Settings settings, + std::unique_ptr animator, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue); ~Engine() override; - fml::WeakPtr GetWeakPtr(); + fml::WeakPtr GetWeakPtr() const; - static void Init(const std::string& bundle_path); + FXL_WARN_UNUSED_RESULT + bool Run(RunConfiguration configuration); - void RunBundle(const std::string& bundle_path, - const std::string& entrypoint = main_entrypoint_, - bool reuse_runtime_controller = false); + // Used to "cold reload" a running application where the shell (along with the + // platform view and its rasterizer bindings) remains the same but the root + // isolate is torn down and restarted with the new configuration. Only used in + // the development workflow. + FXL_WARN_UNUSED_RESULT + bool Restart(RunConfiguration configuration); - // Uses the given provider to locate assets. - void RunBundleWithAssets(fxl::RefPtr asset_provider, - const std::string& bundle_path, - const std::string& entrypoint = main_entrypoint_, - bool reuse_runtime_controller = false); - - // Uses the given source code instead of looking inside the bundle for the - // source code. - void RunBundleAndSource(const std::string& bundle_path, - const std::string& main, - const std::string& packages, - bool reuse_runtime_controller = false); + bool UpdateAssetManager(fxl::RefPtr asset_manager); void BeginFrame(fxl::TimePoint frame_time); + void NotifyIdle(int64_t deadline); - void RunFromSource(const std::string& main, - const std::string& packages, - const std::string& bundle); - void SetAssetBundlePath(const std::string& bundle_path); - Dart_Port GetUIIsolateMainPort(); + std::string GetUIIsolateName(); + bool UIIsolateHasLivePorts(); + tonic::DartErrorHandleType GetUIIsolateLastError(); + tonic::DartErrorHandleType GetLoadScriptError(); - void OnOutputSurfaceCreated(const fxl::Closure& gpu_continuation); - void OnOutputSurfaceDestroyed(const fxl::Closure& gpu_continuation); + std::pair GetUIIsolateReturnCode(); + + void OnOutputSurfaceCreated(); + + void OnOutputSurfaceDestroyed(); + void SetViewportMetrics(const blink::ViewportMetrics& metrics); + void DispatchPlatformMessage(fxl::RefPtr message); - void DispatchPointerDataPacket(const PointerDataPacket& packet); + + void DispatchPointerDataPacket(const blink::PointerDataPacket& packet); + void DispatchSemanticsAction(int id, blink::SemanticsAction action, std::vector args); + void SetSemanticsEnabled(bool enabled); + void ScheduleFrame(bool regenerate_layer_tree = true) override; - void set_rasterizer(fml::WeakPtr rasterizer); - private: - // RuntimeDelegate methods: - std::string DefaultRouteName() override; - void Render(std::unique_ptr layer_tree) override; - void UpdateSemantics(blink::SemanticsNodeUpdates update) override; - void HandlePlatformMessage( - fxl::RefPtr message) override; - void DidCreateMainIsolate(Dart_Isolate isolate) override; - void DidCreateSecondaryIsolate(Dart_Isolate isolate) override; - - void StopAnimator(); - void StartAnimatorIfPossible(); - - void DoRunBundle(const std::string& script_uri, - const std::string& entrypoint, - bool reuse_runtime_controller); - - void ConfigureAssetBundle(const std::string& path); - void ConfigureRuntime(const std::string& script_uri, - bool reuse_runtime_controller = false); - - bool HandleLifecyclePlatformMessage(blink::PlatformMessage* message); - bool HandleNavigationPlatformMessage( - fxl::RefPtr message); - bool HandleLocalizationPlatformMessage(blink::PlatformMessage* message); - void HandleSettingsPlatformMessage(blink::PlatformMessage* message); - - void HandleAssetPlatformMessage(fxl::RefPtr message); - bool GetAssetAsBuffer(const std::string& name, std::vector* data); - - static const std::string main_entrypoint_; - - fxl::RefPtr asset_provider_; - std::weak_ptr platform_view_; + Engine::Delegate& delegate_; + const blink::Settings settings_; std::unique_ptr animator_; - std::unique_ptr runtime_; + std::unique_ptr runtime_controller_; + std::unique_ptr legacy_sky_platform_; tonic::DartErrorHandleType load_script_error_; std::string initial_route_; blink::ViewportMetrics viewport_metrics_; - std::string language_code_; - std::string country_code_; - std::string user_settings_data_; - bool semantics_enabled_ = false; - // TODO(zarah): Remove usage of asset_store_ once app.flx is removed. - fxl::RefPtr asset_store_; - fxl::RefPtr directory_asset_bundle_; - // TODO(eseidel): This should move into an AnimatorStateMachine. + fxl::RefPtr asset_manager_; bool activity_running_; bool have_surface_; + fml::WeakPtr weak_prototype_; fml::WeakPtrFactory weak_factory_; + // |blink::RuntimeDelegate| + std::string DefaultRouteName() override; + + // |blink::RuntimeDelegate| + void Render(std::unique_ptr layer_tree) override; + + // |blink::RuntimeDelegate| + void UpdateSemantics(blink::SemanticsNodeUpdates update) override; + + // |blink::RuntimeDelegate| + void HandlePlatformMessage( + fxl::RefPtr message) override; + + void StopAnimator(); + + void StartAnimatorIfPossible(); + + bool HandleLifecyclePlatformMessage(blink::PlatformMessage* message); + + bool HandleNavigationPlatformMessage( + fxl::RefPtr message); + + bool HandleLocalizationPlatformMessage(blink::PlatformMessage* message); + + void HandleSettingsPlatformMessage(blink::PlatformMessage* message); + + void HandleAssetPlatformMessage(fxl::RefPtr message); + + bool GetAssetAsBuffer(const std::string& name, std::vector* data); + + bool PrepareAndLaunchIsolate(RunConfiguration configuration); + FXL_DISALLOW_COPY_AND_ASSIGN(Engine); }; diff --git a/engine/src/flutter/shell/common/io_manager.cc b/engine/src/flutter/shell/common/io_manager.cc new file mode 100644 index 0000000000..4161422978 --- /dev/null +++ b/engine/src/flutter/shell/common/io_manager.cc @@ -0,0 +1,73 @@ +// Copyright 2017 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. + +#include "flutter/shell/common/io_manager.h" + +#include "flutter/fml/message_loop.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" + +namespace shell { + +sk_sp IOManager::CreateCompatibleResourceLoadingContext( + GrBackend backend) { + if (backend != GrBackend::kOpenGL_GrBackend) { + return nullptr; + } + + GrContextOptions options = {}; + + // There is currently a bug with doing GPU YUV to RGB conversions on the IO + // thread. The necessary work isn't being flushed or synchronized with the + // other threads correctly, so the textures end up blank. For now, suppress + // that feature, which will cause texture uploads to do CPU YUV conversion. + options.fDisableGpuYUVConversion = true; + + if (auto context = GrContext::MakeGL(GrGLMakeNativeInterface(), options)) { + // Do not cache textures created by the image decoder. These textures + // should be deleted when they are no longer referenced by an SkImage. + context->setResourceCacheLimits(0, 0); + return context; + } + + return nullptr; +} + +IOManager::IOManager(sk_sp resource_context, + fxl::RefPtr unref_queue_task_runner) + : resource_context_(std::move(resource_context)), + resource_context_weak_factory_( + resource_context_ ? std::make_unique>( + resource_context_.get()) + : nullptr), + unref_queue_(fxl::MakeRefCounted( + std::move(unref_queue_task_runner), + fxl::TimeDelta::FromMilliseconds(250))), + weak_factory_(this) { + if (!resource_context_) { + FXL_DLOG(WARNING) << "The IO manager was initialized without a resource " + "context. Async texture uploads will be disabled. " + "Expect performance degradation."; + } + + if (resource_context_weak_factory_) { + resource_context_weak_prototype_ = + resource_context_weak_factory_->GetWeakPtr(); + } +} + +IOManager::~IOManager() { + // Last chance to drain the IO queue as the platform side reference to the + // underlying OpenGL context may be going away. + unref_queue_->Drain(); +} + +fml::WeakPtr IOManager::GetResourceContext() const { + return resource_context_weak_prototype_; +} + +fxl::RefPtr IOManager::GetSkiaUnrefQueue() const { + return unref_queue_; +} + +} // namespace shell diff --git a/engine/src/flutter/shell/common/io_manager.h b/engine/src/flutter/shell/common/io_manager.h new file mode 100644 index 0000000000..1077a28d0a --- /dev/null +++ b/engine/src/flutter/shell/common/io_manager.h @@ -0,0 +1,52 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_COMMON_IO_MANAGER_H_ +#define FLUTTER_SHELL_COMMON_IO_MANAGER_H_ + +#include + +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" +#include "third_party/skia/include/gpu/GrContext.h" + +namespace shell { + +class IOManager { + public: + // Convenience methods for platforms to create a GrContext used to supply to + // the IOManager. The platforms may create the context themselves if they so + // desire. + static sk_sp CreateCompatibleResourceLoadingContext( + GrBackend backend); + + IOManager(sk_sp resource_context, + fxl::RefPtr unref_queue_task_runner); + + ~IOManager(); + + fml::WeakPtr GetResourceContext() const; + + fxl::RefPtr GetSkiaUnrefQueue() const; + + private: + // Resource context management. + sk_sp resource_context_; + fml::WeakPtr resource_context_weak_prototype_; + std::unique_ptr> + resource_context_weak_factory_; + + // Unref queue management. + fxl::RefPtr unref_queue_; + + fml::WeakPtrFactory weak_factory_; + + FXL_DISALLOW_COPY_AND_ASSIGN(IOManager); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_IO_MANAGER_H_ diff --git a/engine/src/flutter/shell/common/isolate_configuration.cc b/engine/src/flutter/shell/common/isolate_configuration.cc new file mode 100644 index 0000000000..3c634022a3 --- /dev/null +++ b/engine/src/flutter/shell/common/isolate_configuration.cc @@ -0,0 +1,148 @@ +// Copyright 2018 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. + +#include "flutter/shell/common/isolate_configuration.h" + +#include "flutter/runtime/dart_vm.h" + +#ifdef ERROR +#undef ERROR +#endif + +namespace shell { + +IsolateConfiguration::IsolateConfiguration() = default; + +IsolateConfiguration::~IsolateConfiguration() = default; + +bool IsolateConfiguration::PrepareIsolate( + fml::WeakPtr isolate) { + if (!isolate) { + return false; + } + + if (isolate->GetPhase() != blink::DartIsolate::Phase::LibrariesSetup) { + FXL_DLOG(ERROR) + << "Isolate was in incorrect phase to be prepared for running."; + return false; + } + + return DoPrepareIsolate(*isolate); +} + +class PrecompiledIsolateConfiguration final : public IsolateConfiguration { + public: + PrecompiledIsolateConfiguration() = default; + + // |shell::IsolateConfiguration| + bool DoPrepareIsolate(blink::DartIsolate& isolate) override { + if (!blink::DartVM::IsRunningPrecompiledCode()) { + return false; + } + return isolate.PrepareForRunningFromPrecompiledCode(); + } + + private: + FXL_DISALLOW_COPY_AND_ASSIGN(PrecompiledIsolateConfiguration); +}; + +class SnapshotIsolateConfiguration : public IsolateConfiguration { + public: + SnapshotIsolateConfiguration(std::unique_ptr snapshot) + : snapshot_(std::move(snapshot)) {} + + // |shell::IsolateConfiguration| + bool DoPrepareIsolate(blink::DartIsolate& isolate) override { + if (blink::DartVM::IsRunningPrecompiledCode()) { + return false; + } + return isolate.PrepareForRunningFromSnapshot(std::move(snapshot_)); + } + + private: + std::unique_ptr snapshot_; + + FXL_DISALLOW_COPY_AND_ASSIGN(SnapshotIsolateConfiguration); +}; + +class SourceIsolateConfiguration final : public IsolateConfiguration { + public: + SourceIsolateConfiguration(std::string main_path, std::string packages_path) + : main_path_(std::move(main_path)), + packages_path_(std::move(packages_path)) {} + + // |shell::IsolateConfiguration| + bool DoPrepareIsolate(blink::DartIsolate& isolate) override { + if (blink::DartVM::IsRunningPrecompiledCode()) { + return false; + } + return isolate.PrepareForRunningFromSource(std::move(main_path_), + std::move(packages_path_)); + } + + private: + std::string main_path_; + std::string packages_path_; + + FXL_DISALLOW_COPY_AND_ASSIGN(SourceIsolateConfiguration); +}; + +std::unique_ptr IsolateConfiguration::InferFromSettings( + const blink::Settings& settings, + fxl::RefPtr asset_manager) { + // Running in AOT mode. + if (blink::DartVM::IsRunningPrecompiledCode()) { + return CreateForPrecompiledCode(); + } + + // Run from sources. + { + const auto& main = settings.main_dart_file_path; + const auto& packages = settings.packages_file_path; + if (main.size() != 0 && packages.size() != 0) { + return CreateForSource(std::move(main), std::move(packages)); + } + } + + // Running from kernel snapshot. + { + std::vector kernel; + if (asset_manager && asset_manager->GetAsBuffer( + settings.application_kernel_asset, &kernel)) { + return CreateForSnapshot( + std::make_unique(std::move(kernel))); + } + } + + // Running from script snapshot. + { + std::vector script_snapshot; + if (asset_manager && asset_manager->GetAsBuffer( + settings.script_snapshot_path, &script_snapshot)) { + return CreateForSnapshot( + std::make_unique(std::move(script_snapshot))); + } + } + + return nullptr; +} + +std::unique_ptr +IsolateConfiguration::CreateForPrecompiledCode() { + return std::make_unique(); +} + +std::unique_ptr IsolateConfiguration::CreateForSnapshot( + std::unique_ptr snapshot) { + return std::make_unique(std::move(snapshot)); +} + +std::unique_ptr IsolateConfiguration::CreateForSource( + std::string main_path, + std::string packages_path) { + return std::make_unique(std::move(main_path), + std::move(packages_path)); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/common/isolate_configuration.h b/engine/src/flutter/shell/common/isolate_configuration.h new file mode 100644 index 0000000000..82d06dac62 --- /dev/null +++ b/engine/src/flutter/shell/common/isolate_configuration.h @@ -0,0 +1,51 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ +#define FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ + +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/assets/asset_resolver.h" +#include "flutter/common/settings.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/runtime/dart_isolate.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class IsolateConfiguration { + public: + static std::unique_ptr InferFromSettings( + const blink::Settings& settings, + fxl::RefPtr asset_manager); + + static std::unique_ptr CreateForPrecompiledCode(); + + static std::unique_ptr CreateForSnapshot( + std::unique_ptr snapshot); + + static std::unique_ptr CreateForSource( + std::string main_path, + std::string packages_path); + + IsolateConfiguration(); + + virtual ~IsolateConfiguration(); + + bool PrepareIsolate(fml::WeakPtr isolate); + + protected: + virtual bool DoPrepareIsolate(blink::DartIsolate& isolate) = 0; + + private: + FXL_DISALLOW_COPY_AND_ASSIGN(IsolateConfiguration); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ diff --git a/engine/src/flutter/shell/common/null_platform_view.cc b/engine/src/flutter/shell/common/null_platform_view.cc deleted file mode 100644 index 49fdf5a935..0000000000 --- a/engine/src/flutter/shell/common/null_platform_view.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/null_platform_view.h" - -#include "flutter/shell/common/null_rasterizer.h" -#include "flutter/shell/common/shell.h" - -namespace shell { - -NullPlatformView::NullPlatformView() - : PlatformView(std::make_unique()), weak_factory_(this) {} - -void NullPlatformView::Attach() { - CreateEngine(); -} - -NullPlatformView::~NullPlatformView() = default; - -fxl::WeakPtr NullPlatformView::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - -bool NullPlatformView::ResourceContextMakeCurrent() { - return false; -} - -// Hot-reload of the null platform view is not supported. -void NullPlatformView::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) {} - -void NullPlatformView::SetAssetBundlePath(const std::string& assets_directory) { -} - -} // namespace shell diff --git a/engine/src/flutter/shell/common/null_platform_view.h b/engine/src/flutter/shell/common/null_platform_view.h deleted file mode 100644 index eb23d67b48..0000000000 --- a/engine/src/flutter/shell/common/null_platform_view.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMMON_NULL_PLATFORM_VIEW_H_ -#define COMMON_NULL_PLATFORM_VIEW_H_ - -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class NullPlatformView : public PlatformView { - public: - NullPlatformView(); - - ~NullPlatformView(); - - fxl::WeakPtr GetWeakPtr(); - - virtual void Attach() override; - - bool ResourceContextMakeCurrent() override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - void SetAssetBundlePath(const std::string& assets_directory) override; - - private: - fxl::WeakPtrFactory weak_factory_; - - FXL_DISALLOW_COPY_AND_ASSIGN(NullPlatformView); -}; - -} // namespace shell - -#endif // COMMON_NULL_PLATFORM_VIEW_H_ diff --git a/engine/src/flutter/shell/common/null_rasterizer.cc b/engine/src/flutter/shell/common/null_rasterizer.cc deleted file mode 100644 index 81efdd11d4..0000000000 --- a/engine/src/flutter/shell/common/null_rasterizer.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/null_rasterizer.h" - -namespace shell { - -NullRasterizer::NullRasterizer() : weak_factory_(this) {} - -void NullRasterizer::Setup( - std::unique_ptr surface_or_null, - fxl::Closure rasterizer_continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) { - surface_ = std::move(surface_or_null); - rasterizer_continuation(); - setup_completion_event->Signal(); -} - -void NullRasterizer::Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) { - if (surface_) { - surface_.reset(); - } - teardown_completion_event->Signal(); -} - -fml::WeakPtr NullRasterizer::GetWeakRasterizerPtr() { - return weak_factory_.GetWeakPtr(); -} - -flow::LayerTree* NullRasterizer::GetLastLayerTree() { - return nullptr; -} - -void NullRasterizer::DrawLastLayerTree() { - // Null rasterizer. Nothing to do. -} - -flow::TextureRegistry& NullRasterizer::GetTextureRegistry() { - return *texture_registry_; -} - -void NullRasterizer::Clear(SkColor color, const SkISize& size) { - // Null rasterizer. Nothing to do. -} - -void NullRasterizer::Draw( - fxl::RefPtr> pipeline) { - FXL_ALLOW_UNUSED_LOCAL( - pipeline->Consume([](std::unique_ptr) { - // Drop the layer tree on the floor. We only need the pipeline empty so - // that frame requests are not deferred indefinitely due to - // backpressure. - })); -} - -void NullRasterizer::AddNextFrameCallback(fxl::Closure nextFrameCallback) { - // Null rasterizer. Nothing to do. -} - -void NullRasterizer::SetTextureRegistry( - flow::TextureRegistry* textureRegistry) { - texture_registry_ = textureRegistry; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/common/null_rasterizer.h b/engine/src/flutter/shell/common/null_rasterizer.h deleted file mode 100644 index 8558a3c3ac..0000000000 --- a/engine/src/flutter/shell/common/null_rasterizer.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_COMMON_NULL_RASTERIZER_H_ -#define FLUTTER_SHELL_COMMON_NULL_RASTERIZER_H_ - -#include "flutter/shell/common/rasterizer.h" -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class NullRasterizer : public Rasterizer { - public: - NullRasterizer(); - - void Setup(std::unique_ptr surface_or_null, - fxl::Closure rasterizer_continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) override; - - void Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) override; - - void Clear(SkColor color, const SkISize& size) override; - - fml::WeakPtr GetWeakRasterizerPtr() override; - - flow::LayerTree* GetLastLayerTree() override; - - void DrawLastLayerTree() override; - - flow::TextureRegistry& GetTextureRegistry() override; - - void Draw(fxl::RefPtr> pipeline) override; - - void AddNextFrameCallback(fxl::Closure nextFrameCallback) override; - - void SetTextureRegistry(flow::TextureRegistry* textureRegistry) override; - - private: - std::unique_ptr surface_; - fml::WeakPtrFactory weak_factory_; - flow::TextureRegistry* texture_registry_; - - FXL_DISALLOW_COPY_AND_ASSIGN(NullRasterizer); -}; - -} // namespace shell - -#endif // FLUTTER_SHELL_COMMON_NULL_RASTERIZER_H_ diff --git a/engine/src/flutter/shell/common/platform_view.cc b/engine/src/flutter/shell/common/platform_view.cc index 2d5715a173..b2bfce051a 100644 --- a/engine/src/flutter/shell/common/platform_view.cc +++ b/engine/src/flutter/shell/common/platform_view.cc @@ -6,124 +6,74 @@ #include -#include "flutter/common/threads.h" -#include "flutter/lib/ui/painting/resource_context.h" #include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" #include "flutter/shell/common/vsync_waiter_fallback.h" #include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/gpu/GrContextOptions.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { -PlatformView::PlatformView(std::unique_ptr rasterizer) - : rasterizer_(std::move(rasterizer)), size_(SkISize::Make(0, 0)) { - rasterizer_->SetTextureRegistry(&texture_registry_); - Shell::Shared().AddPlatformView(this); +PlatformView::PlatformView(Delegate& delegate, blink::TaskRunners task_runners) + : delegate_(delegate), + task_runners_(std::move(task_runners)), + size_(SkISize::Make(0, 0)), + weak_factory_(this) { + weak_prototype_ = weak_factory_.GetWeakPtr(); } -PlatformView::~PlatformView() { - Shell::Shared().RemovePlatformView(this); +PlatformView::~PlatformView() = default; - Rasterizer* rasterizer = rasterizer_.release(); - blink::Threads::Gpu()->PostTask([rasterizer]() { delete rasterizer; }); - - Engine* engine = engine_.release(); - blink::Threads::UI()->PostTask([engine]() { delete engine; }); -} - -void PlatformView::SetRasterizer(std::unique_ptr rasterizer) { - Rasterizer* r = rasterizer_.release(); - blink::Threads::Gpu()->PostTask([r]() { delete r; }); - rasterizer_ = std::move(rasterizer); - rasterizer_->SetTextureRegistry(&texture_registry_); - engine_->set_rasterizer(rasterizer_->GetWeakRasterizerPtr()); -} - -void PlatformView::CreateEngine() { - engine_.reset(new Engine(this)); +std::unique_ptr PlatformView::CreateVSyncWaiter() { + FXL_DLOG(WARNING) + << "This platform does not provide a Vsync waiter implementation. A " + "simple timer based fallback is being used."; + return std::make_unique(task_runners_); } void PlatformView::DispatchPlatformMessage( fxl::RefPtr message) { - blink::Threads::UI()->PostTask( - [engine = engine_->GetWeakPtr(), message = std::move(message)] { - if (engine) { - engine->DispatchPlatformMessage(message); - } - }); + delegate_.OnPlatformViewDispatchPlatformMessage(*this, std::move(message)); +} + +void PlatformView::DispatchPointerDataPacket( + std::unique_ptr packet) { + delegate_.OnPlatformViewDispatchPointerDataPacket(*this, std::move(packet)); } void PlatformView::DispatchSemanticsAction(int32_t id, blink::SemanticsAction action, std::vector args) { - blink::Threads::UI()->PostTask( - [engine = engine_->GetWeakPtr(), id, action, args = std::move(args)] { - if (engine) { - engine->DispatchSemanticsAction( - id, static_cast(action), std::move(args)); - } - }); + delegate_.OnPlatformViewDispatchSemanticsAction(*this, id, action, + std::move(args)); } void PlatformView::SetSemanticsEnabled(bool enabled) { - blink::Threads::UI()->PostTask([engine = engine_->GetWeakPtr(), enabled] { - if (engine) - engine->SetSemanticsEnabled(enabled); - }); + delegate_.OnPlatformViewSetSemanticsEnabled(*this, enabled); } -void PlatformView::NotifyCreated(std::unique_ptr surface) { - NotifyCreated(std::move(surface), []() {}); +void PlatformView::SetViewportMetrics(const blink::ViewportMetrics& metrics) { + delegate_.OnPlatformViewSetViewportMetrics(*this, metrics); } -void PlatformView::NotifyCreated(std::unique_ptr surface, - fxl::Closure caller_continuation) { - fxl::AutoResetWaitableEvent latch; - - auto ui_continuation = fxl::MakeCopyable([this, // - surface = std::move(surface), // - caller_continuation, // - &latch]() mutable { - auto gpu_continuation = fxl::MakeCopyable([this, // - surface = std::move(surface), // - caller_continuation, // - &latch]() mutable { - // Runs on the GPU Thread. So does the Caller Continuation. - rasterizer_->Setup(std::move(surface), caller_continuation, &latch); - }); - // Runs on the UI Thread. - engine_->OnOutputSurfaceCreated(std::move(gpu_continuation)); - }); - - // Runs on the Platform Thread. - blink::Threads::UI()->PostTask(std::move(ui_continuation)); - - latch.Wait(); +void PlatformView::NotifyCreated() { + delegate_.OnPlatformViewCreated(*this, CreateRenderingSurface()); } void PlatformView::NotifyDestroyed() { - fxl::AutoResetWaitableEvent latch; - - auto engine_continuation = [this, &latch]() { - rasterizer_->Teardown(&latch); - }; - - blink::Threads::UI()->PostTask([this, engine_continuation]() { - engine_->OnOutputSurfaceDestroyed(engine_continuation); - }); - - latch.Wait(); + delegate_.OnPlatformViewDestroyed(*this); } -std::weak_ptr PlatformView::GetWeakPtr() { - return shared_from_this(); +sk_sp PlatformView::CreateResourceContext() const { + FXL_DLOG(WARNING) << "This platform does not setup the resource " + "context on the IO thread for async texture uploads."; + return nullptr; } -VsyncWaiter* PlatformView::GetVsyncWaiter() { - if (!vsync_waiter_) - vsync_waiter_ = std::make_unique(); - return vsync_waiter_.get(); +fml::WeakPtr PlatformView::GetWeakPtr() const { + return weak_prototype_; } void PlatformView::UpdateSemantics(blink::SemanticsNodeUpdates update) {} @@ -135,71 +85,31 @@ void PlatformView::HandlePlatformMessage( } void PlatformView::RegisterTexture(std::shared_ptr texture) { - ASSERT_IS_PLATFORM_THREAD - blink::Threads::Gpu()->PostTask([this, texture]() { - rasterizer_->GetTextureRegistry().RegisterTexture(texture); - }); + delegate_.OnPlatformViewRegisterTexture(*this, std::move(texture)); } void PlatformView::UnregisterTexture(int64_t texture_id) { - ASSERT_IS_PLATFORM_THREAD - blink::Threads::Gpu()->PostTask([this, texture_id]() { - rasterizer_->GetTextureRegistry().UnregisterTexture(texture_id); - }); + delegate_.OnPlatformViewUnregisterTexture(*this, texture_id); } void PlatformView::MarkTextureFrameAvailable(int64_t texture_id) { - ASSERT_IS_PLATFORM_THREAD - blink::Threads::UI()->PostTask([this]() { engine_->ScheduleFrame(false); }); + delegate_.OnPlatformViewMarkTextureFrameAvailable(*this, texture_id); } -void PlatformView::SetupResourceContextOnIOThread() { - fxl::AutoResetWaitableEvent latch; - - blink::Threads::IO()->PostTask( - [this, &latch]() { SetupResourceContextOnIOThreadPerform(&latch); }); - - latch.Wait(); +std::unique_ptr PlatformView::CreateRenderingSurface() { + // We have a default implementation because tests create a platform view but + // never a rendering surface. + FXL_DCHECK(false) << "This platform does not provide a rendering surface but " + "it was notified of surface rendering surface creation."; + return nullptr; } -void PlatformView::SetupResourceContextOnIOThreadPerform( - fxl::AutoResetWaitableEvent* latch) { - std::unique_ptr resourceContext = - blink::ResourceContext::Acquire(); - if (resourceContext->Get() != nullptr) { - // The resource context was already setup. This could happen if platforms - // try to setup a context multiple times, or, if there are multiple platform - // views. In any case, there is nothing else to do. So just signal the - // latch. - latch->Signal(); +void PlatformView::SetNextFrameCallback(fxl::Closure closure) { + if (!closure) { return; } - bool current = ResourceContextMakeCurrent(); - - if (!current) { - FXL_DLOG(WARNING) - << "WARNING: Could not setup a context on the resource loader."; - latch->Signal(); - return; - } - - GrContextOptions options; - // There is currently a bug with doing GPU YUV to RGB conversions on the IO - // thread. The necessary work isn't being flushed or synchronized with the - // other threads correctly, so the textures end up blank. For now, suppress - // that feature, which will cause texture uploads to do CPU YUV conversion. - options.fDisableGpuYUVConversion = true; - - blink::ResourceContext::Set( - GrContext::MakeGL(GrGLMakeNativeInterface(), options)); - - // Do not cache textures created by the image decoder. These textures should - // be deleted when they are no longer referenced by an SkImage. - if (resourceContext->Get()) - resourceContext->Get()->setResourceCacheLimits(0, 0); - - latch->Signal(); + delegate_.OnPlatformViewSetNextFrameCallback(*this, std::move(closure)); } } // namespace shell diff --git a/engine/src/flutter/shell/common/platform_view.h b/engine/src/flutter/shell/common/platform_view.h index 66b1fcae10..56179a6114 100644 --- a/engine/src/flutter/shell/common/platform_view.h +++ b/engine/src/flutter/shell/common/platform_view.h @@ -7,63 +7,104 @@ #include +#include "flutter/common/task_runners.h" #include "flutter/flow/texture.h" +#include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/semantics_node.h" -#include "flutter/shell/common/engine.h" -#include "flutter/shell/common/shell.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/lib/ui/window/pointer_data_packet.h" +#include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/shell/common/surface.h" #include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" -#include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/gpu/GrContext.h" namespace shell { -class Rasterizer; +class Shell; -class PlatformView : public std::enable_shared_from_this { +class PlatformView { public: - struct SurfaceConfig { - uint8_t red_bits = 8; - uint8_t green_bits = 8; - uint8_t blue_bits = 8; - uint8_t alpha_bits = 8; - uint8_t depth_bits = 0; - uint8_t stencil_bits = 0; + class Delegate { + public: + virtual void OnPlatformViewCreated(const PlatformView& view, + std::unique_ptr surface) = 0; + + virtual void OnPlatformViewDestroyed(const PlatformView& view) = 0; + + virtual void OnPlatformViewSetNextFrameCallback(const PlatformView& view, + fxl::Closure closure) = 0; + + virtual void OnPlatformViewSetViewportMetrics( + const PlatformView& view, + const blink::ViewportMetrics& metrics) = 0; + + virtual void OnPlatformViewDispatchPlatformMessage( + const PlatformView& view, + fxl::RefPtr message) = 0; + + virtual void OnPlatformViewDispatchPointerDataPacket( + const PlatformView& view, + std::unique_ptr packet) = 0; + + virtual void OnPlatformViewDispatchSemanticsAction( + const PlatformView& view, + int32_t id, + blink::SemanticsAction action, + std::vector args) = 0; + + virtual void OnPlatformViewSetSemanticsEnabled(const PlatformView& view, + bool enabled) = 0; + + virtual void OnPlatformViewRegisterTexture( + const PlatformView& view, + std::shared_ptr texture) = 0; + + virtual void OnPlatformViewUnregisterTexture(const PlatformView& view, + int64_t texture_id) = 0; + + virtual void OnPlatformViewMarkTextureFrameAvailable( + const PlatformView& view, + int64_t texture_id) = 0; }; - void SetupResourceContextOnIOThread(); + explicit PlatformView(Delegate& delegate, blink::TaskRunners task_runners); virtual ~PlatformView(); - virtual void Attach() = 0; + virtual std::unique_ptr CreateVSyncWaiter(); void DispatchPlatformMessage(fxl::RefPtr message); + void DispatchSemanticsAction(int32_t id, blink::SemanticsAction action, std::vector args); - void SetSemanticsEnabled(bool enabled); - void NotifyCreated(std::unique_ptr surface); + virtual void SetSemanticsEnabled(bool enabled); - void NotifyCreated(std::unique_ptr surface, - fxl::Closure continuation); + void SetViewportMetrics(const blink::ViewportMetrics& metrics); - void NotifyDestroyed(); + void NotifyCreated(); - std::weak_ptr GetWeakPtr(); + virtual void NotifyDestroyed(); - // The VsyncWaiter will live at least as long as the PlatformView. - virtual VsyncWaiter* GetVsyncWaiter(); + // Unlike all other methods on the platform view, this one may be called on a + // non-platform task runner. + virtual sk_sp CreateResourceContext() const; - virtual bool ResourceContextMakeCurrent() = 0; + fml::WeakPtr GetWeakPtr() const; virtual void UpdateSemantics(blink::SemanticsNodeUpdates update); + virtual void HandlePlatformMessage( fxl::RefPtr message); + void SetNextFrameCallback(fxl::Closure closure); + + void DispatchPointerDataPacket( + std::unique_ptr packet); + // Called once per texture, on the platform thread. void RegisterTexture(std::shared_ptr texture); @@ -71,34 +112,18 @@ class PlatformView : public std::enable_shared_from_this { void UnregisterTexture(int64_t texture_id); // Called once per texture update (e.g. video frame), on the platform thread. - virtual void MarkTextureFrameAvailable(int64_t texture_id); - - void SetRasterizer(std::unique_ptr rasterizer); - - Rasterizer& rasterizer() { return *rasterizer_; } - Engine& engine() { return *engine_; } - - virtual void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) = 0; - - virtual void SetAssetBundlePath(const std::string& assets_directory) = 0; + void MarkTextureFrameAvailable(int64_t texture_id); protected: - explicit PlatformView(std::unique_ptr rasterizer); - - void CreateEngine(); - - void SetupResourceContextOnIOThreadPerform( - fxl::AutoResetWaitableEvent* event); - - SurfaceConfig surface_config_; - std::unique_ptr rasterizer_; - flow::TextureRegistry texture_registry_; - std::unique_ptr engine_; + PlatformView::Delegate& delegate_; + const blink::TaskRunners task_runners_; std::unique_ptr vsync_waiter_; SkISize size_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + virtual std::unique_ptr CreateRenderingSurface(); private: FXL_DISALLOW_COPY_AND_ASSIGN(PlatformView); diff --git a/engine/src/flutter/shell/common/platform_view_service_protocol.cc b/engine/src/flutter/shell/common/platform_view_service_protocol.cc deleted file mode 100644 index 8785c4812e..0000000000 --- a/engine/src/flutter/shell/common/platform_view_service_protocol.cc +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/platform_view_service_protocol.h" - -#include - -#include -#include - -#include "flutter/common/threads.h" -#include "flutter/shell/common/picture_serializer.h" -#include "flutter/shell/common/rasterizer.h" -#include "flutter/shell/common/shell.h" -#include "lib/fxl/memory/weak_ptr.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/src/utils/SkBase64.h" - -namespace shell { -namespace { - -constexpr char kViewIdPrefx[] = "_flutterView/"; -constexpr size_t kViewIdPrefxLength = sizeof(kViewIdPrefx) - 1; - -static intptr_t KeyIndex(const char** param_keys, - intptr_t num_params, - const char* key) { - if (param_keys == NULL) { - return -1; - } - for (intptr_t i = 0; i < num_params; i++) { - if (strcmp(param_keys[i], key) == 0) { - return i; - } - } - return -1; -} - -static const char* ValueForKey(const char** param_keys, - const char** param_values, - intptr_t num_params, - const char* key) { - intptr_t index = KeyIndex(param_keys, num_params, key); - if (index < 0) { - return NULL; - } - return param_values[index]; -} - -static bool ErrorMissingParameter(const char** json_object, const char* name) { - const intptr_t kInvalidParams = -32602; - std::stringstream response; - response << "{\"code\":" << std::to_string(kInvalidParams) << ","; - response << "\"message\":\"Invalid params\","; - response << "\"data\": {\"details\": \"" << name << "\"}}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static bool ErrorBadParameter(const char** json_object, - const char* name, - const char* value) { - const intptr_t kInvalidParams = -32602; - std::stringstream response; - response << "{\"code\":" << std::to_string(kInvalidParams) << ","; - response << "\"message\":\"Invalid params\","; - response << "\"data\": {\"details\": \"parameter: " << name << " has a bad "; - response << "value: " << value << "\"}}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static bool ErrorUnknownView(const char** json_object, const char* view_id) { - const intptr_t kInvalidParams = -32602; - std::stringstream response; - response << "{\"code\":" << std::to_string(kInvalidParams) << ","; - response << "\"message\":\"Invalid params\","; - response << "\"data\": {\"details\": \"view not found: " << view_id << "\"}}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static bool ErrorServer(const char** json_object, const char* message) { - const intptr_t kServerError = -32000; - std::stringstream response; - response << "{\"code\":" << std::to_string(kServerError) << ","; - response << "\"message\":\"" << message << "\"}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static void AppendIsolateRef(std::stringstream* stream, - int64_t main_port, - const std::string name) { - *stream << "{\"type\":\"@Isolate\",\"fixedId\":true,\"id\":\"isolates/"; - *stream << main_port << "\",\"name\":\"" << name << "\","; - *stream << "\"number\":\"" << main_port << "\"}"; -} - -static void AppendFlutterView(std::stringstream* stream, - uintptr_t view_id, - int64_t isolate_id, - const std::string isolate_name) { - *stream << "{\"type\":\"FlutterView\", \"id\": \"" << kViewIdPrefx << "0x" - << std::hex << view_id << std::dec << "\""; - if (isolate_id != ILLEGAL_PORT) { - // Append the isolate (if it exists). - *stream << "," - << "\"isolate\":"; - AppendIsolateRef(stream, isolate_id, isolate_name); - } - *stream << "}"; -} - -} // namespace - -void PlatformViewServiceProtocol::RegisterHook(bool running_precompiled_code) { - // Listing of FlutterViews. - Dart_RegisterRootServiceRequestCallback(kListViewsExtensionName, &ListViews, - nullptr); - // Screenshot. - Dart_RegisterRootServiceRequestCallback(kScreenshotExtensionName, &Screenshot, - nullptr); - - // SkPicture Screenshot. - Dart_RegisterRootServiceRequestCallback(kScreenshotSkpExtensionName, - &ScreenshotSkp, nullptr); - - // The following set of service protocol extensions require debug build - if (running_precompiled_code) { - return; - } - Dart_RegisterRootServiceRequestCallback(kRunInViewExtensionName, &RunInView, - nullptr); - Dart_RegisterRootServiceRequestCallback(kSetAssetBundlePathExtensionName, - &SetAssetBundlePath, nullptr); - // [benchmark helper] Wait for the UI Thread to idle. - Dart_RegisterRootServiceRequestCallback(kFlushUIThreadTasksExtensionName, - &FlushUIThreadTasks, nullptr); -} - -const char* PlatformViewServiceProtocol::kRunInViewExtensionName = - "_flutter.runInView"; - -bool PlatformViewServiceProtocol::RunInView(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - const char* view_id = - ValueForKey(param_keys, param_values, num_params, "viewId"); - const char* asset_directory = - ValueForKey(param_keys, param_values, num_params, "assetDirectory"); - const char* main_script = - ValueForKey(param_keys, param_values, num_params, "mainScript"); - const char* packages_file = - ValueForKey(param_keys, param_values, num_params, "packagesFile"); - if (view_id == NULL) { - return ErrorMissingParameter(json_object, "viewId"); - } - if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) { - return ErrorBadParameter(json_object, "viewId", view_id); - } - if (asset_directory == NULL) { - return ErrorMissingParameter(json_object, "assetDirectory"); - } - if (main_script == NULL) { - return ErrorMissingParameter(json_object, "mainScript"); - } - if (packages_file == NULL) { - return ErrorMissingParameter(json_object, "packagesFile"); - } - - // Convert the actual flutter view hex id into a number. - uintptr_t view_id_as_num = - std::stoull((view_id + kViewIdPrefxLength), nullptr, 16); - - // Ask the Shell to run this script in the specified view. This will run a - // task on the UI thread before returning. - Shell& shell = Shell::Shared(); - bool view_existed = false; - Dart_Port main_port = ILLEGAL_PORT; - std::string isolate_name; - shell.RunInPlatformView(view_id_as_num, main_script, packages_file, - asset_directory, &view_existed, &main_port, - &isolate_name); - - if (!view_existed) { - // If the view did not exist this request has definitely failed. - return ErrorUnknownView(json_object, view_id); - } - - // The view existed and the isolate was created. Success. - std::stringstream response; - response << "{\"type\":\"Success\"," - << "\"view\":"; - AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); - response << "}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -const char* PlatformViewServiceProtocol::kListViewsExtensionName = - "_flutter.listViews"; - -bool PlatformViewServiceProtocol::ListViews(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - std::stringstream response; - response << "{\"type\":\"FlutterViewList\",\"views\":["; - bool prefix_comma = false; - Shell::Shared().IteratePlatformViews( - [&response, &prefix_comma](PlatformView* view) -> bool { - if (prefix_comma) { - response << ','; - } else { - prefix_comma = true; - } - AppendFlutterView(&response, reinterpret_cast(view), - view->engine().GetUIIsolateMainPort(), - view->engine().GetUIIsolateName()); - return true; - }); - response << "]}"; - // Copy the response. - *json_object = strdup(response.str().c_str()); - return true; -} - -const char* PlatformViewServiceProtocol::kScreenshotExtensionName = - "_flutter.screenshot"; - -static sk_sp EncodeBitmapAsPNG(const SkBitmap& bitmap) { - return SkEncodeBitmap(bitmap, SkEncodedImageFormat::kPNG, 100); -} - -static fml::WeakPtr GetRandomRasterizer() { - fml::WeakPtr rasterizer; - Shell::Shared().IteratePlatformViews( - [&rasterizer](PlatformView* view) -> bool { - rasterizer = view->rasterizer().GetWeakRasterizerPtr(); - // We just grab the first rasterizer so there is no need to iterate - // further. - return false; - }); - return rasterizer; -} - -bool PlatformViewServiceProtocol::Screenshot(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - fxl::AutoResetWaitableEvent latch; - SkBitmap bitmap; - blink::Threads::Gpu()->PostTask([&latch, &bitmap]() { - ScreenshotGpuTask(&bitmap); - latch.Signal(); - }); - - latch.Wait(); - - sk_sp png(EncodeBitmapAsPNG(bitmap)); - - if (!png) - return ErrorServer(json_object, "can not encode screenshot"); - - size_t b64_size = SkBase64::Encode(png->data(), png->size(), nullptr); - SkAutoTMalloc b64_data(b64_size); - SkBase64::Encode(png->data(), png->size(), b64_data.get()); - - std::stringstream response; - response << "{\"type\":\"Screenshot\"," - << "\"screenshot\":\"" << std::string{b64_data.get(), b64_size} - << "\"}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -void PlatformViewServiceProtocol::ScreenshotGpuTask(SkBitmap* bitmap) { - auto rasterizer = GetRandomRasterizer(); - - if (!rasterizer) - return; - - flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree(); - if (layer_tree == nullptr) - return; - - const SkISize& frame_size = layer_tree->frame_size(); - if (!bitmap->tryAllocN32Pixels(frame_size.width(), frame_size.height())) - return; - - sk_sp surface = SkSurface::MakeRasterDirect( - bitmap->info(), bitmap->getPixels(), bitmap->rowBytes()); - - flow::CompositorContext compositor_context(nullptr); - SkCanvas* canvas = surface->getCanvas(); - flow::CompositorContext::ScopedFrame frame = - compositor_context.AcquireFrame(nullptr, canvas, false); - - canvas->clear(SK_ColorBLACK); - layer_tree->Raster(frame); - canvas->flush(); -} - -const char* PlatformViewServiceProtocol::kScreenshotSkpExtensionName = - "_flutter.screenshotSkp"; - -bool PlatformViewServiceProtocol::ScreenshotSkp(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - fxl::AutoResetWaitableEvent latch; - sk_sp picture; - blink::Threads::Gpu()->PostTask([&latch, &picture]() { - picture = ScreenshotSkpGpuTask(); - latch.Signal(); - }); - - latch.Wait(); - - sk_sp skp_data = picture->serialize(); - - size_t b64_size = - SkBase64::Encode(skp_data->data(), skp_data->size(), nullptr); - SkAutoTMalloc b64_data(b64_size); - SkBase64::Encode(skp_data->data(), skp_data->size(), b64_data.get()); - - std::stringstream response; - response << "{\"type\":\"ScreenshotSkp\"," - << "\"skp\":\"" << std::string{b64_data.get(), b64_size} << "\"}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -sk_sp PlatformViewServiceProtocol::ScreenshotSkpGpuTask() { - auto rasterizer = GetRandomRasterizer(); - - if (!rasterizer) - return nullptr; - - flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree(); - if (layer_tree == nullptr) - return nullptr; - - SkPictureRecorder recorder; - recorder.beginRecording(SkRect::MakeWH(layer_tree->frame_size().width(), - layer_tree->frame_size().height())); - - flow::CompositorContext compositor_context(nullptr); - flow::CompositorContext::ScopedFrame frame = compositor_context.AcquireFrame( - nullptr, recorder.getRecordingCanvas(), false); - layer_tree->Raster(frame); - - return recorder.finishRecordingAsPicture(); -} - -const char* PlatformViewServiceProtocol::kFlushUIThreadTasksExtensionName = - "_flutter.flushUIThreadTasks"; - -// 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. -bool PlatformViewServiceProtocol::FlushUIThreadTasks(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - fxl::AutoResetWaitableEvent latch; - blink::Threads::UI()->PostTask([&latch]() { - // This task is empty because we just need to synchronize this RPC with the - // UI Thread - latch.Signal(); - }); - - latch.Wait(); - - *json_object = strdup("{\"type\":\"Success\"}"); - return true; -} - -const char* PlatformViewServiceProtocol::kSetAssetBundlePathExtensionName = - "_flutter.setAssetBundlePath"; - -bool PlatformViewServiceProtocol::SetAssetBundlePath(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - const char* view_id = - ValueForKey(param_keys, param_values, num_params, "viewId"); - if (view_id == nullptr) { - return ErrorMissingParameter(json_object, "viewId"); - } - if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) { - return ErrorBadParameter(json_object, "viewId", view_id); - } - const char* asset_directory = - ValueForKey(param_keys, param_values, num_params, "assetDirectory"); - if (asset_directory == nullptr) { - return ErrorMissingParameter(json_object, "assetDirectory"); - } - - // Convert the actual flutter view hex id into a number. - uintptr_t view_id_as_num = - std::stoull((view_id + kViewIdPrefxLength), nullptr, 16); - - // Ask the Shell to update asset bundle path in the specified view. - // This will run a task on the UI thread before returning. - Shell& shell = Shell::Shared(); - bool view_existed = false; - Dart_Port main_port = ILLEGAL_PORT; - std::string isolate_name; - shell.SetAssetBundlePathInPlatformView(view_id_as_num, asset_directory, - &view_existed, &main_port, - &isolate_name); - - if (!view_existed) { - // If the view did not exist this request has definitely failed. - return ErrorUnknownView(json_object, view_id); - } - - // The view existed and the isolate was created. Success. - std::stringstream response; - response << "{\"type\":\"Success\"," - << "\"view\":"; - AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); - response << "}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/common/platform_view_service_protocol.h b/engine/src/flutter/shell/common/platform_view_service_protocol.h deleted file mode 100644 index b7f74b56a0..0000000000 --- a/engine/src/flutter/shell/common/platform_view_service_protocol.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_COMMON_VIEW_SERVICE_PROTOCOL_H_ -#define SHELL_COMMON_VIEW_SERVICE_PROTOCOL_H_ - -#include - -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/synchronization/waitable_event.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" -#include "third_party/skia/include/core/SkBitmap.h" - -namespace shell { - -class PlatformViewServiceProtocol { - public: - static void RegisterHook(bool running_precompiled_code); - - private: - static const char* kRunInViewExtensionName; - // It should be invoked from the VM Service and and blocks it until previous - // UI thread tasks are processed. - static bool RunInView(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - - static const char* kListViewsExtensionName; - static bool ListViews(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - - static const char* kScreenshotExtensionName; - // It should be invoked from the VM Service and and blocks it until previous - // GPU thread tasks are processed. - static bool Screenshot(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - static void ScreenshotGpuTask(SkBitmap* bitmap); - - static const char* kScreenshotSkpExtensionName; - static bool ScreenshotSkp(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - static sk_sp ScreenshotSkpGpuTask(); - - // 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 previous - // GPU thread tasks are processed. - static const char* kFlushUIThreadTasksExtensionName; - static bool FlushUIThreadTasks(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - - static const char* kSetAssetBundlePathExtensionName; - static bool SetAssetBundlePath(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); -}; - -} // namespace shell - -#endif // SHELL_COMMON_VIEW_SERVICE_PROTOCOL_H_ diff --git a/engine/src/flutter/shell/common/rasterizer.cc b/engine/src/flutter/shell/common/rasterizer.cc index 905ee6285c..5a519b3efe 100644 --- a/engine/src/flutter/shell/common/rasterizer.cc +++ b/engine/src/flutter/shell/common/rasterizer.cc @@ -4,8 +4,205 @@ #include "flutter/shell/common/rasterizer.h" +#include + +#include "third_party/skia/include/core/SkEncodedImageFormat.h" +#include "third_party/skia/include/core/SkImageEncoder.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/src/utils/SkBase64.h" + namespace shell { +Rasterizer::Rasterizer(blink::TaskRunners task_runners) + : task_runners_(std::move(task_runners)), weak_factory_(this) { + weak_prototype_ = weak_factory_.GetWeakPtr(); +} + Rasterizer::~Rasterizer() = default; +fml::WeakPtr Rasterizer::GetWeakPtr() const { + return weak_prototype_; +} + +void Rasterizer::Setup(std::unique_ptr surface) { + surface_ = std::move(surface); +} + +void Rasterizer::Teardown() { + surface_.reset(); + last_layer_tree_.reset(); +} + +flow::TextureRegistry* Rasterizer::GetTextureRegistry() { + if (!surface_) { + return nullptr; + } + + return &(surface_->GetCompositorContext().texture_registry()); +} + +flow::LayerTree* Rasterizer::GetLastLayerTree() { + return last_layer_tree_.get(); +} + +void Rasterizer::DrawLastLayerTree() { + if (!last_layer_tree_ || !surface_) { + return; + } + DrawToSurface(*last_layer_tree_); +} + +void Rasterizer::Draw( + fxl::RefPtr> pipeline) { + TRACE_EVENT0("flutter", "GPURasterizer::Draw"); + + flutter::Pipeline::Consumer consumer = + std::bind(&Rasterizer::DoDraw, this, std::placeholders::_1); + + // Consume as many pipeline items as possible. But yield the event loop + // between successive tries. + switch (pipeline->Consume(consumer)) { + case flutter::PipelineConsumeResult::MoreAvailable: { + task_runners_.GetGPUTaskRunner()->PostTask( + [weak_this = weak_factory_.GetWeakPtr(), pipeline]() { + if (weak_this) { + weak_this->Draw(pipeline); + } + }); + break; + } + default: + break; + } +} + +void Rasterizer::DoDraw(std::unique_ptr layer_tree) { + if (!layer_tree || !surface_) { + return; + } + + if (DrawToSurface(*layer_tree)) { + last_layer_tree_ = std::move(layer_tree); + } +} + +bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) { + FXL_DCHECK(surface_); + + auto frame = surface_->AcquireFrame(layer_tree.frame_size()); + + if (frame == nullptr) { + return false; + } + + auto& compositor_context = surface_->GetCompositorContext(); + + // There is no way for the compositor to know how long the layer tree + // construction took. Fortunately, the layer tree does. Grab that time + // for instrumentation. + compositor_context.engine_time().SetLapTime(layer_tree.construction_time()); + + auto compositor_frame = compositor_context.AcquireFrame( + surface_->GetContext(), frame->SkiaCanvas(), true); + + if (compositor_frame && compositor_frame->Raster(layer_tree, false)) { + frame->Submit(); + FireNextFrameCallbackIfPresent(); + return true; + } + + return false; +} + +static sk_sp ScreenshotLayerTreeAsPicture(flow::LayerTree* tree) { + FXL_DCHECK(tree != nullptr); + SkPictureRecorder recorder; + recorder.beginRecording( + SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height())); + + flow::CompositorContext compositor_context; + auto frame = compositor_context.AcquireFrame( + nullptr, recorder.getRecordingCanvas(), false); + + frame->Raster(*tree, true); + + return recorder.finishRecordingAsPicture(); +} + +static sk_sp ScreenshotLayerTreeAsImage(flow::LayerTree* tree, + bool compressed) { + const SkISize& frame_size = tree->frame_size(); + SkBitmap bitmap; + if (!bitmap.tryAllocN32Pixels(frame_size.width(), frame_size.height())) { + return nullptr; + } + auto bitmap_surface = SkSurface::MakeRasterDirect( + bitmap.info(), bitmap.getPixels(), bitmap.rowBytes()); + flow::CompositorContext compositor_context; + auto canvas = bitmap_surface->getCanvas(); + auto frame = compositor_context.AcquireFrame(nullptr, canvas, false); + canvas->clear(SK_ColorBLACK); + frame->Raster(*tree, true); + canvas->flush(); + if (compressed) { + return SkEncodeBitmap(bitmap, SkEncodedImageFormat::kPNG, 100); + } else { + return SkData::MakeWithCopy(bitmap.getPixels(), bitmap.computeByteSize()); + } + return nullptr; +} + +Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( + Rasterizer::ScreenshotType type, + bool base64_encode) { + auto layer_tree = GetLastLayerTree(); + if (layer_tree == nullptr) { + FXL_DLOG(INFO) << "Last layer tree was null when screenshotting."; + return {}; + } + + sk_sp data = nullptr; + + switch (type) { + case ScreenshotType::SkiaPicture: + data = ScreenshotLayerTreeAsPicture(layer_tree)->serialize(); + break; + case ScreenshotType::UncompressedImage: + data = ScreenshotLayerTreeAsImage(layer_tree, false); + break; + case ScreenshotType::CompressedImage: + data = ScreenshotLayerTreeAsImage(layer_tree, true); + break; + } + + if (data == nullptr) { + FXL_DLOG(INFO) << "Sceenshot data was null."; + return {}; + } + + if (base64_encode) { + size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr); + auto b64_data = SkData::MakeUninitialized(b64_size); + SkBase64::Encode(data->data(), data->size(), b64_data->writable_data()); + return Rasterizer::Screenshot{b64_data, layer_tree->frame_size()}; + } + + return Rasterizer::Screenshot{data, layer_tree->frame_size()}; +} + +void Rasterizer::SetNextFrameCallback(fxl::Closure callback) { + next_frame_callback_ = callback; +} + +void Rasterizer::FireNextFrameCallbackIfPresent() { + if (!next_frame_callback_) { + return; + } + // It is safe for the callback to set a new callback. + auto callback = next_frame_callback_; + next_frame_callback_ = nullptr; + callback(); +} + } // namespace shell diff --git a/engine/src/flutter/shell/common/rasterizer.h b/engine/src/flutter/shell/common/rasterizer.h index 6f45f49d81..908717b74c 100644 --- a/engine/src/flutter/shell/common/rasterizer.h +++ b/engine/src/flutter/shell/common/rasterizer.h @@ -7,6 +7,7 @@ #include +#include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" @@ -16,34 +17,64 @@ namespace shell { -class Rasterizer { +class Rasterizer final { public: - virtual ~Rasterizer(); + Rasterizer(blink::TaskRunners task_runners); - virtual void Setup(std::unique_ptr surface_or_null, - fxl::Closure rasterizer_continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) = 0; + ~Rasterizer(); - virtual void Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) = 0; + void Setup(std::unique_ptr surface); - virtual void Clear(SkColor color, const SkISize& size) = 0; + void Teardown(); - virtual fml::WeakPtr GetWeakRasterizerPtr() = 0; + fml::WeakPtr GetWeakPtr() const; - virtual flow::LayerTree* GetLastLayerTree() = 0; + flow::LayerTree* GetLastLayerTree(); - virtual void DrawLastLayerTree() = 0; + void DrawLastLayerTree(); - virtual flow::TextureRegistry& GetTextureRegistry() = 0; + flow::TextureRegistry* GetTextureRegistry(); - virtual void Draw( - fxl::RefPtr> pipeline) = 0; + void Draw(fxl::RefPtr> pipeline); - // Set a callback to be called once when the next frame is drawn. - virtual void AddNextFrameCallback(fxl::Closure nextFrameCallback) = 0; + enum class ScreenshotType { + SkiaPicture, + UncompressedImage, // In kN32_SkColorType format + CompressedImage, + }; - virtual void SetTextureRegistry(flow::TextureRegistry* textureRegistry) = 0; + struct Screenshot { + sk_sp data; + SkISize frame_size = SkISize::MakeEmpty(); + + Screenshot() {} + + Screenshot(sk_sp p_data, SkISize p_size) + : data(std::move(p_data)), frame_size(p_size) {} + }; + + Screenshot ScreenshotLastLayerTree(ScreenshotType type, bool base64_encode); + + // Sets a callback that will be executed after the next frame is submitted to + // the surface on the GPU task runner. + void SetNextFrameCallback(fxl::Closure callback); + + private: + blink::TaskRunners task_runners_; + std::unique_ptr surface_; + std::unique_ptr compositor_context_; + std::unique_ptr last_layer_tree_; + fxl::Closure next_frame_callback_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + void DoDraw(std::unique_ptr layer_tree); + + bool DrawToSurface(flow::LayerTree& layer_tree); + + void FireNextFrameCallbackIfPresent(); + + FXL_DISALLOW_COPY_AND_ASSIGN(Rasterizer); }; } // namespace shell diff --git a/engine/src/flutter/shell/common/run_configuration.cc b/engine/src/flutter/shell/common/run_configuration.cc new file mode 100644 index 0000000000..7fb385fb4d --- /dev/null +++ b/engine/src/flutter/shell/common/run_configuration.cc @@ -0,0 +1,80 @@ +// Copyright 2017 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. + +#include "flutter/shell/common/run_configuration.h" + +#include + +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/assets/zip_asset_store.h" +#include "flutter/fml/file.h" +#include "flutter/runtime/dart_vm.h" + +namespace shell { + +RunConfiguration RunConfiguration::InferFromSettings( + const blink::Settings& settings) { + auto asset_manager = fxl::MakeRefCounted(); + + asset_manager->PushBack(std::make_unique( + fml::Duplicate(settings.assets_dir))); + + asset_manager->PushBack( + std::make_unique(fml::OpenFile( + settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + + asset_manager->PushBack( + std::make_unique(settings.flx_path)); + + return {IsolateConfiguration::InferFromSettings(settings, asset_manager), + asset_manager}; +} + +RunConfiguration::RunConfiguration( + std::unique_ptr configuration) + : RunConfiguration(std::move(configuration), + fxl::MakeRefCounted()) {} + +RunConfiguration::RunConfiguration( + std::unique_ptr configuration, + fxl::RefPtr asset_manager) + : isolate_configuration_(std::move(configuration)), + asset_manager_(std::move(asset_manager)) {} + +RunConfiguration::RunConfiguration(RunConfiguration&&) = default; + +RunConfiguration::~RunConfiguration() = default; + +bool RunConfiguration::IsValid() const { + return asset_manager_ && isolate_configuration_; +} + +bool RunConfiguration::AddAssetResolver( + std::unique_ptr resolver) { + if (!resolver || !resolver->IsValid()) { + return false; + } + + asset_manager_->PushBack(std::move(resolver)); + return true; +} + +void RunConfiguration::SetEntrypoint(std::string entrypoint) { + entrypoint_ = std::move(entrypoint); +} + +fxl::RefPtr RunConfiguration::GetAssetManager() const { + return asset_manager_; +} + +const std::string& RunConfiguration::GetEntrypoint() const { + return entrypoint_; +} + +std::unique_ptr +RunConfiguration::TakeIsolateConfiguration() { + return std::move(isolate_configuration_); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/common/run_configuration.h b/engine/src/flutter/shell/common/run_configuration.h new file mode 100644 index 0000000000..59aa075666 --- /dev/null +++ b/engine/src/flutter/shell/common/run_configuration.h @@ -0,0 +1,56 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_COMMON_RUN_CONFIGURATION_H_ +#define FLUTTER_SHELL_COMMON_RUN_CONFIGURATION_H_ + +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/assets/asset_resolver.h" +#include "flutter/common/settings.h" +#include "flutter/fml/mapping.h" +#include "flutter/shell/common/isolate_configuration.h" +#include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class RunConfiguration { + public: + static RunConfiguration InferFromSettings(const blink::Settings& settings); + + RunConfiguration(std::unique_ptr configuration); + + RunConfiguration(std::unique_ptr configuration, + fxl::RefPtr asset_manager); + + RunConfiguration(RunConfiguration&&); + + ~RunConfiguration(); + + bool IsValid() const; + + bool AddAssetResolver(std::unique_ptr resolver); + + void SetEntrypoint(std::string entrypoint); + + fxl::RefPtr GetAssetManager() const; + + const std::string& GetEntrypoint() const; + + std::unique_ptr TakeIsolateConfiguration(); + + private: + std::unique_ptr isolate_configuration_; + fxl::RefPtr asset_manager_; + std::string entrypoint_ = "main"; + + FXL_DISALLOW_COPY_AND_ASSIGN(RunConfiguration); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_RUN_CONFIGURATION_H_ diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 14f8fee84b..82591c6d49 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -2,362 +2,889 @@ // 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/common/settings.h" -#include "flutter/common/threads.h" +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/fml/file.h" #include "flutter/fml/icu_util.h" #include "flutter/fml/message_loop.h" -#include "flutter/fml/trace_event.h" -#include "flutter/runtime/dart_init.h" +#include "flutter/glue/trace_event.h" +#include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/engine.h" -#include "flutter/shell/common/platform_view_service_protocol.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" +#include "flutter/shell/common/vsync_waiter.h" +#include "lib/fxl/files/path.h" #include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/logging.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/core/SkGraphics.h" +#ifdef ERROR +#undef ERROR +#endif + namespace shell { -namespace { -static Shell* g_shell = nullptr; +std::unique_ptr Shell::CreateShellOnPlatformThread( + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer) { + if (!task_runners.IsValid()) { + return nullptr; + } -template -bool GetSwitchValue(const fxl::CommandLine& command_line, - Switch sw, - T* result) { - std::string switch_string; + auto shell = std::unique_ptr(new Shell(task_runners, settings)); - if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) { + // 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. + fxl::AutoResetWaitableEvent io_latch; + std::unique_ptr io_manager; + fml::WeakPtr resource_context; + fxl::RefPtr unref_queue; + auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(); + fml::TaskRunner::RunNowOrPostTask( + io_task_runner, + [&io_latch, // + &io_manager, // + &resource_context, // + &unref_queue, // + &platform_view, // + io_task_runner // + ]() { + io_manager = std::make_unique( + platform_view->CreateResourceContext(), io_task_runner); + resource_context = io_manager->GetResourceContext(); + unref_queue = io_manager->GetSkiaUnrefQueue(); + io_latch.Signal(); + }); + io_latch.Wait(); + + // Create the rasterizer on the GPU thread. + fxl::AutoResetWaitableEvent gpu_latch; + std::unique_ptr rasterizer; + fml::TaskRunner::RunNowOrPostTask( + task_runners.GetGPUTaskRunner(), [&gpu_latch, // + &rasterizer, // + on_create_rasterizer, // + shell = shell.get() // + ]() { + if (auto new_rasterizer = on_create_rasterizer(*shell)) { + rasterizer = std::move(new_rasterizer); + } + gpu_latch.Signal(); + }); + + // Create the engine on the UI thread. + fxl::AutoResetWaitableEvent ui_latch; + std::unique_ptr engine; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetUITaskRunner(), + fxl::MakeCopyable([&ui_latch, // + &engine, // + shell = shell.get(), // + vsync_waiter = std::move(vsync_waiter), // + resource_context = std::move(resource_context), // + unref_queue = std::move(unref_queue) // + ]() mutable { + 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 = std::make_unique(*shell, // + shell->GetDartVM(), // + task_runners, // + shell->GetSettings(), // + std::move(animator), // + std::move(resource_context), // + std::move(unref_queue) // + ); + ui_latch.Signal(); + })); + + gpu_latch.Wait(); + ui_latch.Wait(); + // We are already on the platform thread. So there is no platform latch to + // wait on. + + if (!shell->Setup(std::move(platform_view), // + std::move(engine), // + std::move(rasterizer), // + std::move(io_manager)) // + ) { + return nullptr; + } + + return shell; +} + +std::unique_ptr Shell::Create( + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer) { + if (!task_runners.IsValid() || !on_create_platform_view || + !on_create_rasterizer) { + return nullptr; + } + + fxl::AutoResetWaitableEvent latch; + std::unique_ptr shell; + fml::TaskRunner::RunNowOrPostTask( + task_runners.GetPlatformTaskRunner(), + [&latch, &shell, task_runners = std::move(task_runners), settings, + on_create_platform_view, on_create_rasterizer]() { + shell = CreateShellOnPlatformThread(std::move(task_runners), settings, + on_create_platform_view, + on_create_rasterizer); + latch.Signal(); + }); + latch.Wait(); + return shell; +} + +Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) + : task_runners_(std::move(task_runners)), + settings_(std::move(settings)), + vm_(blink::DartVM::ForProcess(settings_)) { + FXL_DCHECK(task_runners_.IsValid()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + if (settings_.icu_data_path.size() != 0) { + fml::icu::InitializeICU(settings_.icu_data_path); + } else { + FXL_DLOG(WARNING) << "Skipping ICU initialization in the shell."; + } + + if (settings_.trace_skia) { + InitSkiaEventTracer(settings_.trace_skia); + } + + if (!settings_.skia_deterministic_rendering_on_cpu) { + SkGraphics::Init(); + } else { + FXL_DLOG(INFO) << "Skia deterministic rendering is enabled."; + } + + // Install service protocol handlers. + + service_protocol_handlers_[blink::ServiceProtocol::kScreenshotExtensionName + .ToString()] = { + task_runners_.GetGPUTaskRunner(), + std::bind(&Shell::OnServiceProtocolScreenshot, this, + std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_[blink::ServiceProtocol::kScreenshotSkpExtensionName + .ToString()] = { + task_runners_.GetGPUTaskRunner(), + std::bind(&Shell::OnServiceProtocolScreenshotSKP, this, + std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_[blink::ServiceProtocol::kRunInViewExtensionName + .ToString()] = { + task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1, + std::placeholders::_2)}; + service_protocol_handlers_ + [blink::ServiceProtocol::kFlushUIThreadTasksExtensionName.ToString()] = { + task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this, + std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_ + [blink::ServiceProtocol::kSetAssetBundlePathExtensionName.ToString()] = { + task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, + std::placeholders::_1, std::placeholders::_2)}; +} + +Shell::~Shell() { + if (auto vm = blink::DartVM::ForProcessIfInitialized()) { + vm->GetServiceProtocol().RemoveHandler(this); + } + + fxl::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch; + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + fxl::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable { + engine.reset(); + ui_latch.Signal(); + })); + ui_latch.Wait(); + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), + fxl::MakeCopyable( + [rasterizer = std::move(rasterizer_), &gpu_latch]() mutable { + rasterizer.reset(); + gpu_latch.Signal(); + })); + gpu_latch.Wait(); + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetIOTaskRunner(), + fxl::MakeCopyable( + [io_manager = std::move(io_manager_), &io_latch]() mutable { + io_manager.reset(); + 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(), + fxl::MakeCopyable([platform_view = std::move(platform_view_), + &platform_latch]() mutable { + platform_view.reset(); + platform_latch.Signal(); + })); + platform_latch.Wait(); +} + +bool Shell::IsSetup() const { + return is_setup_; +} + +bool Shell::Setup(std::unique_ptr platform_view, + std::unique_ptr engine, + std::unique_ptr rasterizer, + std::unique_ptr io_manager) { + if (is_setup_) { return false; } - std::stringstream stream(switch_string); - T value = 0; - if (stream >> value) { - *result = value; - return true; + if (!platform_view || !engine || !rasterizer || !io_manager) { + return false; } + platform_view_ = std::move(platform_view); + engine_ = std::move(engine); + rasterizer_ = std::move(rasterizer); + io_manager_ = std::move(io_manager); + + is_setup_ = true; + + if (auto vm = blink::DartVM::ForProcessIfInitialized()) { + vm->GetServiceProtocol().AddHandler(this); + } + + return true; +} + +const blink::Settings& Shell::GetSettings() const { + return settings_; +} + +const blink::TaskRunners& Shell::GetTaskRunners() const { + return task_runners_; +} + +fml::WeakPtr Shell::GetRasterizer() { + FXL_DCHECK(is_setup_); + return rasterizer_->GetWeakPtr(); +} + +fml::WeakPtr Shell::GetEngine() { + FXL_DCHECK(is_setup_); + return engine_->GetWeakPtr(); +} + +fml::WeakPtr Shell::GetPlatformView() { + FXL_DCHECK(is_setup_); + return platform_view_->GetWeakPtr(); +} + +const blink::DartVM& Shell::GetDartVM() const { + return *vm_; +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewCreated(const PlatformView& view, + std::unique_ptr surface) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_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. + + fxl::AutoResetWaitableEvent latch; + auto gpu_task = fxl::MakeCopyable([rasterizer = rasterizer_->GetWeakPtr(), // + surface = std::move(surface), // + &latch]() mutable { + if (rasterizer) { + rasterizer->Setup(std::move(surface)); + } + // Step 2: All done. Signal the latch that the platform thread is waiting + // on. + latch.Signal(); + }); + + auto ui_task = [engine = engine_->GetWeakPtr(), // + gpu_task_runner = task_runners_.GetGPUTaskRunner(), // + gpu_task // + ] { + if (engine) { + engine->OnOutputSurfaceCreated(); + } + // Step 1: Next, tell the GPU thread that it should create a surface for its + // rasterizer. + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + }; + + // Step 0: Post a task onto the UI thread to tell the engine that it has an + // output surface. + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); + latch.Wait(); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDestroyed(const PlatformView& view) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_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. + + fxl::AutoResetWaitableEvent latch; + + auto gpu_task = [rasterizer = rasterizer_->GetWeakPtr(), &latch]() { + if (rasterizer) { + rasterizer->Teardown(); + } + // Step 2: All done. Signal the latch that the platform thread is waiting + // on. + latch.Signal(); + }; + + auto ui_task = [engine = engine_->GetWeakPtr(), + gpu_task_runner = task_runners_.GetGPUTaskRunner(), + gpu_task]() { + if (engine) { + engine->OnOutputSurfaceDestroyed(); + } + // Step 1: Next, tell the GPU thread that its rasterizer should suspend + // access to the underlying surface. + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + }; + + // 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(); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewSetViewportMetrics( + const PlatformView& view, + const blink::ViewportMetrics& metrics) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), metrics]() { + if (engine) { + engine->SetViewportMetrics(metrics); + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDispatchPlatformMessage( + const PlatformView& view, + fxl::RefPtr message) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), message = std::move(message)] { + if (engine) { + engine->DispatchPlatformMessage(std::move(message)); + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDispatchPointerDataPacket( + const PlatformView& view, + std::unique_ptr packet) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + task_runners_.GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = engine_->GetWeakPtr(), packet = std::move(packet)] { + if (engine) { + engine->DispatchPointerDataPacket(*packet); + } + })); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDispatchSemanticsAction(const PlatformView& view, + int32_t id, + blink::SemanticsAction action, + std::vector args) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_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)); + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewSetSemanticsEnabled(const PlatformView& view, + bool enabled) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), enabled] { + if (engine) { + engine->SetSemanticsEnabled(enabled); + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewRegisterTexture( + const PlatformView& view, + std::shared_ptr texture) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), texture] { + if (rasterizer) { + if (auto registry = rasterizer->GetTextureRegistry()) { + registry->RegisterTexture(texture); + } + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewUnregisterTexture(const PlatformView& view, + int64_t texture_id) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { + if (rasterizer) { + if (auto registry = rasterizer->GetTextureRegistry()) { + registry->UnregisterTexture(texture_id); + } + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewMarkTextureFrameAvailable(const PlatformView& view, + int64_t texture_id) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + // Tell the rasterizer that one of its textures has a new frame available. + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { + auto registry = rasterizer->GetTextureRegistry(); + + if (!registry) { + return; + } + + auto texture = registry->GetTexture(texture_id); + + if (!texture) { + return; + } + + texture->MarkNewFrameAvailable(); + }); + + // Schedule a new frame without having to rebuild the layer tree. + task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() { + if (engine) { + engine->ScheduleFrame(false); + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewSetNextFrameCallback(const PlatformView& view, + fxl::Closure closure) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), closure = std::move(closure)]() { + if (rasterizer) { + rasterizer->SetNextFrameCallback(std::move(closure)); + } + }); +} + +// |shell::Animator::Delegate| +void Shell::OnAnimatorBeginFrame(const Animator& animator, + fxl::TimePoint frame_time) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + if (engine_) { + engine_->BeginFrame(frame_time); + } +} + +// |shell::Animator::Delegate| +void Shell::OnAnimatorNotifyIdle(const Animator& animator, int64_t deadline) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + if (engine_) { + engine_->NotifyIdle(deadline); + } +} + +// |shell::Animator::Delegate| +void Shell::OnAnimatorDraw( + const Animator& animator, + fxl::RefPtr> pipeline) { + FXL_DCHECK(is_setup_); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), + pipeline = std::move(pipeline)]() { + if (rasterizer) { + rasterizer->Draw(pipeline); + } + }); +} + +// |shell::Animator::Delegate| +void Shell::OnAnimatorDrawLastLayerTree(const Animator& animator) { + FXL_DCHECK(is_setup_); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr()]() { + if (rasterizer) { + rasterizer->DrawLastLayerTree(); + } + }); +} + +// |shell::Engine::Delegate| +void Shell::OnEngineUpdateSemantics(const Engine& engine, + blink::SemanticsNodeUpdates update) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetPlatformTaskRunner()->PostTask( + [view = platform_view_->GetWeakPtr(), update = std::move(update)] { + if (view) { + view->UpdateSemantics(std::move(update)); + } + }); +} + +// |shell::Engine::Delegate| +void Shell::OnEngineHandlePlatformMessage( + const Engine& engine, + fxl::RefPtr message) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetPlatformTaskRunner()->PostTask( + [view = platform_view_->GetWeakPtr(), message = std::move(message)]() { + if (view) { + view->HandlePlatformMessage(std::move(message)); + } + }); +} + +// |blink::ServiceProtocol::Handler| +fxl::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( + fxl::StringView method) const { + FXL_DCHECK(is_setup_); + auto found = service_protocol_handlers_.find(method.ToString()); + if (found != service_protocol_handlers_.end()) { + return found->second.first; + } + return task_runners_.GetUITaskRunner(); +} + +// |blink::ServiceProtocol::Handler| +bool Shell::HandleServiceProtocolMessage( + fxl::StringView method, // one if the extension names specified above. + const ServiceProtocolMap& params, + rapidjson::Document& response) { + auto found = service_protocol_handlers_.find(method.ToString()); + if (found != service_protocol_handlers_.end()) { + return found->second.second(params, response); + } return false; } -} // namespace - -Shell::Shell(fxl::CommandLine command_line) - : command_line_(std::move(command_line)) { - FXL_DCHECK(!g_shell); - - gpu_thread_.reset(new fml::Thread("gpu_thread")); - ui_thread_.reset(new fml::Thread("ui_thread")); - io_thread_.reset(new fml::Thread("io_thread")); - - // Since we are not using fml::Thread, we need to initialize the message loop - // manually. - fml::MessageLoop::EnsureInitializedForCurrentThread(); - blink::Threads threads(fml::MessageLoop::GetCurrent().GetTaskRunner(), - gpu_thread_->GetTaskRunner(), - ui_thread_->GetTaskRunner(), - io_thread_->GetTaskRunner()); - blink::Threads::Set(threads); - - blink::Threads::Gpu()->PostTask([this]() { InitGpuThread(); }); - blink::Threads::UI()->PostTask([this]() { InitUIThread(); }); - - blink::SetRegisterNativeServiceProtocolExtensionHook( - PlatformViewServiceProtocol::RegisterHook); +// |blink::ServiceProtocol::Handler| +blink::ServiceProtocol::Handler::Description +Shell::GetServiceProtocolDescription() const { + return { + engine_->GetUIIsolateMainPort(), + engine_->GetUIIsolateName(), + }; } -Shell::~Shell() {} - -void Shell::InitStandalone(fxl::CommandLine command_line, - std::string icu_data_path, - std::string application_library_path, - std::string bundle_path) { - TRACE_EVENT0("flutter", "Shell::InitStandalone"); - - fml::icu::InitializeICU(icu_data_path); - - if (!command_line.HasOption( - FlagForSwitch(Switch::SkiaDeterministicRendering))) - SkGraphics::Init(); - - blink::Settings settings; - settings.application_library_path = application_library_path; - - // Enable Observatory - settings.enable_observatory = - !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory)); - - // Set Observatory Port - if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) { - if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort, - &settings.observatory_port)) { - FXL_LOG(INFO) - << "Observatory port specified was malformed. Will default to " - << settings.observatory_port; - } - } - - // Checked mode overrides. - settings.dart_non_checked_mode = - command_line.HasOption(FlagForSwitch(Switch::DartNonCheckedMode)); - - settings.ipv6 = command_line.HasOption(FlagForSwitch(Switch::IPv6)); - - settings.start_paused = - command_line.HasOption(FlagForSwitch(Switch::StartPaused)); - - settings.enable_dart_profiling = - command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling)); - - settings.enable_software_rendering = - command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering)); - - settings.using_blink = - command_line.HasOption(FlagForSwitch(Switch::EnableBlink)); - - settings.endless_trace_buffer = - command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer)); - - settings.trace_startup = - command_line.HasOption(FlagForSwitch(Switch::TraceStartup)); - - command_line.GetOptionValue(FlagForSwitch(Switch::AotSnapshotPath), - &settings.aot_snapshot_path); - - command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotData), - &settings.aot_vm_snapshot_data_filename); - - command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotInstructions), - &settings.aot_vm_snapshot_instr_filename); - - command_line.GetOptionValue(FlagForSwitch(Switch::AotIsolateSnapshotData), - &settings.aot_isolate_snapshot_data_filename); - - command_line.GetOptionValue(FlagForSwitch(Switch::AotSharedLibraryPath), - &settings.aot_shared_library_path); - - command_line.GetOptionValue( - FlagForSwitch(Switch::AotIsolateSnapshotInstructions), - &settings.aot_isolate_snapshot_instr_filename); - - command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath), - &settings.temp_directory_path); - - settings.use_test_fonts = - command_line.HasOption(FlagForSwitch(Switch::UseTestFonts)); - - std::string all_dart_flags; - if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags), - &all_dart_flags)) { - std::stringstream stream(all_dart_flags); - std::istream_iterator end; - for (std::istream_iterator it(stream); it != end; ++it) - settings.dart_flags.push_back(*it); - } - - command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag); - - blink::Settings::Set(settings); - - Init(std::move(command_line), bundle_path); -} - -void Shell::Init(fxl::CommandLine command_line, - const std::string& bundle_path) { -#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - bool trace_skia = command_line.HasOption(FlagForSwitch(Switch::TraceSkia)); - InitSkiaEventTracer(trace_skia); -#endif - - FXL_DCHECK(!g_shell); - g_shell = new Shell(std::move(command_line)); - blink::Threads::UI()->PostTask( - [bundle_path]() { Engine::Init(bundle_path); }); -} - -Shell& Shell::Shared() { - FXL_DCHECK(g_shell); - return *g_shell; -} - -const fxl::CommandLine& Shell::GetCommandLine() const { - return command_line_; -} - -void Shell::InitGpuThread() { - gpu_thread_checker_.reset(new fxl::ThreadChecker()); -} - -void Shell::InitUIThread() { - ui_thread_checker_.reset(new fxl::ThreadChecker()); -} - -void Shell::AddPlatformView(PlatformView* platform_view) { - if (platform_view == nullptr) { - return; - } - std::lock_guard lock(platform_views_mutex_); - platform_views_.insert(platform_view); -} - -void Shell::RemovePlatformView(PlatformView* platform_view) { - if (platform_view == nullptr) { - return; - } - std::lock_guard lock(platform_views_mutex_); - platform_views_.erase(platform_view); -} - -void Shell::IteratePlatformViews( - std::function iterator) const { - if (iterator == nullptr) { - return; - } - std::lock_guard lock(platform_views_mutex_); - for (PlatformView* view : platform_views_) { - if (!iterator(view)) { - return; - } +static void ServiceProtocolParameterError(rapidjson::Document& response, + std::string parameter_name) { + 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", parameter_name, allocator); + response.AddMember("data", details, allocator); } } -void Shell::RunInPlatformView(uintptr_t view_id, - const char* main_script, - const char* packages_file, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name) { +// Service protocol handler +bool Shell::OnServiceProtocolScreenshot( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + auto screenshot = rasterizer_->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::CompressedImage, true); + if (screenshot.data) { + response.SetObject(); + auto& allocator = response.GetAllocator(); + response.AddMember("type", "Screenshot", allocator); + rapidjson::Value image; + image.SetString(static_cast(screenshot.data->data()), + screenshot.data->size(), allocator); + response.AddMember("screenshot", image, allocator); + return true; + } + ServiceProtocolParameterError(response, + "Could not capture image screenshot."); + return false; +} + +// Service protocol handler +bool Shell::OnServiceProtocolScreenshotSKP( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + auto screenshot = rasterizer_->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::SkiaPicture, true); + if (screenshot.data) { + response.SetObject(); + auto& allocator = response.GetAllocator(); + response.AddMember("type", "ScreenshotSkp", allocator); + rapidjson::Value skp; + skp.SetString(static_cast(screenshot.data->data()), + screenshot.data->size(), allocator); + response.AddMember("skp", skp, allocator); + return true; + } + ServiceProtocolParameterError(response, "Could not capture SKP screenshot."); + return false; +} + +static bool FileNameIsDill(const std::string& name) { + const std::string suffix = ".dill"; + + if (name.size() < suffix.size()) { + return false; + } + + if (name.rfind(suffix, name.size()) == name.size() - suffix.size()) { + return true; + } + return false; +} + +// Service protocol handler +bool Shell::OnServiceProtocolRunInView( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + if (params.count("mainScript") == 0) { + ServiceProtocolParameterError(response, + "'mainScript' parameter is missing."); + return false; + } + + // TODO(chinmaygarde): In case of hot-reload from .dill files, the packages + // file is ignored. Currently, the tool is passing a junk packages file to + // pass this check. Update the service protocol interface and remove this + // workaround. + if (params.count("packagesFile") == 0) { + ServiceProtocolParameterError(response, + "'packagesFile' parameter is missing."); + return false; + } + + if (params.count("assetDirectory") == 0) { + ServiceProtocolParameterError(response, + "'assetDirectory' parameter is missing."); + return false; + } + + auto main_script_file = + files::AbsolutePath(params.at("mainScript").ToString()); + + auto isolate_configuration = + FileNameIsDill(main_script_file) + ? IsolateConfiguration::CreateForSnapshot( + std::make_unique(main_script_file, false)) + : IsolateConfiguration::CreateForSource( + main_script_file, params.at("packagesFile").ToString()); + + RunConfiguration configuration(std::move(isolate_configuration)); + + configuration.AddAssetResolver(std::make_unique( + fml::OpenFile(params.at("assetDirectory").ToString().c_str(), + fml::OpenPermission::kRead, true))); + + 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 { + FXL_DLOG(ERROR) << "Could not run configuration in engine."; + response.AddMember("type", "Failure", allocator); + return false; + } + + FXL_DCHECK(false); + return false; +} + +// Service protocol handler +bool Shell::OnServiceProtocolFlushUIThreadTasks( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_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; +} + +// Service protocol handler +bool Shell::OnServiceProtocolSetAssetBundlePath( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_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 = fxl::MakeRefCounted(); + + asset_manager->PushFront(std::make_unique( + fml::OpenFile(params.at("assetDirectory").ToString().c_str(), + fml::OpenPermission::kRead, true))); + + 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 { + FXL_DLOG(ERROR) << "Could not update asset directory."; + response.AddMember("type", "Failure", allocator); + return false; + } + + FXL_DCHECK(false); + return false; +} + +Rasterizer::Screenshot Shell::Screenshot( + Rasterizer::ScreenshotType screenshot_type, + bool base64_encode) { + TRACE_EVENT0("flutter", "Shell::Screenshot"); fxl::AutoResetWaitableEvent latch; - FXL_DCHECK(view_id != 0); - FXL_DCHECK(main_script); - FXL_DCHECK(packages_file); - FXL_DCHECK(asset_directory); - FXL_DCHECK(view_existed); - - blink::Threads::UI()->PostTask([this, view_id, main_script, packages_file, - asset_directory, view_existed, - dart_isolate_id, isolate_name, &latch]() { - RunInPlatformViewUIThread(view_id, main_script, packages_file, - asset_directory, view_existed, dart_isolate_id, - isolate_name, &latch); - }); - latch.Wait(); -} - -void Shell::RunInPlatformViewUIThread(uintptr_t view_id, - const std::string& main, - const std::string& packages, - const std::string& assets_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch) { - FXL_DCHECK(ui_thread_checker_ && - ui_thread_checker_->IsCreationThreadCurrent()); - - *view_existed = false; - - IteratePlatformViews( - [view_id, // argument -#if !defined(OS_WIN) - // Using std::move on const references inside lambda capture is - // not supported on Windows for some reason. - assets_directory = std::move(assets_directory), // argument - main = std::move(main), // argument - packages = std::move(packages), // argument -#else - assets_directory, // argument - main, // argument - packages, // argument -#endif - &view_existed, // out - &dart_isolate_id, // out - &isolate_name // out - ](PlatformView* view) -> bool { - if (reinterpret_cast(view) != view_id) { - // Keep looking. - return true; + Rasterizer::Screenshot screenshot; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), [&latch, // + rasterizer = GetRasterizer(), // + &screenshot, // + screenshot_type, // + base64_encode // + ]() { + if (rasterizer) { + screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type, + base64_encode); } - *view_existed = true; - view->RunFromSource(assets_directory, main, packages); - *dart_isolate_id = view->engine().GetUIIsolateMainPort(); - *isolate_name = view->engine().GetUIIsolateName(); - // We found the requested view. Stop iterating over platform views. - return false; + latch.Signal(); }); - - latch->Signal(); -} - -void Shell::SetAssetBundlePathInPlatformView(uintptr_t view_id, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name) { - fxl::AutoResetWaitableEvent latch; - FXL_DCHECK(view_id != 0); - FXL_DCHECK(asset_directory); - FXL_DCHECK(view_existed); - - blink::Threads::UI()->PostTask([this, view_id, asset_directory, view_existed, - dart_isolate_id, isolate_name, &latch]() { - SetAssetBundlePathInPlatformViewUIThread(view_id, asset_directory, - view_existed, dart_isolate_id, - isolate_name, &latch); - }); latch.Wait(); -} - -void Shell::SetAssetBundlePathInPlatformViewUIThread( - uintptr_t view_id, - const std::string& assets_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch) { - FXL_DCHECK(ui_thread_checker_ && - ui_thread_checker_->IsCreationThreadCurrent()); - - *view_existed = false; - - IteratePlatformViews( - [view_id, // argument -#if !defined(OS_WIN) - // Using std::move on const references inside lambda capture is - // not supported on Windows for some reason. - // TODO(https://github.com/flutter/flutter/issues/13908): - // Investigate the root cause of the difference. - assets_directory = std::move(assets_directory), // argument -#else - assets_directory, // argument -#endif - &view_existed, // out - &dart_isolate_id, // out - &isolate_name // out - ](PlatformView* view) -> bool { - if (reinterpret_cast(view) != view_id) { - // Keep looking. - return true; - } - *view_existed = true; - view->SetAssetBundlePath(assets_directory); - *dart_isolate_id = view->engine().GetUIIsolateMainPort(); - *isolate_name = view->engine().GetUIIsolateName(); - // We found the requested view. Stop iterating over - // platform views. - return false; - }); - - latch->Signal(); + return screenshot; } } // namespace shell diff --git a/engine/src/flutter/shell/common/shell.h b/engine/src/flutter/shell/common/shell.h index 92c315dcf2..02a642b0ba 100644 --- a/engine/src/flutter/shell/common/shell.h +++ b/engine/src/flutter/shell/common/shell.h @@ -5,96 +5,214 @@ #ifndef SHELL_COMMON_SHELL_H_ #define SHELL_COMMON_SHELL_H_ -#include -#include +#include +#include +#include "flutter/common/settings.h" +#include "flutter/common/task_runners.h" +#include "flutter/flow/texture.h" +#include "flutter/fml/memory/thread_checker.h" +#include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/thread.h" -#include "flutter/shell/common/tracing_controller.h" -#include "lib/fxl/command_line.h" +#include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/runtime/service_protocol.h" +#include "flutter/shell/common/animator.h" +#include "flutter/shell/common/engine.h" +#include "flutter/shell/common/io_manager.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/surface.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_ptr.h" #include "lib/fxl/memory/weak_ptr.h" +#include "lib/fxl/strings/string_view.h" +#include "lib/fxl/synchronization/thread_annotations.h" #include "lib/fxl/synchronization/thread_checker.h" #include "lib/fxl/synchronization/waitable_event.h" -#include "lib/fxl/tasks/task_runner.h" namespace shell { -class PlatformView; - -class Shell { +class Shell final : public PlatformView::Delegate, + public Animator::Delegate, + public Engine::Delegate, + public blink::ServiceProtocol::Handler { public: + template + using CreateCallback = std::function(Shell&)>; + static std::unique_ptr Create( + blink::TaskRunners task_runners, + blink::Settings settings, + CreateCallback on_create_platform_view, + CreateCallback on_create_rasterizer); + ~Shell(); - static void InitStandalone(fxl::CommandLine command_line, - std::string icu_data_path = "", - std::string application_library_path = "", - std::string bundle_path = ""); + const blink::Settings& GetSettings() const; - static Shell& Shared(); + const blink::TaskRunners& GetTaskRunners() const; - const fxl::CommandLine& GetCommandLine() const; + fml::WeakPtr GetRasterizer(); - void AddPlatformView(PlatformView* platform_view); + fml::WeakPtr GetEngine(); - void RemovePlatformView(PlatformView* platform_view); + fml::WeakPtr GetPlatformView(); - void IteratePlatformViews( - std::function iterator) const; + const blink::DartVM& GetDartVM() const; - // Attempt to run a script inside a flutter view indicated by |view_id|. - // Will set |view_existed| to true if the view was found and false otherwise. - void RunInPlatformView(uintptr_t view_id, - const char* main_script, - const char* packages_file, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name); + bool IsSetup() const; - void SetAssetBundlePathInPlatformView(uintptr_t view_id, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name); + Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type, + bool base64_encode); private: - fxl::CommandLine command_line_; - std::unique_ptr gpu_thread_; - std::unique_ptr ui_thread_; - std::unique_ptr io_thread_; - std::unique_ptr gpu_thread_checker_; - std::unique_ptr ui_thread_checker_; - TracingController tracing_controller_; - mutable std::mutex platform_views_mutex_; - std::unordered_set platform_views_; + using ServiceProtocolHandler = std::function; - static void Init(fxl::CommandLine command_line, - const std::string& bundle_path); + const blink::TaskRunners task_runners_; + const blink::Settings settings_; + fxl::RefPtr vm_; + std::unique_ptr platform_view_; // on platform task runner + std::unique_ptr engine_; // on UI task runner + std::unique_ptr rasterizer_; // on GPU task runner + std::unique_ptr io_manager_; // on IO task runner - Shell(fxl::CommandLine command_line); + std::unordered_map, + ServiceProtocolHandler> // task-runner/function + // pair + > + service_protocol_handlers_; + bool is_setup_ = false; - void InitGpuThread(); + Shell(blink::TaskRunners task_runners, blink::Settings settings); - void InitUIThread(); + static std::unique_ptr CreateShellOnPlatformThread( + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer); - void RunInPlatformViewUIThread(uintptr_t view_id, - const std::string& main, - const std::string& packages, - const std::string& assets_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch); + bool Setup(std::unique_ptr platform_view, + std::unique_ptr engine, + std::unique_ptr rasterizer, + std::unique_ptr io_manager); - void SetAssetBundlePathInPlatformViewUIThread( - uintptr_t view_id, - const std::string& main, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch); + // |shell::PlatformView::Delegate| + void OnPlatformViewCreated(const PlatformView& view, + std::unique_ptr surface) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDestroyed(const PlatformView& view) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewSetViewportMetrics( + const PlatformView& view, + const blink::ViewportMetrics& metrics) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDispatchPlatformMessage( + const PlatformView& view, + fxl::RefPtr message) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDispatchPointerDataPacket( + const PlatformView& view, + std::unique_ptr packet) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDispatchSemanticsAction( + const PlatformView& view, + int32_t id, + blink::SemanticsAction action, + std::vector args) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewSetSemanticsEnabled(const PlatformView& view, + bool enabled) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewRegisterTexture( + const PlatformView& view, + std::shared_ptr texture) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewUnregisterTexture(const PlatformView& view, + int64_t texture_id) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewMarkTextureFrameAvailable(const PlatformView& view, + int64_t texture_id) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewSetNextFrameCallback(const PlatformView& view, + fxl::Closure closure) override; + + // |shell::Animator::Delegate| + void OnAnimatorBeginFrame(const Animator& animator, + fxl::TimePoint frame_time) override; + + // |shell::Animator::Delegate| + void OnAnimatorNotifyIdle(const Animator& animator, + int64_t deadline) override; + + // |shell::Animator::Delegate| + void OnAnimatorDraw( + const Animator& animator, + fxl::RefPtr> pipeline) override; + + // |shell::Animator::Delegate| + void OnAnimatorDrawLastLayerTree(const Animator& animator) override; + + // |shell::Engine::Delegate| + void OnEngineUpdateSemantics(const Engine& engine, + blink::SemanticsNodeUpdates update) override; + + // |shell::Engine::Delegate| + void OnEngineHandlePlatformMessage( + const Engine& engine, + fxl::RefPtr message) override; + + // |blink::ServiceProtocol::Handler| + fxl::RefPtr GetServiceProtocolHandlerTaskRunner( + fxl::StringView method) const override; + + // |blink::ServiceProtocol::Handler| + bool HandleServiceProtocolMessage( + fxl::StringView method, // one if the extension names specified above. + const ServiceProtocolMap& params, + rapidjson::Document& response) override; + + // |blink::ServiceProtocol::Handler| + blink::ServiceProtocol::Handler::Description GetServiceProtocolDescription() + const override; + + // Service protocol handler + bool OnServiceProtocolScreenshot( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolScreenshotSKP( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolRunInView( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolFlushUIThreadTasks( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolSetAssetBundlePath( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); FXL_DISALLOW_COPY_AND_ASSIGN(Shell); }; diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc new file mode 100644 index 0000000000..67689341a1 --- /dev/null +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -0,0 +1,133 @@ +// Copyright 2017 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 FML_USED_ON_EMBEDDER + +#include +#include +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" +#include "gtest/gtest.h" +#include "lib/fxl/synchronization/waitable_event.h" + +#define CURRENT_TEST_NAME \ + std::string { \ + ::testing::UnitTest::GetInstance()->current_test_info()->name() \ + } + +namespace shell { + +TEST(ShellTest, InitializeWithInvalidThreads) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + blink::TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_FALSE(shell); +} + +TEST(ShellTest, InitializeWithDifferentThreads) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + ThreadHost thread_host("io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::Platform | ThreadHost::Type::GPU | + ThreadHost::Type::IO | ThreadHost::Type::UI); + blink::TaskRunners task_runners("test", + thread_host.platform_thread->GetTaskRunner(), + thread_host.gpu_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +TEST(ShellTest, InitializeWithSingleThread) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + ThreadHost thread_host("io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::Platform); + auto task_runner = thread_host.platform_thread->GetTaskRunner(); + blink::TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +TEST(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + blink::TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +TEST(ShellTest, InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + ThreadHost thread_host( + "io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + blink::TaskRunners task_runners( + "test", fml::MessageLoop::GetCurrent().GetTaskRunner(), + thread_host.gpu_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/common/surface.cc b/engine/src/flutter/shell/common/surface.cc index 01d288a5e5..228647e8b3 100644 --- a/engine/src/flutter/shell/common/surface.cc +++ b/engine/src/flutter/shell/common/surface.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/shell/common/surface.h" + #include "lib/fxl/logging.h" #include "third_party/skia/include/core/SkColorSpaceXformCanvas.h" #include "third_party/skia/include/core/SkSurface.h" @@ -59,27 +60,22 @@ bool SurfaceFrame::PerformSubmit() { return false; } -Surface::Surface() : scale_(1.0) {} +Surface::Surface() : Surface(std::make_unique()) {} -Surface::~Surface() = default; - -bool Surface::SupportsScaling() const { - return false; +Surface::Surface(std::unique_ptr compositor_context) + : compositor_context_(std::move(compositor_context)) { + FXL_DCHECK(compositor_context_); + // TODO: Get rid of these explicit calls and move the logic to the c/dtors of + // the compositor context. + compositor_context_->OnGrContextCreated(); } -double Surface::GetScale() const { - return scale_; +Surface::~Surface() { + compositor_context_->OnGrContextDestroyed(); } -void Surface::SetScale(double scale) { - static constexpr double kMaxScale = 1.0; - static constexpr double kMinScale = 0.25; - if (scale > kMaxScale) { - scale = kMaxScale; - } else if (scale < kMinScale) { - scale = kMinScale; - } - scale_ = scale; +flow::CompositorContext& Surface::GetCompositorContext() { + return *compositor_context_; } } // namespace shell diff --git a/engine/src/flutter/shell/common/surface.h b/engine/src/flutter/shell/common/surface.h index 906480237f..6133a7d519 100644 --- a/engine/src/flutter/shell/common/surface.h +++ b/engine/src/flutter/shell/common/surface.h @@ -7,7 +7,7 @@ #include -#include "lib/fxl/compiler_specific.h" +#include "flutter/flow/compositor_context.h" #include "lib/fxl/macros.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -45,6 +45,8 @@ class Surface { public: Surface(); + Surface(std::unique_ptr compositor_context); + virtual ~Surface(); virtual bool IsValid() = 0; @@ -53,14 +55,12 @@ class Surface { virtual GrContext* GetContext() = 0; - virtual bool SupportsScaling() const; - - double GetScale() const; - - void SetScale(double scale); + flow::CompositorContext& GetCompositorContext(); private: - double scale_; + std::unique_ptr compositor_context_; + + FXL_DISALLOW_COPY_AND_ASSIGN(Surface); }; } // namespace shell diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index c1c82ff166..82da6ee180 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -90,4 +91,125 @@ const fxl::StringView FlagForSwitch(Switch swtch) { return fxl::StringView(); } +template +static bool GetSwitchValue(const fxl::CommandLine& command_line, + shell::Switch sw, + T* result) { + std::string switch_string; + + if (!command_line.GetOptionValue(shell::FlagForSwitch(sw), &switch_string)) { + return false; + } + + std::stringstream stream(switch_string); + T value = 0; + if (stream >> value) { + *result = value; + return true; + } + + return false; +} + +blink::Settings SettingsFromCommandLine(const fxl::CommandLine& command_line) { + blink::Settings settings = {}; + + // Enable Observatory + settings.enable_observatory = + !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory)); + + // Set Observatory Port + if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) { + if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort, + &settings.observatory_port)) { + FXL_LOG(INFO) + << "Observatory port specified was malformed. Will default to " + << settings.observatory_port; + } + } + + // Checked mode overrides. + settings.dart_non_checked_mode = + command_line.HasOption(FlagForSwitch(Switch::DartNonCheckedMode)); + + settings.ipv6 = command_line.HasOption(FlagForSwitch(Switch::IPv6)); + + settings.start_paused = + command_line.HasOption(FlagForSwitch(Switch::StartPaused)); + + settings.enable_dart_profiling = + command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling)); + + settings.enable_software_rendering = + command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering)); + + settings.using_blink = + command_line.HasOption(FlagForSwitch(Switch::EnableBlink)); + + settings.endless_trace_buffer = + command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer)); + + settings.trace_startup = + command_line.HasOption(FlagForSwitch(Switch::TraceStartup)); + + settings.skia_deterministic_rendering_on_cpu = + command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering)); + + command_line.GetOptionValue(FlagForSwitch(Switch::FLX), &settings.flx_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir), + &settings.assets_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::Snapshot), + &settings.script_snapshot_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::MainDartFile), + &settings.main_dart_file_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::Packages), + &settings.packages_file_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotSnapshotPath), + &settings.aot_snapshot_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotData), + &settings.aot_vm_snapshot_data_filename); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotInstructions), + &settings.aot_vm_snapshot_instr_filename); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotIsolateSnapshotData), + &settings.aot_isolate_snapshot_data_filename); + + command_line.GetOptionValue( + FlagForSwitch(Switch::AotIsolateSnapshotInstructions), + &settings.aot_isolate_snapshot_instr_filename); + + command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath), + &settings.temp_directory_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath), + &settings.icu_data_path); + + settings.use_test_fonts = + command_line.HasOption(FlagForSwitch(Switch::UseTestFonts)); + + command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag); + std::string all_dart_flags; + if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags), + &all_dart_flags)) { + std::stringstream stream(all_dart_flags); + std::istream_iterator end; + for (std::istream_iterator it(stream); it != end; ++it) + settings.dart_flags.push_back(*it); + } + +#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE + settings.trace_skia = + command_line.HasOption(FlagForSwitch(Switch::TraceSkia)); +#endif + + return settings; +} + } // namespace shell diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 91778a78e9..6b67b1f6a1 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/common/settings.h" +#include "lib/fxl/command_line.h" #include "lib/fxl/strings/string_view.h" #ifndef SHELL_COMMON_SWITCHES_H_ @@ -23,12 +25,29 @@ namespace shell { DEF_SWITCHES_START DEF_SWITCH(AotSharedLibraryPath, "aot-shared-library-path", "Path to the *.so.") -DEF_SWITCH(AotSnapshotPath, "aot-snapshot-path", "Path to the AOT snapshot.") -DEF_SWITCH(AotVmSnapshotData, "vm-snapshot-data", "") -DEF_SWITCH(AotVmSnapshotInstructions, "vm-snapshot-instr", "") -DEF_SWITCH(AotIsolateSnapshotData, "isolate-snapshot-data", "") -DEF_SWITCH(AotIsolateSnapshotInstructions, "isolate-snapshot-instr", "") +DEF_SWITCH(AotSnapshotPath, + "aot-snapshot-path", + "Path to the directory containing the four files specified by " + "AotVmSnapshotData, AotVmSnapshotInstructions, " + "AotVmSnapshotInstructions and AotIsolateSnapshotInstructions.") +DEF_SWITCH(AotVmSnapshotData, + "vm-snapshot-data", + "The VM snapshot data that will be memory mapped as read-only. " + "AotSnapshotPath must be present.") +DEF_SWITCH(AotVmSnapshotInstructions, + "vm-snapshot-instr", + "The VM instructions snapshot that will be memory mapped as read " + "and executable. AotSnapshotPath must be present.") +DEF_SWITCH(AotIsolateSnapshotData, + "isolate-snapshot-data", + "The isolate snapshot data that will be memory mapped as read-only. " + "AotSnapshotPath must be present.") +DEF_SWITCH(AotIsolateSnapshotInstructions, + "isolate-snapshot-instr", + "The isolate instructions snapshot that will be memory mapped as " + "read and executable. AotSnapshotPath must be present.") DEF_SWITCH(CacheDirPath, "cache-dir-path", "Path to the cache directory.") +DEF_SWITCH(ICUDataFilePath, "icu-data-file-path", "Path to the ICU data file.") DEF_SWITCH(DartFlags, "dart-flags", "Flags passed directly to the Dart VM without being interpreted " @@ -73,10 +92,6 @@ DEF_SWITCH(FlutterAssetsDir, DEF_SWITCH(Help, "help", "Display this help text.") DEF_SWITCH(LogTag, "log-tag", "Tag associated with log messages.") DEF_SWITCH(MainDartFile, "dart-main", "The path to the main Dart file.") -DEF_SWITCH(NonInteractive, - "non-interactive", - "Make the shell non-interactive. By default, the shell attempts " - "to setup a window and create an OpenGL context.") DEF_SWITCH(Packages, "packages", "Specify the path to the packages.") DEF_SWITCH(Snapshot, "snapshot-blob", "Specify the path to the snapshot blob") DEF_SWITCH(StartPaused, @@ -113,7 +128,9 @@ DEF_SWITCHES_END void PrintUsage(const std::string& executable_name); -const fxl::StringView FlagForSwitch(Switch sw); +const fxl::StringView FlagForSwitch(Switch swtch); + +blink::Settings SettingsFromCommandLine(const fxl::CommandLine& command_line); } // namespace shell diff --git a/engine/src/flutter/shell/common/thread_host.cc b/engine/src/flutter/shell/common/thread_host.cc new file mode 100644 index 0000000000..f35594829d --- /dev/null +++ b/engine/src/flutter/shell/common/thread_host.cc @@ -0,0 +1,38 @@ +// Copyright 2017 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. + +#include "flutter/shell/common/thread_host.h" + +namespace shell { + +ThreadHost::ThreadHost() = default; + +ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) { + if (mask & ThreadHost::Type::Platform) { + platform_thread = std::make_unique(name_prefix + ".platform"); + } + + if (mask & ThreadHost::Type::UI) { + ui_thread = std::make_unique(name_prefix + ".ui"); + } + + if (mask & ThreadHost::Type::GPU) { + gpu_thread = std::make_unique(name_prefix + ".gpu"); + } + + if (mask & ThreadHost::Type::IO) { + io_thread = std::make_unique(name_prefix + ".io"); + } +} + +ThreadHost::~ThreadHost() = default; + +void ThreadHost::Reset() { + platform_thread.reset(); + ui_thread.reset(); + gpu_thread.reset(); + io_thread.reset(); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/common/thread_host.h b/engine/src/flutter/shell/common/thread_host.h new file mode 100644 index 0000000000..a688aa6a06 --- /dev/null +++ b/engine/src/flutter/shell/common/thread_host.h @@ -0,0 +1,43 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_COMMON_THREAD_HOST_H_ +#define FLUTTER_SHELL_COMMON_THREAD_HOST_H_ + +#include + +#include "flutter/fml/thread.h" +#include "lib/fxl/macros.h" + +namespace shell { + +struct ThreadHost { + enum Type { + Platform = 1 << 0, + UI = 1 << 1, + GPU = 1 << 2, + IO = 1 << 3, + }; + + std::unique_ptr platform_thread; + std::unique_ptr ui_thread; + std::unique_ptr gpu_thread; + std::unique_ptr io_thread; + + ThreadHost(); + + ThreadHost(ThreadHost&&) = default; + + ThreadHost& operator=(ThreadHost&&) = default; + + ThreadHost(std::string name_prefix, uint64_t type_mask); + + ~ThreadHost(); + + void Reset(); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_THREAD_HOST_H_ diff --git a/engine/src/flutter/shell/common/tracing_controller.cc b/engine/src/flutter/shell/common/tracing_controller.cc deleted file mode 100644 index 6fa8d9d5ee..0000000000 --- a/engine/src/flutter/shell/common/tracing_controller.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/tracing_controller.h" - -#include - -#include "flutter/common/threads.h" -#include "flutter/fml/trace_event.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/shell/common/shell.h" -#include "lib/fxl/logging.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" - -namespace shell { - -TracingController::TracingController() : tracing_active_(false) { - blink::SetEmbedderTracingCallbacks( - std::unique_ptr( - new blink::EmbedderTracingCallbacks([this]() { StartTracing(); }, - [this]() { StopTracing(); }))); -} - -TracingController::~TracingController() { - blink::SetEmbedderTracingCallbacks(nullptr); -} - -static void AddTraceMetadata() { - blink::Threads::Gpu()->PostTask([]() { Dart_SetThreadName("gpu_thread"); }); - blink::Threads::UI()->PostTask([]() { Dart_SetThreadName("ui_thread"); }); - blink::Threads::IO()->PostTask([]() { Dart_SetThreadName("io_thread"); }); - blink::Threads::Platform()->PostTask( - []() { Dart_SetThreadName("platform_thread"); }); -} - -void TracingController::StartTracing() { - if (tracing_active_) - return; - tracing_active_ = true; - AddTraceMetadata(); -} - -void TracingController::StopTracing() { - if (!tracing_active_) { - return; - } - tracing_active_ = false; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/common/tracing_controller.h b/engine/src/flutter/shell/common/tracing_controller.h deleted file mode 100644 index 3f9e6f03ab..0000000000 --- a/engine/src/flutter/shell/common/tracing_controller.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_COMMON_TRACING_CONTROLLER_H_ -#define SHELL_COMMON_TRACING_CONTROLLER_H_ - -#include - -#include "lib/fxl/macros.h" - -namespace shell { - -class TracingController { - public: - TracingController(); - - ~TracingController(); - - void StartTracing(); - - void StopTracing(); - - bool tracing_active() const { return tracing_active_; } - - private: - bool tracing_active_; - - FXL_DISALLOW_COPY_AND_ASSIGN(TracingController); -}; - -} // namespace shell - -#endif // SHELL_COMMON_TRACING_CONTROLLER_H_ diff --git a/engine/src/flutter/shell/common/vsync_waiter.cc b/engine/src/flutter/shell/common/vsync_waiter.cc index 5acc235b1f..ba2f21c389 100644 --- a/engine/src/flutter/shell/common/vsync_waiter.cc +++ b/engine/src/flutter/shell/common/vsync_waiter.cc @@ -4,8 +4,45 @@ #include "flutter/shell/common/vsync_waiter.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/trace_event.h" + namespace shell { +VsyncWaiter::VsyncWaiter(blink::TaskRunners task_runners) + : task_runners_(std::move(task_runners)) {} + VsyncWaiter::~VsyncWaiter() = default; +void VsyncWaiter::AsyncWaitForVsync(Callback callback) { + { + std::lock_guard lock(callback_mutex_); + callback_ = std::move(callback); + } + AwaitVSync(); +} + +void VsyncWaiter::FireCallback(fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time) { + Callback callback; + + { + std::lock_guard lock(callback_mutex_); + callback = std::move(callback_); + } + + if (!callback) { + return; + } + + task_runners_.GetUITaskRunner()->PostTask( + [callback, frame_start_time, frame_target_time]() { + // Note: The tag name must be "VSYNC" (it is special) so that the + // "Highlight + // Vsync" checkbox in the timeline can be enabled. + TRACE_EVENT0("flutter", "VSYNC"); + callback(frame_start_time, frame_target_time); + }); +} + } // namespace shell diff --git a/engine/src/flutter/shell/common/vsync_waiter.h b/engine/src/flutter/shell/common/vsync_waiter.h index 77319ed8b6..82231fdf4f 100644 --- a/engine/src/flutter/shell/common/vsync_waiter.h +++ b/engine/src/flutter/shell/common/vsync_waiter.h @@ -6,7 +6,10 @@ #define FLUTTER_SHELL_COMMON_VSYNC_WAITER_H_ #include +#include +#include +#include "flutter/common/task_runners.h" #include "lib/fxl/time/time_point.h" namespace shell { @@ -16,9 +19,23 @@ class VsyncWaiter { using Callback = std::function; - virtual void AsyncWaitForVsync(Callback callback) = 0; - virtual ~VsyncWaiter(); + + void AsyncWaitForVsync(Callback callback); + + protected: + const blink::TaskRunners task_runners_; + std::mutex callback_mutex_; + Callback callback_; + + VsyncWaiter(blink::TaskRunners task_runners); + + virtual void AwaitVSync() = 0; + + void FireCallback(fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time); + + FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiter); }; } // namespace shell diff --git a/engine/src/flutter/shell/common/vsync_waiter_fallback.cc b/engine/src/flutter/shell/common/vsync_waiter_fallback.cc index 01c86cdedc..bcf061bb76 100644 --- a/engine/src/flutter/shell/common/vsync_waiter_fallback.cc +++ b/engine/src/flutter/shell/common/vsync_waiter_fallback.cc @@ -4,7 +4,6 @@ #include "flutter/shell/common/vsync_waiter_fallback.h" -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" namespace shell { @@ -21,28 +20,25 @@ fxl::TimePoint SnapToNextTick(fxl::TimePoint value, } // namespace -VsyncWaiterFallback::VsyncWaiterFallback() - : phase_(fxl::TimePoint::Now()), weak_factory_(this) {} +VsyncWaiterFallback::VsyncWaiterFallback(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), + phase_(fxl::TimePoint::Now()), + weak_factory_(this) {} VsyncWaiterFallback::~VsyncWaiterFallback() = default; constexpr fxl::TimeDelta interval = fxl::TimeDelta::FromSecondsF(1.0 / 60.0); -void VsyncWaiterFallback::AsyncWaitForVsync(Callback callback) { - FXL_DCHECK(!callback_); - callback_ = std::move(callback); - +void VsyncWaiterFallback::AwaitVSync() { fxl::TimePoint now = fxl::TimePoint::Now(); fxl::TimePoint next = SnapToNextTick(now, phase_, interval); - blink::Threads::UI()->PostDelayedTask( + task_runners_.GetUITaskRunner()->PostDelayedTask( [self = weak_factory_.GetWeakPtr()] { - if (!self) - return; - fxl::TimePoint frame_time = fxl::TimePoint::Now(); - Callback callback = std::move(self->callback_); - self->callback_ = Callback(); - callback(frame_time, frame_time + interval); + if (self) { + const auto frame_time = fxl::TimePoint::Now(); + self->FireCallback(frame_time, frame_time + interval); + } }, next - now); } diff --git a/engine/src/flutter/shell/common/vsync_waiter_fallback.h b/engine/src/flutter/shell/common/vsync_waiter_fallback.h index bfb7e118b1..d3cc8faaf6 100644 --- a/engine/src/flutter/shell/common/vsync_waiter_fallback.h +++ b/engine/src/flutter/shell/common/vsync_waiter_fallback.h @@ -5,25 +5,24 @@ #ifndef FLUTTER_SHELL_COMMON_VSYNC_WAITER_FALLBACK_H_ #define FLUTTER_SHELL_COMMON_VSYNC_WAITER_FALLBACK_H_ -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" #include "lib/fxl/time/time_point.h" namespace shell { -class VsyncWaiterFallback : public VsyncWaiter { +class VsyncWaiterFallback final : public VsyncWaiter { public: - VsyncWaiterFallback(); - ~VsyncWaiterFallback() override; + VsyncWaiterFallback(blink::TaskRunners task_runners); - void AsyncWaitForVsync(Callback callback) override; + ~VsyncWaiterFallback() override; private: fxl::TimePoint phase_; - Callback callback_; + fxl::WeakPtrFactory weak_factory_; - fml::WeakPtrFactory weak_factory_; + void AwaitVSync() override; FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterFallback); }; diff --git a/engine/src/flutter/shell/gpu/BUILD.gn b/engine/src/flutter/shell/gpu/BUILD.gn index c050e120b0..cd093e1238 100644 --- a/engine/src/flutter/shell/gpu/BUILD.gn +++ b/engine/src/flutter/shell/gpu/BUILD.gn @@ -6,14 +6,17 @@ import("$flutter_root/shell/config.gni") source_set("gpu") { sources = [ - "gpu_rasterizer.cc", - "gpu_rasterizer.h", - "gpu_surface_gl.cc", - "gpu_surface_gl.h", "gpu_surface_software.cc", "gpu_surface_software.h", ] + if (!is_fuchsia) { + sources += [ + "gpu_surface_gl.cc", + "gpu_surface_gl.h", + ] + } + if (shell_enable_vulkan) { sources += [ "gpu_surface_vulkan.cc", diff --git a/engine/src/flutter/shell/gpu/gpu_rasterizer.cc b/engine/src/flutter/shell/gpu/gpu_rasterizer.cc deleted file mode 100644 index 3424f989b3..0000000000 --- a/engine/src/flutter/shell/gpu/gpu_rasterizer.cc +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "gpu_rasterizer.h" - -#include -#include - -#include "flutter/common/threads.h" -#include "flutter/glue/trace_event.h" -#include "flutter/shell/common/picture_serializer.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "third_party/skia/include/core/SkPicture.h" - -namespace shell { - -GPURasterizer::GPURasterizer(std::unique_ptr info) - : compositor_context_(std::move(info)), weak_factory_(this) {} - -GPURasterizer::~GPURasterizer() = default; - -fml::WeakPtr GPURasterizer::GetWeakRasterizerPtr() { - return weak_factory_.GetWeakPtr(); -} - -void GPURasterizer::Setup(std::unique_ptr surface, - fxl::Closure continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) { - surface_ = std::move(surface); - compositor_context_.OnGrContextCreated(); - - continuation(); - - setup_completion_event->Signal(); -} - -void GPURasterizer::Clear(SkColor color, const SkISize& size) { - if (surface_ == nullptr) { - return; - } - - auto frame = surface_->AcquireFrame(size); - - if (frame == nullptr) { - return; - } - - SkCanvas* canvas = frame->SkiaCanvas(); - - if (canvas == nullptr) { - return; - } - - canvas->clear(color); - - frame->Submit(); -} - -void GPURasterizer::Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) { - compositor_context_.OnGrContextDestroyed(); - if (surface_) { - surface_.reset(); - } - last_layer_tree_.reset(); - teardown_completion_event->Signal(); -} - -flow::LayerTree* GPURasterizer::GetLastLayerTree() { - return last_layer_tree_.get(); -} - -void GPURasterizer::DrawLastLayerTree() { - if (!last_layer_tree_ || !surface_) { - return; - } - DrawToSurface(*last_layer_tree_); -} - -flow::TextureRegistry& GPURasterizer::GetTextureRegistry() { - return compositor_context_.texture_registry(); -} - -void GPURasterizer::Draw( - fxl::RefPtr> pipeline) { - TRACE_EVENT0("flutter", "GPURasterizer::Draw"); - - flutter::Pipeline::Consumer consumer = - std::bind(&GPURasterizer::DoDraw, this, std::placeholders::_1); - - // Consume as many pipeline items as possible. But yield the event loop - // between successive tries. - switch (pipeline->Consume(consumer)) { - case flutter::PipelineConsumeResult::MoreAvailable: { - auto weak_this = weak_factory_.GetWeakPtr(); - blink::Threads::Gpu()->PostTask([weak_this, pipeline]() { - if (weak_this) { - weak_this->Draw(pipeline); - } - }); - break; - } - default: - break; - } -} - -void GPURasterizer::DoDraw(std::unique_ptr layer_tree) { - if (!layer_tree || !surface_) { - return; - } - - // There is no way for the compositor to know how long the layer tree - // construction took. Fortunately, the layer tree does. Grab that time - // for instrumentation. - compositor_context_.engine_time().SetLapTime(layer_tree->construction_time()); - - DrawToSurface(*layer_tree); - - NotifyNextFrameOnce(); - - last_layer_tree_ = std::move(layer_tree); -} - -void GPURasterizer::DrawToSurface(flow::LayerTree& layer_tree) { - auto frame = surface_->AcquireFrame(layer_tree.frame_size()); - - if (frame == nullptr) { - return; - } - - auto canvas = frame->SkiaCanvas(); - - if (canvas == nullptr) { - return; - } - - auto compositor_frame = - compositor_context_.AcquireFrame(surface_->GetContext(), canvas); - - canvas->clear(SK_ColorBLACK); - - layer_tree.Raster(compositor_frame); - - frame->Submit(); -} - -void GPURasterizer::AddNextFrameCallback(fxl::Closure nextFrameCallback) { - nextFrameCallback_ = nextFrameCallback; -} - -void GPURasterizer::NotifyNextFrameOnce() { - if (nextFrameCallback_) { - blink::Threads::Platform()->PostTask([callback = nextFrameCallback_] { - TRACE_EVENT0("flutter", "GPURasterizer::NotifyNextFrameOnce"); - callback(); - }); - nextFrameCallback_ = nullptr; - } -} - -void GPURasterizer::SetTextureRegistry(flow::TextureRegistry* textureRegistry) { - compositor_context_.SetTextureRegistry(textureRegistry); -} - -} // namespace shell diff --git a/engine/src/flutter/shell/gpu/gpu_rasterizer.h b/engine/src/flutter/shell/gpu/gpu_rasterizer.h deleted file mode 100644 index ad16ee2c47..0000000000 --- a/engine/src/flutter/shell/gpu/gpu_rasterizer.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_GPU_DIRECT_GPU_RASTERIZER_H_ -#define SHELL_GPU_DIRECT_GPU_RASTERIZER_H_ - -#include "flutter/flow/compositor_context.h" -#include "flutter/shell/common/rasterizer.h" -#include "lib/fxl/memory/weak_ptr.h" -#include "lib/fxl/synchronization/waitable_event.h" - -namespace shell { - -class Surface; - -class GPURasterizer : public Rasterizer { - public: - GPURasterizer(std::unique_ptr info); - - ~GPURasterizer() override; - - void Setup(std::unique_ptr surface, - fxl::Closure continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) override; - - void Clear(SkColor color, const SkISize& size) override; - - void Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) override; - - fml::WeakPtr GetWeakRasterizerPtr() override; - - flow::LayerTree* GetLastLayerTree() override; - - void DrawLastLayerTree() override; - - flow::TextureRegistry& GetTextureRegistry() override; - - void Draw(fxl::RefPtr> pipeline) override; - - // Set a callback to be called once when the next frame is drawn. - void AddNextFrameCallback(fxl::Closure nextFrameCallback) override; - - void SetTextureRegistry(flow::TextureRegistry* textureRegistry) override; - - private: - std::unique_ptr surface_; - flow::CompositorContext compositor_context_; - std::unique_ptr last_layer_tree_; - // A closure to be called when the underlaying surface presents a frame the - // next time. NULL if there is no callback or the callback was set back to - // NULL after being called. - fxl::Closure nextFrameCallback_; - fml::WeakPtrFactory weak_factory_; - - void DoDraw(std::unique_ptr layer_tree); - - void DrawToSurface(flow::LayerTree& layer_tree); - - void NotifyNextFrameOnce(); - - FXL_DISALLOW_COPY_AND_ASSIGN(GPURasterizer); -}; - -} // namespace shell - -#endif // SHELL_GPU_DIRECT_GPU_RASTERIZER_H_ diff --git a/engine/src/flutter/shell/gpu/gpu_surface_gl.cc b/engine/src/flutter/shell/gpu/gpu_surface_gl.cc index 331fd188a9..284a10e413 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl.cc @@ -72,6 +72,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { return; } + GetCompositorContext().OnGrContextDestroyed(); + onscreen_surface_ = nullptr; context_->releaseResourcesAndAbandonContext(); context_ = nullptr; @@ -210,12 +212,11 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - auto weak_this = weak_factory_.GetWeakPtr(); - - SurfaceFrame::SubmitCallback submit_callback = - [weak_this](const SurfaceFrame& surface_frame, SkCanvas* canvas) { - return weak_this ? weak_this->PresentSurface(canvas) : false; - }; + SurfaceFrame::SubmitCallback submit_callback = [weak = weak_factory_ + .GetWeakPtr()]( + const SurfaceFrame& surface_frame, SkCanvas* canvas) { + return weak ? weak->PresentSurface(canvas) : false; + }; return std::make_unique(surface, submit_callback); } diff --git a/engine/src/flutter/shell/gpu/gpu_surface_gl.h b/engine/src/flutter/shell/gpu/gpu_surface_gl.h index 5cf092dae6..abc9f46aee 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl.h +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl.h @@ -5,10 +5,9 @@ #ifndef SHELL_GPU_GPU_SURFACE_GL_H_ #define SHELL_GPU_GPU_SURFACE_GL_H_ -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" -#include "flutter/synchronization/debug_thread_checker.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" #include "third_party/skia/include/gpu/GrContext.h" namespace shell { @@ -44,7 +43,7 @@ class GPUSurfaceGL : public Surface { sk_sp onscreen_surface_; sk_sp offscreen_surface_; bool valid_ = false; - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; bool CreateOrUpdateSurfaces(const SkISize& size); diff --git a/engine/src/flutter/shell/gpu/gpu_surface_software.cc b/engine/src/flutter/shell/gpu/gpu_surface_software.cc index e340e60565..ea8c827b22 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_software.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_software.cc @@ -18,20 +18,13 @@ bool GPUSurfaceSoftware::IsValid() { return delegate_ != nullptr; } -bool GPUSurfaceSoftware::SupportsScaling() const { - return true; -} - std::unique_ptr GPUSurfaceSoftware::AcquireFrame( const SkISize& logical_size) { if (!IsValid()) { return nullptr; } - // Check if we need to support surface scaling. - const auto scale = SupportsScaling() ? GetScale() : 1.0; - const auto size = SkISize::Make(logical_size.width() * scale, - logical_size.height() * scale); + const auto size = SkISize::Make(logical_size.width(), logical_size.height()); sk_sp backing_store = delegate_->AcquireBackingStore(size); @@ -48,12 +41,10 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( // irrespective of surface scaling. SkCanvas* canvas = backing_store->getCanvas(); canvas->resetMatrix(); - canvas->scale(scale, scale); - SurfaceFrame::SubmitCallback - on_submit = [self = weak_factory_.GetWeakPtr()]( - const SurfaceFrame& surface_frame, SkCanvas* canvas) - ->bool { + SurfaceFrame::SubmitCallback on_submit = + [self = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame, + SkCanvas* canvas) -> bool { // If the surface itself went away, there is nothing more to do. if (!self || !self->IsValid() || canvas == nullptr) { return false; diff --git a/engine/src/flutter/shell/gpu/gpu_surface_software.h b/engine/src/flutter/shell/gpu/gpu_surface_software.h index f7312153db..238754f312 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_software.h +++ b/engine/src/flutter/shell/gpu/gpu_surface_software.h @@ -5,9 +5,9 @@ #ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ #define FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" #include "third_party/skia/include/core/SkSurface.h" namespace shell { @@ -30,12 +30,9 @@ class GPUSurfaceSoftware : public Surface { GrContext* GetContext() override; - bool SupportsScaling() const override; - private: GPUSurfaceSoftwareDelegate* delegate_; - - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; FXL_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceSoftware); }; diff --git a/engine/src/flutter/shell/gpu/gpu_surface_vulkan.h b/engine/src/flutter/shell/gpu/gpu_surface_vulkan.h index eafed43a62..f1e25fbe98 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_vulkan.h +++ b/engine/src/flutter/shell/gpu/gpu_surface_vulkan.h @@ -6,11 +6,12 @@ #define SHELL_GPU_GPU_SURFACE_VULKAN_H_ #include -#include "flutter/fml/memory/weak_ptr.h" + #include "flutter/shell/common/surface.h" #include "flutter/vulkan/vulkan_native_surface.h" #include "flutter/vulkan/vulkan_window.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" namespace shell { @@ -29,7 +30,7 @@ class GPUSurfaceVulkan : public Surface { private: vulkan::VulkanWindow window_; - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; FXL_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceVulkan); }; diff --git a/engine/src/flutter/shell/platform/BUILD.gn b/engine/src/flutter/shell/platform/BUILD.gn index 4b1e1a9e0b..ae3b1003f2 100644 --- a/engine/src/flutter/shell/platform/BUILD.gn +++ b/engine/src/flutter/shell/platform/BUILD.gn @@ -13,13 +13,11 @@ group("platform") { ] } else if (is_linux) { deps = [ - "linux", "embedder", ] } else if (is_win) { - deps = [ - "win" - ] + # There is no platform target on windows. Instead, only a tester is used. + deps = [] } else { assert(false, "Unknown/Unsupported platform.") } diff --git a/engine/src/flutter/shell/platform/android/BUILD.gn b/engine/src/flutter/shell/platform/android/BUILD.gn index d9744ad890..129c877fae 100644 --- a/engine/src/flutter/shell/platform/android/BUILD.gn +++ b/engine/src/flutter/shell/platform/android/BUILD.gn @@ -17,21 +17,25 @@ shared_library("flutter_shell_native") { "android_context_gl.h", "android_environment_gl.cc", "android_environment_gl.h", - "android_external_texture_gl.h", "android_external_texture_gl.cc", + "android_external_texture_gl.h", "android_native_window.cc", "android_native_window.h", + "android_shell_holder.cc", + "android_shell_holder.h", "android_surface.cc", "android_surface.h", "android_surface_gl.cc", "android_surface_gl.h", - "android_surface_software.h", "android_surface_software.cc", - "apk_asset_provider.h", + "android_surface_software.h", "apk_asset_provider.cc", + "apk_asset_provider.h", "flutter_main.cc", "flutter_main.h", "library_loader.cc", + "platform_message_response_android.cc", + "platform_message_response_android.h", "platform_view_android.cc", "platform_view_android.h", "platform_view_android_jni.cc", @@ -41,10 +45,10 @@ shared_library("flutter_shell_native") { ] deps = [ + "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", "$flutter_root/fml", - "$flutter_root/assets", "$flutter_root/lib/ui", "$flutter_root/runtime", "$flutter_root/shell/common", @@ -59,9 +63,7 @@ shared_library("flutter_shell_native") { deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] } - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] defines = [] diff --git a/engine/src/flutter/shell/platform/android/android_context_gl.cc b/engine/src/flutter/shell/platform/android/android_context_gl.cc index f54629f864..6338dc9690 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl.cc +++ b/engine/src/flutter/shell/platform/android/android_context_gl.cc @@ -3,9 +3,13 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/android_context_gl.h" + #include + #include +#include "flutter/fml/trace_event.h" + namespace shell { template @@ -65,19 +69,17 @@ static EGLResult CreateContext(EGLDisplay display, return {context != EGL_NO_CONTEXT, context}; } -static EGLResult ChooseEGLConfiguration( - EGLDisplay display, - PlatformView::SurfaceConfig config) { +static EGLResult ChooseEGLConfiguration(EGLDisplay display) { EGLint attributes[] = { // clang-format off EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, config.red_bits, - EGL_GREEN_SIZE, config.green_bits, - EGL_BLUE_SIZE, config.blue_bits, - EGL_ALPHA_SIZE, config.alpha_bits, - EGL_DEPTH_SIZE, config.depth_bits, - EGL_STENCIL_SIZE, config.stencil_bits, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, EGL_NONE, // termination sentinel // clang-format on }; @@ -142,7 +144,6 @@ bool AndroidContextGL::CreatePBufferSurface() { } AndroidContextGL::AndroidContextGL(fxl::RefPtr env, - PlatformView::SurfaceConfig config, const AndroidContextGL* share_context) : environment_(env), window_(nullptr), @@ -158,8 +159,7 @@ AndroidContextGL::AndroidContextGL(fxl::RefPtr env, // Choose a valid configuration. - std::tie(success, config_) = - ChooseEGLConfiguration(environment_->Display(), config); + std::tie(success, config_) = ChooseEGLConfiguration(environment_->Display()); if (!success) { FXL_LOG(ERROR) << "Could not choose an EGL configuration."; diff --git a/engine/src/flutter/shell/platform/android/android_context_gl.h b/engine/src/flutter/shell/platform/android/android_context_gl.h index 207f621c66..b29851b345 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl.h +++ b/engine/src/flutter/shell/platform/android/android_context_gl.h @@ -44,7 +44,6 @@ class AndroidContextGL : public fxl::RefCountedThreadSafe { bool valid_; AndroidContextGL(fxl::RefPtr env, - PlatformView::SurfaceConfig config, const AndroidContextGL* share_context = nullptr); ~AndroidContextGL(); diff --git a/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc b/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc index 02a40c2247..35db9cbbfd 100644 --- a/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc @@ -5,7 +5,7 @@ #include "flutter/shell/platform/android/android_external_texture_gl.h" #include -#include "flutter/common/threads.h" + #include "flutter/shell/platform/android/platform_view_android_jni.h" #include "third_party/skia/include/gpu/GrTexture.h" @@ -19,17 +19,14 @@ AndroidExternalTextureGL::AndroidExternalTextureGL( AndroidExternalTextureGL::~AndroidExternalTextureGL() = default; void AndroidExternalTextureGL::OnGrContextCreated() { - ASSERT_IS_GPU_THREAD; state_ = AttachmentState::uninitialized; } void AndroidExternalTextureGL::MarkNewFrameAvailable() { - ASSERT_IS_GPU_THREAD; new_frame_ready_ = true; } void AndroidExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { - ASSERT_IS_GPU_THREAD; if (state_ == AttachmentState::detached) { return; } @@ -42,7 +39,8 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { Update(); new_frame_ready_ = false; } - GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_, GL_RGBA8_OES}; + GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_, + GL_RGBA8_OES}; GrBackendTexture backendTexture(1, 1, GrMipMapped::kNo, textureInfo); sk_sp image = SkImage::MakeFromTexture( canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin, @@ -82,7 +80,6 @@ void AndroidExternalTextureGL::UpdateTransform() { } void AndroidExternalTextureGL::OnGrContextDestroyed() { - ASSERT_IS_GPU_THREAD; if (state_ == AttachmentState::attached) { Detach(); } diff --git a/engine/src/flutter/shell/platform/android/android_external_texture_gl.h b/engine/src/flutter/shell/platform/android/android_external_texture_gl.h index df890b91c8..67d6541d96 100644 --- a/engine/src/flutter/shell/platform/android/android_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/android/android_external_texture_gl.h @@ -25,8 +25,7 @@ class AndroidExternalTextureGL : public flow::Texture { virtual void OnGrContextDestroyed() override; - // Called on GPU thread. - void MarkNewFrameAvailable(); + void MarkNewFrameAvailable() override; private: void Attach(jint textureName); diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder.cc b/engine/src/flutter/shell/platform/android/android_shell_holder.cc new file mode 100644 index 0000000000..7dc454f1fb --- /dev/null +++ b/engine/src/flutter/shell/platform/android/android_shell_holder.cc @@ -0,0 +1,169 @@ +// Copyright 2018 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 FML_USED_ON_EMBEDDER + +#include "flutter/shell/platform/android/android_shell_holder.h" + +#include +#include + +#include +#include +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/platform/android/platform_view_android.h" +#include "lib/fxl/functional/make_copyable.h" + +namespace shell { + +AndroidShellHolder::AndroidShellHolder( + blink::Settings settings, + fml::jni::JavaObjectWeakGlobalRef java_object) + : settings_(std::move(settings)), java_object_(java_object) { + static size_t shell_count = 1; + auto thread_label = std::to_string(shell_count++); + + thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU | + ThreadHost::Type::IO}; + + fml::WeakPtr weak_platform_view; + Shell::CreateCallback on_create_platform_view = + [java_object, &weak_platform_view](Shell& shell) { + auto platform_view_android = std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + java_object, // java object handle for JNI interop + shell.GetSettings() + .enable_software_rendering // use software rendering + ); + weak_platform_view = platform_view_android->GetWeakPtr(); + return platform_view_android; + }; + + Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // The current thread will be used as the platform thread. Ensure that the + // message loop is initialized. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + blink::TaskRunners task_runners( + thread_label, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + thread_host_.gpu_thread->GetTaskRunner(), // gpu + thread_host_.ui_thread->GetTaskRunner(), // ui + thread_host_.io_thread->GetTaskRunner() // io + ); + + shell_ = + Shell::Create(task_runners, // task runners + settings_, // settings + on_create_platform_view, // platform view create callback + on_create_rasterizer // rasterizer create callback + ); + + platform_view_ = weak_platform_view; + FXL_DCHECK(platform_view_); + + is_valid_ = shell_ != nullptr; + + if (is_valid_) { + task_runners.GetGPUTaskRunner()->PostTask( + []() { ::setpriority(PRIO_PROCESS, gettid(), -2); }); + task_runners.GetUITaskRunner()->PostTask( + []() { ::setpriority(PRIO_PROCESS, gettid(), -1); }); + } +} + +AndroidShellHolder::~AndroidShellHolder() = default; + +bool AndroidShellHolder::IsValid() const { + return is_valid_; +} + +const blink::Settings& AndroidShellHolder::GetSettings() const { + return settings_; +} + +void AndroidShellHolder::Launch(RunConfiguration config) { + if (!IsValid()) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = shell_->GetEngine(), // + config = std::move(config) // + ]() mutable { + if (engine) { + if (!engine->Run(std::move(config))) { + FXL_LOG(ERROR) << "Could not launch engine in configuration."; + } + } + })); +} + +void AndroidShellHolder::SetViewportMetrics( + const blink::ViewportMetrics& metrics) { + if (!IsValid()) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), metrics]() { + if (engine) { + engine->SetViewportMetrics(metrics); + } + }); +} + +void AndroidShellHolder::DispatchPointerDataPacket( + std::unique_ptr packet) { + if (!IsValid()) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = shell_->GetEngine(), packet = std::move(packet)] { + if (engine) { + engine->DispatchPointerDataPacket(*packet); + } + })); +} + +Rasterizer::Screenshot AndroidShellHolder::Screenshot( + Rasterizer::ScreenshotType type, + bool base64_encode) { + if (!IsValid()) { + return {nullptr, SkISize::MakeEmpty()}; + } + return shell_->Screenshot(type, base64_encode); +} + +fml::WeakPtr AndroidShellHolder::GetPlatformView() { + FXL_DCHECK(platform_view_); + return platform_view_; +} + +void AndroidShellHolder::UpdateAssetManager( + fxl::RefPtr asset_manager) { + if (!IsValid() || !asset_manager) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), + asset_manager = std::move(asset_manager)]() { + if (engine) { + if (!engine->UpdateAssetManager(std::move(asset_manager))) { + FXL_DLOG(ERROR) << "Could not update asset asset manager."; + } + } + }); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder.h b/engine/src/flutter/shell/platform/android/android_shell_holder.h new file mode 100644 index 0000000000..e0905f0f9e --- /dev/null +++ b/engine/src/flutter/shell/platform/android/android_shell_holder.h @@ -0,0 +1,59 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SHELL_HOLDER_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SHELL_HOLDER_H_ + +#include + +#include "flutter/fml/platform/android/jni_weak_ref.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/android/platform_view_android.h" +#include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class AndroidShellHolder { + public: + AndroidShellHolder(blink::Settings settings, + fml::jni::JavaObjectWeakGlobalRef java_object); + + ~AndroidShellHolder(); + + bool IsValid() const; + + void Launch(RunConfiguration configuration); + + void SetViewportMetrics(const blink::ViewportMetrics& metrics); + + void DispatchPointerDataPacket( + std::unique_ptr packet); + + const blink::Settings& GetSettings() const; + + fml::WeakPtr GetPlatformView(); + + Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type, + bool base64_encode); + + void UpdateAssetManager(fxl::RefPtr asset_manager); + + private: + const blink::Settings settings_; + const fml::jni::JavaObjectWeakGlobalRef java_object_; + fml::WeakPtr platform_view_; + ThreadHost thread_host_; + std::unique_ptr shell_; + bool is_valid_ = false; + + FXL_DISALLOW_COPY_AND_ASSIGN(AndroidShellHolder); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SHELL_HOLDER_H_ diff --git a/engine/src/flutter/shell/platform/android/android_surface.cc b/engine/src/flutter/shell/platform/android/android_surface.cc index 4dd08609da..a8b41bacbf 100644 --- a/engine/src/flutter/shell/platform/android/android_surface.cc +++ b/engine/src/flutter/shell/platform/android/android_surface.cc @@ -4,8 +4,32 @@ #include "flutter/shell/platform/android/android_surface.h" +#include + +#include "flutter/shell/platform/android/android_surface_gl.h" +#include "flutter/shell/platform/android/android_surface_software.h" +#if SHELL_ENABLE_VULKAN +#include "flutter/shell/platform/android/android_surface_vulkan.h" +#endif // SHELL_ENABLE_VULKAN + namespace shell { +std::unique_ptr AndroidSurface::Create( + bool use_software_rendering) { + if (use_software_rendering) { + auto software_surface = std::make_unique(); + return software_surface->IsValid() ? std::move(software_surface) : nullptr; + } +#if SHELL_ENABLE_VULKAN + auto vulkan_surface = std::make_unique(); + return vulkan_surface->IsValid() ? std::move(vulkan_surface) : nullptr; +#else // SHELL_ENABLE_VULKAN + auto gl_surface = std::make_unique(); + return gl_surface->IsOffscreenContextValid() ? std::move(gl_surface) + : nullptr; +#endif // SHELL_ENABLE_VULKAN +} + AndroidSurface::~AndroidSurface() = default; } // namespace shell diff --git a/engine/src/flutter/shell/platform/android/android_surface.h b/engine/src/flutter/shell/platform/android/android_surface.h index 2425a61e20..858b07eb45 100644 --- a/engine/src/flutter/shell/platform/android/android_surface.h +++ b/engine/src/flutter/shell/platform/android/android_surface.h @@ -19,6 +19,8 @@ namespace shell { class AndroidSurface { public: + static std::unique_ptr Create(bool use_software_rendering); + virtual ~AndroidSurface(); virtual bool IsValid() const = 0; @@ -27,14 +29,11 @@ class AndroidSurface { virtual std::unique_ptr CreateGPUSurface() = 0; - virtual SkISize OnScreenSurfaceSize() const = 0; - virtual bool OnScreenSurfaceResize(const SkISize& size) const = 0; virtual bool ResourceContextMakeCurrent() = 0; - virtual bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config = {}) = 0; + virtual bool SetNativeWindow(fxl::RefPtr window) = 0; }; } // namespace shell diff --git a/engine/src/flutter/shell/platform/android/android_surface_gl.cc b/engine/src/flutter/shell/platform/android/android_surface_gl.cc index f30ac1272b..274b652a97 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_gl.cc +++ b/engine/src/flutter/shell/platform/android/android_surface_gl.cc @@ -6,14 +6,12 @@ #include -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" #include "lib/fxl/memory/ref_ptr.h" namespace shell { -static fxl::RefPtr GlobalResourceLoadingContext( - PlatformView::SurfaceConfig offscreen_config) { +static fxl::RefPtr GlobalResourceLoadingContext() { // AndroidSurfaceGL instances are only ever created on the platform thread. So // there is no need to lock here. @@ -29,11 +27,7 @@ static fxl::RefPtr GlobalResourceLoadingContext( return nullptr; } - // TODO(chinmaygarde): We should check that the configurations are stable - // across multiple invocations. - - auto context = - fxl::MakeRefCounted(environment, offscreen_config); + auto context = fxl::MakeRefCounted(environment); if (!context->IsValid()) { return nullptr; @@ -43,10 +37,9 @@ static fxl::RefPtr GlobalResourceLoadingContext( return global_context; } -AndroidSurfaceGL::AndroidSurfaceGL( - PlatformView::SurfaceConfig offscreen_config) { +AndroidSurfaceGL::AndroidSurfaceGL() { // Acquire the offscreen context. - offscreen_context_ = GlobalResourceLoadingContext(offscreen_config); + offscreen_context_ = GlobalResourceLoadingContext(); if (!offscreen_context_ || !offscreen_context_->IsValid()) { offscreen_context_ = nullptr; @@ -60,14 +53,9 @@ bool AndroidSurfaceGL::IsOffscreenContextValid() const { } void AndroidSurfaceGL::TeardownOnScreenContext() { - fxl::AutoResetWaitableEvent latch; - blink::Threads::Gpu()->PostTask([this, &latch]() { - if (IsValid()) { - GLContextClearCurrent(); - } - latch.Signal(); - }); - latch.Wait(); + if (onscreen_context_) { + onscreen_context_->ClearCurrent(); + } onscreen_context_ = nullptr; } @@ -84,11 +72,6 @@ std::unique_ptr AndroidSurfaceGL::CreateGPUSurface() { return surface->IsValid() ? std::move(surface) : nullptr; } -SkISize AndroidSurfaceGL::OnScreenSurfaceSize() const { - FXL_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return onscreen_context_->GetSize(); -} - bool AndroidSurfaceGL::OnScreenSurfaceResize(const SkISize& size) const { FXL_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); return onscreen_context_->Resize(size); @@ -99,8 +82,8 @@ bool AndroidSurfaceGL::ResourceContextMakeCurrent() { return offscreen_context_->MakeCurrent(); } -bool AndroidSurfaceGL::SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) { +bool AndroidSurfaceGL::SetNativeWindow( + fxl::RefPtr window) { // In any case, we want to get rid of our current onscreen context. onscreen_context_ = nullptr; @@ -112,7 +95,7 @@ bool AndroidSurfaceGL::SetNativeWindow(fxl::RefPtr window, // Create the onscreen context. onscreen_context_ = fxl::MakeRefCounted( - offscreen_context_->Environment(), config, + offscreen_context_->Environment(), offscreen_context_.get() /* sharegroup */); if (!onscreen_context_->IsValid()) { diff --git a/engine/src/flutter/shell/platform/android/android_surface_gl.h b/engine/src/flutter/shell/platform/android/android_surface_gl.h index 08aee081c8..e26f2bf50d 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_gl.h +++ b/engine/src/flutter/shell/platform/android/android_surface_gl.h @@ -16,35 +16,43 @@ namespace shell { -class AndroidSurfaceGL : public GPUSurfaceGLDelegate, public AndroidSurface { +class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, + public AndroidSurface { public: - explicit AndroidSurfaceGL(PlatformView::SurfaceConfig offscreen_config); + AndroidSurfaceGL(); ~AndroidSurfaceGL() override; - bool IsValid() const override; - bool IsOffscreenContextValid() const; + // |shell::AndroidSurface| + bool IsValid() const override; + + // |shell::AndroidSurface| std::unique_ptr CreateGPUSurface() override; + // |shell::AndroidSurface| void TeardownOnScreenContext() override; - SkISize OnScreenSurfaceSize() const override; - + // |shell::AndroidSurface| bool OnScreenSurfaceResize(const SkISize& size) const override; + // |shell::AndroidSurface| bool ResourceContextMakeCurrent() override; - bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) override; + // |shell::AndroidSurface| + bool SetNativeWindow(fxl::RefPtr window) override; + // |shell::GPUSurfaceGLDelegate| bool GLContextMakeCurrent() override; + // |shell::GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; + // |shell::GPUSurfaceGLDelegate| bool GLContextPresent() override; + // |shell::GPUSurfaceGLDelegate| intptr_t GLContextFBO() const override; private: diff --git a/engine/src/flutter/shell/platform/android/android_surface_software.cc b/engine/src/flutter/shell/platform/android/android_surface_software.cc index 68bf99b27b..4b1378802d 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_software.cc +++ b/engine/src/flutter/shell/platform/android/android_surface_software.cc @@ -3,15 +3,14 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/android_surface_software.h" -#include "flutter/common/threads.h" -#include "flutter/fml/platform/android/jni_weak_ref.h" -#include "flutter/fml/platform/android/scoped_java_ref.h" -#include "flutter/shell/platform/android/platform_view_android_jni.h" #include #include +#include "flutter/fml/platform/android/jni_weak_ref.h" +#include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/platform/android/platform_view_android_jni.h" #include "lib/fxl/logging.h" namespace shell { @@ -132,17 +131,12 @@ bool AndroidSurfaceSoftware::PresentBackingStore( void AndroidSurfaceSoftware::TeardownOnScreenContext() {} -SkISize AndroidSurfaceSoftware::OnScreenSurfaceSize() const { - return SkISize(); -} - bool AndroidSurfaceSoftware::OnScreenSurfaceResize(const SkISize& size) const { return true; } bool AndroidSurfaceSoftware::SetNativeWindow( - fxl::RefPtr window, - PlatformView::SurfaceConfig config) { + fxl::RefPtr window) { native_window_ = std::move(window); if (!(native_window_ && native_window_->IsValid())) return false; diff --git a/engine/src/flutter/shell/platform/android/android_surface_software.h b/engine/src/flutter/shell/platform/android/android_surface_software.h index 76184b707c..0f82fc5dfe 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_software.h +++ b/engine/src/flutter/shell/platform/android/android_surface_software.h @@ -13,35 +13,39 @@ namespace shell { -class AndroidSurfaceSoftware : public AndroidSurface, - public GPUSurfaceSoftwareDelegate { +class AndroidSurfaceSoftware final : public AndroidSurface, + public GPUSurfaceSoftwareDelegate { public: AndroidSurfaceSoftware(); ~AndroidSurfaceSoftware() override; + // |shell::AndroidSurface| bool IsValid() const override; + // |shell::AndroidSurface| bool ResourceContextMakeCurrent() override; + // |shell::AndroidSurface| std::unique_ptr CreateGPUSurface() override; - sk_sp AcquireBackingStore(const SkISize& size) override; - - bool PresentBackingStore(sk_sp backing_store) override; - + // |shell::AndroidSurface| void TeardownOnScreenContext() override; - SkISize OnScreenSurfaceSize() const override; - + // |shell::AndroidSurface| bool OnScreenSurfaceResize(const SkISize& size) const override; - bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) override; + // |shell::AndroidSurface| + bool SetNativeWindow(fxl::RefPtr window) override; + + // |shell::GPUSurfaceSoftwareDelegate| + sk_sp AcquireBackingStore(const SkISize& size) override; + + // |shell::GPUSurfaceSoftwareDelegate| + bool PresentBackingStore(sk_sp backing_store) override; private: sk_sp sk_surface_; - fxl::RefPtr native_window_; SkColorType target_color_type_; SkAlphaType target_alpha_type_; diff --git a/engine/src/flutter/shell/platform/android/android_surface_vulkan.cc b/engine/src/flutter/shell/platform/android/android_surface_vulkan.cc index ccacf538b8..e881769018 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_vulkan.cc +++ b/engine/src/flutter/shell/platform/android/android_surface_vulkan.cc @@ -21,10 +21,12 @@ bool AndroidSurfaceVulkan::IsValid() const { return proc_table_->HasAcquiredMandatoryProcAddresses(); } +// |shell::AndroidSurface| void AndroidSurfaceVulkan::TeardownOnScreenContext() { - // + // Nothing to do. } +// |shell::AndroidSurface| std::unique_ptr AndroidSurfaceVulkan::CreateGPUSurface() { if (!IsValid()) { return nullptr; @@ -52,21 +54,20 @@ std::unique_ptr AndroidSurfaceVulkan::CreateGPUSurface() { return gpu_surface; } -SkISize AndroidSurfaceVulkan::OnScreenSurfaceSize() const { - return native_window_ ? native_window_->GetSize() : SkISize::Make(0, 0); -} - +// |shell::AndroidSurface| bool AndroidSurfaceVulkan::OnScreenSurfaceResize(const SkISize& size) const { return true; } +// |shell::AndroidSurface| bool AndroidSurfaceVulkan::ResourceContextMakeCurrent() { + FXL_DLOG(ERROR) << "The vulkan backend does not support resource contexts."; return false; } +// |shell::AndroidSurface| bool AndroidSurfaceVulkan::SetNativeWindow( - fxl::RefPtr window, - PlatformView::SurfaceConfig config) { + fxl::RefPtr window) { native_window_ = std::move(window); return native_window_ && native_window_->IsValid(); } diff --git a/engine/src/flutter/shell/platform/android/android_surface_vulkan.h b/engine/src/flutter/shell/platform/android/android_surface_vulkan.h index f1ecf610c8..fd3f493d49 100644 --- a/engine/src/flutter/shell/platform/android/android_surface_vulkan.h +++ b/engine/src/flutter/shell/platform/android/android_surface_vulkan.h @@ -20,20 +20,23 @@ class AndroidSurfaceVulkan : public AndroidSurface { ~AndroidSurfaceVulkan() override; + // |shell::AndroidSurface| bool IsValid() const override; - void TeardownOnScreenContext() override; - + // |shell::AndroidSurface| std::unique_ptr CreateGPUSurface() override; - SkISize OnScreenSurfaceSize() const override; + // |shell::AndroidSurface| + void TeardownOnScreenContext() override; + // |shell::AndroidSurface| bool OnScreenSurfaceResize(const SkISize& size) const override; + // |shell::AndroidSurface| bool ResourceContextMakeCurrent() override; - bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) override; + // |shell::AndroidSurface| + bool SetNativeWindow(fxl::RefPtr window) override; private: fxl::RefPtr proc_table_; diff --git a/engine/src/flutter/shell/platform/android/apk_asset_provider.cc b/engine/src/flutter/shell/platform/android/apk_asset_provider.cc index fdd4910a12..4cf6da16dc 100644 --- a/engine/src/flutter/shell/platform/android/apk_asset_provider.cc +++ b/engine/src/flutter/shell/platform/android/apk_asset_provider.cc @@ -7,32 +7,41 @@ namespace blink { +APKAssetProvider::APKAssetProvider(JNIEnv* env, + jobject jassetManager, + std::string directory) + : directory_(std::move(directory)) { + assetManager_ = AAssetManager_fromJava(env, jassetManager); +} + +APKAssetProvider::~APKAssetProvider() = default; + +bool APKAssetProvider::IsValid() const { + return true; +} + bool APKAssetProvider::GetAsBuffer(const std::string& asset_name, - std::vector* data) { + std::vector* data) const { std::stringstream ss; ss << directory_.c_str() << "/" << asset_name; - AAsset* asset = AAssetManager_open(assetManager_, ss.str().c_str(), AASSET_MODE_BUFFER); + AAsset* asset = + AAssetManager_open(assetManager_, ss.str().c_str(), AASSET_MODE_BUFFER); if (!asset) { - return false; + return false; } uint8_t* buffer = (uint8_t*)AAsset_getBuffer(asset); if (!buffer) { FXL_LOG(ERROR) << "Got null trying to acquire buffer for asset:" << asset; + AAsset_close(asset); return false; } data->resize(AAsset_getLength(asset)); std::copy(buffer, buffer + data->size(), data->begin()); + AAsset_close(asset); return true; } -APKAssetProvider::~APKAssetProvider() {} - -APKAssetProvider::APKAssetProvider(JNIEnv* env, jobject jassetManager, std::string directory) - : directory_(std::move(directory)) { - assetManager_ = AAssetManager_fromJava(env, jassetManager); -} - } // namespace blink diff --git a/engine/src/flutter/shell/platform/android/apk_asset_provider.h b/engine/src/flutter/shell/platform/android/apk_asset_provider.h index c40b1ed2a1..70ddbe454b 100644 --- a/engine/src/flutter/shell/platform/android/apk_asset_provider.h +++ b/engine/src/flutter/shell/platform/android/apk_asset_provider.h @@ -5,28 +5,35 @@ #ifndef FLUTTER_ASSETS_APK_ASSET_PROVIDER_H_ #define FLUTTER_ASSETS_APK_ASSET_PROVIDER_H_ -#include #include +#include -#include "flutter/assets/asset_provider.h" +#include "flutter/assets/asset_resolver.h" #include "lib/fxl/memory/ref_counted.h" namespace blink { -class APKAssetProvider - : public AssetProvider { +class APKAssetProvider final : public AssetResolver { public: - explicit APKAssetProvider(JNIEnv* env, jobject assetManager, std::string directory); + explicit APKAssetProvider(JNIEnv* env, + jobject assetManager, + std::string directory); virtual ~APKAssetProvider(); - virtual bool GetAsBuffer(const std::string& asset_name, - std::vector* data); - private: - AAssetManager* assetManager_; - const std::string directory_; + AAssetManager* assetManager_; + const std::string directory_; + + // |blink::AssetResolver| + bool IsValid() const override; + + // |blink::AssetResolver| + virtual bool GetAsBuffer(const std::string& asset_name, + std::vector* data) const override; + + FXL_DISALLOW_COPY_AND_ASSIGN(APKAssetProvider); }; } // namespace blink -#endif // FLUTTER_ASSETS_APK_ASSET_PROVIDER_H \ No newline at end of file +#endif // FLUTTER_ASSETS_APK_ASSET_PROVIDER_H \ No newline at end of file diff --git a/engine/src/flutter/shell/platform/android/flutter_main.cc b/engine/src/flutter/shell/platform/android/flutter_main.cc index 6df346a128..08d97add93 100644 --- a/engine/src/flutter/shell/platform/android/flutter_main.cc +++ b/engine/src/flutter/shell/platform/android/flutter_main.cc @@ -2,38 +2,85 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/shell/platform/android/flutter_main.h" #include +#include "flutter/fml/message_loop.h" +#include "flutter/fml/paths.h" #include "flutter/fml/platform/android/jni_util.h" +#include "flutter/runtime/dart_vm.h" #include "flutter/runtime/start_up.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" #include "lib/fxl/arraysize.h" #include "lib/fxl/command_line.h" +#include "lib/fxl/files/file.h" #include "lib/fxl/macros.h" #include "third_party/dart/runtime/include/dart_tools_api.h" namespace shell { -static void Init(JNIEnv* env, - jclass clazz, - jobject context, - jobjectArray jargs, - jstring bundlePath) { - // Prepare command line arguments and initialize the shell. +FlutterMain::FlutterMain(blink::Settings settings) + : settings_(std::move(settings)) {} + +FlutterMain::~FlutterMain() = default; + +static std::unique_ptr g_flutter_main; + +FlutterMain& FlutterMain::Get() { + FXL_CHECK(g_flutter_main) << "ensureInitializationComplete must have already " + "been called."; + return *g_flutter_main; +} + +const blink::Settings& FlutterMain::GetSettings() const { + return settings_; +} + +void FlutterMain::Init(JNIEnv* env, + jclass clazz, + jobject context, + jobjectArray jargs, + jstring bundlePath) { std::vector args; - args.push_back("flutter_tester"); + args.push_back("flutter"); for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) { args.push_back(std::move(arg)); } - auto command_line = fxl::CommandLineFromIterators(args.begin(), args.end()); - std::string icu_data_path = - command_line.GetOptionValueWithDefault("icu-data-file-path", ""); - Shell::InitStandalone(std::move(command_line), std::move(icu_data_path), - /* application_library_path= */ "", - fml::jni::JavaStringToString(env, bundlePath)); + + auto settings = SettingsFromCommandLine(command_line); + + settings.assets_path = fml::jni::JavaStringToString(env, bundlePath); + + if (!blink::DartVM::IsRunningPrecompiledCode()) { + // Check to see if the appropriate kernel files are present and configure + // settings accordingly. + auto platform_kernel_path = + fml::paths::JoinPaths({settings.assets_path, "platform.dill"}); + auto application_kernel_path = + fml::paths::JoinPaths({settings.assets_path, "kernel_blob.bin"}); + + if (files::IsFile(platform_kernel_path) && + files::IsFile(application_kernel_path)) { + settings.kernel_snapshot_path = platform_kernel_path; + settings.application_kernel_asset = application_kernel_path; + } + } + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + // Not thread safe. Will be removed when FlutterMain is refactored to no + // longer be a singleton. + g_flutter_main.reset(new FlutterMain(std::move(settings))); } static void RecordStartTimestamp(JNIEnv* env, @@ -44,7 +91,7 @@ static void RecordStartTimestamp(JNIEnv* env, blink::engine_main_enter_ts = Dart_TimelineGetMicros() - initTimeMicros; } -bool RegisterFlutterMain(JNIEnv* env) { +bool FlutterMain::Register(JNIEnv* env) { static const JNINativeMethod methods[] = { { .name = "nativeInit", diff --git a/engine/src/flutter/shell/platform/android/flutter_main.h b/engine/src/flutter/shell/platform/android/flutter_main.h index f4f65c499f..6c8717e9ce 100644 --- a/engine/src/flutter/shell/platform/android/flutter_main.h +++ b/engine/src/flutter/shell/platform/android/flutter_main.h @@ -7,9 +7,34 @@ #include +#include "flutter/common/settings.h" +#include "lib/fxl/macros.h" + namespace shell { -bool RegisterFlutterMain(JNIEnv* env); +class FlutterMain { + public: + ~FlutterMain(); + + static bool Register(JNIEnv* env); + + static FlutterMain& Get(); + + const blink::Settings& GetSettings() const; + + private: + const blink::Settings settings_; + + FlutterMain(blink::Settings settings); + + static void Init(JNIEnv* env, + jclass clazz, + jobject context, + jobjectArray jargs, + jstring bundlePath); + + FXL_DISALLOW_COPY_AND_ASSIGN(FlutterMain); +}; } // namespace shell diff --git a/engine/src/flutter/shell/platform/android/library_loader.cc b/engine/src/flutter/shell/platform/android/library_loader.cc index c51d011428..569825ce33 100644 --- a/engine/src/flutter/shell/platform/android/library_loader.cc +++ b/engine/src/flutter/shell/platform/android/library_loader.cc @@ -16,7 +16,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { bool result = false; // Register FlutterMain. - result = shell::RegisterFlutterMain(env); + result = shell::FlutterMain::Register(env); FXL_CHECK(result); // Register PlatformView diff --git a/engine/src/flutter/shell/platform/android/platform_message_response_android.cc b/engine/src/flutter/shell/platform/android/platform_message_response_android.cc new file mode 100644 index 0000000000..214080ec5b --- /dev/null +++ b/engine/src/flutter/shell/platform/android/platform_message_response_android.cc @@ -0,0 +1,62 @@ +// Copyright 2018 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. + +#include "flutter/shell/platform/android/platform_message_response_android.h" + +#include "flutter/shell/platform/android/platform_view_android_jni.h" +#include "lib/fxl/functional/make_copyable.h" + +namespace shell { + +PlatformMessageResponseAndroid::PlatformMessageResponseAndroid( + int response_id, + fml::jni::JavaObjectWeakGlobalRef weak_java_object, + fxl::RefPtr platform_task_runner) + : response_id_(response_id), + weak_java_object_(weak_java_object), + platform_task_runner_(std::move(platform_task_runner)) {} + +// |blink::PlatformMessageResponse| +void PlatformMessageResponseAndroid::Complete(std::vector data) { + platform_task_runner_->PostTask( + fxl::MakeCopyable([response = response_id_, // + weak_java_object = weak_java_object_, // + data = std::move(data) // + ]() { + // We are on the platform thread. Attempt to get the strong reference to + // the Java object. + auto env = fml::jni::AttachCurrentThread(); + auto java_object = weak_java_object.get(env); + + if (java_object.is_null()) { + // The Java object was collected before this message response got to + // it. Drop the response on the floor. + return; + } + + if (data.size() == 0) { + // If the data is empty, there is no reason to create a Java byte + // array. Make the response now with a nullptr now. + FlutterViewHandlePlatformMessageResponse(env, java_object.obj(), + response, nullptr); + } + + // Convert the vector to a Java byte array. + fml::jni::ScopedJavaLocalRef data_array( + env, env->NewByteArray(data.size())); + env->SetByteArrayRegion(data_array.obj(), 0, data.size(), + reinterpret_cast(data.data())); + + // Make the response call into Java. + FlutterViewHandlePlatformMessageResponse(env, java_object.obj(), + response, data_array.obj()); + })); +} + +// |blink::PlatformMessageResponse| +void PlatformMessageResponseAndroid::CompleteEmpty() { + Complete(std::vector{}); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/platform/android/platform_message_response_android.h b/engine/src/flutter/shell/platform/android/platform_message_response_android.h new file mode 100644 index 0000000000..2ee7f3336a --- /dev/null +++ b/engine/src/flutter/shell/platform/android/platform_message_response_android.h @@ -0,0 +1,39 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_MESSAGE_RESPONSE_ANDROID_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_MESSAGE_RESPONSE_ANDROID_H_ + +#include "flutter/fml/platform/android/jni_weak_ref.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse { + public: + // |blink::PlatformMessageResponse| + void Complete(std::vector data) override; + + // |blink::PlatformMessageResponse| + void CompleteEmpty() override; + + private: + PlatformMessageResponseAndroid( + int response_id, + fml::jni::JavaObjectWeakGlobalRef weak_java_object, + fxl::RefPtr platform_task_runner); + + int response_id_; + fml::jni::JavaObjectWeakGlobalRef weak_java_object_; + fxl::RefPtr platform_task_runner_; + + FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseAndroid); + FXL_DISALLOW_COPY_AND_ASSIGN(PlatformMessageResponseAndroid); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_MESSAGE_RESPONSE_ANDROID_H_ diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.cc b/engine/src/flutter/shell/platform/android/platform_view_android.cc index 8f677045fb..00d1e4b59e 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -4,288 +4,55 @@ #include "flutter/shell/platform/android/platform_view_android.h" -#include -#include -#include -#include - +#include #include -#include "flutter/common/settings.h" -#include "flutter/common/threads.h" -#include "flutter/fml/platform/android/jni_util.h" -#include "flutter/fml/platform/android/scoped_java_ref.h" -#include "flutter/runtime/dart_service_isolate.h" -#include "flutter/shell/common/null_rasterizer.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" +#include "flutter/shell/common/io_manager.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" #include "flutter/shell/platform/android/android_surface_gl.h" -#include "flutter/shell/platform/android/android_surface_software.h" -#include "flutter/shell/platform/android/apk_asset_provider.h" +#include "flutter/shell/platform/android/platform_message_response_android.h" #include "flutter/shell/platform/android/platform_view_android_jni.h" #include "flutter/shell/platform/android/vsync_waiter_android.h" -#include "lib/fxl/functional/make_copyable.h" - -#if SHELL_ENABLE_VULKAN -#include "flutter/shell/platform/android/android_surface_vulkan.h" -#endif // SHELL_ENABLE_VULKAN +#include "lib/fxl/synchronization/waitable_event.h" namespace shell { -class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse { - FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseAndroid); - - public: - void Complete(std::vector data) override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask( - fxl::MakeCopyable([ self, data = std::move(data) ]() mutable { - std::shared_ptr view = self->view_.lock(); - if (!view) - return; - static_cast(view.get()) - ->HandlePlatformMessageResponse(self->response_id_, - std::move(data)); - })); - } - - void CompleteEmpty() override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask(fxl::MakeCopyable([self]() mutable { - std::shared_ptr view = self->view_.lock(); - if (!view) - return; - static_cast(view.get()) - ->HandlePlatformMessageEmptyResponse(self->response_id_); - })); - } - - private: - PlatformMessageResponseAndroid(int response_id, - std::weak_ptr view) - : response_id_(response_id), view_(view) {} - - int response_id_; - std::weak_ptr view_; -}; - -static std::unique_ptr InitializePlatformSurfaceGL() { - const PlatformView::SurfaceConfig offscreen_config = { - .red_bits = 8, - .green_bits = 8, - .blue_bits = 8, - .alpha_bits = 8, - .depth_bits = 0, - .stencil_bits = 0, - }; - auto surface = std::make_unique(offscreen_config); - return surface->IsOffscreenContextValid() ? std::move(surface) : nullptr; +PlatformViewAndroid::PlatformViewAndroid( + PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + fml::jni::JavaObjectWeakGlobalRef java_object, + bool use_software_rendering) + : PlatformView(delegate, std::move(task_runners)), + java_object_(java_object), + android_surface_(AndroidSurface::Create(use_software_rendering)) { + FXL_CHECK(android_surface_) + << "Could not create an OpenGL, Vulkan or Software surface to setup " + "rendering."; } -static std::unique_ptr InitializePlatformSurfaceVulkan() { -#if SHELL_ENABLE_VULKAN - auto surface = std::make_unique(); - return surface->IsValid() ? std::move(surface) : nullptr; -#else // SHELL_ENABLE_VULKAN - return nullptr; -#endif // SHELL_ENABLE_VULKAN -} - -static std::unique_ptr InitializePlatformSurfaceSoftware() { - auto surface = std::make_unique(); - return surface->IsValid() ? std::move(surface) : nullptr; -} - -static std::unique_ptr InitializePlatformSurface() { - if (blink::Settings::Get().enable_software_rendering) { - if (auto surface = InitializePlatformSurfaceSoftware()) { - FXL_DLOG(INFO) << "Software surface initialized."; - return surface; - } - } - - if (auto surface = InitializePlatformSurfaceVulkan()) { - FXL_DLOG(INFO) << "Vulkan surface initialized."; - return surface; - } - - FXL_DLOG(INFO) - << "Could not initialize Vulkan surface. Falling back to OpenGL."; - - if (auto surface = InitializePlatformSurfaceGL()) { - FXL_DLOG(INFO) << "GL surface initialized."; - return surface; - } - - if (auto surface = InitializePlatformSurfaceSoftware()) { - FXL_DLOG(INFO) << "Software surface initialized."; - return surface; - } - - FXL_CHECK(false) - << "Could not initialize either the Vulkan, OpenGL, or Software" - "surface backends. Flutter requires a GPU to render."; - return nullptr; -} - -PlatformViewAndroid::PlatformViewAndroid() - : PlatformView(std::make_unique()), - android_surface_(InitializePlatformSurface()) {} - PlatformViewAndroid::~PlatformViewAndroid() = default; -void PlatformViewAndroid::Attach() { - CreateEngine(); - - // Eagerly setup the IO thread context. We have already setup the surface. - SetupResourceContextOnIOThread(); - - UpdateThreadPriorities(); +void PlatformViewAndroid::NotifyCreated( + fxl::RefPtr native_window) { + InstallFirstFrameCallback(); + android_surface_->SetNativeWindow(native_window); + PlatformView::NotifyCreated(); } -void PlatformViewAndroid::Detach() { - ReleaseSurface(); +void PlatformViewAndroid::NotifyDestroyed() { + PlatformView::NotifyDestroyed(); + android_surface_->TeardownOnScreenContext(); } -void PlatformViewAndroid::SurfaceCreated(JNIEnv* env, - jobject jsurface, - jint backgroundColor) { - // Note: This frame ensures that any local references used by - // ANativeWindow_fromSurface are released immediately. This is needed as a - // workaround for https://code.google.com/p/android/issues/detail?id=68174 - fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env); - - // We have a drawing surface, so swap in a non-Null rasterizer. - SetRasterizer(std::make_unique(nullptr)); - - rasterizer_->AddNextFrameCallback([this]() { - JNIEnv* env = fml::jni::AttachCurrentThread(); - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - if (!view.is_null()) { - FlutterViewOnFirstFrame(env, view.obj()); - } - }); - - auto native_window = fxl::MakeRefCounted( - ANativeWindow_fromSurface(env, jsurface)); - - if (!native_window->IsValid()) { - return; - } - - if (!android_surface_->SetNativeWindow(native_window)) { - return; - } - - std::unique_ptr gpu_surface = android_surface_->CreateGPUSurface(); - - if (gpu_surface == nullptr || !gpu_surface->IsValid()) { - return; - } - - NotifyCreated(std::move(gpu_surface), [ - this, backgroundColor, native_window_size = native_window->GetSize() - ] { rasterizer().Clear(backgroundColor, native_window_size); }); -} - -void PlatformViewAndroid::SurfaceChanged(jint width, jint height) { - blink::Threads::Gpu()->PostTask([this, width, height]() { - if (android_surface_) { - android_surface_->OnScreenSurfaceResize(SkISize::Make(width, height)); - } - }); -} - -void PlatformViewAndroid::UpdateThreadPriorities() { - blink::Threads::Gpu()->PostTask( - []() { ::setpriority(PRIO_PROCESS, gettid(), -2); }); - - blink::Threads::UI()->PostTask( - []() { ::setpriority(PRIO_PROCESS, gettid(), -1); }); -} - -void PlatformViewAndroid::SurfaceDestroyed() { - ReleaseSurface(); -} - -void PlatformViewAndroid::RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path, - std::string snapshot_override, - std::string entrypoint, - bool reuse_runtime_controller, - jobject assetManager) { - // TODO(jsimmons): remove snapshot_override from the public FlutterView API - FXL_CHECK(snapshot_override.empty()) << "snapshot_override is obsolete"; - - // The flutter assets directory name is the last directory of the bundle_path - // and the path into the APK - size_t last_slash_idx = bundle_path.rfind("/", bundle_path.size()); - std::string flutter_assets_dir = bundle_path.substr( - last_slash_idx + 1, bundle_path.size() - last_slash_idx); - - fxl::RefPtr asset_provider = - fxl::MakeRefCounted(env, assetManager, - flutter_assets_dir); - blink::Threads::UI()->PostTask( - [engine = engine_->GetWeakPtr(), - asset_provider = std::move(asset_provider), - bundle_path = std::move(bundle_path), entrypoint = std::move(entrypoint), - reuse_runtime_controller = reuse_runtime_controller] { - if (engine) - engine->RunBundleWithAssets( - std::move(asset_provider), std::move(bundle_path), - std::move(entrypoint), reuse_runtime_controller); +void PlatformViewAndroid::NotifyChanged(const SkISize& size) { + fxl::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), // + [&latch, surface = android_surface_.get(), size]() { + surface->OnScreenSurfaceResize(size); + latch.Signal(); }); -} - -void PlatformViewAndroid::RunBundleAndSource(std::string bundle_path, - std::string main, - std::string packages) { - blink::Threads::UI()->PostTask([ - engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path), - main = std::move(main), packages = std::move(packages) - ] { - if (engine) - engine->RunBundleAndSource(std::move(bundle_path), std::move(main), - std::move(packages)); - }); -} - -void PlatformViewAndroid::SetAssetBundlePathOnUI(std::string bundle_path) { - blink::Threads::UI()->PostTask( - [ engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path) ] { - if (engine) - engine->SetAssetBundlePath(std::move(bundle_path)); - }); -} - -void PlatformViewAndroid::SetViewportMetrics(jfloat device_pixel_ratio, - jint physical_width, - jint physical_height, - jint physical_padding_top, - jint physical_padding_right, - jint physical_padding_bottom, - jint physical_padding_left, - jint physical_view_inset_top, - jint physical_view_inset_right, - jint physical_view_inset_bottom, - jint physical_view_inset_left) { - blink::ViewportMetrics metrics; - metrics.device_pixel_ratio = device_pixel_ratio; - metrics.physical_width = physical_width; - metrics.physical_height = physical_height; - metrics.physical_padding_top = physical_padding_top; - metrics.physical_padding_right = physical_padding_right; - metrics.physical_padding_bottom = physical_padding_bottom; - metrics.physical_padding_left = physical_padding_left; - metrics.physical_view_inset_top = physical_view_inset_top; - metrics.physical_view_inset_right = physical_view_inset_right; - metrics.physical_view_inset_bottom = physical_view_inset_bottom; - metrics.physical_view_inset_left = physical_view_inset_left; - - blink::Threads::UI()->PostTask([ engine = engine_->GetWeakPtr(), metrics ] { - if (engine) - engine->SetViewportMetrics(metrics); - }); + latch.Wait(); } void PlatformViewAndroid::DispatchPlatformMessage(JNIEnv* env, @@ -301,7 +68,7 @@ void PlatformViewAndroid::DispatchPlatformMessage(JNIEnv* env, fxl::RefPtr response; if (response_id) { response = fxl::MakeRefCounted( - response_id, GetWeakPtr()); + response_id, java_object_, task_runners_.GetPlatformTaskRunner()); } PlatformView::DispatchPlatformMessage( @@ -315,7 +82,7 @@ void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env, fxl::RefPtr response; if (response_id) { response = fxl::MakeRefCounted( - response_id, GetWeakPtr()); + response_id, java_object_, task_runners_.GetPlatformTaskRunner()); } PlatformView::DispatchPlatformMessage( @@ -323,20 +90,6 @@ void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env, std::move(response))); } -void PlatformViewAndroid::DispatchPointerDataPacket(JNIEnv* env, - jobject buffer, - jint position) { - uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); - - blink::Threads::UI()->PostTask(fxl::MakeCopyable([ - engine = engine_->GetWeakPtr(), - packet = std::make_unique(data, position) - ] { - if (engine.get()) - engine->DispatchPointerDataPacket(*packet); - })); -} - void PlatformViewAndroid::InvokePlatformMessageResponseCallback( JNIEnv* env, jint response_id, @@ -369,10 +122,11 @@ void PlatformViewAndroid::InvokePlatformMessageEmptyResponseCallback( message_response->CompleteEmpty(); } +// |shell::PlatformView| void PlatformViewAndroid::HandlePlatformMessage( fxl::RefPtr message) { JNIEnv* env = fml::jni::AttachCurrentThread(); - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); + fml::jni::ScopedJavaLocalRef view = java_object_.get(env); if (view.is_null()) return; @@ -402,35 +156,6 @@ void PlatformViewAndroid::HandlePlatformMessage( } } -void PlatformViewAndroid::HandlePlatformMessageResponse( - int response_id, - std::vector data) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - - if (view.is_null()) - return; - fml::jni::ScopedJavaLocalRef data_array( - env, env->NewByteArray(data.size())); - env->SetByteArrayRegion(data_array.obj(), 0, data.size(), - reinterpret_cast(data.data())); - - FlutterViewHandlePlatformMessageResponse(env, view.obj(), response_id, - data_array.obj()); -} - -void PlatformViewAndroid::HandlePlatformMessageEmptyResponse(int response_id) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - - if (view.is_null()) - return; - FlutterViewHandlePlatformMessageResponse(env, view.obj(), response_id, - nullptr); -} - void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, jint id, jint action, @@ -451,35 +176,14 @@ void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, id, static_cast(action), std::move(args_vector)); } -void PlatformViewAndroid::SetSemanticsEnabled(jboolean enabled) { - PlatformView::SetSemanticsEnabled(enabled); -} - -void PlatformViewAndroid::ReleaseSurface() { - NotifyDestroyed(); - android_surface_->TeardownOnScreenContext(); - SetRasterizer(std::make_unique()); -} - -VsyncWaiter* PlatformViewAndroid::GetVsyncWaiter() { - if (!vsync_waiter_) - vsync_waiter_ = std::make_unique(); - return vsync_waiter_.get(); -} - -bool PlatformViewAndroid::ResourceContextMakeCurrent() { - FXL_CHECK(android_surface_); - return android_surface_->ResourceContextMakeCurrent(); -} - -void PlatformViewAndroid::UpdateSemantics( - blink::SemanticsNodeUpdates update) { +// |shell::PlatformView| +void PlatformViewAndroid::UpdateSemantics(blink::SemanticsNodeUpdates update) { constexpr size_t kBytesPerNode = 36 * sizeof(int32_t); constexpr size_t kBytesPerChild = sizeof(int32_t); JNIEnv* env = fml::jni::AttachCurrentThread(); { - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); + fml::jni::ScopedJavaLocalRef view = java_object_.get(env); if (view.is_null()) return; @@ -560,79 +264,6 @@ void PlatformViewAndroid::UpdateSemantics( } } -void PlatformViewAndroid::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - FXL_CHECK(env); - - { - fml::jni::ScopedJavaLocalRef local_flutter_view = - flutter_view_.get(env); - if (local_flutter_view.is_null()) { - // Collected. - return; - } - - // Grab the class of the flutter view. - jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); - FXL_CHECK(flutter_view_class); - - // Grab the runFromSource method id. - jmethodID run_from_source_method_id = env->GetMethodID( - flutter_view_class, "runFromSource", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - FXL_CHECK(run_from_source_method_id); - - // Invoke runFromSource on the Android UI thread. - jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); - FXL_CHECK(java_assets_directory); - jstring java_main = env->NewStringUTF(main.c_str()); - FXL_CHECK(java_main); - jstring java_packages = env->NewStringUTF(packages.c_str()); - FXL_CHECK(java_packages); - env->CallVoidMethod(local_flutter_view.obj(), run_from_source_method_id, - java_assets_directory, java_main, java_packages); - } - - // Detaching from the VM deletes any stray local references. - fml::jni::DetachFromVM(); -} - -void PlatformViewAndroid::SetAssetBundlePath( - const std::string& assets_directory) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - FXL_CHECK(env); - - { - fml::jni::ScopedJavaLocalRef local_flutter_view = - flutter_view_.get(env); - if (local_flutter_view.is_null()) { - // Collected. - return; - } - - // Grab the class of the flutter view. - jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); - FXL_CHECK(flutter_view_class); - - // Grab the setAssetBundlePath method id. - jmethodID method_id = env->GetMethodID( - flutter_view_class, "setAssetBundlePathOnUI", "(Ljava/lang/String;)V"); - FXL_CHECK(method_id); - - // Invoke setAssetBundlePath on the Android UI thread. - jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); - FXL_CHECK(java_assets_directory); - - env->CallVoidMethod(local_flutter_view.obj(), method_id, - java_assets_directory); - } - - // Detaching from the VM deletes any stray local references. - fml::jni::DetachFromVM(); -} - void PlatformViewAndroid::RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture) { @@ -640,116 +271,56 @@ void PlatformViewAndroid::RegisterExternalTexture( std::make_shared(texture_id, surface_texture)); } -void PlatformViewAndroid::MarkTextureFrameAvailable(int64_t texture_id) { - blink::Threads::Gpu()->PostTask([this, texture_id]() { - std::shared_ptr texture = - static_pointer_cast( - rasterizer_->GetTextureRegistry().GetTexture(texture_id)); - if (texture) { - texture->MarkNewFrameAvailable(); - } - }); - PlatformView::MarkTextureFrameAvailable(texture_id); +// |shell::PlatformView| +std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { + return std::make_unique(task_runners_); } -fml::jni::ScopedJavaLocalRef PlatformViewAndroid::GetBitmap( - JNIEnv* env) { - // Render the last frame to an array of pixels on the GPU thread. - // The pixels will be returned as a global JNI reference to an int array. - fxl::AutoResetWaitableEvent latch; - jobject pixels_ref = nullptr; - SkISize frame_size; - blink::Threads::Gpu()->PostTask([this, &latch, &pixels_ref, &frame_size]() { - GetBitmapGpuTask(&pixels_ref, &frame_size); - latch.Signal(); - }); - - latch.Wait(); - - // Convert the pixel array to an Android bitmap. - if (pixels_ref == nullptr) - return fml::jni::ScopedJavaLocalRef(); - - fml::jni::ScopedJavaGlobalRef pixels(env, pixels_ref); - - jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); - FXL_CHECK(bitmap_class); - - jmethodID create_bitmap = env->GetStaticMethodID( - bitmap_class, "createBitmap", - "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); - FXL_CHECK(create_bitmap); - - jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); - FXL_CHECK(bitmap_config_class); - - jmethodID bitmap_config_value_of = env->GetStaticMethodID( - bitmap_config_class, "valueOf", - "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); - FXL_CHECK(bitmap_config_value_of); - - jstring argb = env->NewStringUTF("ARGB_8888"); - FXL_CHECK(argb); - - jobject bitmap_config = env->CallStaticObjectMethod( - bitmap_config_class, bitmap_config_value_of, argb); - FXL_CHECK(bitmap_config); - - jobject bitmap = env->CallStaticObjectMethod( - bitmap_class, create_bitmap, pixels.obj(), frame_size.width(), - frame_size.height(), bitmap_config); - - return fml::jni::ScopedJavaLocalRef(env, bitmap); +// |shell::PlatformView| +std::unique_ptr PlatformViewAndroid::CreateRenderingSurface() { + return android_surface_->CreateGPUSurface(); } -void PlatformViewAndroid::GetBitmapGpuTask(jobject* pixels_out, - SkISize* size_out) { - flow::LayerTree* layer_tree = rasterizer_->GetLastLayerTree(); - if (layer_tree == nullptr) - return; - - JNIEnv* env = fml::jni::AttachCurrentThread(); - FXL_CHECK(env); - - const SkISize& frame_size = layer_tree->frame_size(); - jsize pixels_size = frame_size.width() * frame_size.height(); - jintArray pixels_array = env->NewIntArray(pixels_size); - FXL_CHECK(pixels_array); - - jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); - FXL_CHECK(pixels); - - SkImageInfo image_info = - SkImageInfo::Make(frame_size.width(), frame_size.height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); - - sk_sp surface = SkSurface::MakeRasterDirect( - image_info, pixels, frame_size.width() * sizeof(jint)); - - flow::CompositorContext compositor_context(nullptr); - compositor_context.SetTextureRegistry(&texture_registry_); - SkCanvas* canvas = surface->getCanvas(); - flow::CompositorContext::ScopedFrame frame = - compositor_context.AcquireFrame(nullptr, canvas, false); - - canvas->clear(SK_ColorBLACK); - layer_tree->Raster(frame); - canvas->flush(); - - // Our configuration of Skia does not support rendering to the - // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. - // Convert from kRGBA_8888 to kBGRA_8888 (equivalent to ARGB_8888). - for (int i = 0; i < pixels_size; i++) { - uint8_t* bytes = reinterpret_cast(pixels + i); - std::swap(bytes[0], bytes[2]); +// |shell::PlatformView| +sk_sp PlatformViewAndroid::CreateResourceContext() const { + sk_sp resource_context; + if (android_surface_->ResourceContextMakeCurrent()) { + // TODO(chinmaygarde): Currently, this code depends on the fact that only + // the OpenGL surface will be able to make a resource context current. If + // this changes, this assumption breaks. Handle the same. + resource_context = IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend); + } else { + FXL_DLOG(ERROR) << "Could not make the resource context current."; } - env->ReleaseIntArrayElements(pixels_array, pixels, 0); + return resource_context; +} - *pixels_out = env->NewGlobalRef(pixels_array); - *size_out = frame_size; +void PlatformViewAndroid::InstallFirstFrameCallback() { + // On Platform Task Runner. + SetNextFrameCallback( + [platform_view = GetWeakPtr(), + platform_task_runner = task_runners_.GetPlatformTaskRunner()]() { + // On GPU Task Runner. + platform_task_runner->PostTask([platform_view]() { + // Back on Platform Task Runner. + if (platform_view) { + reinterpret_cast(platform_view.get()) + ->FireFirstFrameCallback(); + } + }); + }); +} - fml::jni::DetachFromVM(); +void PlatformViewAndroid::FireFirstFrameCallback() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + fml::jni::ScopedJavaLocalRef view = java_object_.get(env); + if (view.is_null()) { + // The Java object died. + return; + } + FlutterViewOnFirstFrame(fml::jni::AttachCurrentThread(), view.obj()); } } // namespace shell diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.h b/engine/src/flutter/shell/platform/android/platform_view_android.h index 4779ea16ab..9976c443f4 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.h +++ b/engine/src/flutter/shell/platform/android/platform_view_android.h @@ -20,45 +20,23 @@ namespace shell { -class PlatformViewAndroid : public PlatformView { +class PlatformViewAndroid final : public PlatformView { public: static bool Register(JNIEnv* env); - PlatformViewAndroid(); + PlatformViewAndroid(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + fml::jni::JavaObjectWeakGlobalRef java_object, + bool use_software_rendering); ~PlatformViewAndroid() override; - virtual void Attach() override; + void NotifyCreated(fxl::RefPtr native_window); - void Detach(); + void NotifyChanged(const SkISize& size); - void SurfaceCreated(JNIEnv* env, jobject jsurface, jint backgroundColor); - - void SurfaceChanged(jint width, jint height); - - void SurfaceDestroyed(); - - void RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path, - std::string snapshot_override, - std::string entrypoint, - bool reuse_isolate, - jobject assetManager); - - void RunBundleAndSource(std::string bundle_path, - std::string main, - std::string packages); - - void SetViewportMetrics(jfloat device_pixel_ratio, - jint physical_width, - jint physical_height, - jint physical_padding_top, - jint physical_padding_right, - jint physical_padding_bottom, - jint physical_padding_left, - jint physical_view_inset_top, - jint physical_view_inset_right, - jint physical_view_inset_bottom, - jint physical_view_inset_left); + // |shell::PlatformView| + void NotifyDestroyed() override; void DispatchPlatformMessage(JNIEnv* env, std::string name, @@ -70,8 +48,6 @@ class PlatformViewAndroid : public PlatformView { std::string name, jint response_id); - void DispatchPointerDataPacket(JNIEnv* env, jobject buffer, jint position); - void InvokePlatformMessageResponseCallback(JNIEnv* env, jint response_id, jobject java_response_data, @@ -86,55 +62,37 @@ class PlatformViewAndroid : public PlatformView { jobject args, jint args_position); - void SetSemanticsEnabled(jboolean enabled); - - fml::jni::ScopedJavaLocalRef GetBitmap(JNIEnv* env); - - VsyncWaiter* GetVsyncWaiter() override; - - bool ResourceContextMakeCurrent() override; - - void UpdateSemantics(blink::SemanticsNodeUpdates update) override; - - void HandlePlatformMessage( - fxl::RefPtr message) override; - - void HandlePlatformMessageResponse(int response_id, - std::vector data); - - void HandlePlatformMessageEmptyResponse(int response_id); - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePathOnUI(std::string bundle_path); - - void SetAssetBundlePath(const std::string& assets_directory) override; - void RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture); - void MarkTextureFrameAvailable(int64_t texture_id) override; - - void set_flutter_view(const fml::jni::JavaObjectWeakGlobalRef& flutter_view) { - flutter_view_ = flutter_view; - } - private: - std::unique_ptr android_surface_; - fml::jni::JavaObjectWeakGlobalRef flutter_view_; + const fml::jni::JavaObjectWeakGlobalRef java_object_; + const std::unique_ptr android_surface_; // We use id 0 to mean that no response is expected. int next_response_id_ = 1; std::unordered_map> pending_responses_; - void UpdateThreadPriorities(); + // |shell::PlatformView| + void UpdateSemantics(blink::SemanticsNodeUpdates update) override; - void ReleaseSurface(); + // |shell::PlatformView| + void HandlePlatformMessage( + fxl::RefPtr message) override; - void GetBitmapGpuTask(jobject* pixels_out, SkISize* size_out); + // |shell::PlatformView| + std::unique_ptr CreateVSyncWaiter() override; + + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; + + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; + + void InstallFirstFrameCallback(); + + void FireFirstFrameCallback(); FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewAndroid); }; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc b/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc index c819f3bfb5..f75f54a896 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc @@ -4,17 +4,26 @@ #include "flutter/shell/platform/android/platform_view_android_jni.h" +#include + +#include + +#include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" +#include "flutter/fml/file.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/jni_weak_ref.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/runtime/dart_service_isolate.h" +#include "flutter/shell/common/run_configuration.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" +#include "flutter/shell/platform/android/android_shell_holder.h" +#include "flutter/shell/platform/android/apk_asset_provider.h" +#include "flutter/shell/platform/android/flutter_main.h" #include "lib/fxl/arraysize.h" -#include "lib/fxl/logging.h" -#define PLATFORM_VIEW \ - (*reinterpret_cast*>(platform_view)) +#define ANDROID_SHELL_HOLDER \ + (reinterpret_cast(shell_holder)) namespace shell { @@ -78,14 +87,12 @@ void FlutterViewOnFirstFrame(JNIEnv* env, jobject obj) { static jmethodID g_attach_to_gl_context_method = nullptr; void SurfaceTextureAttachToGLContext(JNIEnv* env, jobject obj, jint textureId) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_attach_to_gl_context_method, textureId); FXL_CHECK(CheckException(env)); } static jmethodID g_update_tex_image_method = nullptr; void SurfaceTextureUpdateTexImage(JNIEnv* env, jobject obj) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_update_tex_image_method); FXL_CHECK(CheckException(env)); } @@ -94,14 +101,12 @@ static jmethodID g_get_transform_matrix_method = nullptr; void SurfaceTextureGetTransformMatrix(JNIEnv* env, jobject obj, jfloatArray result) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_get_transform_matrix_method, result); FXL_CHECK(CheckException(env)); } static jmethodID g_detach_from_gl_context_method = nullptr; void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_detach_from_gl_context_method); FXL_CHECK(CheckException(env)); } @@ -109,22 +114,22 @@ void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { // Called By Java static jlong Attach(JNIEnv* env, jclass clazz, jobject flutterView) { - auto view = new PlatformViewAndroid(); - auto storage = new std::shared_ptr(view); - // Create a weak reference to the flutterView Java object so that we can make - // calls into it later. - view->Attach(); - view->set_flutter_view(fml::jni::JavaObjectWeakGlobalRef(env, flutterView)); - return reinterpret_cast(storage); + fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterView); + auto shell_holder = std::make_unique( + FlutterMain::Get().GetSettings(), java_object); + if (shell_holder->IsValid()) { + return reinterpret_cast(shell_holder.release()); + } else { + return 0; + } } -static void Detach(JNIEnv* env, jobject jcaller, jlong platform_view) { - PLATFORM_VIEW->Detach(); +static void Detach(JNIEnv* env, jobject jcaller, jlong shell_holder) { + // Nothing to do. } -static void Destroy(JNIEnv* env, jobject jcaller, jlong platform_view) { - PLATFORM_VIEW->Detach(); - delete &PLATFORM_VIEW; +static void Destroy(JNIEnv* env, jobject jcaller, jlong shell_holder) { + delete ANDROID_SHELL_HOLDER; } static jstring GetObservatoryUri(JNIEnv* env, jclass clazz) { @@ -134,67 +139,180 @@ static jstring GetObservatoryUri(JNIEnv* env, jclass clazz) { static void SurfaceCreated(JNIEnv* env, jobject jcaller, - jlong platform_view, - jobject surface, + jlong shell_holder, + jobject jsurface, jint backgroundColor) { - return PLATFORM_VIEW->SurfaceCreated(env, surface, backgroundColor); + // Note: This frame ensures that any local references used by + // ANativeWindow_fromSurface are released immediately. This is needed as a + // workaround for https://code.google.com/p/android/issues/detail?id=68174 + fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env); + auto window = fxl::MakeRefCounted( + ANativeWindow_fromSurface(env, jsurface)); + ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window)); } static void SurfaceChanged(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint width, jint height) { - return PLATFORM_VIEW->SurfaceChanged(width, height); + ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyChanged( + SkISize::Make(width, height)); } -static void SurfaceDestroyed(JNIEnv* env, - jobject jcaller, - jlong platform_view) { - return PLATFORM_VIEW->SurfaceDestroyed(); +static void SurfaceDestroyed(JNIEnv* env, jobject jcaller, jlong shell_holder) { + ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyDestroyed(); } -static void RunBundleAndSnapshot(JNIEnv* env, - jobject jcaller, - jlong platform_view, - jstring bundlePath, - jstring snapshotOverride, - jstring entrypoint, - jboolean reuse_runtime_controller, - jobject assetManager) { - return PLATFORM_VIEW->RunBundleAndSnapshot( - env, - fml::jni::JavaStringToString(env, bundlePath), // - fml::jni::JavaStringToString(env, snapshotOverride), // - fml::jni::JavaStringToString(env, entrypoint), // - reuse_runtime_controller, // - assetManager - ); +std::unique_ptr CreateIsolateConfiguration( + const blink::AssetManager& asset_manager) { + if (blink::DartVM::IsRunningPrecompiledCode()) { + return IsolateConfiguration::CreateForPrecompiledCode(); + } + + const auto configuration_from_blob = + [&asset_manager](const std::string& snapshot_name) + -> std::unique_ptr { + std::vector blob; + if (asset_manager.GetAsBuffer(snapshot_name, &blob)) { + return IsolateConfiguration::CreateForSnapshot( + std::make_unique(std::move(blob))); + } + return nullptr; + }; + + if (auto kernel = configuration_from_blob("kernel_blob.bin")) { + return kernel; + } + + if (auto script = configuration_from_blob("snapshot_blob.bin")) { + return script; + } + + return nullptr; } -void RunBundleAndSource(JNIEnv* env, - jobject jcaller, - jlong platform_view, - jstring bundlePath, - jstring main, - jstring packages) { - return PLATFORM_VIEW->RunBundleAndSource( - fml::jni::JavaStringToString(env, bundlePath), - fml::jni::JavaStringToString(env, main), - fml::jni::JavaStringToString(env, packages)); +static void RunBundleAndSnapshot( + JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jstring jbundlepath, + jstring /* snapshot override (unused) */, + jstring jEntrypoint, + jboolean /* reuse runtime controller (unused) */, + jobject jAssetManager) { + auto asset_manager = fxl::MakeRefCounted(); + + const auto bundlepath = fml::jni::JavaStringToString(env, jbundlepath); + + if (bundlepath.size() > 0) { + // If we got a bundle path, attempt to use that as a directory asset + // bundle. + asset_manager->PushBack(std::make_unique( + fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true))); + + // Use the last path component of the bundle path to determine the + // directory in the APK assets. + const auto last_slash_index = bundlepath.rfind("/", bundlepath.size()); + if (last_slash_index != std::string::npos) { + auto apk_asset_dir = bundlepath.substr( + last_slash_index + 1, bundlepath.size() - last_slash_index); + + asset_manager->PushBack(std::make_unique( + env, // jni environment + jAssetManager, // asset manager + std::move(apk_asset_dir)) // apk asset dir + ); + } + } + + auto isolate_configuration = CreateIsolateConfiguration(*asset_manager); + + if (!isolate_configuration) { + FXL_DLOG(ERROR) + << "Isolate configuration could not be determined for engine launch."; + return; + } + + RunConfiguration config(std::move(isolate_configuration), + std::move(asset_manager)); + + { + auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint); + if (entrypoint.size() > 0) { + config.SetEntrypoint(std::move(entrypoint)); + } + } + + ANDROID_SHELL_HOLDER->Launch(std::move(config)); +} + +static void RunBundleAndSource(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jstring jBundlePath, + jstring main, + jstring packages) { + auto asset_manager = fxl::MakeRefCounted(); + + const auto bundlepath = fml::jni::JavaStringToString(env, jBundlePath); + + if (bundlepath.size() > 0) { + auto directory = + fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true); + asset_manager->PushBack( + std::make_unique(std::move(directory))); + } + + auto main_file_path = fml::jni::JavaStringToString(env, main); + auto packages_file_path = fml::jni::JavaStringToString(env, packages); + + auto config = + IsolateConfiguration::CreateForSource(main_file_path, packages_file_path); + + if (!config) { + return; + } + + RunConfiguration run_configuration(std::move(config), + std::move(asset_manager)); + + ANDROID_SHELL_HOLDER->Launch(std::move(run_configuration)); } void SetAssetBundlePathOnUI(JNIEnv* env, jobject jcaller, - jlong platform_view, - jstring bundlePath) { - return PLATFORM_VIEW->SetAssetBundlePathOnUI( - fml::jni::JavaStringToString(env, bundlePath)); + jlong shell_holder, + jstring jBundlePath) { + const auto bundlepath = fml::jni::JavaStringToString(env, jBundlePath); + + if (bundlepath.size() == 0) { + return; + } + + auto directory = + fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true); + + if (!directory.is_valid()) { + return; + } + + std::unique_ptr directory_asset_bundle = + std::make_unique(std::move(directory)); + + if (!directory_asset_bundle->IsValid()) { + return; + } + + auto asset_manager = fxl::MakeRefCounted(); + asset_manager->PushBack(std::move(directory_asset_bundle)); + + ANDROID_SHELL_HOLDER->UpdateAssetManager(std::move(asset_manager)); } static void SetViewportMetrics(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jfloat devicePixelRatio, jint physicalWidth, jint physicalHeight, @@ -206,115 +324,194 @@ static void SetViewportMetrics(JNIEnv* env, jint physicalViewInsetRight, jint physicalViewInsetBottom, jint physicalViewInsetLeft) { - return PLATFORM_VIEW->SetViewportMetrics(devicePixelRatio, // - physicalWidth, // - physicalHeight, // - physicalPaddingTop, // - physicalPaddingRight, // - physicalPaddingBottom, // - physicalPaddingLeft, // - physicalViewInsetTop, // - physicalViewInsetRight, // - physicalViewInsetBottom, // - physicalViewInsetLeft); + const blink::ViewportMetrics metrics = { + .device_pixel_ratio = devicePixelRatio, + .physical_width = physicalWidth, + .physical_height = physicalHeight, + .physical_padding_top = physicalPaddingTop, + .physical_padding_right = physicalPaddingRight, + .physical_padding_bottom = physicalPaddingBottom, + .physical_padding_left = physicalPaddingLeft, + .physical_view_inset_top = physicalViewInsetTop, + .physical_view_inset_right = physicalViewInsetRight, + .physical_view_inset_bottom = physicalViewInsetBottom, + .physical_view_inset_left = physicalViewInsetLeft, + }; + + ANDROID_SHELL_HOLDER->SetViewportMetrics(metrics); } -static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong platform_view) { - return PLATFORM_VIEW->GetBitmap(env).Release(); +static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { + auto screenshot = ANDROID_SHELL_HOLDER->Screenshot( + Rasterizer::ScreenshotType::UncompressedImage, false); + if (screenshot.data == nullptr) { + return nullptr; + } + + const SkISize& frame_size = screenshot.frame_size; + jsize pixels_size = frame_size.width() * frame_size.height(); + jintArray pixels_array = env->NewIntArray(pixels_size); + FXL_CHECK(pixels_array); + + jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); + FXL_CHECK(pixels); + + auto pixels_src = static_cast(screenshot.data->data()); + + // Our configuration of Skia does not support rendering to the + // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. + // Convert from kRGBA_8888 to kBGRA_8888 (equivalent to ARGB_8888). + for (int i = 0; i < pixels_size; i++) { + int32_t src_pixel = pixels_src[i]; + uint8_t* src_bytes = reinterpret_cast(&src_pixel); + std::swap(src_bytes[0], src_bytes[2]); + pixels[i] = src_pixel; + } + + env->ReleaseIntArrayElements(pixels_array, pixels, 0); + + jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); + FXL_CHECK(bitmap_class); + + jmethodID create_bitmap = env->GetStaticMethodID( + bitmap_class, "createBitmap", + "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + FXL_CHECK(create_bitmap); + + jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); + FXL_CHECK(bitmap_config_class); + + jmethodID bitmap_config_value_of = env->GetStaticMethodID( + bitmap_config_class, "valueOf", + "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); + FXL_CHECK(bitmap_config_value_of); + + jstring argb = env->NewStringUTF("ARGB_8888"); + FXL_CHECK(argb); + + jobject bitmap_config = env->CallStaticObjectMethod( + bitmap_config_class, bitmap_config_value_of, argb); + FXL_CHECK(bitmap_config); + + return env->CallStaticObjectMethod(bitmap_class, create_bitmap, pixels_array, + frame_size.width(), frame_size.height(), + bitmap_config); } static void DispatchPlatformMessage(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jstring channel, jobject message, jint position, jint responseId) { - return PLATFORM_VIEW->DispatchPlatformMessage( - env, fml::jni::JavaStringToString(env, channel), message, position, - responseId); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPlatformMessage( + env, // + fml::jni::JavaStringToString(env, channel), // + message, // + position, // + responseId // + ); } static void DispatchEmptyPlatformMessage(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jstring channel, jint responseId) { - return PLATFORM_VIEW->DispatchEmptyPlatformMessage( - env, fml::jni::JavaStringToString(env, channel), responseId); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchEmptyPlatformMessage( + env, // + fml::jni::JavaStringToString(env, channel), // + responseId // + ); } static void DispatchPointerDataPacket(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jobject buffer, jint position) { - return PLATFORM_VIEW->DispatchPointerDataPacket(env, buffer, position); + uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); + auto packet = std::make_unique(data, position); + ANDROID_SHELL_HOLDER->DispatchPointerDataPacket(std::move(packet)); } static void DispatchSemanticsAction(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint id, jint action, jobject args, jint args_position) { - return PLATFORM_VIEW->DispatchSemanticsAction(env, id, action, args, - args_position); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchSemanticsAction( + env, // + id, // + action, // + args, // + args_position // + ); } static void SetSemanticsEnabled(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jboolean enabled) { - return PLATFORM_VIEW->SetSemanticsEnabled(enabled); + ANDROID_SHELL_HOLDER->GetPlatformView()->SetSemanticsEnabled(enabled); } static jboolean GetIsSoftwareRendering(JNIEnv* env, jobject jcaller) { - return blink::Settings::Get().enable_software_rendering; + return FlutterMain::Get().GetSettings().enable_software_rendering; } static void RegisterTexture(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jlong texture_id, jobject surface_texture) { - PLATFORM_VIEW->RegisterExternalTexture( - static_cast(texture_id), - fml::jni::JavaObjectWeakGlobalRef(env, surface_texture)); + ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterExternalTexture( + static_cast(texture_id), // + fml::jni::JavaObjectWeakGlobalRef(env, surface_texture) // + ); } static void MarkTextureFrameAvailable(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jlong texture_id) { - return PLATFORM_VIEW->MarkTextureFrameAvailable( + ANDROID_SHELL_HOLDER->GetPlatformView()->MarkTextureFrameAvailable( static_cast(texture_id)); } static void UnregisterTexture(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jlong texture_id) { - PLATFORM_VIEW->UnregisterTexture(static_cast(texture_id)); + ANDROID_SHELL_HOLDER->GetPlatformView()->UnregisterTexture( + static_cast(texture_id)); } static void InvokePlatformMessageResponseCallback(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint responseId, jobject message, jint position) { - return PLATFORM_VIEW->InvokePlatformMessageResponseCallback( - env, responseId, message, position); + ANDROID_SHELL_HOLDER->GetPlatformView() + ->InvokePlatformMessageResponseCallback(env, // + responseId, // + message, // + position // + ); } static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint responseId) { - return PLATFORM_VIEW->InvokePlatformMessageEmptyResponseCallback(env, - responseId); + ANDROID_SHELL_HOLDER->GetPlatformView() + ->InvokePlatformMessageEmptyResponseCallback(env, // + responseId // + ); } bool PlatformViewAndroid::Register(JNIEnv* env) { @@ -353,8 +550,8 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { }, { .name = "nativeRunBundleAndSnapshot", - .signature = - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLandroid/content/res/AssetManager;)V", + .signature = "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/" + "String;ZLandroid/content/res/AssetManager;)V", .fnPtr = reinterpret_cast(&shell::RunBundleAndSnapshot), }, { @@ -373,11 +570,6 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .signature = "(J)V", .fnPtr = reinterpret_cast(&shell::Detach), }, - { - .name = "nativeDestroy", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Destroy), - }, { .name = "nativeGetObservatoryUri", .signature = "()Ljava/lang/String;", diff --git a/engine/src/flutter/shell/platform/android/vsync_waiter_android.cc b/engine/src/flutter/shell/platform/android/vsync_waiter_android.cc index 29e1958dcc..052de023b9 100644 --- a/engine/src/flutter/shell/platform/android/vsync_waiter_android.cc +++ b/engine/src/flutter/shell/platform/android/vsync_waiter_android.cc @@ -7,7 +7,7 @@ #include #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/trace_event.h" @@ -16,68 +16,49 @@ namespace shell { +static jlong CreatePendingCallback(VsyncWaiter::Callback callback); + +static void ConsumePendingCallback(jlong java_baton, + fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time); + static fml::jni::ScopedJavaGlobalRef* g_vsync_waiter_class = nullptr; static jmethodID g_async_wait_for_vsync_method_ = nullptr; -VsyncWaiterAndroid::VsyncWaiterAndroid() : weak_factory_(this) {} +VsyncWaiterAndroid::VsyncWaiterAndroid(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)) {} VsyncWaiterAndroid::~VsyncWaiterAndroid() = default; -void VsyncWaiterAndroid::AsyncWaitForVsync(Callback callback) { - FXL_DCHECK(!callback_); - callback_ = std::move(callback); - fml::WeakPtr* weak = - new fml::WeakPtr(); - *weak = weak_factory_.GetWeakPtr(); +// |shell::VsyncWaiter| +void VsyncWaiterAndroid::AwaitVSync() { + auto java_baton = + CreatePendingCallback(std::bind(&VsyncWaiterAndroid::FireCallback, // + this, // + std::placeholders::_1, // + std::placeholders::_2 // + )); - blink::Threads::Platform()->PostTask([weak] { + task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { JNIEnv* env = fml::jni::AttachCurrentThread(); - env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), - g_async_wait_for_vsync_method_, - reinterpret_cast(weak)); + env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), // + g_async_wait_for_vsync_method_, // + java_baton // + ); }); } -void VsyncWaiterAndroid::OnVsync(int64_t frameTimeNanos, - int64_t frameTargetTimeNanos) { - Callback callback = std::move(callback_); - callback_ = Callback(); - blink::Threads::UI()->PostTask( - [callback, frameTimeNanos, frameTargetTimeNanos] { - callback(fxl::TimePoint::FromEpochDelta( - fxl::TimeDelta::FromNanoseconds(frameTimeNanos)), - fxl::TimePoint::FromEpochDelta( - fxl::TimeDelta::FromNanoseconds(frameTargetTimeNanos))); - }); -} - static void OnNativeVsync(JNIEnv* env, jclass jcaller, jlong frameTimeNanos, jlong frameTargetTimeNanos, - jlong cookie) { - // Note: The tag name must be "VSYNC" (it is special) so that the "Highlight - // Vsync" checkbox in the timeline can be enabled. - // See: https://github.com/catapult-project/catapult/blob/2091404475cbba9b786 - // 442979b6ec631305275a6/tracing/tracing/extras/vsync/vsync_auditor.html#L26 -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - TRACE_EVENT1("flutter", "VSYNC", "mode", "basic"); -#else - { - constexpr size_t num_chars = sizeof(jlong) * CHAR_BIT * 3.4 + 2; - char deadline[num_chars]; - sprintf(deadline, "%lld", frameTargetTimeNanos / 1000); // microseconds - TRACE_EVENT2("flutter", "VSYNC", "mode", "basic", "deadline", deadline); - } -#endif - fml::WeakPtr* weak = - reinterpret_cast*>(cookie); - VsyncWaiterAndroid* waiter = weak->get(); - delete weak; - if (waiter) { - waiter->OnVsync(static_cast(frameTimeNanos), - static_cast(frameTargetTimeNanos)); - } + jlong java_baton) { + auto frame_time = fxl::TimePoint::FromEpochDelta( + fxl::TimeDelta::FromNanoseconds(frameTimeNanos)); + auto target_time = fxl::TimePoint::FromEpochDelta( + fxl::TimeDelta::FromNanoseconds(frameTargetTimeNanos)); + + ConsumePendingCallback(java_baton, frame_time, target_time); } bool VsyncWaiterAndroid::Register(JNIEnv* env) { @@ -105,4 +86,27 @@ bool VsyncWaiterAndroid::Register(JNIEnv* env) { return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0; } +struct PendingCallbackData { + VsyncWaiter::Callback callback; + + PendingCallbackData(VsyncWaiter::Callback p_callback) + : callback(std::move(p_callback)) { + FXL_DCHECK(callback); + } +}; + +static jlong CreatePendingCallback(VsyncWaiter::Callback callback) { + // This delete for this new is balanced in the consume call. + auto data = new PendingCallbackData(std::move(callback)); + return reinterpret_cast(data); +} + +static void ConsumePendingCallback(jlong java_baton, + fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time) { + auto data = reinterpret_cast(java_baton); + data->callback(frame_start_time, frame_target_time); + delete data; +} + } // namespace shell diff --git a/engine/src/flutter/shell/platform/android/vsync_waiter_android.h b/engine/src/flutter/shell/platform/android/vsync_waiter_android.h index c73af4bfca..fd72a0a21f 100644 --- a/engine/src/flutter/shell/platform/android/vsync_waiter_android.h +++ b/engine/src/flutter/shell/platform/android/vsync_waiter_android.h @@ -12,22 +12,17 @@ namespace shell { -class VsyncWaiterAndroid : public VsyncWaiter { +class VsyncWaiterAndroid final : public VsyncWaiter { public: - VsyncWaiterAndroid(); + static bool Register(JNIEnv* env); + + VsyncWaiterAndroid(blink::TaskRunners task_runners); ~VsyncWaiterAndroid() override; - static bool Register(JNIEnv* env); - - void AsyncWaitForVsync(Callback callback) override; - - void OnVsync(int64_t frameTimeNanos, int64_t frameTargetTimeNanos); - private: - Callback callback_; - fml::WeakPtr self_; - fml::WeakPtrFactory weak_factory_; + // |shell::VsyncWaiter| + void AwaitVSync() override; FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterAndroid); }; diff --git a/engine/src/flutter/shell/platform/darwin/BUILD.gn b/engine/src/flutter/shell/platform/darwin/BUILD.gn index c971c443bc..c6f7acd6be 100644 --- a/engine/src/flutter/shell/platform/darwin/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/BUILD.gn @@ -6,11 +6,8 @@ assert(is_mac || is_ios) group("darwin") { if (is_mac) { - deps = [ - "desktop:shell_standalone", - ] if (!is_fuchsia_host) { - deps += [ + deps = [ "desktop:shell_application_bundle", ] } @@ -45,15 +42,12 @@ source_set("flutter_channels") { "$flutter_root/runtime", "$flutter_root/shell/common", "$flutter_root/shell/gpu", - "$flutter_root/shell/testing", "$flutter_root/sky/engine/wtf", "//garnet/public/lib/fxl", "//third_party/skia", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } executable("flutter_channels_unittests") { @@ -68,7 +62,7 @@ executable("flutter_channels_unittests") { deps = [ ":flutter_channels", - "//third_party/dart/runtime:libdart_jit", "$flutter_root/testing", + "//third_party/dart/runtime:libdart_jit", ] } diff --git a/engine/src/flutter/shell/platform/darwin/common/BUILD.gn b/engine/src/flutter/shell/platform/darwin/common/BUILD.gn index e6fa86366b..a102373797 100644 --- a/engine/src/flutter/shell/platform/darwin/common/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/common/BUILD.gn @@ -9,28 +9,23 @@ source_set("common") { sources = [ "buffer_conversions.h", "buffer_conversions.mm", - "platform_mac.h", - "platform_mac.mm", - "process_info_mac.cc", - "process_info_mac.h", + "command_line.h", + "command_line.mm", ] set_sources_assignment_filter(sources_assignment_filter) deps = [ - "//third_party/dart/runtime:dart_api", "$flutter_root/common", "$flutter_root/flow", "$flutter_root/fml", "$flutter_root/runtime", "$flutter_root/shell/common", "$flutter_root/shell/gpu", - "$flutter_root/shell/testing", "$flutter_root/sky/engine/wtf", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:dart_api", "//third_party/skia", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } diff --git a/engine/src/flutter/shell/platform/darwin/common/command_line.h b/engine/src/flutter/shell/platform/darwin/common/command_line.h new file mode 100644 index 0000000000..dfc995b90f --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/common/command_line.h @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_COMMAND_LINE_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_COMMAND_LINE_H_ + +#include "lib/fxl/command_line.h" +#include "lib/fxl/macros.h" + +namespace shell { + +fxl::CommandLine CommandLineFromNSProcessInfo(); + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_COMMAND_LINE_H_ diff --git a/engine/src/flutter/shell/platform/darwin/common/command_line.mm b/engine/src/flutter/shell/platform/darwin/common/command_line.mm new file mode 100644 index 0000000000..bf8d4cc9d4 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/common/command_line.mm @@ -0,0 +1,21 @@ +// Copyright 2017 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. + +#include "flutter/shell/platform/darwin/common/command_line.h" + +#import + +namespace shell { + +fxl::CommandLine CommandLineFromNSProcessInfo() { + std::vector args_vector; + + for (NSString* arg in [NSProcessInfo processInfo].arguments) { + args_vector.emplace_back(arg.UTF8String); + } + + return fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); +} + +} // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/common/platform_mac.h b/engine/src/flutter/shell/platform/darwin/common/platform_mac.h deleted file mode 100644 index 1989b25f11..0000000000 --- a/engine/src/flutter/shell/platform/darwin/common/platform_mac.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ -#define SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ - -#include "flutter/shell/common/engine.h" - -namespace shell { - -void PlatformMacMain(std::string icu_data_path, - std::string application_library_path, - std::string bundle_path); - -bool AttemptLaunchFromCommandLineSwitches(Engine* engine); - -} // namespace shell - -#endif // SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ diff --git a/engine/src/flutter/shell/platform/darwin/common/platform_mac.mm b/engine/src/flutter/shell/platform/darwin/common/platform_mac.mm deleted file mode 100644 index 5af35a430b..0000000000 --- a/engine/src/flutter/shell/platform/darwin/common/platform_mac.mm +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/common/platform_mac.h" - -#include - -#include "flutter/common/threads.h" -#include "flutter/fml/trace_event.h" -#include "flutter/runtime/start_up.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/common/switches.h" -#include "flutter/shell/common/tracing_controller.h" -#include "flutter/sky/engine/wtf/MakeUnique.h" -#include "lib/fxl/command_line.h" -#include "lib/fxl/strings/string_view.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" - -namespace shell { - -static fxl::CommandLine InitializedCommandLine() { - std::vector args_vector; - - for (NSString* arg in [NSProcessInfo processInfo].arguments) { - args_vector.emplace_back(arg.UTF8String); - } - - return fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); -} - -class EmbedderState { - public: - EmbedderState(std::string icu_data_path, - std::string application_library_path, - std::string bundle_path) { - blink::engine_main_enter_ts = Dart_TimelineGetMicros(); - FXL_DCHECK([NSThread isMainThread]) - << "Embedder initialization must occur on the main platform thread"; - - auto command_line = InitializedCommandLine(); - - // This is about as early as tracing of any kind can start. Add an instant - // marker that can be used as a reference for startup. - TRACE_EVENT_INSTANT0("flutter", "main"); - - shell::Shell::InitStandalone(std::move(command_line), icu_data_path, application_library_path, - bundle_path); - } - - ~EmbedderState() {} - - private: - FXL_DISALLOW_COPY_AND_ASSIGN(EmbedderState); -}; - -void PlatformMacMain(std::string icu_data_path, - std::string application_library_path, - std::string bundle_path) { - static std::unique_ptr g_embedder; - static std::once_flag once_main; - - std::call_once(once_main, [&]() { - g_embedder = - WTF::MakeUnique(icu_data_path, application_library_path, bundle_path); - }); -} - -static bool FlagsValidForCommandLineLaunch(const std::string& bundle_path, - const std::string& main, - const std::string& packages) { - if (main.empty() || packages.empty() || bundle_path.empty()) { - return false; - } - - // Ensure that the paths exists. This catches cases where the user has - // successfully launched the application from the tooling but has since moved - // the source files on disk and is launching again directly. - - NSFileManager* manager = [NSFileManager defaultManager]; - - if (![manager fileExistsAtPath:@(main.c_str())]) { - return false; - } - - if (![manager fileExistsAtPath:@(packages.c_str())]) { - return false; - } - - if (![manager fileExistsAtPath:@(bundle_path.c_str())]) { - return false; - } - - return true; -} - -static std::string ResolveCommandLineLaunchFlag(const fxl::StringView name) { - const auto& command_line = shell::Shell::Shared().GetCommandLine(); - - std::string command_line_option; - if (command_line.GetOptionValue(name, &command_line_option)) { - return command_line_option; - } - - const char* saved_default = - [[NSUserDefaults standardUserDefaults] stringForKey:@(name.data())].UTF8String; - - if (saved_default != NULL) { - return saved_default; - } - - return ""; -} - -bool AttemptLaunchFromCommandLineSwitches(Engine* engine) { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - - const auto& command_line = shell::Shell::Shared().GetCommandLine(); - - if (command_line.HasOption(FlagForSwitch(Switch::FlutterAssetsDir)) || - command_line.HasOption(FlagForSwitch(Switch::MainDartFile)) || - command_line.HasOption(FlagForSwitch(Switch::Packages))) { - // The main dart file, Flutter assets directory and the package root must be - // specified in one go. We dont want to end up in a situation where we take - // one value from the command line and the others from user defaults. In - // case, any new flags are specified, forget about all the old ones. - [defaults removeObjectForKey:@(FlagForSwitch(Switch::FlutterAssetsDir).data())]; - [defaults removeObjectForKey:@(FlagForSwitch(Switch::MainDartFile).data())]; - [defaults removeObjectForKey:@(FlagForSwitch(Switch::Packages).data())]; - - [defaults synchronize]; - } - - std::string bundle_path = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::FlutterAssetsDir)); - std::string main = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::MainDartFile)); - std::string packages = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::Packages)); - - if (!FlagsValidForCommandLineLaunch(bundle_path, main, packages)) { - return false; - } - - // Save the newly resolved dart main file and the package root to user - // defaults so that the next time the user launches the application in the - // simulator without the tooling, the application boots up. - [defaults setObject:@(bundle_path.c_str()) - forKey:@(FlagForSwitch(Switch::FlutterAssetsDir).data())]; - [defaults setObject:@(main.c_str()) forKey:@(FlagForSwitch(Switch::MainDartFile).data())]; - [defaults setObject:@(packages.c_str()) forKey:@(FlagForSwitch(Switch::Packages).data())]; - - [defaults synchronize]; - - blink::Threads::UI()->PostTask([ engine = engine->GetWeakPtr(), bundle_path, main, packages ] { - if (engine) - engine->RunBundleAndSource(bundle_path, main, packages); - }); - - return true; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/common/process_info_mac.cc b/engine/src/flutter/shell/platform/darwin/common/process_info_mac.cc deleted file mode 100644 index 11f70f305a..0000000000 --- a/engine/src/flutter/shell/platform/darwin/common/process_info_mac.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/common/process_info_mac.h" - -namespace shell { - -ProcessInfoMac::ProcessInfoMac() = default; - -ProcessInfoMac::~ProcessInfoMac() = default; - -bool ProcessInfoMac::SampleNow() { - mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; - kern_return_t result = - task_info(mach_task_self(), // - MACH_TASK_BASIC_INFO, // - reinterpret_cast(&last_sample_), // - &size); - if (result == KERN_SUCCESS) { - return true; - } - - last_sample_ = {}; - return false; -} - -size_t ProcessInfoMac::GetVirtualMemorySize() { - return last_sample_.virtual_size; -} - -size_t ProcessInfoMac::GetResidentMemorySize() { - return last_sample_.resident_size; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/common/process_info_mac.h b/engine/src/flutter/shell/platform/darwin/common/process_info_mac.h deleted file mode 100644 index 7edc803417..0000000000 --- a/engine/src/flutter/shell/platform/darwin/common/process_info_mac.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_PROCESS_INFO_MAC_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_PROCESS_INFO_MAC_H_ - -#include -#include -#include -#include "flutter/flow/process_info.h" -#include "lib/fxl/macros.h" - -namespace shell { - -class ProcessInfoMac : public flow::ProcessInfo { - public: - ProcessInfoMac(); - - ~ProcessInfoMac(); - - bool SampleNow() override; - - size_t GetVirtualMemorySize() override; - - size_t GetResidentMemorySize() override; - - private: - struct mach_task_basic_info last_sample_; - - FXL_DISALLOW_COPY_AND_ASSIGN(ProcessInfoMac); -}; - -} // namespace shell - -#endif // FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_PROCESS_INFO_MAC_H_ diff --git a/engine/src/flutter/shell/platform/darwin/desktop/BUILD.gn b/engine/src/flutter/shell/platform/darwin/desktop/BUILD.gn index a9c05f85ef..5ec3298be4 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/desktop/BUILD.gn @@ -8,10 +8,8 @@ source_set("mac_desktop_platform") { visibility = [ ":*" ] sources = [ - "flutter_app_delegate.h", - "flutter_app_delegate.m", - "flutter_application.h", - "flutter_application.mm", + "flutter_application_delegate.h", + "flutter_application_delegate.mm", "flutter_window.h", "flutter_window.mm", "main_mac.mm", @@ -22,32 +20,22 @@ source_set("mac_desktop_platform") { ] deps = [ - "//third_party/dart/runtime:libdart_jit", "$flutter_root/common", "$flutter_root/fml", "$flutter_root/shell/common", "$flutter_root/shell/gpu", "$flutter_root/shell/platform/darwin/common", - "$flutter_root/shell/testing", "$flutter_root/synchronization", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", "//third_party/skia", + "//third_party/skia:gpu", ] - public_configs = [ - "$flutter_root:config", - ] -} - -executable("shell_standalone") { - output_name = "flutter_tester" - deps = [ - ":mac_desktop_platform", - ] + public_configs = [ "$flutter_root:config" ] } if (!is_fuchsia_host) { - import("//build/config/mac/rules.gni") resource_copy_mac("mac_desktop_resources") { diff --git a/engine/src/flutter/shell/platform/darwin/desktop/Info.plist b/engine/src/flutter/shell/platform/darwin/desktop/Info.plist index 31d3f1240d..048afb6dad 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/Info.plist +++ b/engine/src/flutter/shell/platform/darwin/desktop/Info.plist @@ -9,11 +9,11 @@ CFBundleIconFile CFBundleIdentifier - io.flutter + io.flutter.engine CFBundleInfoDictionaryVersion 6.0 CFBundleName - Flutter + Flutter Engine CFBundlePackageType APPL CFBundleShortVersionString @@ -26,9 +26,7 @@ 10.6 NSHumanReadableCopyright Copyright 2015 The Flutter Authors. All rights reserved. - NSMainNibFile - flutter_mac NSPrincipalClass - FlutterApplication + NSApplication diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_app_delegate.h b/engine/src/flutter/shell/platform/darwin/desktop/flutter_app_delegate.h deleted file mode 100644 index d6addcb17d..0000000000 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_app_delegate.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APP_DELEGATE__ -#define __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APP_DELEGATE__ - -#import - -@interface FlutterAppDelegate : NSObject - -@end - -#endif /* defined(__SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APP_DELEGATE__) */ diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_app_delegate.m b/engine/src/flutter/shell/platform/darwin/desktop/flutter_app_delegate.m deleted file mode 100644 index 7e2dfd6838..0000000000 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_app_delegate.m +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "flutter_app_delegate.h" - -@interface FlutterAppDelegate () - -@property(assign) IBOutlet NSWindow* window; - -@end - -@implementation FlutterAppDelegate - -@end diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_application.mm b/engine/src/flutter/shell/platform/darwin/desktop/flutter_application.mm deleted file mode 100644 index 57b1c83ba0..0000000000 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_application.mm +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/desktop/flutter_application.h" - -@implementation FlutterApplication -@end diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_application.h b/engine/src/flutter/shell/platform/darwin/desktop/flutter_application_delegate.h similarity index 65% rename from engine/src/flutter/shell/platform/darwin/desktop/flutter_application.h rename to engine/src/flutter/shell/platform/darwin/desktop/flutter_application_delegate.h index 6a4167b5f2..3995557b25 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_application.h +++ b/engine/src/flutter/shell/platform/darwin/desktop/flutter_application_delegate.h @@ -1,13 +1,14 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2017 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. -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_H_ +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_DELEGATE_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_DELEGATE_H_ #import -@interface FlutterApplication : NSApplication +@interface FlutterApplicationDelegate : NSObject + @end -#endif // FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_H_ +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_DELEGATE_H_ diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_application_delegate.mm b/engine/src/flutter/shell/platform/darwin/desktop/flutter_application_delegate.mm new file mode 100644 index 0000000000..03076b848b --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/desktop/flutter_application_delegate.mm @@ -0,0 +1,80 @@ +// Copyright 2017 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. + +#include "flutter/shell/platform/darwin/desktop/flutter_application_delegate.h" +#include "flutter/shell/platform/darwin/desktop/flutter_window.h" + +#include + +@implementation FlutterApplicationDelegate + +- (void)applicationWillFinishLaunching:(NSNotification*)notification { + [self configureMainMenuBar]; + [self onNewFlutterWindow:self]; +} + +- (void)configureMainMenuBar { + NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@"MainMenu"] autorelease]; + + NSMenuItem* engineItem = + [[[NSMenuItem alloc] initWithTitle:@"Engine" action:NULL keyEquivalent:@""] autorelease]; + + NSMenu* engineMenu = [[[NSMenu alloc] initWithTitle:@"EngineMenu"] autorelease]; + + NSMenuItem* newEngineItem = [[[NSMenuItem alloc] initWithTitle:@"New Engine" + action:@selector(onNewFlutterWindow:) + keyEquivalent:@""] autorelease]; + newEngineItem.keyEquivalent = @"n"; + newEngineItem.keyEquivalentModifierMask = NSCommandKeyMask; + + NSMenuItem* shutdownEngineItem = + [[[NSMenuItem alloc] initWithTitle:@"Shutdown Engine" + action:@selector(onShutdownFlutterWindow:) + keyEquivalent:@""] autorelease]; + shutdownEngineItem.keyEquivalent = @"w"; + shutdownEngineItem.keyEquivalentModifierMask = NSCommandKeyMask; + + NSMenuItem* quitItem = [[[NSMenuItem alloc] initWithTitle:@"Quit" + action:@selector(onQuitFlutterApplication:) + keyEquivalent:@""] autorelease]; + quitItem.keyEquivalent = @"q"; + quitItem.keyEquivalentModifierMask = NSCommandKeyMask; + + [mainMenu addItem:engineItem]; + [engineItem setSubmenu:engineMenu]; + [engineMenu addItem:newEngineItem]; + [engineMenu addItem:shutdownEngineItem]; + [engineMenu addItem:quitItem]; + + [NSApplication sharedApplication].mainMenu = mainMenu; +} + +- (void)onNewFlutterWindow:(id)sender { + FlutterWindow* window = [[FlutterWindow alloc] init]; + [window setReleasedWhenClosed:YES]; + + NSWindow* currentKeyWindow = [NSApplication sharedApplication].keyWindow; + + if (currentKeyWindow == nil) { + [window center]; + } else { + [window center]; + NSPoint currentWindowFrameOrigin = window.frame.origin; + currentWindowFrameOrigin.x = currentKeyWindow.frame.origin.x + 20; + currentWindowFrameOrigin.y = currentKeyWindow.frame.origin.y - 20; + [window setFrameOrigin:currentWindowFrameOrigin]; + } + + [window makeKeyAndOrderFront:sender]; +} + +- (void)onShutdownFlutterWindow:(id)sender { + [[NSApplication sharedApplication].keyWindow close]; +} + +- (void)onQuitFlutterApplication:(id)sender { + exit(0); +} + +@end diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_mac.xib b/engine/src/flutter/shell/platform/darwin/desktop/flutter_mac.xib deleted file mode 100644 index c02ab7912d..0000000000 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_mac.xib +++ /dev/null @@ -1,695 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.h b/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.h index 851535ba38..e07fe4eeb7 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.h +++ b/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW__ -#define __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW__ +#ifndef SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW_H_ +#define SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW_H_ #import @@ -11,4 +11,4 @@ @end -#endif /* defined(__SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW__) */ +#endif // SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW_H_ diff --git a/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.mm b/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.mm index ca080e9275..4c87200fb3 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.mm +++ b/engine/src/flutter/shell/platform/darwin/desktop/flutter_window.mm @@ -2,16 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #import "flutter_window.h" -#include "flutter/common/threads.h" +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" #include "flutter/shell/gpu/gpu_surface_gl.h" +#include "flutter/shell/platform/darwin/common/command_line.h" #include "flutter/shell/platform/darwin/desktop/platform_view_mac.h" +#include "lib/fxl/functional/make_copyable.h" -@interface FlutterWindow () +@interface FlutterWindow () -@property(assign) IBOutlet NSOpenGLView* renderSurface; -@property(getter=isSurfaceSetup) BOOL surfaceSetup; +@property(strong) NSOpenGLView* renderSurface; @end @@ -37,34 +46,130 @@ static inline blink::PointerData::Change PointerChangeFromNSEventPhase(NSEventPh } @implementation FlutterWindow { - std::shared_ptr _platformView; + shell::ThreadHost _thread_host; + std::unique_ptr _shell; bool _mouseIsDown; } -@synthesize renderSurface = _renderSurface; -@synthesize surfaceSetup = _surfaceSetup; +- (instancetype)init { + self = + [super initWithContentRect:NSMakeRect(10.0, 10.0, 800.0, 600.0) + styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask + backing:NSBackingStoreBuffered + defer:YES]; + if (self) { + self.delegate = self; + [self setupRenderSurface]; + [self setupShell]; + [self updateWindowSize]; + } -- (void)awakeFromNib { - [super awakeFromNib]; - - self.delegate = self; - - [self updateWindowSize]; + return self; } -- (void)setupPlatformView { - FXL_DCHECK(_platformView == nullptr) << "The platform view must not already be set."; - - _platformView = std::make_shared(self.renderSurface); - _platformView->Attach(); - _platformView->SetupResourceContextOnIOThread(); - _platformView->NotifyCreated(std::make_unique(_platformView.get())); +- (void)setupRenderSurface { + NSOpenGLView* renderSurface = [[[NSOpenGLView alloc] init] autorelease]; + const NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPFADoubleBuffer, // + NSOpenGLPFAAllowOfflineRenderers, // + 0 // + }; + renderSurface.pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; + renderSurface.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + renderSurface.frame = + NSMakeRect(0.0, 0.0, self.contentView.bounds.size.width, self.contentView.bounds.size.height); + [self.contentView addSubview:renderSurface]; + self.renderSurface = renderSurface; } -// TODO(eseidel): This does not belong in flutter_window! -// Probably belongs in NSApplicationDelegate didFinishLaunching. -- (void)setupAndLoadDart { - _platformView->SetupAndLoadDart(); +static std::string CreateThreadLabel() { + std::stringstream stream; + static int index = 1; + stream << "io.flutter." << index++; + return stream.str(); +} + +- (void)setupShell { + FXL_DCHECK(!_shell) << "The shell must not already be set."; + + auto thread_label = CreateThreadLabel(); + + // Create the threads on which to run the shell. + _thread_host = {thread_label, shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::UI | + shell::ThreadHost::Type::IO}; + + // Grab the task runners for the newly created threads. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + blink::TaskRunners task_runners(thread_label, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + _thread_host.gpu_thread->GetTaskRunner(), // GPU + _thread_host.ui_thread->GetTaskRunner(), // UI + _thread_host.io_thread->GetTaskRunner() // IO + ); + + // Figure out the settings from the command line arguments. + auto settings = shell::SettingsFromCommandLine(shell::CommandLineFromNSProcessInfo()); + + if (settings.icu_data_path.size() == 0) { + settings.icu_data_path = + [[NSBundle mainBundle] pathForResource:@"icudtl.dat" ofType:@""].UTF8String; + } + + settings.using_blink = false; + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + // Setup the callback that will be run on the appropriate threads. + shell::Shell::CreateCallback on_create_platform_view = + [render_surface = self.renderSurface](shell::Shell& shell) { + return std::make_unique(shell, render_surface); + }; + + shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // Finally, create the shell. + _shell = shell::Shell::Create(std::move(task_runners), settings, on_create_platform_view, + on_create_rasterizer); + + // Launch the engine with the inferred run configuration. + _shell->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = _shell->GetEngine(), + config = shell::RunConfiguration::InferFromSettings(_shell->GetSettings())]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (!result) { + FXL_LOG(ERROR) << "Could not launch the engine with configuration."; + } + } + })); + + [self notifySurfaceCreated]; +} + +- (void)notifySurfaceCreated { + if (!_shell || !_shell->IsSetup()) { + return; + } + + // Tell the platform view that it has a GL surface. + _shell->GetPlatformView()->NotifyCreated(); +} + +- (void)notifySurfaceDestroyed { + if (!_shell || !_shell->IsSetup()) { + return; + } + + // Tell the platform view that its surface is about to be lost. + _shell->GetPlatformView()->NotifyDestroyed(); } - (void)windowDidResize:(NSNotification*)notification { @@ -72,34 +177,28 @@ static inline blink::PointerData::Change PointerChangeFromNSEventPhase(NSEventPh } - (void)updateWindowSize { - [self setupSurfaceIfNecessary]; + if (!_shell) { + return; + } blink::ViewportMetrics metrics; auto size = self.renderSurface.frame.size; metrics.physical_width = size.width; metrics.physical_height = size.height; - - blink::Threads::UI()->PostTask([ engine = _platformView->engine().GetWeakPtr(), metrics ] { - if (engine.get()) { + _shell->GetTaskRunners().GetUITaskRunner()->PostTask([engine = _shell->GetEngine(), metrics]() { + if (engine) { engine->SetViewportMetrics(metrics); } }); } -- (void)setupSurfaceIfNecessary { - if (self.isSurfaceSetup) { - return; - } - - self.surfaceSetup = YES; - - [self setupPlatformView]; - [self setupAndLoadDart]; -} - #pragma mark - Responder overrides - (void)dispatchEvent:(NSEvent*)event phase:(NSEventPhase)phase { + if (!_shell) { + return; + } + NSPoint location = [_renderSurface convertPoint:event.locationInWindow fromView:nil]; location.y = _renderSurface.frame.size.height - location.y; @@ -134,13 +233,14 @@ static inline blink::PointerData::Change PointerChangeFromNSEventPhase(NSEventPh break; } - blink::Threads::UI()->PostTask([ engine = _platformView->engine().GetWeakPtr(), pointer_data ] { - if (engine.get()) { - blink::PointerDataPacket packet(1); - packet.SetPointerData(0, pointer_data); - engine->DispatchPointerDataPacket(packet); - } - }); + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = _shell->GetEngine(), pointer_data] { + if (engine) { + blink::PointerDataPacket packet(1); + packet.SetPointerData(0, pointer_data); + engine->DispatchPointerDataPacket(packet); + } + }); } - (void)mouseDown:(NSEvent*)event { @@ -155,11 +255,18 @@ static inline blink::PointerData::Change PointerChangeFromNSEventPhase(NSEventPh [self dispatchEvent:event phase:NSEventPhaseEnded]; } -- (void)dealloc { - if (_platformView) { - _platformView->NotifyDestroyed(); - } +- (void)reset { + [self notifySurfaceDestroyed]; + _shell.reset(); + _thread_host.Reset(); +} +- (void)windowWillClose:(NSNotification*)notification { + [self reset]; +} + +- (void)dealloc { + [self reset]; [super dealloc]; } diff --git a/engine/src/flutter/shell/platform/darwin/desktop/main_mac.mm b/engine/src/flutter/shell/platform/darwin/desktop/main_mac.mm index 808a5f63f1..edd08d0c8b 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/main_mac.mm +++ b/engine/src/flutter/shell/platform/darwin/desktop/main_mac.mm @@ -7,91 +7,20 @@ #include #include "flutter/fml/message_loop.h" -#include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/desktop/flutter_application.h" -#include "flutter/shell/testing/test_runner.h" -#include "flutter/shell/testing/testing.h" +#include "flutter/shell/platform/darwin/desktop/flutter_application_delegate.h" #include "lib/fxl/command_line.h" #include "lib/fxl/logging.h" -#include "lib/tonic/dart_microtask_queue.h" -// Exit codes used by the Dart command line tool. -const int kApiErrorExitCode = 253; -const int kCompilationErrorExitCode = 254; -const int kErrorExitCode = 255; - -// Checks whether the engine's main Dart isolate has no pending work. If so, -// then exit the given message loop. -class ScriptCompletionTaskObserver : public fml::TaskObserver { - public: - ScriptCompletionTaskObserver(fxl::RefPtr task_runner) - : main_task_runner_(std::move(task_runner)), - prev_live_(false), - last_error_(tonic::kNoError) {} - - void DidProcessTask() override { - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - shell::Engine& engine = test_runner.platform_view().engine(); - - if (engine.GetLoadScriptError() != tonic::kNoError) { - last_error_ = engine.GetLoadScriptError(); - main_task_runner_->PostTask([]() { fml::MessageLoop::GetCurrent().Terminate(); }); - return; - } - - bool live = engine.UIIsolateHasLivePorts(); - if (prev_live_ && !live) { - last_error_ = engine.GetUIIsolateLastError(); - main_task_runner_->PostTask([]() { fml::MessageLoop::GetCurrent().Terminate(); }); - } - prev_live_ = live; - } - - tonic::DartErrorHandleType last_error() { return last_error_; } - - private: - fxl::RefPtr main_task_runner_; - bool prev_live_; - tonic::DartErrorHandleType last_error_; -}; - -int ConvertErrorTypeToExitCode(tonic::DartErrorHandleType error) { - switch (error) { - case tonic::kCompilationErrorType: - return kCompilationErrorExitCode; - case tonic::kApiErrorType: - return kApiErrorExitCode; - case tonic::kUnknownErrorType: - return kErrorExitCode; - default: - return 0; - } -} - -static fxl::CommandLine InitializedCommandLine() { +int main(int argc, const char* argv[]) { std::vector args_vector; for (NSString* arg in [NSProcessInfo processInfo].arguments) { args_vector.emplace_back(arg.UTF8String); } - return fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); -} - -int main(int argc, const char* argv[]) { - [FlutterApplication sharedApplication]; - - // Can't use shell::Shell::Shared().GetCommandLine() because it is initialized only - // in shell::PlatformMacMain call below. - auto command_line = InitializedCommandLine(); - - std::string bundle_path = ""; - command_line.GetOptionValue(FlagForSwitch(shell::Switch::FlutterAssetsDir), &bundle_path); - - shell::PlatformMacMain("", "", bundle_path); + auto command_line = fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); // Print help. if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { @@ -99,36 +28,7 @@ int main(int argc, const char* argv[]) { return EXIT_SUCCESS; } - // Decide between interactive and non-interactive modes. - if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::NonInteractive))) { - if (!shell::InitForTesting(std::move(command_line))) - return 1; - - // Note that this task observer must be added after the observer that drains - // the microtask queue. - ScriptCompletionTaskObserver task_observer(fml::MessageLoop::GetCurrent().GetTaskRunner()); - blink::Threads::UI()->PostTask( - [&task_observer] { fml::MessageLoop::GetCurrent().AddTaskObserver(&task_observer); }); - - fml::MessageLoop::GetCurrent().Run(); - - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - tonic::DartErrorHandleType error = test_runner.platform_view().engine().GetLoadScriptError(); - if (error == tonic::kNoError) - error = task_observer.last_error(); - if (error == tonic::kNoError) { - fxl::AutoResetWaitableEvent latch; - blink::Threads::UI()->PostTask([&error, &latch] { - error = tonic::DartMicrotaskQueue::GetForCurrentThread()->GetLastError(); - latch.Signal(); - }); - latch.Wait(); - } - - // The script has completed and the engine may not be in a clean state, - // so just stop the process. - exit(ConvertErrorTypeToExitCode(error)); - } else { - return NSApplicationMain(argc, argv); - } + [NSApplication sharedApplication].delegate = + [[[FlutterApplicationDelegate alloc] init] autorelease]; + return NSApplicationMain(argc, argv); } diff --git a/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.h b/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.h index 501400b680..d4b19b94d1 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.h +++ b/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.h @@ -7,6 +7,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "lib/fxl/memory/weak_ptr.h" @@ -15,15 +16,13 @@ namespace shell { -class PlatformViewMac : public PlatformView, public GPUSurfaceGLDelegate { +class PlatformViewMac final : public PlatformView, public GPUSurfaceGLDelegate { public: - PlatformViewMac(NSOpenGLView* gl_view); + PlatformViewMac(Shell& shell, NSOpenGLView* gl_view); ~PlatformViewMac() override; - virtual void Attach() override; - - void SetupAndLoadDart(); + std::unique_ptr CreateVSyncWaiter() override; bool GLContextMakeCurrent() override; @@ -33,27 +32,17 @@ class PlatformViewMac : public PlatformView, public GPUSurfaceGLDelegate { intptr_t GLContextFBO() const override; - VsyncWaiter* GetVsyncWaiter() override; - - bool ResourceContextMakeCurrent() override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePath(const std::string& assets_directory) override; - private: fml::scoped_nsobject opengl_view_; fml::scoped_nsobject resource_loading_context_; bool IsValid() const; - void SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages); + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; - void SetAssetBundlePathOnUI(const std::string& assets_directory); + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewMac); }; diff --git a/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.mm b/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.mm index 42948386fb..f25fa6945a 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.mm +++ b/engine/src/flutter/shell/platform/darwin/desktop/platform_view_mac.mm @@ -7,77 +7,26 @@ #include #include -#include "flutter/common/threads.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/io_manager.h" +#include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/switches.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/common/process_info_mac.h" #include "flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h" #include "lib/fxl/command_line.h" #include "lib/fxl/synchronization/waitable_event.h" namespace shell { -PlatformViewMac::PlatformViewMac(NSOpenGLView* gl_view) - : PlatformView(std::make_unique(std::make_unique())), +PlatformViewMac::PlatformViewMac(Shell& shell, NSOpenGLView* gl_view) + : PlatformView(shell, shell.GetTaskRunners()), opengl_view_([gl_view retain]), resource_loading_context_([[NSOpenGLContext alloc] initWithFormat:gl_view.pixelFormat shareContext:gl_view.openGLContext]) {} PlatformViewMac::~PlatformViewMac() = default; -void PlatformViewMac::Attach() { - CreateEngine(); -} - -void PlatformViewMac::SetupAndLoadDart() { - if (AttemptLaunchFromCommandLineSwitches(&engine())) { - // This attempts launching from a Flutter assets directory that does not - // contain a dart snapshot. - return; - } - - const auto& command_line = shell::Shell::Shared().GetCommandLine(); - - std::string bundle_path = - command_line.GetOptionValueWithDefault(FlagForSwitch(Switch::FlutterAssetsDir), ""); - if (!bundle_path.empty()) { - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), bundle_path ] { - if (engine) - engine->RunBundle(bundle_path); - }); - return; - } - - auto args = command_line.positional_args(); - if (args.size() > 0) { - std::string main = args[0]; - std::string packages = - command_line.GetOptionValueWithDefault(FlagForSwitch(Switch::Packages), ""); - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), main, packages ] { - if (engine) - engine->RunBundleAndSource(std::string(), main, packages); - }); - return; - } -} - -void PlatformViewMac::SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - blink::Threads::UI()->PostTask( - [ engine = engine().GetWeakPtr(), assets_directory, main, packages ] { - if (engine) - engine->RunBundleAndSource(assets_directory, main, packages); - }); -} - -void PlatformViewMac::SetAssetBundlePathOnUI(const std::string& assets_directory) { - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), assets_directory ] { - if (engine) - engine->SetAssetBundlePath(assets_directory); - }); +std::unique_ptr PlatformViewMac::CreateVSyncWaiter() { + return std::make_unique(task_runners_); } intptr_t PlatformViewMac::GLContextFBO() const { @@ -115,21 +64,9 @@ bool PlatformViewMac::GLContextPresent() { return true; } -VsyncWaiter* PlatformViewMac::GetVsyncWaiter() { - if (!vsync_waiter_) - vsync_waiter_ = std::make_unique(); - return vsync_waiter_.get(); -} - -bool PlatformViewMac::ResourceContextMakeCurrent() { - NSOpenGLContext* context = resource_loading_context_.get(); - - if (context == nullptr) { - return false; - } - - [context makeCurrentContext]; - return true; +sk_sp PlatformViewMac::CreateResourceContext() const { + [resource_loading_context_.get() makeCurrentContext]; + return IOManager::CreateCompatibleResourceLoadingContext(GrBackend::kOpenGL_GrBackend); } bool PlatformViewMac::IsValid() const { @@ -146,30 +83,8 @@ bool PlatformViewMac::IsValid() const { return true; } -void PlatformViewMac::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetupAndLoadFromSource(assets_directory, main, packages); - latch->Signal(); - }); - - latch->Wait(); - delete latch; -} - -void PlatformViewMac::SetAssetBundlePath(const std::string& assets_directory) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetAssetBundlePathOnUI(assets_directory); - latch->Signal(); - }); - - latch->Wait(); - delete latch; +std::unique_ptr PlatformViewMac::CreateRenderingSurface() { + return std::make_unique(this); } } // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.cc b/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.cc index a28ff62edb..0ccadaf427 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.cc +++ b/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.cc @@ -6,14 +6,14 @@ #include -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" namespace shell { #define link_ (reinterpret_cast(opaque_)) -VsyncWaiterMac::VsyncWaiterMac() : opaque_(nullptr) { +VsyncWaiterMac::VsyncWaiterMac(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), opaque_(nullptr) { // Create the link. CVDisplayLinkRef link = nullptr; CVDisplayLinkCreateWithActiveCGDisplays(&link); @@ -48,18 +48,10 @@ void VsyncWaiterMac::OnDisplayLink() { CVDisplayLinkStop(link_); - auto callback = std::move(callback_); - callback_ = Callback(); - - blink::Threads::UI()->PostTask( - [callback, frame_start_time, frame_target_time] { - callback(frame_start_time, frame_target_time); - }); + FireCallback(frame_start_time, frame_target_time); } -void VsyncWaiterMac::AsyncWaitForVsync(Callback callback) { - FXL_DCHECK(!callback_); - callback_ = std::move(callback); +void VsyncWaiterMac::AwaitVSync() { CVDisplayLinkStart(link_); } diff --git a/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h b/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h index 15f551f212..0ad929a509 100644 --- a/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h +++ b/engine/src/flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h @@ -10,19 +10,19 @@ namespace shell { -class VsyncWaiterMac : public VsyncWaiter { +class VsyncWaiterMac final : public VsyncWaiter { public: - VsyncWaiterMac(); + VsyncWaiterMac(blink::TaskRunners task_runners); ~VsyncWaiterMac() override; - void AsyncWaitForVsync(Callback callback) override; - private: void* opaque_; - Callback callback_; + + void AwaitVSync() override; static void OnDisplayLink(void* context); + void OnDisplayLink(); FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterMac); diff --git a/engine/src/flutter/shell/platform/darwin/ios/BUILD.gn b/engine/src/flutter/shell/platform/darwin/ios/BUILD.gn index 310af79c2e..8946ee954c 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/ios/BUILD.gn @@ -40,8 +40,6 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterDartProject.mm", "framework/Source/FlutterDartProject_Internal.h", "framework/Source/FlutterHeadlessDartRunner.mm", - "framework/Source/FlutterDartSource.h", - "framework/Source/FlutterDartSource.mm", "framework/Source/FlutterNavigationController.mm", "framework/Source/FlutterPlatformPlugin.h", "framework/Source/FlutterPlatformPlugin.mm", @@ -53,14 +51,15 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterView.h", "framework/Source/FlutterView.mm", "framework/Source/FlutterViewController.mm", + "framework/Source/FlutterViewController_Internal.h", "framework/Source/accessibility_bridge.h", "framework/Source/accessibility_bridge.mm", "framework/Source/accessibility_text_entry.h", "framework/Source/accessibility_text_entry.mm", - "framework/Source/flutter_main_ios.h", - "framework/Source/flutter_main_ios.mm", "framework/Source/flutter_touch_mapper.h", "framework/Source/flutter_touch_mapper.mm", + "framework/Source/platform_message_response_darwin.h", + "framework/Source/platform_message_response_darwin.mm", "framework/Source/platform_message_router.h", "framework/Source/platform_message_router.mm", "framework/Source/vsync_waiter_ios.h", @@ -86,6 +85,7 @@ shared_library("create_flutter_framework_dylib") { "$flutter_root/fml", "$flutter_root/glue", "$flutter_root/lib/ui", + "$flutter_root/runtime", "$flutter_root/shell/common", "$flutter_root/shell/gpu", "$flutter_root/shell/platform/darwin/common", @@ -95,7 +95,10 @@ shared_library("create_flutter_framework_dylib") { "//third_party/skia", ] if (flutter_runtime_mode == "debug") { - deps += [ "//third_party/dart/runtime:libdart_jit" ] + deps += [ + "$flutter_root/lib/snapshot", + "//third_party/dart/runtime:libdart_jit", + ] } else { deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] } diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index d5f30d0266..3c613c9fba 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -2,42 +2,121 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" +#include "flutter/fml/message_loop.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h" -#include "lib/fxl/strings/string_view.h" -#include "third_party/dart/runtime/include/dart_api.h" +#include "flutter/shell/platform/darwin/common/command_line.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -static NSURL* URLForSwitch(const fxl::StringView name) { - const auto& cmd = shell::Shell::Shared().GetCommandLine(); - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; +static const char* kScriptSnapshotFileName = "snapshot_blob.bin"; +static const char* kVMKernelSnapshotFileName = "platform.dill"; +static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin"; - std::string switch_value; - if (cmd.GetOptionValue(name, &switch_value)) { - auto url = [NSURL fileURLWithPath:@(switch_value.c_str())]; - [defaults setURL:url forKey:@(name.data())]; - [defaults synchronize]; - return url; +static blink::Settings DefaultSettingsForProcess() { + auto command_line = shell::CommandLineFromNSProcessInfo(); + + // Settings passed in explicitly via command line arguments take priority. + auto settings = shell::SettingsFromCommandLine(command_line); + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + // The command line arguments may not always be complete. If they aren't, attempt to fill in + // defaults. + + // Flutter ships the ICU data file in the the bundle of the engine. Look for it there. + if (settings.icu_data_path.size() == 0) { + NSBundle* bundle = [NSBundle bundleForClass:[FlutterViewController class]]; + NSString* icuDataPath = [bundle pathForResource:@"icudtl" ofType:@"dat"]; + if (icuDataPath.length > 0) { + settings.icu_data_path = icuDataPath.UTF8String; + } } - return [defaults URLForKey:@(name.data())]; + if (blink::DartVM::IsRunningPrecompiledCode()) { + // The application bundle could be specified in the Info.plist. + if (settings.application_library_path.size() == 0) { + NSString* libraryName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTLibraryPath"]; + NSString* libraryPath = [[NSBundle mainBundle] pathForResource:libraryName ofType:nil]; + if (libraryPath.length > 0) { + settings.application_library_path = + [NSBundle bundleWithPath:libraryPath].executablePath.UTF8String; + } + } + + // In case the application bundle is still not specified, look for the App.framework in the + // Frameworks directory. + if (settings.application_library_path.size() == 0) { + NSString* applicationFrameworkPath = + [[NSBundle mainBundle] pathForResource:@"Frameworks/App.framework" ofType:@""]; + if (applicationFrameworkPath.length > 0) { + settings.application_library_path = + [NSBundle bundleWithPath:applicationFrameworkPath].executablePath.UTF8String; + } + } + } + + // Checks to see if the flutter assets directory is already present. + if (settings.assets_path.size() == 0) { + NSString* assetsPath = [[NSBundle mainBundle] pathForResource:@"flutter_assets" ofType:@""]; + + if (assetsPath.length > 0) { + settings.assets_path = assetsPath.UTF8String; + + if (!blink::DartVM::IsRunningPrecompiledCode()) { + // Looking for the various script and kernel snapshot buffers only makes sense if we have a + // VM that can use these buffers. + { + // Check if there is a script snapshot in the assets directory we could potentially use. + NSURL* scriptSnapshotURL = [NSURL URLWithString:@(kScriptSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:scriptSnapshotURL.path]) { + settings.script_snapshot_path = scriptSnapshotURL.path.UTF8String; + } + } + + { + // Check if there is a VM kernel snapshot in the assets directory we could potentially + // use. + NSURL* vmKernelSnapshotURL = [NSURL URLWithString:@(kVMKernelSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:vmKernelSnapshotURL.path]) { + settings.kernel_snapshot_path = vmKernelSnapshotURL.path.UTF8String; + } + } + + { + // Check if there is an application kernel snapshot in the assets directory we could + // potentially use. + NSURL* applicationKernelSnapshotURL = + [NSURL URLWithString:@(kApplicationKernelSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) { + settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String; + } + } + } + } + } + + return settings; } @implementation FlutterDartProject { - NSBundle* _precompiledDartBundle; - FlutterDartSource* _dartSource; - - VMType _vmTypeRequirement; -} - -+ (void)initialize { - if (self == [FlutterDartProject class]) { - shell::FlutterMain(); - } + fml::scoped_nsobject _precompiledDartBundle; + blink::Settings _settings; } #pragma mark - Override base class designated initializers @@ -52,9 +131,16 @@ static NSURL* URLForSwitch(const fxl::StringView name) { self = [super init]; if (self) { - _precompiledDartBundle = [bundle retain]; + _precompiledDartBundle.reset([bundle retain]); - [self checkReadiness]; + _settings = DefaultSettingsForProcess(); + + if (bundle != nil) { + NSString* executablePath = _precompiledDartBundle.get().executablePath; + if ([[NSFileManager defaultManager] fileExistsAtPath:executablePath]) { + _settings.application_library_path = executablePath.UTF8String; + } + } } return self; @@ -66,11 +152,15 @@ static NSURL* URLForSwitch(const fxl::StringView name) { self = [super init]; if (self) { - _dartSource = [[FlutterDartSource alloc] initWithDartMain:dartMainURL - packages:dartPackages - flutterAssets:flutterAssetsURL]; + _settings = DefaultSettingsForProcess(); - [self checkReadiness]; + if ([[NSFileManager defaultManager] fileExistsAtPath:dartMainURL.path]) { + _settings.main_dart_file_path = dartMainURL.path.UTF8String; + } + + if ([[NSFileManager defaultManager] fileExistsAtPath:dartPackages.path]) { + _settings.packages_file_path = dartPackages.path.UTF8String; + } } return self; @@ -80,10 +170,17 @@ static NSURL* URLForSwitch(const fxl::StringView name) { self = [super init]; if (self) { - _dartSource = - [[FlutterDartSource alloc] initWithFlutterAssetsWithScriptSnapshot:flutterAssetsURL]; + _settings = DefaultSettingsForProcess(); - [self checkReadiness]; + if ([[NSFileManager defaultManager] fileExistsAtPath:flutterAssetsURL.path]) { + _settings.assets_path = flutterAssetsURL.path.UTF8String; + + NSURL* scriptSnapshotPath = + [NSURL URLWithString:@(kScriptSnapshotFileName) relativeToURL:flutterAssetsURL]; + if ([[NSFileManager defaultManager] fileExistsAtPath:scriptSnapshotPath.path]) { + _settings.script_snapshot_path = scriptSnapshotPath.path.UTF8String; + } + } } return self; @@ -92,56 +189,19 @@ static NSURL* URLForSwitch(const fxl::StringView name) { #pragma mark - Convenience initializers - (instancetype)initFromDefaultSourceForConfiguration { - NSBundle* bundle = [NSBundle mainBundle]; - - if (Dart_IsPrecompiledRuntime()) { - // Load from an AOTC snapshot. - return [self initWithPrecompiledDartBundle:bundle]; + if (blink::DartVM::IsRunningPrecompiledCode()) { + return [self initWithPrecompiledDartBundle:nil]; } else { - // Load directly from sources if the appropriate command line flags are - // specified. If not, try loading from a script snapshot in the framework - // bundle. - NSURL* flutterAssetsURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::FlutterAssetsDir)); - - if (flutterAssetsURL == nil) { - // If the URL was not specified on the command line, look inside the - // FlutterApplication bundle. - NSString* flutterAssetsPath = [FlutterDartProject pathForFlutterAssetsFromBundle:bundle]; - if (flutterAssetsPath != nil) { - flutterAssetsURL = [NSURL fileURLWithPath:flutterAssetsPath isDirectory:NO]; - } - } - - if (flutterAssetsURL == nil) { - NSLog(@"Error: flutterAssets directory not present in bundle; unable to start app."); - [self release]; - return nil; - } - - NSURL* dartMainURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::MainDartFile)); - NSURL* dartPackagesURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::Packages)); - - return - [self initWithFlutterAssets:flutterAssetsURL dartMain:dartMainURL packages:dartPackagesURL]; + return [self initWithFlutterAssets:nil dartMain:nil packages:nil]; } - - NSAssert(NO, @"Unreachable"); - [self release]; - return nil; } -#pragma mark - Common initialization tasks +- (const blink::Settings&)settings { + return _settings; +} -- (void)checkReadiness { - if (_precompiledDartBundle != nil) { - _vmTypeRequirement = VMTypePrecompilation; - return; - } - - if (_dartSource != nil) { - _vmTypeRequirement = VMTypeInterpreter; - return; - } +- (shell::RunConfiguration)runConfiguration { + return shell::RunConfiguration::InferFromSettings(_settings); } #pragma mark - Assets-related utilities @@ -161,7 +221,7 @@ static NSURL* URLForSwitch(const fxl::StringView name) { } + (NSString*)lookupKeyForAsset:(NSString*)asset { - NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName: [NSBundle mainBundle]]; + NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:[NSBundle mainBundle]]; return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset]; } @@ -169,179 +229,4 @@ static NSURL* URLForSwitch(const fxl::StringView name) { return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]]; } -#pragma mark - Launching the project in a preconfigured engine. - -static NSString* NSStringFromVMType(VMType type) { - switch (type) { - case VMTypeInvalid: - return @"Invalid"; - case VMTypeInterpreter: - return @"Interpreter"; - case VMTypePrecompilation: - return @"Precompilation"; - } - - return @"Unknown"; -} - -- (void)launchInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - embedderVMType:(VMType)embedderVMType - result:(LaunchResult)result { - if (_vmTypeRequirement == VMTypeInvalid) { - result(NO, @"The Dart project is invalid and cannot be loaded by any VM."); - return; - } - - if (embedderVMType == VMTypeInvalid) { - result(NO, @"The embedder is invalid."); - return; - } - - if (_vmTypeRequirement != embedderVMType) { - NSString* message = - [NSString stringWithFormat: - @"Could not load the project because of differing project type. " - @"The project can run in '%@' but the embedder is configured as " - @"'%@'", - NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)]; - result(NO, message); - return; - } - - switch (_vmTypeRequirement) { - case VMTypeInterpreter: - [self runFromSourceInEngine:engine withEntrypoint:entrypoint result:result]; - return; - case VMTypePrecompilation: - [self runFromPrecompiledSourceInEngine:engine withEntrypoint:entrypoint result:result]; - return; - case VMTypeInvalid: - break; - } - - return result(NO, @"Internal error"); -} - -- (void)launchInEngine:(shell::Engine*)engine - embedderVMType:(VMType)embedderVMType - result:(LaunchResult)result { - if (_vmTypeRequirement == VMTypeInvalid) { - result(NO, @"The Dart project is invalid and cannot be loaded by any VM."); - return; - } - - if (embedderVMType == VMTypeInvalid) { - result(NO, @"The embedder is invalid."); - return; - } - - if (_vmTypeRequirement != embedderVMType) { - NSString* message = - [NSString stringWithFormat: - @"Could not load the project because of differing project type. " - @"The project can run in '%@' but the embedder is configured as " - @"'%@'", - NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)]; - result(NO, message); - return; - } - - switch (_vmTypeRequirement) { - case VMTypeInterpreter: - [self runFromSourceInEngine:engine withEntrypoint:@"main" result:result]; - return; - case VMTypePrecompilation: - [self runFromPrecompiledSourceInEngine:engine withEntrypoint:@"main" result:result]; - return; - case VMTypeInvalid: - break; - } - - return result(NO, @"Internal error"); -} - -#pragma mark - Running from precompiled application bundles - -- (void)runFromPrecompiledSourceInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - result:(LaunchResult)result { - if (![_precompiledDartBundle load]) { - NSString* message = [NSString - stringWithFormat:@"Could not load the framework ('%@') containing precompiled code.", - _precompiledDartBundle.bundleIdentifier]; - result(NO, message); - return; - } - - NSString* path = [FlutterDartProject pathForFlutterAssetsFromBundle:_precompiledDartBundle]; - if (path.length == 0) { - NSString* message = [NSString stringWithFormat: - @"Could not find the 'flutter_assets' dir in " - @"the precompiled Dart bundle with ID '%@'", - _precompiledDartBundle.bundleIdentifier]; - result(NO, message); - return; - } - - std::string bundle_path = path.UTF8String; - blink::Threads::UI()->PostTask([ - engine = engine->GetWeakPtr(), bundle_path, entrypoint = std::string([entrypoint UTF8String]) - ] { - if (engine) - engine->RunBundle(bundle_path, entrypoint); - }); - - result(YES, @"Success"); -} - -#pragma mark - Running from source - -- (void)runFromSourceInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - result:(LaunchResult)result { - if (_dartSource == nil) { - result(NO, @"Dart source not specified."); - return; - } - - [_dartSource validate:^(BOOL success, NSString* message) { - if (!success) { - return result(NO, message); - } - - std::string bundle_path = _dartSource.flutterAssets.absoluteURL.path.UTF8String; - - if (_dartSource.assetsDirContainsScriptSnapshot) { - blink::Threads::UI()->PostTask([ - engine = engine->GetWeakPtr(), bundle_path, - entrypoint = std::string([entrypoint UTF8String]) - ] { - if (engine) - engine->RunBundle(bundle_path, entrypoint); - }); - } else { - std::string main = _dartSource.dartMain.absoluteURL.path.UTF8String; - std::string packages = _dartSource.packages.absoluteURL.path.UTF8String; - blink::Threads::UI()->PostTask( - [ engine = engine->GetWeakPtr(), bundle_path, main, packages ] { - if (engine) - engine->RunBundleAndSource(bundle_path, main, packages); - }); - } - - result(YES, @"Success"); - }]; -} - -#pragma mark - Misc. - -- (void)dealloc { - [_precompiledDartBundle unload]; - [_precompiledDartBundle release]; - [_dartSource release]; - - [super dealloc]; -} - @end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h index 75db7c4049..7fe1fc364f 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h @@ -5,32 +5,15 @@ #ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTPROJECT_INTERNAL_H_ #define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTPROJECT_INTERNAL_H_ +#include "flutter/common/settings.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h" -enum VMType { - // An invalid VM configuration. - VMTypeInvalid = 0, - // VM can execute Dart code as an interpreter. - VMTypeInterpreter, - // VM can execute precompiled Dart code. - VMTypePrecompilation, -}; - -typedef void (^LaunchResult)(BOOL success, NSString* message); - @interface FlutterDartProject () -- (void)launchInEngine:(shell::Engine*)engine - embedderVMType:(VMType)type - result:(LaunchResult)result; +- (const blink::Settings&)settings; -- (void)launchInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - embedderVMType:(VMType)type - result:(LaunchResult)result; - -+ (NSString*)pathForFlutterAssetsFromBundle:(NSBundle*)bundle; +- (shell::RunConfiguration)runConfiguration; @end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h deleted file mode 100644 index c3881ce065..0000000000 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTSOURCE_H_ -#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTSOURCE_H_ - -#import - -typedef void (^ValidationResult)(BOOL result, NSString* message); - -@interface FlutterDartSource : NSObject - -@property(nonatomic, readonly) NSURL* dartMain; -@property(nonatomic, readonly) NSURL* packages; -@property(nonatomic, readonly) NSURL* flutterAssets; -@property(nonatomic, readonly) BOOL assetsDirContainsScriptSnapshot; - -- (instancetype)initWithDartMain:(NSURL*)dartMain - packages:(NSURL*)packages - flutterAssets:(NSURL*)flutterAssets NS_DESIGNATED_INITIALIZER; - -- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssets - NS_DESIGNATED_INITIALIZER; - -- (void)validate:(ValidationResult)result; - -@end - -#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTSOURCE_H_ diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm deleted file mode 100644 index aecb4e5806..0000000000 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h" - -@implementation FlutterDartSource - -@synthesize dartMain = _dartMain; -@synthesize packages = _packages; -@synthesize flutterAssets = _flutterAssets; -@synthesize assetsDirContainsScriptSnapshot = _assetsDirContainsScriptSnapshot; - -#pragma mark - Convenience Initializers - -- (instancetype)init { - return [self initWithDartMain:nil packages:nil flutterAssets:nil]; -} - -#pragma mark - Designated Initializers - -- (instancetype)initWithDartMain:(NSURL*)dartMain - packages:(NSURL*)packages - flutterAssets:(NSURL*)flutterAssets { - self = [super init]; - - if (self) { - _dartMain = [dartMain copy]; - _packages = [packages copy]; - _flutterAssets = [flutterAssets copy]; - - NSFileManager* fileManager = [NSFileManager defaultManager]; - - const BOOL dartMainExists = [fileManager fileExistsAtPath:dartMain.absoluteURL.path]; - const BOOL packagesExists = [fileManager fileExistsAtPath:packages.absoluteURL.path]; - - if (!dartMainExists || !packagesExists) { - // We cannot actually verify this without opening up the directory. This is - // just an assumption. - _assetsDirContainsScriptSnapshot = YES; - } - } - - return self; -} - -- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssets { - self = [super init]; - - if (self) { - _flutterAssets = [flutterAssets copy]; - _assetsDirContainsScriptSnapshot = YES; - } - - return self; -} - -static BOOL CheckDartProjectURL(NSMutableString* log, NSURL* url, NSString* logLabel) { - if (url == nil) { - [log appendFormat:@"The %@ was not specified.\n", logLabel]; - return false; - } - - if (!url.isFileURL) { - [log appendFormat:@"The %@ must be a file URL.\n", logLabel]; - return false; - } - - if (![[NSFileManager defaultManager] fileExistsAtPath:url.absoluteURL.path]) { - [log appendFormat:@"No file found at '%@' when looking for the %@.\n", url, logLabel]; - return false; - } - - return true; -} - -- (void)validate:(ValidationResult)result { - NSMutableString* log = [[[NSMutableString alloc] init] autorelease]; - - BOOL isValid = YES; - - isValid &= CheckDartProjectURL(log, _flutterAssets, @"Flutter assets"); - - if (!_assetsDirContainsScriptSnapshot) { - isValid &= CheckDartProjectURL(log, _dartMain, @"Dart main"); - isValid &= CheckDartProjectURL(log, _packages, @"Dart packages"); - } - - result(isValid, log); -} - -- (void)dealloc { - [_dartMain release]; - [_packages release]; - [_flutterAssets release]; - - [super dealloc]; -} - -@end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index 2143f362f7..dee11d0893 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -2,38 +2,84 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h" +#include #include -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/null_platform_view.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/engine.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/darwin/common/command_line.h" +#include "lib/fxl/functional/make_copyable.h" -@interface FlutterHeadlessDartRunner () -@end - -@implementation FlutterHeadlessDartRunner { - fml::scoped_nsprotocol _dartProject; - std::shared_ptr _platformView; +static std::unique_ptr CreateHeadlessPlatformView(shell::Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); } -- (instancetype)init { - _dartProject.reset([[FlutterDartProject alloc] initFromDefaultSourceForConfiguration]); - _platformView = std::make_shared(); - _platformView->Attach(); - return self; +static std::unique_ptr CreateHeadlessRasterizer(shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); +} + +@implementation FlutterHeadlessDartRunner { + shell::ThreadHost _threadHost; + std::unique_ptr _shell; } - (void)runWithEntrypoint:(NSString*)entrypoint { - const enum VMType type = Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter; - [_dartProject launchInEngine:&_platformView->engine() - withEntrypoint:entrypoint - embedderVMType:type - result:^(BOOL success, NSString* message) { - if (!success) - NSLog(@"%@", message); - }]; + if (_shell != nullptr || entrypoint.length == 0) { + FXL_LOG(ERROR) << "This headless dart runner was already used to run some code."; + return; + } + + const auto label = "io.flutter.headless"; + + // Create the threads to run the shell on. + _threadHost = { + label, // native thread label + shell::ThreadHost::Type::UI // managed threads to create + }; + + // Configure shell task runners. + auto current_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + auto single_task_runner = _threadHost.ui_thread->GetTaskRunner(); + blink::TaskRunners task_runners(label, // dart thread label + current_task_runner, // platform + single_task_runner, // gpu + single_task_runner, // ui + single_task_runner // io + ); + + auto settings = shell::SettingsFromCommandLine(shell::CommandLineFromNSProcessInfo()); + + // Create the shell. This is a blocking operation. + _shell = shell::Shell::Create( + std::move(task_runners), // task runners + std::move(settings), // settings + std::bind(&CreateHeadlessPlatformView, std::placeholders::_1), // platform view creation + std::bind(&CreateHeadlessRasterizer, std::placeholders::_1) // rasterzier creation + ); + + if (_shell == nullptr) { + FXL_LOG(ERROR) << "Could not start a shell for the headless dart runner with entrypoint: " + << entrypoint.UTF8String; + return; + } + + // Override the default run configuration with the specified entrypoint. + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = _shell->GetEngine(), + config = shell::RunConfiguration::InferFromSettings(settings)]() mutable { + if (!engine || !engine->Run(std::move(config))) { + FXL_LOG(ERROR) << "Could not launch engine with configuration."; + } + })); } @end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h index 661940620c..5e3d303401 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -7,8 +7,14 @@ #include +#include + +#include "flutter/shell/platform/darwin/ios/ios_surface.h" + @interface FlutterView : UIView +- (std::unique_ptr)createSurface; + @end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_VIEW_H_ diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 7c382838c6..27e3d48c4c 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -5,20 +5,41 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/common/settings.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" +#include "flutter/fml/trace_event.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_software.h" #include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" -@interface FlutterView () +@interface FlutterView () @end @implementation FlutterView +- (FlutterViewController*)flutterViewController { + // Find the first view controller in the responder chain and see if it is a FlutterViewController. + for (UIResponder* responder = self.nextResponder; responder != nil; + responder = responder.nextResponder) { + if ([responder isKindOfClass:[UIViewController class]]) { + if ([responder isKindOfClass:[FlutterViewController class]]) { + return reinterpret_cast(responder); + } else { + // Should only happen if a non-FlutterViewController tries to somehow (via dynamic class + // resolution or reparenting) set a FlutterView as its view. + return nil; + } + } + } + return nil; +} + - (void)layoutSubviews { if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { CAEAGLLayer* layer = reinterpret_cast(self.layer); @@ -40,13 +61,24 @@ #endif // TARGET_IPHONE_SIMULATOR } +- (std::unique_ptr)createSurface { + if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { + fml::scoped_nsobject eagl_layer( + reinterpret_cast([self.layer retain])); + return std::make_unique(std::move(eagl_layer)); + } else { + fml::scoped_nsobject layer(reinterpret_cast([self.layer retain])); + return std::make_unique(std::move(layer)); + } +} + - (BOOL)enableInputClicksWhenVisible { return YES; } -void SnapshotRasterizer(fml::WeakPtr rasterizer, - CGContextRef context, - bool is_opaque) { +static void SnapshotRasterizer(fml::WeakPtr rasterizer, + CGContextRef context, + bool is_opaque) { if (!rasterizer) { return; } @@ -77,10 +109,11 @@ void SnapshotRasterizer(fml::WeakPtr rasterizer, SkCanvas canvas(bitmap); - { - flow::CompositorContext compositor_context(nullptr); - auto frame = compositor_context.AcquireFrame(nullptr, &canvas, false /* instrumentation */); - layer_tree->Raster(frame, false /* ignore raster cache. */); + flow::CompositorContext compositor_context; + + if (auto frame = compositor_context.AcquireFrame(nullptr, &canvas, false /* instrumentation */)) { + layer_tree->Preroll(*frame, true /* ignore raster cache */); + layer_tree->Paint(*frame); } canvas.flush(); @@ -89,41 +122,6 @@ void SnapshotRasterizer(fml::WeakPtr rasterizer, SkCGDrawBitmap(context, bitmap, 0, 0); } -static fml::WeakPtr GetRandomRasterizer() { - fml::WeakPtr rasterizer; - shell::Shell::Shared().IteratePlatformViews([&rasterizer](shell::PlatformView* view) -> bool { - rasterizer = view->rasterizer().GetWeakRasterizerPtr(); - // We just grab the first rasterizer so there is no need to iterate - // further. - return false; - }); - return rasterizer; -} - -void SnapshotContents(CGContextRef context, bool is_opaque) { - // TODO(chinmaygarde): Currently, there is no way to get the rasterizer for - // a particular platform view from the shell. But, for now, we only have one - // platform view. So use that. Once we support multiple platform views, the - // shell will need to provide a way to get the rasterizer for a specific - // platform view. - SnapshotRasterizer(GetRandomRasterizer(), context, is_opaque); -} - -void SnapshotContentsSync(CGContextRef context, UIView* view) { - auto gpu_thread = blink::Threads::Gpu(); - - if (!gpu_thread) { - return; - } - - fxl::AutoResetWaitableEvent latch; - gpu_thread->PostTask([&latch, context, view]() { - SnapshotContents(context, [view isOpaque]); - latch.Signal(); - }); - latch.Wait(); -} - // Override the default CALayerDelegate method so that APIs that attempt to // screenshot the view display contents correctly. We cannot depend on // reading @@ -132,7 +130,22 @@ void SnapshotContentsSync(CGContextRef context, UIView* view) { // 2: The call is made of the platform thread and not the GPU thread. // 3: There may be a software rasterizer. - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { - SnapshotContentsSync(context, self); + TRACE_EVENT0("flutter", "SnapshotFlutterView"); + FlutterViewController* controller = [self flutterViewController]; + + if (controller == nil) { + return; + } + + auto& shell = [controller shell]; + + fxl::AutoResetWaitableEvent latch; + shell.GetTaskRunners().GetGPUTaskRunner()->PostTask( + [&latch, rasterizer = shell.GetRasterizer(), context, opaque = layer.opaque]() { + SnapshotRasterizer(std::move(rasterizer), context, opaque); + latch.Signal(); + }); + latch.Wait(); } @end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index a467a5a8cf..17e9a4eff4 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -2,90 +2,54 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#define FML_USED_ON_EMBEDDER + +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include -#include "flutter/common/threads.h" -#include "flutter/flow/texture.h" +#include "flutter/fml/message_loop.h" #include "flutter/fml/platform/darwin/platform_version.h" -#include "flutter/fml/platform/darwin/scoped_block.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/lib/ui/painting/resource_context.h" -#include "flutter/shell/platform/darwin/common/buffer_conversions.h" -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h" +#include "flutter/shell/common/thread_host.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.h" -#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" -#include "lib/fxl/functional/make_copyable.h" -#include "lib/fxl/time/time_delta.h" -namespace { - -typedef void (^PlatformMessageResponseCallback)(NSData*); - -class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { - FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDarwin); - - public: - void Complete(std::vector data) override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask( - fxl::MakeCopyable([ self, data = std::move(data) ]() mutable { - self->callback_.get()(shell::GetNSDataFromVector(data)); - })); - } - - void CompleteEmpty() override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask( - fxl::MakeCopyable([self]() mutable { self->callback_.get()(nil); })); - } - - private: - explicit PlatformMessageResponseDarwin(PlatformMessageResponseCallback callback) - : callback_(callback, fml::OwnershipPolicy::Retain) {} - - fml::ScopedBlock callback_; -}; - -} // namespace - -@interface FlutterViewController () +@interface FlutterViewController () @end @implementation FlutterViewController { - fml::scoped_nsprotocol _dartProject; + fml::scoped_nsobject _dartProject; + shell::ThreadHost _threadHost; + std::unique_ptr _shell; + + // Channels + fml::scoped_nsobject _platformPlugin; + fml::scoped_nsobject _textInputPlugin; + fml::scoped_nsobject _localizationChannel; + fml::scoped_nsobject _navigationChannel; + fml::scoped_nsobject _platformChannel; + fml::scoped_nsobject _textInputChannel; + fml::scoped_nsobject _lifecycleChannel; + fml::scoped_nsobject _systemChannel; + fml::scoped_nsobject _settingsChannel; + + // We keep a separate reference to this and create it ahead of time because we want to be able to + // setup a shell along with its platform view before the view has to appear. + fml::scoped_nsobject _flutterView; + fml::scoped_nsobject _launchView; UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; blink::ViewportMetrics _viewportMetrics; shell::TouchMapper _touchMapper; - std::shared_ptr _platformView; - fml::scoped_nsprotocol _platformPlugin; - fml::scoped_nsprotocol _textInputPlugin; - fml::scoped_nsprotocol _localizationChannel; - fml::scoped_nsprotocol _navigationChannel; - fml::scoped_nsprotocol _platformChannel; - fml::scoped_nsprotocol _textInputChannel; - fml::scoped_nsprotocol _lifecycleChannel; - fml::scoped_nsprotocol _systemChannel; - fml::scoped_nsprotocol _settingsChannel; - fml::scoped_nsprotocol _launchView; int64_t _nextTextureId; BOOL _initialized; - BOOL _connected; -} - -+ (void)initialize { - if (self == [FlutterViewController class]) { - shell::FlutterMain(); - } } #pragma mark - Manage and override all designated initializers @@ -125,26 +89,92 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; - _platformView = std::make_shared( - reinterpret_cast(self.view.layer), self); - _platformView->Attach( - // First frame callback. - [self]() { - TRACE_EVENT0("flutter", "First Frame"); - if (_launchView) { - [UIView animateWithDuration:0.2 - animations:^{ - _launchView.get().alpha = 0; - } - completion:^(BOOL finished) { - [_launchView.get() removeFromSuperview]; - _launchView.reset(); - }]; + if ([self setupShell]) { + [self setupChannels]; + [self setupNotificationCenterObservers]; + } +} + +- (shell::Shell&)shell { + FXL_DCHECK(_shell); + return *_shell; +} + +- (fml::WeakPtr)iosPlatformView { + FXL_DCHECK(_shell); + return _shell->GetPlatformView(); +} + +- (BOOL)setupShell { + FXL_DCHECK(_shell == nullptr); + + static size_t shell_count = 1; + + auto threadLabel = [NSString stringWithFormat:@"io.flutter.%zu", shell_count++]; + + _threadHost = { + threadLabel.UTF8String, // label + shell::ThreadHost::Type::UI | shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::IO}; + + // The current thread will be used as the platform thread. Ensure that the message loop is + // initialized. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + blink::TaskRunners task_runners(threadLabel.UTF8String, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + _threadHost.gpu_thread->GetTaskRunner(), // gpu + _threadHost.ui_thread->GetTaskRunner(), // ui + _threadHost.io_thread->GetTaskRunner() // io + ); + + _flutterView.reset([[FlutterView alloc] init]); + + // Lambda captures by pointers to ObjC objects are fine here because the create call is + // synchronous. + shell::Shell::CreateCallback on_create_platform_view = + [flutter_view_controller = self, flutter_view = _flutterView.get()](shell::Shell& shell) { + auto platform_view_ios = std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + flutter_view_controller, // flutter view controller owner + flutter_view // flutter view owner + ); + return platform_view_ios; + }; + + shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // Create the shell. + _shell = shell::Shell::Create(std::move(task_runners), // + [_dartProject settings], // + on_create_platform_view, // + on_create_rasterizer // + ); + + if (!_shell) { + FXL_LOG(ERROR) << "Could not setup a shell to run the Dart application."; + return false; + } + + // Launch the Dart application with the inferred run configuration. + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = _shell->GetEngine(), // + config = [_dartProject.get() runConfiguration] // + ]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (!result) { + FXL_LOG(ERROR) << "Could not launch engine with configuration."; + } } - }); - _platformView->SetupResourceContextOnIOThread(); + })); + return true; +} +- (void)setupChannels { _localizationChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/localization" binaryMessenger:self @@ -190,9 +220,8 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [_textInputPlugin.get() handleMethodCall:call result:result]; }]; - _platformView->SetTextInputPlugin(_textInputPlugin); - - [self setupNotificationCenterObservers]; + static_cast(_shell->GetPlatformView().get()) + ->SetTextInputPlugin(_textInputPlugin); } - (void)setupNotificationCenterObservers { @@ -261,50 +290,24 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { - (void)setInitialRoute:(NSString*)route { [_navigationChannel.get() invokeMethod:@"setInitialRoute" arguments:route]; } -#pragma mark - Initializing the engine - -- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - exit(0); -} - -- (void)connectToEngineAndLoad { - if (_connected) - return; - _connected = YES; - - TRACE_EVENT0("flutter", "connectToEngineAndLoad"); - - // We ask the VM to check what it supports. - const enum VMType type = Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter; - - [_dartProject launchInEngine:&_platformView->engine() - embedderVMType:type - result:^(BOOL success, NSString* message) { - if (!success) { - UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Launch Error" - message:message - delegate:self - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [alert show]; - [alert release]; - } - }]; -} #pragma mark - Loading the view - (void)loadView { - FlutterView* view = [[FlutterView alloc] init]; - - self.view = view; + self.view = _flutterView.get(); self.view.multipleTouchEnabled = YES; self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [view release]; + [self installLaunchViewIfNecessary]; +} +#pragma mark - Managing launch views + +- (void)installLaunchViewIfNecessary { // Show the launch screen view again on top of the FlutterView if available. // This launch screen view will be removed once the first Flutter frame is rendered. + [_launchView.get() removeFromSuperview]; + _launchView.reset(); NSString* launchStoryboardName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"]; if (launchStoryboardName && !self.isBeingPresented && !self.isMovingToParentViewController) { @@ -319,16 +322,57 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { } } +- (void)removeLaunchViewIfPresent { + if (!_launchView) { + return; + } + + [UIView animateWithDuration:0.2 + animations:^{ + _launchView.get().alpha = 0; + } + completion:^(BOOL finished) { + [_launchView.get() removeFromSuperview]; + _launchView.reset(); + }]; +} + +- (void)installLaunchViewCallback { + if (!_shell || !_launchView) { + return; + } + auto weak_platform_view = _shell->GetPlatformView(); + if (!weak_platform_view) { + return; + } + __unsafe_unretained auto weak_flutter_view_controller = self; + // This is on the platform thread. + weak_platform_view->SetNextFrameCallback( + [weak_platform_view, weak_flutter_view_controller, + task_runner = _shell->GetTaskRunners().GetPlatformTaskRunner()]() { + // This is on the GPU thread. + task_runner->PostTask([weak_platform_view, weak_flutter_view_controller]() { + // We check if the weak platform view is alive. If it is alive, then the view controller + // also has to be alive since the view controller owns the platform view via the shell + // association. Thus, we are not convinced that the unsafe unretained weak object is in + // fact alive. + if (weak_platform_view) { + [weak_flutter_view_controller removeLaunchViewIfPresent]; + } + }); + }); +} + #pragma mark - Surface creation and teardown updates - (void)surfaceUpdated:(BOOL)appeared { - FXL_CHECK(_platformView != nullptr); - // NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and GPU thread. if (appeared) { - _platformView->NotifyCreated(); + [self installLaunchViewCallback]; + _shell->GetPlatformView()->NotifyCreated(); + } else { - _platformView->NotifyDestroyed(); + _shell->GetPlatformView()->NotifyDestroyed(); } } @@ -336,7 +380,6 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { - (void)viewWillAppear:(BOOL)animated { TRACE_EVENT0("flutter", "viewWillAppear"); - [self connectToEngineAndLoad]; // Only recreate surface on subsequent appearances when viewport metrics are known. // First time surface creation is done on viewDidLayoutSubviews. if (_viewportMetrics.physical_width) @@ -391,8 +434,6 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { - (void)applicationDidEnterBackground:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationDidEnterBackground"); [self surfaceUpdated:NO]; - // GrContext operations are blocked when the app is in the background. - blink::ResourceContext::Freeze(); [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"]; } @@ -400,7 +441,6 @@ class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { TRACE_EVENT0("flutter", "applicationWillEnterForeground"); if (_viewportMetrics.physical_width) [self surfaceUpdated:YES]; - blink::ResourceContext::Unfreeze(); [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; } @@ -547,10 +587,11 @@ static inline blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* to packet->SetPointerData(i++, pointer_data); } - blink::Threads::UI()->PostTask(fxl::MakeCopyable( - [ engine = _platformView->engine().GetWeakPtr(), packet = std::move(packet) ] { - if (engine.get()) + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = _shell->GetEngine(), packet = std::move(packet)] { + if (engine) { engine->DispatchPointerDataPacket(*packet); + } })); } @@ -573,13 +614,11 @@ static inline blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* to #pragma mark - Handle view resizing - (void)updateViewportMetrics { - blink::Threads::UI()->PostTask( - [ weak_platform_view = _platformView->GetWeakPtr(), metrics = _viewportMetrics ] { - if (!weak_platform_view) { - return; + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = _shell->GetEngine(), metrics = _viewportMetrics]() { + if (engine) { + engine->SetViewportMetrics(std::move(metrics)); } - weak_platform_view->UpdateSurfaceSize(); - weak_platform_view->engine().SetViewportMetrics(metrics); }); } @@ -709,7 +748,7 @@ static inline blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* to #else bool enabled = UIAccessibilityIsVoiceOverRunning(); #endif - _platformView->ToggleAccessibility(self.view, enabled); + _shell->GetPlatformView()->SetSemanticsEnabled(enabled); } #pragma mark - Memory Notifications @@ -875,46 +914,50 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0; message:(NSData*)message binaryReply:(FlutterBinaryReply)callback { NSAssert(channel, @"The channel must not be null"); - fxl::RefPtr response = + fxl::RefPtr response = (callback == nil) ? nullptr - : fxl::MakeRefCounted(^(NSData* reply) { - callback(reply); - }); + : fxl::MakeRefCounted( + ^(NSData* reply) { + callback(reply); + }, + _shell->GetTaskRunners().GetPlatformTaskRunner()); fxl::RefPtr platformMessage = (message == nil) ? fxl::MakeRefCounted(channel.UTF8String, response) : fxl::MakeRefCounted( channel.UTF8String, shell::GetVectorFromNSData(message), response); - _platformView->DispatchPlatformMessage(platformMessage); + + _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); } - (void)setMessageHandlerOnChannel:(NSString*)channel binaryMessageHandler:(FlutterBinaryMessageHandler)handler { NSAssert(channel, @"The channel must not be null"); - _platformView->platform_message_router().SetMessageHandler(channel.UTF8String, handler); + [self iosPlatformView] -> GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, + handler); } #pragma mark - FlutterTextureRegistry - (int64_t)registerTexture:(NSObject*)texture { int64_t textureId = _nextTextureId++; - _platformView->RegisterExternalTexture(textureId, texture); + [self iosPlatformView] -> RegisterExternalTexture(textureId, texture); return textureId; } - (void)unregisterTexture:(int64_t)textureId { - _platformView->UnregisterTexture(textureId); + _shell->GetPlatformView()->UnregisterTexture(textureId); } - (void)textureFrameAvailable:(int64_t)textureId { - _platformView->MarkTextureFrameAvailable(textureId); + _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); } - (NSString*)lookupKeyForAsset:(NSString*)asset { - return [FlutterDartProject lookupKeyForAsset:asset]; + return [FlutterDartProject lookupKeyForAsset:asset]; } - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { - return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; + return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; } @end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h new file mode 100644 index 0000000000..482379c8f1 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ + +#include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" + +@interface FlutterViewController () + +- (shell::Shell&)shell; + +@end + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 3f96f37a9c..6a545b95e4 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -427,7 +427,7 @@ AccessibilityBridge::AccessibilityBridge(UIView* view, PlatformViewIOS* platform weak_factory_(this) { accessibility_channel_.reset([[FlutterBasicMessageChannel alloc] initWithName:@"flutter/accessibility" - binaryMessenger:platform_view->binary_messenger() + binaryMessenger:platform_view->GetOwnerViewController() codec:[FlutterStandardMessageCodec sharedInstance]]); [accessibility_channel_.get() setMessageHandler:^(id message, FlutterReply reply) { HandleEvent((NSDictionary*)message); @@ -440,7 +440,7 @@ AccessibilityBridge::~AccessibilityBridge() { } UIView* AccessibilityBridge::textInputView() { - return [platform_view_->text_input_plugin() textInputView]; + return [platform_view_->GetTextInputPlugin() textInputView]; } void AccessibilityBridge::UpdateSemantics(blink::SemanticsNodeUpdates nodes) { diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h deleted file mode 100644 index f2e54dd2c2..0000000000 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTER_MAIN_IOS_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTER_MAIN_IOS_H_ - -#include "lib/fxl/macros.h" - -namespace shell { - -/// Initializes the Flutter shell. This must be called before interacting with -/// the engine in any way. It is safe to call this method multiple times. -void FlutterMain(); - -} // namespace shell - -#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTER_MAIN_IOS_H_ diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm deleted file mode 100644 index aa7ad5fd85..0000000000 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h" - -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" - -namespace shell { - -void FlutterMain() { - NSBundle* bundle = [NSBundle bundleForClass:[FlutterViewController class]]; - NSString* icuDataPath = [bundle pathForResource:@"icudtl" ofType:@"dat"]; - NSString* libraryName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTLibraryPath"]; - - NSBundle* mainBundle = [NSBundle mainBundle]; - NSString* flutterAssetsPath = [FlutterDartProject pathForFlutterAssetsFromBundle:mainBundle]; - - shell::PlatformMacMain(icuDataPath.UTF8String, libraryName != nil ? libraryName.UTF8String : "", - flutterAssetsPath.UTF8String); -} - -} // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h new file mode 100644 index 0000000000..b861c5036e --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h @@ -0,0 +1,50 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_RESPONSE_DARWIN_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_RESPONSE_DARWIN_H_ + +#include + +#include "flutter/fml/platform/darwin/scoped_block.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "flutter/shell/platform/darwin/common/buffer_conversions.h" +#include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/macros.h" + +typedef void (^PlatformMessageResponseCallback)(NSData*); + +namespace shell { + +class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { + public: + void Complete(std::vector data) override { + fxl::RefPtr self(this); + platform_task_runner_->PostTask(fxl::MakeCopyable([self, data = std::move(data)]() mutable { + self->callback_.get()(shell::GetNSDataFromVector(data)); + })); + } + + void CompleteEmpty() override { + fxl::RefPtr self(this); + platform_task_runner_->PostTask( + fxl::MakeCopyable([self]() mutable { self->callback_.get()(nil); })); + } + + private: + explicit PlatformMessageResponseDarwin(PlatformMessageResponseCallback callback, + fxl::RefPtr platform_task_runner) + : callback_(callback, fml::OwnershipPolicy::Retain), + platform_task_runner_(std::move(platform_task_runner)) {} + + fml::ScopedBlock callback_; + fxl::RefPtr platform_task_runner_; + + FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDarwin); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_RESPONSE_DARWIN_H_ diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm new file mode 100644 index 0000000000..8590b4e369 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" + +namespace shell { + +// + +} // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h index cfa91c0477..d2488f82fe 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h @@ -7,6 +7,7 @@ #include +#include "flutter/fml/platform/darwin/scoped_block.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" #include "lib/fxl/memory/weak_ptr.h" @@ -18,13 +19,13 @@ class PlatformMessageRouter { PlatformMessageRouter(); ~PlatformMessageRouter(); - void HandlePlatformMessage(fxl::RefPtr message); + void HandlePlatformMessage(fxl::RefPtr message) const; void SetMessageHandler(const std::string& channel, FlutterBinaryMessageHandler handler); private: - std::unordered_map + std::unordered_map> message_handlers_; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformMessageRouter); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm index 70625143b6..3ab75bff52 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm @@ -14,7 +14,8 @@ PlatformMessageRouter::PlatformMessageRouter() = default; PlatformMessageRouter::~PlatformMessageRouter() = default; -void PlatformMessageRouter::HandlePlatformMessage(fxl::RefPtr message) { +void PlatformMessageRouter::HandlePlatformMessage( + fxl::RefPtr message) const { fxl::RefPtr completer = message->response(); auto it = message_handlers_.find(message->channel()); if (it != message_handlers_.end()) { @@ -41,14 +42,10 @@ void PlatformMessageRouter::HandlePlatformMessage(fxl::RefPtrsecond release]; - message_handlers_.erase(it); - } + message_handlers_.erase(channel); + if (handler) { + message_handlers_[channel] = + fml::ScopedBlock{handler, fml::OwnershipPolicy::Retain}; } } diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h index 6a3362b215..23aaf02510 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h @@ -5,27 +5,26 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_VSYNC_WAITER_IOS_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_VSYNC_WAITER_IOS_H_ +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/macros.h" -#if __OBJC__ @class VSyncClient; -#else // __OBJC__ -class VSyncClient; -#endif // __OBJC__ namespace shell { -class VsyncWaiterIOS : public VsyncWaiter { +class VsyncWaiterIOS final : public VsyncWaiter { public: - VsyncWaiterIOS(); + VsyncWaiterIOS(blink::TaskRunners task_runners); + ~VsyncWaiterIOS() override; - void AsyncWaitForVsync(Callback callback) override; - private: - Callback callback_; - VSyncClient* client_; + fml::scoped_nsobject client_; + + // |shell::VsyncWaiter| + void AwaitVSync() override; FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterIOS); }; diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm index 78d6678189..4589368acd 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm @@ -10,29 +10,62 @@ #include #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/glue/trace_event.h" #include "lib/fxl/logging.h" @interface VSyncClient : NSObject +- (instancetype)initWithTaskRunner:(fxl::RefPtr)task_runner + callback:(shell::VsyncWaiter::Callback)callback; + +- (void)await; + +- (void)invalidate; + @end -@implementation VSyncClient { - CADisplayLink* _displayLink; - shell::VsyncWaiter::Callback _pendingCallback; +namespace shell { + +VsyncWaiterIOS::VsyncWaiterIOS(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), + client_([[VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner() + callback:std::bind(&VsyncWaiterIOS::FireCallback, + this, + std::placeholders::_1, + std::placeholders::_2)]) {} + +VsyncWaiterIOS::~VsyncWaiterIOS() { + // This way, we will get no more callbacks from the display link that holds a weak (non-nilling) + // reference to this C++ object. + [client_.get() invalidate]; } -- (instancetype)init { +void VsyncWaiterIOS::AwaitVSync() { + [client_.get() await]; +} + +} // namespace shell + +@implementation VSyncClient { + shell::VsyncWaiter::Callback callback_; + fml::scoped_nsobject display_link_; +} + +- (instancetype)initWithTaskRunner:(fxl::RefPtr)task_runner + callback:(shell::VsyncWaiter::Callback)callback { self = [super init]; if (self) { - _displayLink = - [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain]; - _displayLink.paused = YES; + callback_ = std::move(callback); + display_link_ = fml::scoped_nsobject { + [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain] + }; + display_link_.get().paused = YES; - blink::Threads::UI()->PostTask([client = [self retain]]() { - [client->_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + task_runner->PostTask([client = [self retain]]() { + [client->display_link_.get() addToRunLoop:[NSRunLoop currentRunLoop] + forMode:NSRunLoopCommonModes]; [client release]; }); } @@ -40,68 +73,28 @@ return self; } -- (void)await:(shell::VsyncWaiter::Callback)callback { - FXL_DCHECK(!_pendingCallback); - _pendingCallback = std::move(callback); - _displayLink.paused = NO; +- (void)await { + display_link_.get().paused = NO; } - (void)onDisplayLink:(CADisplayLink*)link { fxl::TimePoint frame_start_time = fxl::TimePoint::Now(); fxl::TimePoint frame_target_time = frame_start_time + fxl::TimeDelta::FromSecondsF(link.duration); - _displayLink.paused = YES; + display_link_.get().paused = YES; - // Note: The tag name must be "VSYNC" (it is special) so that the "Highlight - // Vsync" checkbox in the timeline can be enabled. - // See: https://github.com/catapult-project/catapult/blob/2091404475cbba9b786 - // 442979b6ec631305275a6/tracing/tracing/extras/vsync/vsync_auditor.html#L26 -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - TRACE_EVENT1("flutter", "VSYNC", "mode", "basic"); -#else - { - fxl::TimeDelta delta = frame_target_time.ToEpochDelta(); - constexpr size_t num_chars = sizeof(int64_t) * CHAR_BIT * 3.4 + 2; - char deadline[num_chars]; - sprintf(deadline, "%lld", delta.ToMicroseconds()); - TRACE_EVENT2("flutter", "VSYNC", "mode", "basic", "deadline", deadline); - } -#endif + callback_(frame_start_time, frame_target_time); +} - // Note: Even though we know we are on the UI thread already (since the - // display link was scheduled on the UI thread in the contructor), we use - // the PostTask mechanism because the callback may have side-effects that need - // to be addressed via a task observer. Invoking the callback by itself - // bypasses such task observers. - // - // We are not using the PostTask for thread switching, but to make task - // observers work. - blink::Threads::UI()->PostTask([ - callback = _pendingCallback, frame_start_time, frame_target_time - ]() { callback(frame_start_time, frame_target_time); }); - - _pendingCallback = nullptr; +- (void)invalidate { + // [CADisplayLink invalidate] is thread-safe. + [display_link_.get() invalidate]; } - (void)dealloc { - [_displayLink invalidate]; - [_displayLink release]; + [self invalidate]; [super dealloc]; } @end - -namespace shell { - -VsyncWaiterIOS::VsyncWaiterIOS() : client_([[VSyncClient alloc] init]) {} - -VsyncWaiterIOS::~VsyncWaiterIOS() { - [client_ release]; -} - -void VsyncWaiterIOS::AsyncWaitForVsync(Callback callback) { - [client_ await:callback]; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h index a658fdf316..baedf298bd 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h @@ -25,6 +25,8 @@ class IOSExternalTextureGL : public flow::Texture { virtual void OnGrContextDestroyed() override; + virtual void MarkNewFrameAvailable() override; + private: NSObject* external_texture_; fml::CFRef cache_ref_; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm index 2cc9721e7f..0bd9a8ca3c 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -4,11 +4,10 @@ #include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" -#include +#import #import #import -#include "flutter/common/threads.h" -#include "flutter/lib/ui/painting/resource_context.h" + #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -26,7 +25,6 @@ IOSExternalTextureGL::IOSExternalTextureGL(int64_t textureId, IOSExternalTextureGL::~IOSExternalTextureGL() = default; void IOSExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { - ASSERT_IS_GPU_THREAD; if (!cache_ref_) { CVOpenGLESTextureCacheRef cache; CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, @@ -57,10 +55,8 @@ void IOSExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { return; } GrGLTextureInfo textureInfo = {CVOpenGLESTextureGetTarget(texture_ref_), - CVOpenGLESTextureGetName(texture_ref_), - GL_RGBA8_OES}; - GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, - textureInfo); + CVOpenGLESTextureGetName(texture_ref_), GL_RGBA8_OES}; + GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, textureInfo); sk_sp image = SkImage::MakeFromTexture(canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); @@ -69,14 +65,13 @@ void IOSExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { } } -void IOSExternalTextureGL::OnGrContextCreated() { - ASSERT_IS_GPU_THREAD -} +void IOSExternalTextureGL::OnGrContextCreated() {} void IOSExternalTextureGL::OnGrContextDestroyed() { - ASSERT_IS_GPU_THREAD texture_ref_.Reset(nullptr); cache_ref_.Reset(nullptr); } +void IOSExternalTextureGL::MarkNewFrameAvailable() {} + } // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.h b/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.h index 89ad7e0347..f42c143646 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.h @@ -18,7 +18,7 @@ namespace shell { class IOSGLContext { public: - IOSGLContext(PlatformView::SurfaceConfig config, CAEAGLLayer* layer); + IOSGLContext(fml::scoped_nsobject layer); ~IOSGLContext(); diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.mm index de94018d06..77a124e64a 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_gl_context.mm @@ -3,21 +3,17 @@ // found in the LICENSE file. #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" -#include "third_party/skia/include/gpu/GrContextOptions.h" -#include "third_party/skia/include/gpu/gl/GrGLInterface.h" #include +#include "flutter/fml/trace_event.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" + namespace shell { -#define VERIFY(x) \ - if (!(x)) { \ - FXL_DLOG(ERROR) << "Failed: " #x; \ - return; \ - }; - -IOSGLContext::IOSGLContext(PlatformView::SurfaceConfig config, CAEAGLLayer* layer) - : layer_([layer retain]), +IOSGLContext::IOSGLContext(fml::scoped_nsobject layer) + : layer_(std::move(layer)), context_([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]), resource_context_([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:context_.get().sharegroup]), @@ -26,34 +22,34 @@ IOSGLContext::IOSGLContext(PlatformView::SurfaceConfig config, CAEAGLLayer* laye storage_size_width_(0), storage_size_height_(0), valid_(false) { - VERIFY(layer_ != nullptr); - VERIFY(context_ != nullptr); - VERIFY(resource_context_ != nullptr); + FXL_DCHECK(layer_ != nullptr); + FXL_DCHECK(context_ != nullptr); + FXL_DCHECK(resource_context_ != nullptr); bool context_current = [EAGLContext setCurrentContext:context_]; - VERIFY(context_current); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(context_current); + FXL_DCHECK(glGetError() == GL_NO_ERROR); // Generate the framebuffer glGenFramebuffers(1, &framebuffer_); - VERIFY(glGetError() == GL_NO_ERROR); - VERIFY(framebuffer_ != GL_NONE); + FXL_DCHECK(glGetError() == GL_NO_ERROR); + FXL_DCHECK(framebuffer_ != GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(glGetError() == GL_NO_ERROR); // Setup color attachment glGenRenderbuffers(1, &colorbuffer_); - VERIFY(colorbuffer_ != GL_NONE); + FXL_DCHECK(colorbuffer_ != GL_NONE); glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(glGetError() == GL_NO_ERROR); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer_); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(glGetError() == GL_NO_ERROR); // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -139,24 +135,12 @@ bool IOSGLContext::UpdateStorageSizeIfNecessary() { return false; } - GLint width = 0; - GLint height = 0; + // Fetch the dimensions of the color buffer whose backing was just updated. + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &storage_size_width_); + FXL_DCHECK(glGetError() == GL_NO_ERROR); - if (colorbuffer_ != GL_NONE) { - // Fetch the dimensions of the color buffer whose backing was just updated - // so that backing of the attachments can be updated - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); - FXL_DCHECK(glGetError() == GL_NO_ERROR); - - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); - FXL_DCHECK(glGetError() == GL_NO_ERROR); - - glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - FXL_DCHECK(glGetError() == GL_NO_ERROR); - } - - storage_size_width_ = width; - storage_size_height_ = height; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &storage_size_height_); + FXL_DCHECK(glGetError() == GL_NO_ERROR); FXL_DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_surface.h b/engine/src/flutter/shell/platform/darwin/ios/ios_surface.h index c164fab155..b629c2709e 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_surface.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_surface.h @@ -5,25 +5,17 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_ -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/macros.h" +#include -@class CALayer; +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/surface.h" +#include "lib/fxl/macros.h" namespace shell { class IOSSurface { public: - static std::unique_ptr Create( - PlatformView::SurfaceConfig surface_config, - CALayer* layer); - - IOSSurface(PlatformView::SurfaceConfig surface_config, CALayer* layer); - - CALayer* GetLayer() const; - - PlatformView::SurfaceConfig GetSurfaceConfig() const; + IOSSurface(); virtual ~IOSSurface(); @@ -36,9 +28,6 @@ class IOSSurface { virtual std::unique_ptr CreateGPUSurface() = 0; public: - PlatformView::SurfaceConfig surface_config_; - fml::scoped_nsobject layer_; - FXL_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_surface.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_surface.mm index 9106783833..b0b9cc3245 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_surface.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_surface.mm @@ -4,40 +4,15 @@ #include "flutter/shell/platform/darwin/ios/ios_surface.h" -#include -#include #include -@class CALayer; -@class CAEAGLLayer; +#include +#include namespace shell { -std::unique_ptr IOSSurface::Create(PlatformView::SurfaceConfig surface_config, - CALayer* layer) { - // Check if we can use OpenGL. - if ([layer isKindOfClass:[CAEAGLLayer class]]) { - return std::make_unique(surface_config, reinterpret_cast(layer)); - } - - // If we ever support the metal rendering API, a check for CAMetalLayer would - // go here. - - // Finally, fallback to software rendering. - return std::make_unique(surface_config, layer); -} - -IOSSurface::IOSSurface(PlatformView::SurfaceConfig surface_config, CALayer* layer) - : surface_config_(surface_config), layer_([layer retain]) {} +IOSSurface::IOSSurface() = default; IOSSurface::~IOSSurface() = default; -CALayer* IOSSurface::GetLayer() const { - return layer_; -} - -PlatformView::SurfaceConfig IOSSurface::GetSurfaceConfig() const { - return surface_config_; -} - } // namespace shell diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.h b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.h index f7e43e2588..7486d343eb 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_GL_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_GL_H_ +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -16,7 +17,7 @@ namespace shell { class IOSSurfaceGL : public IOSSurface, public GPUSurfaceGLDelegate { public: - IOSSurfaceGL(PlatformView::SurfaceConfig surface_config, CAEAGLLayer* layer); + IOSSurfaceGL(fml::scoped_nsobject layer); ~IOSSurfaceGL() override; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.mm index 60756d6094..253531c480 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_gl.mm @@ -4,13 +4,12 @@ #include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include "flutter/fml/trace_event.h" #include "flutter/shell/gpu/gpu_surface_gl.h" namespace shell { -IOSSurfaceGL::IOSSurfaceGL(PlatformView::SurfaceConfig surface_config, CAEAGLLayer* layer) - : IOSSurface(surface_config, reinterpret_cast(layer)), - context_(surface_config, layer) {} +IOSSurfaceGL::IOSSurfaceGL(fml::scoped_nsobject layer) : context_(std::move(layer)) {} IOSSurfaceGL::~IOSSurfaceGL() = default; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.h b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.h index 7e3f264b28..e8fc332f06 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.h @@ -5,17 +5,19 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_SOFTWARE_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_SOFTWARE_H_ +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" #include "lib/fxl/macros.h" +@class CALayer; + namespace shell { -class IOSSurfaceSoftware : public IOSSurface, - public GPUSurfaceSoftwareDelegate { +class IOSSurfaceSoftware final : public IOSSurface, + public GPUSurfaceSoftwareDelegate { public: - IOSSurfaceSoftware(PlatformView::SurfaceConfig surface_config, - CALayer* layer); + IOSSurfaceSoftware(fml::scoped_nsobject layer); ~IOSSurfaceSoftware() override; @@ -32,6 +34,7 @@ class IOSSurfaceSoftware : public IOSSurface, bool PresentBackingStore(sk_sp backing_store) override; private: + fml::scoped_nsobject layer_; sk_sp sk_surface_; FXL_DISALLOW_COPY_AND_ASSIGN(IOSSurfaceSoftware); diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.mm index 9a4e90e45d..b09a5d9a2d 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_surface_software.mm @@ -15,15 +15,15 @@ namespace shell { -IOSSurfaceSoftware::IOSSurfaceSoftware(PlatformView::SurfaceConfig surface_config, CALayer* layer) - : IOSSurface(surface_config, layer) { +IOSSurfaceSoftware::IOSSurfaceSoftware(fml::scoped_nsobject layer) + : layer_(std::move(layer)) { UpdateStorageSizeIfNecessary(); } IOSSurfaceSoftware::~IOSSurfaceSoftware() = default; bool IOSSurfaceSoftware::IsValid() const { - return GetLayer() != nullptr; + return layer_; } bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { @@ -120,8 +120,7 @@ bool IOSSurfaceSoftware::PresentBackingStore(sk_sp backing_store) { return false; } - CALayer* layer = GetLayer(); - layer.contents = reinterpret_cast(static_cast(pixmap_image)); + layer_.get().contents = reinterpret_cast(static_cast(pixmap_image)); return true; } diff --git a/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.h b/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.h index dab19a0f9c..e7849dda44 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.h +++ b/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.h @@ -8,95 +8,66 @@ #include #include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h" #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" -@class CALayer; -@class UIView; - namespace shell { -class PlatformViewIOS : public PlatformView { +class PlatformViewIOS final : public PlatformView { public: - explicit PlatformViewIOS(CALayer* layer, - NSObject* binaryMessenger); + explicit PlatformViewIOS(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + FlutterViewController* owner_controller_, + FlutterView* owner_view_); ~PlatformViewIOS() override; - void Attach() override; + PlatformMessageRouter& GetPlatformMessageRouter(); - void Attach(fxl::Closure firstFrameCallback); - - void NotifyCreated(); - - void ToggleAccessibility(UIView* view, bool enabled); - - PlatformMessageRouter& platform_message_router() { - return platform_message_router_; - } - - fml::WeakPtr GetWeakPtr(); - - void UpdateSurfaceSize(); - - VsyncWaiter* GetVsyncWaiter() override; - - bool ResourceContextMakeCurrent() override; - - void HandlePlatformMessage( - fxl::RefPtr message) override; + FlutterViewController* GetOwnerViewController() const; void RegisterExternalTexture(int64_t id, NSObject* texture); - void UpdateSemantics(blink::SemanticsNodeUpdates update) override; + fml::scoped_nsprotocol GetTextInputPlugin() const; - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePath(const std::string& assets_directory) override; - - /** - * Exposes the `FlutterTextInputPlugin` singleton for the - * `AccessibilityBridge` to be able to interact with the text entry system. - */ - fml::scoped_nsprotocol text_input_plugin() { - return text_input_plugin_; - } - - /** - * Sets the `FlutterTextInputPlugin` singleton returned by - * `text_input_plugin`. - */ void SetTextInputPlugin( - fml::scoped_nsprotocol textInputPlugin) { - text_input_plugin_ = textInputPlugin; - } - - NSObject* binary_messenger() const { - return binary_messenger_; - } + fml::scoped_nsprotocol plugin); private: + FlutterViewController* owner_controller_; // weak reference. + FlutterView* owner_view_; // weak reference. std::unique_ptr ios_surface_; PlatformMessageRouter platform_message_router_; std::unique_ptr accessibility_bridge_; - fxl::Closure firstFrameCallback_; - fml::WeakPtrFactory weak_factory_; - NSObject* binary_messenger_; fml::scoped_nsprotocol text_input_plugin_; + fxl::Closure firstFrameCallback_; - void SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages); + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; - void SetAssetBundlePathOnUI(const std::string& assets_directory); + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; + + // |shell::PlatformView| + void SetSemanticsEnabled(bool enabled) override; + + // |shell::PlatformView| + void HandlePlatformMessage( + fxl::RefPtr message) override; + + // |shell::PlatformView| + void UpdateSemantics(blink::SemanticsNodeUpdates update) override; + + // |shell::PlatformView| + std::unique_ptr CreateVSyncWaiter() override; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewIOS); }; diff --git a/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm b/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm index 19c5dd4e66..1fba8e5e02 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm @@ -8,104 +8,36 @@ #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" -#include "flutter/shell/platform/darwin/common/process_info_mac.h" +#include "flutter/shell/common/io_manager.h" #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" #include "lib/fxl/synchronization/waitable_event.h" namespace shell { -PlatformViewIOS::PlatformViewIOS(CALayer* layer, NSObject* binaryMessenger) - : PlatformView(std::make_unique(std::make_unique())), - ios_surface_(IOSSurface::Create(surface_config_, layer)), - weak_factory_(this), - binary_messenger_(binaryMessenger) {} +PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + FlutterViewController* owner_controller, + FlutterView* owner_view) + : PlatformView(delegate, std::move(task_runners)), + owner_controller_(owner_controller), + owner_view_(owner_view), + ios_surface_(owner_view_.createSurface) { + FXL_DCHECK(ios_surface_ != nullptr); + FXL_DCHECK(owner_controller_ != nullptr); + FXL_DCHECK(owner_view_ != nullptr); +} PlatformViewIOS::~PlatformViewIOS() = default; -void PlatformViewIOS::Attach() { - Attach(NULL); +FlutterViewController* PlatformViewIOS::GetOwnerViewController() const { + return owner_controller_; } -void PlatformViewIOS::Attach(fxl::Closure firstFrameCallback) { - CreateEngine(); - - if (firstFrameCallback) { - firstFrameCallback_ = firstFrameCallback; - rasterizer_->AddNextFrameCallback([weakSelf = GetWeakPtr()] { - if (weakSelf) { - weakSelf->firstFrameCallback_(); - weakSelf->firstFrameCallback_ = nullptr; - } - }); - } -} - -void PlatformViewIOS::NotifyCreated() { - PlatformView::NotifyCreated(ios_surface_->CreateGPUSurface()); -} - -void PlatformViewIOS::ToggleAccessibility(UIView* view, bool enabled) { - if (enabled) { - if (!accessibility_bridge_) { - accessibility_bridge_.reset(new shell::AccessibilityBridge(view, this)); - } - } else { - accessibility_bridge_ = nullptr; - } - SetSemanticsEnabled(enabled); -} - -void PlatformViewIOS::SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - blink::Threads::UI()->PostTask( - [ engine = engine().GetWeakPtr(), assets_directory, main, packages ] { - if (engine) - engine->RunBundleAndSource(assets_directory, main, packages); - }); -} - -void PlatformViewIOS::SetAssetBundlePathOnUI(const std::string& assets_directory) { - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), assets_directory ] { - if (engine) - engine->SetAssetBundlePath(assets_directory); - }); -} - -fml::WeakPtr PlatformViewIOS::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - -void PlatformViewIOS::UpdateSurfaceSize() { - blink::Threads::Gpu()->PostTask([self = GetWeakPtr()]() { - if (self && self->ios_surface_ != nullptr) { - self->ios_surface_->UpdateStorageSizeIfNecessary(); - } - }); -} - -VsyncWaiter* PlatformViewIOS::GetVsyncWaiter() { - if (!vsync_waiter_) { - vsync_waiter_ = std::make_unique(); - } - return vsync_waiter_.get(); -} - -bool PlatformViewIOS::ResourceContextMakeCurrent() { - return ios_surface_ != nullptr ? ios_surface_->ResourceContextMakeCurrent() : false; -} - -void PlatformViewIOS::UpdateSemantics(blink::SemanticsNodeUpdates update) { - if (accessibility_bridge_) - accessibility_bridge_->UpdateSemantics(std::move(update)); -} - -void PlatformViewIOS::HandlePlatformMessage(fxl::RefPtr message) { - platform_message_router_.HandlePlatformMessage(std::move(message)); +PlatformMessageRouter& PlatformViewIOS::GetPlatformMessageRouter() { + return platform_message_router_; } void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, @@ -113,30 +45,55 @@ void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, RegisterTexture(std::make_shared(texture_id, texture)); } -void PlatformViewIOS::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetupAndLoadFromSource(assets_directory, main, packages); - latch->Signal(); - }); - - latch->Wait(); - delete latch; +// |shell::PlatformView| +std::unique_ptr PlatformViewIOS::CreateRenderingSurface() { + return ios_surface_->CreateGPUSurface(); } -void PlatformViewIOS::SetAssetBundlePath(const std::string& assets_directory) { - auto latch = new fxl::ManualResetWaitableEvent(); +// |shell::PlatformView| +sk_sp PlatformViewIOS::CreateResourceContext() const { + if (!ios_surface_->ResourceContextMakeCurrent()) { + FXL_DLOG(INFO) << "Could not make resource context current on IO thread. Async texture uploads " + "will be disabled."; + return nullptr; + } - dispatch_async(dispatch_get_main_queue(), ^{ - SetAssetBundlePathOnUI(assets_directory); - latch->Signal(); - }); + return IOManager::CreateCompatibleResourceLoadingContext(GrBackend::kOpenGL_GrBackend); +} - latch->Wait(); - delete latch; +// |shell::PlatformView| +void PlatformViewIOS::SetSemanticsEnabled(bool enabled) { + if (enabled && !accessibility_bridge_) { + accessibility_bridge_ = std::make_unique(owner_view_, this); + } else { + accessibility_bridge_.reset(); + } + PlatformView::SetSemanticsEnabled(enabled); +} + +// |shell::PlatformView| +void PlatformViewIOS::UpdateSemantics(blink::SemanticsNodeUpdates update) { + if (accessibility_bridge_) { + accessibility_bridge_->UpdateSemantics(std::move(update)); + } +} + +// |shell::PlatformView| +void PlatformViewIOS::HandlePlatformMessage(fxl::RefPtr message) { + platform_message_router_.HandlePlatformMessage(std::move(message)); +} + +// |shell::PlatformView| +std::unique_ptr PlatformViewIOS::CreateVSyncWaiter() { + return std::make_unique(task_runners_); +} + +fml::scoped_nsprotocol PlatformViewIOS::GetTextInputPlugin() const { + return text_input_plugin_; +} + +void PlatformViewIOS::SetTextInputPlugin(fml::scoped_nsprotocol plugin) { + text_input_plugin_ = plugin; } } // namespace shell diff --git a/engine/src/flutter/shell/platform/embedder/BUILD.gn b/engine/src/flutter/shell/platform/embedder/BUILD.gn index 7ce6f7e8d5..b1e0b3e4f3 100644 --- a/engine/src/flutter/shell/platform/embedder/BUILD.gn +++ b/engine/src/flutter/shell/platform/embedder/BUILD.gn @@ -4,16 +4,21 @@ import("$flutter_root/testing/testing.gni") -source_set("embedder") { +static_library("embedder") { + complete_static_lib = true + sources = [ "embedder.cc", "embedder.h", + "embedder_engine.cc", + "embedder_engine.h", "embedder_include.c", "platform_view_embedder.cc", "platform_view_embedder.h", ] deps = [ + "$flutter_root/assets", "$flutter_root/common", "$flutter_root/fml", "$flutter_root/shell/common", @@ -22,6 +27,7 @@ source_set("embedder") { "//third_party/dart/runtime:libdart_jit", "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/skia", + "//third_party/skia:gpu", "//topaz/lib/tonic", ] diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 866413cbc0..a7ee807934 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -2,14 +2,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + +#include "lib/fxl/build_config.h" + +#if OS_WIN +#define FLUTTER_EXPORT __declspec(dllexport) +#else // OS_WIN #define FLUTTER_EXPORT __attribute__((visibility("default"))) +#endif // OS_WIN #include "flutter/shell/platform/embedder/embedder.h" #include -#include "flutter/common/threads.h" + +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/common/task_runners.h" +#include "flutter/fml/file.h" #include "flutter/fml/message_loop.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder_engine.h" #include "flutter/shell/platform/embedder/platform_view_embedder.h" +#include "lib/fxl/command_line.h" #include "lib/fxl/functional/make_copyable.h" #define SAFE_ACCESS(pointer, member, default_value) \ @@ -41,21 +57,6 @@ bool IsRendererValid(const FlutterRendererConfig* config) { return true; } -class PlatformViewHolder { - public: - PlatformViewHolder(std::shared_ptr ptr) - : platform_view_(std::move(ptr)) {} - - std::shared_ptr view() const { - return platform_view_; - } - - private: - std::shared_ptr platform_view_; - - FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewHolder); -}; - struct _FlutterPlatformMessageResponseHandle { fxl::RefPtr message; }; @@ -65,6 +66,7 @@ FlutterResult FlutterEngineRun(size_t version, const FlutterProjectArgs* args, void* user_data, FlutterEngine* engine_out) { + // Step 0: Figure out arguments for shell creation. if (version != FLUTTER_ENGINE_VERSION) { return kInvalidLibraryVersion; } @@ -87,51 +89,44 @@ FlutterResult FlutterEngineRun(size_t version, return kInvalidArguments; } - auto make_current = - [ ptr = config->open_gl.make_current, user_data ]()->bool { + auto make_current = [ptr = config->open_gl.make_current, + user_data]() -> bool { return ptr(user_data); }; + + auto clear_current = [ptr = config->open_gl.clear_current, + user_data]() -> bool { return ptr(user_data); }; + + auto present = [ptr = config->open_gl.present, user_data]() -> bool { return ptr(user_data); }; - auto clear_current = - [ ptr = config->open_gl.clear_current, user_data ]()->bool { - return ptr(user_data); - }; - - auto present = [ ptr = config->open_gl.present, user_data ]()->bool { - return ptr(user_data); - }; - - auto fbo_callback = - [ ptr = config->open_gl.fbo_callback, user_data ]()->intptr_t { - return ptr(user_data); - }; + auto fbo_callback = [ptr = config->open_gl.fbo_callback, + user_data]() -> intptr_t { return ptr(user_data); }; shell::PlatformViewEmbedder::PlatformMessageResponseCallback platform_message_response_callback = nullptr; if (SAFE_ACCESS(args, platform_message_callback, nullptr) != nullptr) { platform_message_response_callback = - [ ptr = args->platform_message_callback, - user_data ](fxl::RefPtr message) { - auto handle = new FlutterPlatformMessageResponseHandle(); - const FlutterPlatformMessage incoming_message = { - .struct_size = sizeof(FlutterPlatformMessage), - .channel = message->channel().c_str(), - .message = message->data().data(), - .message_size = message->data().size(), - .response_handle = handle, - }; - handle->message = std::move(message); - return ptr(&incoming_message, user_data); - }; + [ptr = args->platform_message_callback, + user_data](fxl::RefPtr message) { + auto handle = new FlutterPlatformMessageResponseHandle(); + const FlutterPlatformMessage incoming_message = { + .struct_size = sizeof(FlutterPlatformMessage), + .channel = message->channel().c_str(), + .message = message->data().data(), + .message_size = message->data().size(), + .response_handle = handle, + }; + handle->message = std::move(message); + return ptr(&incoming_message, user_data); + }; } const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; std::function make_resource_current_callback = nullptr; if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) { - make_resource_current_callback = - [ ptr = config->open_gl.make_resource_current, user_data ]() { - return ptr(user_data); - }; + make_resource_current_callback = [ptr = + config->open_gl.make_resource_current, + user_data]() { return ptr(user_data); }; } std::string icu_data_path; @@ -147,18 +142,33 @@ FlutterResult FlutterEngineRun(size_t version, SAFE_ACCESS(args, command_line_argv, nullptr)); } - static std::once_flag once_shell_initialization; - std::call_once(once_shell_initialization, [&]() { - fxl::CommandLine null_command_line; - shell::Shell::InitStandalone( - std::move(command_line), - icu_data_path, // icu data path default lookup. - "", // application library not supported in JIT mode. - args->assets_path - ); - }); + blink::Settings settings = shell::SettingsFromCommandLine(command_line); + settings.icu_data_path = icu_data_path; + settings.main_dart_file_path = args->main_path; + settings.packages_file_path = args->packages_path; + settings.assets_path = args->assets_path; + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; - shell::PlatformViewEmbedder::DispatchTable table = { + // Create a thread host with the current thread as the platform thread and all + // other threads managed. + shell::ThreadHost thread_host("io.flutter", shell::ThreadHost::Type::GPU | + shell::ThreadHost::Type::IO | + shell::ThreadHost::Type::UI); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + blink::TaskRunners task_runners( + "io.flutter", + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + thread_host.gpu_thread->GetTaskRunner(), // gpu + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io + ); + + shell::PlatformViewEmbedder::DispatchTable dispatch_table = { .gl_make_current_callback = make_current, .gl_clear_current_callback = clear_current, .gl_present_callback = present, @@ -167,31 +177,55 @@ FlutterResult FlutterEngineRun(size_t version, .gl_make_resource_current_callback = make_resource_current_callback, }; - auto platform_view = std::make_shared(table); - platform_view->Attach(); + shell::Shell::CreateCallback on_create_platform_view = + [dispatch_table](shell::Shell& shell) { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + dispatch_table // embedder dispatch table + ); + }; - std::string assets(args->assets_path); - std::string main(args->main_path); - std::string packages(args->packages_path); + shell::Shell::CreateCallback on_create_rasterizer = + [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; - blink::Threads::UI()->PostTask([ - weak_engine = platform_view->engine().GetWeakPtr(), // - assets = std::move(assets), // - main = std::move(main), // - packages = std::move(packages) // - ] { - if (auto engine = weak_engine) { - if (main.empty()) { - engine->RunBundle(assets); - } else { - engine->RunBundleAndSource(assets, main, packages); - } - } - }); + // Step 1: Create the engine. + auto embedder_engine = + std::make_unique(std::move(thread_host), // + std::move(task_runners), // + settings, // + on_create_platform_view, // + on_create_rasterizer // + ); - *engine_out = reinterpret_cast( - new PlatformViewHolder(std::move(platform_view))); + if (!embedder_engine->IsValid()) { + return kInvalidArguments; + } + // Step 2: Setup the rendering surface. + if (!embedder_engine->NotifyCreated()) { + return kInvalidArguments; + } + + // Step 3: Run the engine. + auto run_configuration = shell::RunConfiguration::InferFromSettings(settings); + + run_configuration.AddAssetResolver( + std::make_unique( + fml::Duplicate(settings.assets_dir))); + + run_configuration.AddAssetResolver( + std::make_unique(fml::OpenFile( + settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + + if (!embedder_engine->Run(std::move(run_configuration))) { + return kInvalidArguments; + } + + // Finally! Release the ownership of the embedder engine to the caller. + *engine_out = reinterpret_cast(embedder_engine.release()); return kSuccess; } @@ -199,7 +233,9 @@ FlutterResult FlutterEngineShutdown(FlutterEngine engine) { if (engine == nullptr) { return kInvalidArguments; } - delete reinterpret_cast(engine); + auto embedder_engine = reinterpret_cast(engine); + embedder_engine->NotifyDestroyed(); + delete embedder_engine; return kSuccess; } @@ -210,21 +246,16 @@ FlutterResult FlutterEngineSendWindowMetricsEvent( return kInvalidArguments; } - auto holder = reinterpret_cast(engine); - blink::ViewportMetrics metrics; metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0); - blink::Threads::UI()->PostTask( - [ weak_engine = holder->view()->engine().GetWeakPtr(), metrics ] { - if (auto engine = weak_engine) { - engine->SetViewportMetrics(metrics); - } - }); - return kSuccess; + return reinterpret_cast(engine)->SetViewportMetrics( + std::move(metrics)) + ? kSuccess + : kInvalidArguments; } inline blink::PointerData::Change ToPointerDataChange( @@ -267,19 +298,10 @@ FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, reinterpret_cast(current) + current->struct_size); } - blink::Threads::UI()->PostTask(fxl::MakeCopyable([ - weak_engine = reinterpret_cast(engine) - ->view() - ->engine() - .GetWeakPtr(), - packet = std::move(packet) - ] { - if (auto engine = weak_engine) { - engine->DispatchPointerDataPacket(*packet); - } - })); - - return kSuccess; + return reinterpret_cast(engine) + ->DispatchPointerDataPacket(std::move(packet)) + ? kSuccess + : kInvalidArguments; } FlutterResult FlutterEngineSendPlatformMessage( @@ -294,8 +316,6 @@ FlutterResult FlutterEngineSendPlatformMessage( return kInvalidArguments; } - auto holder = reinterpret_cast(engine); - auto message = fxl::MakeRefCounted( flutter_message->channel, std::vector( @@ -303,13 +323,10 @@ FlutterResult FlutterEngineSendPlatformMessage( flutter_message->message + flutter_message->message_size), nullptr); - blink::Threads::UI()->PostTask( - [ weak_engine = holder->view()->engine().GetWeakPtr(), message ] { - if (auto engine = weak_engine) { - engine->DispatchPlatformMessage(message); - } - }); - return kSuccess; + return reinterpret_cast(engine)->SendPlatformMessage( + std::move(message)) + ? kSuccess + : kInvalidArguments; } FlutterResult FlutterEngineSendPlatformMessageResponse( diff --git a/engine/src/flutter/shell/platform/embedder/embedder_engine.cc b/engine/src/flutter/shell/platform/embedder/embedder_engine.cc new file mode 100644 index 0000000000..7416441d79 --- /dev/null +++ b/engine/src/flutter/shell/platform/embedder/embedder_engine.cc @@ -0,0 +1,119 @@ +// Copyright 2017 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. + +#include "flutter/shell/platform/embedder/embedder_engine.h" + +#include "lib/fxl/functional/make_copyable.h" + +#ifdef ERROR +#undef ERROR +#endif + +namespace shell { + +EmbedderEngine::EmbedderEngine( + ThreadHost thread_host, + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer) + : thread_host_(std::move(thread_host)), + shell_(Shell::Create(std::move(task_runners), + std::move(settings), + on_create_platform_view, + on_create_rasterizer)) { + is_valid_ = shell_ != nullptr; +} + +EmbedderEngine::~EmbedderEngine() = default; + +bool EmbedderEngine::IsValid() const { + return is_valid_; +} + +bool EmbedderEngine::NotifyCreated() { + if (!IsValid()) { + return false; + } + + shell_->GetPlatformView()->NotifyCreated(); + return true; +} + +bool EmbedderEngine::NotifyDestroyed() { + if (!IsValid()) { + return false; + } + + shell_->GetPlatformView()->NotifyDestroyed(); + return true; +} + +bool EmbedderEngine::Run(RunConfiguration run_configuration) { + if (!IsValid()) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = shell_->GetEngine(), // engine + config = std::move(run_configuration) // config + ]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (!result) { + FXL_LOG(ERROR) << "Could not launch the engine with configuration."; + } + } + })); + + return true; +} + +bool EmbedderEngine::SetViewportMetrics(blink::ViewportMetrics metrics) { + if (!IsValid()) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), metrics = std::move(metrics)]() { + if (engine) { + engine->SetViewportMetrics(std::move(metrics)); + } + }); + return true; +} + +bool EmbedderEngine::DispatchPointerDataPacket( + std::unique_ptr packet) { + if (!IsValid() || !packet) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = shell_->GetEngine(), packet = std::move(packet)] { + if (engine) { + engine->DispatchPointerDataPacket(*packet); + } + })); + + return true; +} + +bool EmbedderEngine::SendPlatformMessage( + fxl::RefPtr message) { + if (!IsValid() || !message) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), message] { + if (engine) { + engine->DispatchPlatformMessage(message); + } + }); + + return true; +} + +} // namespace shell diff --git a/engine/src/flutter/shell/platform/embedder/embedder_engine.h b/engine/src/flutter/shell/platform/embedder/embedder_engine.h new file mode 100644 index 0000000000..c01b86c2aa --- /dev/null +++ b/engine/src/flutter/shell/platform/embedder/embedder_engine.h @@ -0,0 +1,53 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ + +#include + +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "lib/fxl/macros.h" + +namespace shell { + +// The object that is returned to the embedder as an opaque pointer to the +// instance of the Flutter engine. +class EmbedderEngine { + public: + EmbedderEngine(ThreadHost thread_host, blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer); + + ~EmbedderEngine(); + + bool NotifyCreated(); + + bool NotifyDestroyed(); + + bool Run(RunConfiguration run_configuration); + + bool IsValid() const; + + bool SetViewportMetrics(blink::ViewportMetrics metrics); + + bool DispatchPointerDataPacket( + std::unique_ptr packet); + + bool SendPlatformMessage(fxl::RefPtr message); + + private: + const ThreadHost thread_host_; + std::unique_ptr shell_; + bool is_valid_ = false; + + FXL_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ diff --git a/engine/src/flutter/shell/platform/embedder/platform_view_embedder.cc b/engine/src/flutter/shell/platform/embedder/platform_view_embedder.cc index 46e0390770..db1bea35c6 100644 --- a/engine/src/flutter/shell/platform/embedder/platform_view_embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/platform_view_embedder.cc @@ -3,17 +3,18 @@ // found in the LICENSE file. #include "flutter/shell/platform/embedder/platform_view_embedder.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" + +#include "flutter/shell/common/io_manager.h" namespace shell { -PlatformViewEmbedder::PlatformViewEmbedder(DispatchTable dispatch_table) - : PlatformView(std::make_unique(nullptr)), +PlatformViewEmbedder::PlatformViewEmbedder(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + DispatchTable dispatch_table) + : PlatformView(delegate, std::move(task_runners)), dispatch_table_(dispatch_table) {} -PlatformViewEmbedder::~PlatformViewEmbedder() { - NotifyDestroyed(); -} +PlatformViewEmbedder::~PlatformViewEmbedder() = default; bool PlatformViewEmbedder::GLContextMakeCurrent() { return dispatch_table_.gl_make_current_callback(); @@ -31,33 +32,6 @@ intptr_t PlatformViewEmbedder::GLContextFBO() const { return dispatch_table_.gl_fbo_callback(); } -void PlatformViewEmbedder::Attach() { - CreateEngine(); - NotifyCreated(std::make_unique(this)); - - if (dispatch_table_.gl_make_resource_current_callback != nullptr) { - SetupResourceContextOnIOThread(); - } -} - -bool PlatformViewEmbedder::ResourceContextMakeCurrent() { - if (dispatch_table_.gl_make_resource_current_callback == nullptr) { - return false; - } - return dispatch_table_.gl_make_resource_current_callback(); -} - -void PlatformViewEmbedder::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - FXL_LOG(INFO) << "Hot reloading is unsupported on this platform."; -} - -void PlatformViewEmbedder::SetAssetBundlePath( - const std::string& assets_directory) { - FXL_LOG(INFO) << "Set asset bundle path is unsupported on this platform."; -} - void PlatformViewEmbedder::HandlePlatformMessage( fxl::RefPtr message) { if (!message) { @@ -76,4 +50,17 @@ void PlatformViewEmbedder::HandlePlatformMessage( dispatch_table_.platform_message_response_callback(std::move(message)); } +std::unique_ptr PlatformViewEmbedder::CreateRenderingSurface() { + return std::make_unique(this); +} + +sk_sp PlatformViewEmbedder::CreateResourceContext() const { + auto callback = dispatch_table_.gl_make_resource_current_callback; + if (callback && callback()) { + return IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend); + } + return nullptr; +} + } // namespace shell diff --git a/engine/src/flutter/shell/platform/embedder/platform_view_embedder.h b/engine/src/flutter/shell/platform/embedder/platform_view_embedder.h index c287fc5ac8..a5b76dfaa0 100644 --- a/engine/src/flutter/shell/platform/embedder/platform_view_embedder.h +++ b/engine/src/flutter/shell/platform/embedder/platform_view_embedder.h @@ -12,7 +12,8 @@ namespace shell { -class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { +class PlatformViewEmbedder final : public PlatformView, + public GPUSurfaceGLDelegate { public: using PlatformMessageResponseCallback = std::function)>; @@ -26,9 +27,11 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { std::function gl_make_resource_current_callback; // optional }; - PlatformViewEmbedder(DispatchTable dispatch_table); + PlatformViewEmbedder(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + DispatchTable dispatch_table); - ~PlatformViewEmbedder(); + ~PlatformViewEmbedder() override; // |shell::GPUSurfaceGLDelegate| bool GLContextMakeCurrent() override; @@ -42,20 +45,6 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { // |shell::GPUSurfaceGLDelegate| intptr_t GLContextFBO() const override; - // |shell::PlatformView| - void Attach() override; - - // |shell::PlatformView| - bool ResourceContextMakeCurrent() override; - - // |shell::PlatformView| - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - // |shell::PlatformView| - void SetAssetBundlePath(const std::string& assets_directory) override; - // |shell::PlatformView| void HandlePlatformMessage( fxl::RefPtr message) override; @@ -63,6 +52,12 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { private: DispatchTable dispatch_table_; + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; + + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; + FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewEmbedder); }; diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn deleted file mode 100644 index 6e9f1f596b..0000000000 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -executable("linux") { - output_name = "flutter_tester" - - sources = [ - "main_linux.cc", - ] - - deps = [ - "//third_party/dart/runtime:libdart_jit", - "//third_party/dart/runtime/bin:embedded_dart_io", - "$flutter_root/common", - "$flutter_root/fml", - "$flutter_root/shell/common", - "$flutter_root/shell/testing", - "//garnet/public/lib/fxl", - "//third_party/skia", - "//topaz/lib/tonic", - ] -} diff --git a/engine/src/flutter/shell/platform/linux/main_linux.cc b/engine/src/flutter/shell/platform/linux/main_linux.cc deleted file mode 100644 index a20d425ed1..0000000000 --- a/engine/src/flutter/shell/platform/linux/main_linux.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/common/threads.h" -#include "flutter/fml/message_loop.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/common/switches.h" -#include "flutter/shell/testing/test_runner.h" -#include "flutter/shell/testing/testing.h" -#include "flutter/sky/engine/public/web/Sky.h" -#include "lib/fxl/command_line.h" -#include "lib/tonic/dart_microtask_queue.h" -#include "third_party/dart/runtime/bin/embedded_dart_io.h" - -namespace { - -// Exit codes used by the Dart command line tool. -const int kApiErrorExitCode = 253; -const int kCompilationErrorExitCode = 254; -const int kErrorExitCode = 255; - -// Checks whether the engine's main Dart isolate has no pending work. If so, -// then exit the given message loop. -class ScriptCompletionTaskObserver : public fml::TaskObserver { - public: - ScriptCompletionTaskObserver(fxl::RefPtr task_runner) - : main_task_runner_(std::move(task_runner)), - prev_live_(false), - last_error_(tonic::kNoError) {} - - void DidProcessTask() override { - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - shell::Engine& engine = test_runner.platform_view().engine(); - - if (engine.GetLoadScriptError() != tonic::kNoError) { - last_error_ = engine.GetLoadScriptError(); - main_task_runner_->PostTask( - []() { fml::MessageLoop::GetCurrent().Terminate(); }); - return; - } - - bool live = engine.UIIsolateHasLivePorts(); - if (prev_live_ && !live) { - last_error_ = engine.GetUIIsolateLastError(); - main_task_runner_->PostTask( - []() { fml::MessageLoop::GetCurrent().Terminate(); }); - } - prev_live_ = live; - } - - tonic::DartErrorHandleType last_error() { return last_error_; } - - private: - fxl::RefPtr main_task_runner_; - bool prev_live_; - tonic::DartErrorHandleType last_error_; -}; - -int ConvertErrorTypeToExitCode(tonic::DartErrorHandleType error) { - switch (error) { - case tonic::kCompilationErrorType: - return kCompilationErrorExitCode; - case tonic::kApiErrorType: - return kApiErrorExitCode; - case tonic::kUnknownErrorType: - return kErrorExitCode; - default: - return 0; - } -} - -void RunNonInteractive(fxl::CommandLine initial_command_line, - bool run_forever) { - // This is a platform thread (i.e not one created by fml::Thread), so perform - // one time initialization. - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - std::string bundle_path = ""; - initial_command_line.GetOptionValue( - FlagForSwitch(shell::Switch::FlutterAssetsDir), &bundle_path); - - shell::Shell::InitStandalone(initial_command_line, - /* icu_data_path= */ "", - /* application_library_path= */ "", bundle_path); - - // Note that this task observer must be added after the observer that drains - // the microtask queue. - ScriptCompletionTaskObserver task_observer( - fml::MessageLoop::GetCurrent().GetTaskRunner()); - if (!run_forever) { - blink::Threads::UI()->PostTask([&task_observer] { - fml::MessageLoop::GetCurrent().AddTaskObserver(&task_observer); - }); - } - - if (!shell::InitForTesting(initial_command_line)) { - shell::PrintUsage("flutter_tester"); - exit(EXIT_FAILURE); - return; - } - - fml::MessageLoop::GetCurrent().Run(); - - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - tonic::DartErrorHandleType error = - test_runner.platform_view().engine().GetLoadScriptError(); - if (error == tonic::kNoError) - error = task_observer.last_error(); - if (error == tonic::kNoError) { - fxl::AutoResetWaitableEvent latch; - blink::Threads::UI()->PostTask([&error, &latch] { - error = tonic::DartMicrotaskQueue::GetForCurrentThread()->GetLastError(); - latch.Signal(); - }); - latch.Wait(); - } - - // The script has completed and the engine may not be in a clean state, - // so just stop the process. - exit(ConvertErrorTypeToExitCode(error)); -} - -} // namespace - -int main(int argc, char* argv[]) { - dart::bin::SetExecutableName(argv[0]); - dart::bin::SetExecutableArguments(argc - 1, argv); - - auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); - - if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { - shell::PrintUsage("flutter_tester"); - return EXIT_SUCCESS; - } - - bool run_forever = - command_line.HasOption(shell::FlagForSwitch(shell::Switch::RunForever)); - RunNonInteractive(std::move(command_line), run_forever); - return EXIT_SUCCESS; -} diff --git a/engine/src/flutter/shell/platform/win/BUILD.gn b/engine/src/flutter/shell/platform/win/BUILD.gn deleted file mode 100644 index b4a2fe5033..0000000000 --- a/engine/src/flutter/shell/platform/win/BUILD.gn +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -executable("win") { - output_name = "flutter_tester" - - sources = [ - "main_win.cc", - ] - - deps = [ - "//flutter/common", - "//flutter/flow", - "//flutter/fml", - "//flutter/shell/common", - "//flutter/shell/testing", - "//flutter/sky/engine/wtf", - "//garnet/public/lib/fxl", - "//third_party/dart/runtime/bin:embedded_dart_io", - "//third_party/dart/runtime:libdart_jit", - "//third_party/skia", - "//topaz/lib/tonic", - ] - - libs = [ - "iphlpapi.lib", - "Rpcrt4.lib" - ] -} diff --git a/engine/src/flutter/shell/platform/win/main_win.cc b/engine/src/flutter/shell/platform/win/main_win.cc deleted file mode 100644 index c31c88ca91..0000000000 --- a/engine/src/flutter/shell/platform/win/main_win.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "flutter/common/threads.h" -#include "flutter/fml/message_loop.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/common/switches.h" -#include "flutter/shell/testing/test_runner.h" -#include "flutter/shell/testing/testing.h" -#include "lib/fxl/command_line.h" -#include "lib/tonic/dart_microtask_queue.h" -#include "third_party/dart/runtime/bin/embedded_dart_io.h" - -namespace { - -// Exit codes used by the Dart command line tool. -const int kApiErrorExitCode = 253; -const int kCompilationErrorExitCode = 254; -const int kErrorExitCode = 255; - -// Checks whether the engine's main Dart isolate has no pending work. If so, -// then exit the given message loop. -class ScriptCompletionTaskObserver : public fml::TaskObserver { - public: - ScriptCompletionTaskObserver(fxl::RefPtr task_runner) - : main_task_runner_(std::move(task_runner)), - prev_live_(false), - last_error_(tonic::kNoError) {} - - void DidProcessTask() override { - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - shell::Engine& engine = test_runner.platform_view().engine(); - - if (engine.GetLoadScriptError() != tonic::kNoError) { - last_error_ = engine.GetLoadScriptError(); - main_task_runner_->PostTask( - []() { fml::MessageLoop::GetCurrent().Terminate(); }); - return; - } - - bool live = engine.UIIsolateHasLivePorts(); - if (prev_live_ && !live) { - last_error_ = engine.GetUIIsolateLastError(); - main_task_runner_->PostTask( - []() { fml::MessageLoop::GetCurrent().Terminate(); }); - } - prev_live_ = live; - } - - tonic::DartErrorHandleType last_error() { return last_error_; } - - private: - fxl::RefPtr main_task_runner_; - bool prev_live_; - tonic::DartErrorHandleType last_error_; -}; - -int ConvertErrorTypeToExitCode(tonic::DartErrorHandleType error) { - switch (error) { - case tonic::kCompilationErrorType: - return kCompilationErrorExitCode; - case tonic::kApiErrorType: - return kApiErrorExitCode; - case tonic::kUnknownErrorType: - return kErrorExitCode; - default: - return 0; - } -} - -void RunNonInteractive(fxl::CommandLine initial_command_line, - bool run_forever) { - // This is a platform thread (i.e not one created by fml::Thread), so perform - // one time initialization. - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - std::string bundle_path = ""; - initial_command_line.GetOptionValue( - FlagForSwitch(shell::Switch::FlutterAssetsDir), &bundle_path); - - shell::Shell::InitStandalone(initial_command_line, - /* icu_data_path= */ "", - /* application_library_path= */ "", bundle_path); - - // Note that this task observer must be added after the observer that drains - // the microtask queue. - ScriptCompletionTaskObserver task_observer( - fml::MessageLoop::GetCurrent().GetTaskRunner()); - if (!run_forever) { - blink::Threads::UI()->PostTask([&task_observer] { - fml::MessageLoop::GetCurrent().AddTaskObserver(&task_observer); - }); - } - - if (!shell::InitForTesting(initial_command_line)) { - shell::PrintUsage("flutter_tester"); - ::ExitProcess(EXIT_FAILURE); - return; - } - - fml::MessageLoop::GetCurrent().Run(); - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - tonic::DartErrorHandleType error = - test_runner.platform_view().engine().GetLoadScriptError(); - if (error == tonic::kNoError) - error = task_observer.last_error(); - if (error == tonic::kNoError) { - fxl::AutoResetWaitableEvent latch; - blink::Threads::UI()->PostTask([&error, &latch] { - error = tonic::DartMicrotaskQueue::GetForCurrentThread()->GetLastError(); - latch.Signal(); - }); - latch.Wait(); - } - - // The script has completed and the engine may not be in a clean state, - // so just stop the process. - ::ExitProcess(ConvertErrorTypeToExitCode(error)); -} - -} // namespace - -int main(int argc, char* argv[]) { - dart::bin::SetExecutableName(argv[0]); - dart::bin::SetExecutableArguments(argc - 1, argv); - - auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); - - if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { - shell::PrintUsage("flutter_tester"); - return EXIT_SUCCESS; - } - - bool run_forever = - command_line.HasOption(shell::FlagForSwitch(shell::Switch::RunForever)); - RunNonInteractive(std::move(command_line), run_forever); - return EXIT_SUCCESS; -} diff --git a/engine/src/flutter/shell/testing/BUILD.gn b/engine/src/flutter/shell/testing/BUILD.gn index 659c241cc4..f11a3a2d07 100644 --- a/engine/src/flutter/shell/testing/BUILD.gn +++ b/engine/src/flutter/shell/testing/BUILD.gn @@ -2,24 +2,30 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("testing") { +executable("testing") { + testonly = true + + output_name = "flutter_tester" + + public_configs = [ "$flutter_root:config" ] + sources = [ - "platform_view_test.cc", - "platform_view_test.h", - "test_runner.cc", - "test_runner.h", - "testing.cc", - "testing.h", + "tester_main.cc", ] deps = [ + "$flutter_root/assets", "$flutter_root/common", + "$flutter_root/fml", "$flutter_root/shell/common", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", + "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/skia", + "//topaz/lib/tonic", ] - public_configs = [ - "$flutter_root:config", - ] + if (is_linux) { + ldflags = [ "-rdynamic" ] + } } diff --git a/engine/src/flutter/shell/testing/observatory/test.dart b/engine/src/flutter/shell/testing/observatory/test.dart index 3a00383a48..5786153ed8 100644 --- a/engine/src/flutter/shell/testing/observatory/test.dart +++ b/engine/src/flutter/shell/testing/observatory/test.dart @@ -111,12 +111,20 @@ Future testStartPaused(Uri uri) async { } // Grab the isolate. - Map isolate = await serviceClient.invokeRPC('getIsolate', { - 'isolateId': isolateId, - }); - Expect.equals(isolate['type'], 'Isolate'); - // Verify that it is paused at start. - Expect.isNotNull(isolate['pauseEvent']); + Map isolate; + while(true) { + isolate = await serviceClient.invokeRPC('getIsolate', { + 'isolateId': isolateId, + }); + Expect.equals(isolate['type'], 'Isolate'); + // Verify that it is paused at start. + Expect.isNotNull(isolate['pauseEvent']); + if (isolate['pauseEvent']['kind'] != 'None') { + break; + } + } + + Expect.isNotNull(isolate); Expect.equals(isolate['pauseEvent']['kind'], 'PauseStart'); // Resume the isolate. diff --git a/engine/src/flutter/shell/testing/platform_view_test.cc b/engine/src/flutter/shell/testing/platform_view_test.cc deleted file mode 100644 index a59684d31b..0000000000 --- a/engine/src/flutter/shell/testing/platform_view_test.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/testing/platform_view_test.h" - -#include "flutter/shell/common/null_rasterizer.h" -#include "flutter/shell/common/shell.h" - -namespace shell { - -PlatformViewTest::PlatformViewTest() - : PlatformView(std::unique_ptr(new NullRasterizer())) {} - -void PlatformViewTest::Attach() { - CreateEngine(); -} - -PlatformViewTest::~PlatformViewTest() = default; - -bool PlatformViewTest::ResourceContextMakeCurrent() { - return false; -} - -void PlatformViewTest::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) {} - -void PlatformViewTest::SetAssetBundlePath(const std::string& assets_directory) { -} - -} // namespace shell diff --git a/engine/src/flutter/shell/testing/platform_view_test.h b/engine/src/flutter/shell/testing/platform_view_test.h deleted file mode 100644 index 3612d0363a..0000000000 --- a/engine/src/flutter/shell/testing/platform_view_test.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_TESTING_PLATFORM_VIEW_TEST_H_ -#define SHELL_TESTING_PLATFORM_VIEW_TEST_H_ - -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class Shell; - -class PlatformViewTest : public PlatformView { - public: - PlatformViewTest(); - - ~PlatformViewTest(); - - virtual void Attach() override; - - bool ResourceContextMakeCurrent() override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePath(const std::string& assets_directory) override; - - private: - FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewTest); -}; - -} // namespace shell - -#endif // SHELL_TESTING_PLATFORM_VIEW_TEST_H_ diff --git a/engine/src/flutter/shell/testing/test_runner.cc b/engine/src/flutter/shell/testing/test_runner.cc deleted file mode 100644 index 838b04120c..0000000000 --- a/engine/src/flutter/shell/testing/test_runner.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/testing/test_runner.h" - -#include - -#include "flutter/common/threads.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/testing/platform_view_test.h" - -namespace shell { - -TestRunner::TestRunner() - : platform_view_(std::make_shared()) { - platform_view_->Attach(); - blink::ViewportMetrics metrics; - metrics.device_pixel_ratio = 3.0; - metrics.physical_width = 2400; // 800 at 3x resolution - metrics.physical_height = 1800; // 600 at 3x resolution - - blink::Threads::UI()->PostTask( - [ engine = platform_view_->engine().GetWeakPtr(), metrics ] { - if (engine) - engine->SetViewportMetrics(metrics); - }); -} - -TestRunner::~TestRunner() = default; - -TestRunner& TestRunner::Shared() { - static TestRunner* g_test_runner = nullptr; - if (!g_test_runner) - g_test_runner = new TestRunner(); - return *g_test_runner; -} - -void TestRunner::Run(const TestDescriptor& test) { - blink::Threads::UI()->PostTask( - [ engine = platform_view_->engine().GetWeakPtr(), test ] { - if (engine) - engine->RunBundleAndSource(std::string(), test.path, test.packages); - }); -} - -} // namespace shell diff --git a/engine/src/flutter/shell/testing/test_runner.h b/engine/src/flutter/shell/testing/test_runner.h deleted file mode 100644 index 8f303d7eb7..0000000000 --- a/engine/src/flutter/shell/testing/test_runner.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_TESTING_TEST_RUNNER_H_ -#define SHELL_TESTING_TEST_RUNNER_H_ - -#include -#include - -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class PlatformView; - -class TestRunner { - public: - static TestRunner& Shared(); - - struct TestDescriptor { - std::string path; - std::string packages; - }; - - void Run(const TestDescriptor& test); - - PlatformView& platform_view() { return *platform_view_; } - - private: - TestRunner(); - ~TestRunner(); - - std::shared_ptr platform_view_; - - FXL_DISALLOW_COPY_AND_ASSIGN(TestRunner); -}; - -} // namespace shell - -#endif // SHELL_TESTING_TEST_RUNNER_H_ diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc new file mode 100644 index 0000000000..a8afd32503 --- /dev/null +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -0,0 +1,250 @@ +// Copyright 2018 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 FML_USED_ON_EMBEDDER + +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/fml/file.h" +#include "flutter/fml/message_loop.h" +#include "flutter/fml/paths.h" +#include "flutter/fml/task_runner.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" +#include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/synchronization/waitable_event.h" +#include "third_party/dart/runtime/bin/embedded_dart_io.h" + +#ifdef ERROR +#undef ERROR +#endif + +namespace shell { + +// Checks whether the engine's main Dart isolate has no pending work. If so, +// then exit the given message loop. +class ScriptCompletionTaskObserver { + public: + ScriptCompletionTaskObserver(Shell& shell, + fxl::RefPtr main_task_runner, + bool run_forever) + : engine_(shell.GetEngine()), + main_task_runner_(std::move(main_task_runner)), + run_forever_(run_forever) {} + + int GetExitCodeForLastError() const { + // Exit codes used by the Dart command line tool. + const int kApiErrorExitCode = 253; + const int kCompilationErrorExitCode = 254; + const int kErrorExitCode = 255; + switch (last_error_) { + case tonic::kCompilationErrorType: + return kCompilationErrorExitCode; + case tonic::kApiErrorType: + return kApiErrorExitCode; + case tonic::kUnknownErrorType: + return kErrorExitCode; + default: + return 0; + } + } + + void DidProcessTask() { + if (engine_) { + last_error_ = engine_->GetUIIsolateLastError(); + if (engine_->UIIsolateHasLivePorts()) { + // The UI isolate still has live ports and is running. Nothing to do + // just yet. + return; + } + } + + if (run_forever_) { + // We need this script to run forever. We have already recorded the last + // error. Keep going. + return; + } + + if (!has_terminated) { + // Only try to terminate the loop once. + has_terminated = true; + main_task_runner_->PostTask( + []() { fml::MessageLoop::GetCurrent().Terminate(); }); + } + } + + private: + fml::WeakPtr engine_; + fxl::RefPtr main_task_runner_; + bool run_forever_ = false; + tonic::DartErrorHandleType last_error_ = tonic::kUnknownErrorType; + bool has_terminated = false; + + FXL_DISALLOW_COPY_AND_ASSIGN(ScriptCompletionTaskObserver); +}; + +int RunTester(const blink::Settings& settings, bool run_forever) { + const auto thread_label = "io.flutter.test"; + + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + auto current_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + + // Setup a single threaded test runner configuration. + const blink::TaskRunners task_runners(thread_label, // dart thread label + current_task_runner, // platform + current_task_runner, // gpu + current_task_runner, // ui + current_task_runner // io + ); + + Shell::CreateCallback on_create_platform_view = + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }; + + Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + auto shell = Shell::Create(task_runners, // + settings, // + on_create_platform_view, // + on_create_rasterizer // + ); + + if (!shell || !shell->IsSetup()) { + FXL_LOG(ERROR) << "Could not setup the shell."; + return EXIT_FAILURE; + } + + auto isolate_configuration = IsolateConfiguration::CreateForSource( + settings.main_dart_file_path, settings.packages_file_path); + + if (!isolate_configuration) { + FXL_LOG(ERROR) << "Could create isolate configuration."; + return EXIT_FAILURE; + } + + auto asset_manager = fxl::MakeRefCounted(); + asset_manager->PushBack(std::make_unique( + fml::Duplicate(settings.assets_dir))); + asset_manager->PushBack( + std::make_unique(fml::OpenFile( + settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + + RunConfiguration run_configuration(std::move(isolate_configuration), + std::move(asset_manager)); + + // The script completion task observer that will be installed on the UI thread + // that watched if the engine has any live ports. + ScriptCompletionTaskObserver completion_observer( + *shell, // a valid shell + fml::MessageLoop::GetCurrent() + .GetTaskRunner(), // the message loop to terminate + run_forever // should the exit be ignored + ); + + bool engine_did_run = false; + + fxl::AutoResetWaitableEvent sync_run_latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetUITaskRunner(), + fxl::MakeCopyable([&sync_run_latch, &completion_observer, + engine = shell->GetEngine(), + config = std::move(run_configuration), + &engine_did_run]() mutable { + fml::MessageLoop::GetCurrent().AddTaskObserver( + reinterpret_cast(&completion_observer), + [&completion_observer]() { completion_observer.DidProcessTask(); }); + if (engine->Run(std::move(config))) { + engine_did_run = true; + + blink::ViewportMetrics metrics; + metrics.device_pixel_ratio = 3.0; + metrics.physical_width = 2400; // 800 at 3x resolution + metrics.physical_height = 1800; // 600 at 3x resolution + engine->SetViewportMetrics(metrics); + + } else { + FXL_DLOG(ERROR) << "Could not launch the engine with configuration."; + } + sync_run_latch.Signal(); + })); + sync_run_latch.Wait(); + + // Run the message loop and wait for the script to do its thing. + fml::MessageLoop::GetCurrent().Run(); + + // Cleanup the completion observer synchronously as it is living on the + // stack. + fxl::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetUITaskRunner(), + [&latch, &completion_observer] { + fml::MessageLoop::GetCurrent().RemoveTaskObserver( + reinterpret_cast(&completion_observer)); + latch.Signal(); + }); + latch.Wait(); + + if (!engine_did_run) { + // If the engine itself didn't have a chance to run, there is no point in + // asking it if there was an error. Signal a failure unconditionally. + return EXIT_FAILURE; + } + + return completion_observer.GetExitCodeForLastError(); +} + +} // namespace shell + +int main(int argc, char* argv[]) { + dart::bin::SetExecutableName(argv[0]); + dart::bin::SetExecutableArguments(argc - 1, argv); + + auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); + + if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { + shell::PrintUsage("flutter_tester"); + return EXIT_SUCCESS; + } + + auto settings = shell::SettingsFromCommandLine(command_line); + if (command_line.positional_args().size() > 0) { + // The tester may not use the switch for the main dart file path. Specifying + // it as a positional argument instead. + settings.main_dart_file_path = command_line.positional_args()[0]; + } + + if (settings.main_dart_file_path.size() == 0) { + FXL_LOG(ERROR) << "Main dart file path not specified."; + return EXIT_FAILURE; + } + + settings.icu_data_path = "icudtl.dat"; + + settings.kernel_snapshot_path = + fml::paths::JoinPaths({settings.assets_path, "platform.dill"}); + + // The tools that read logs get confused if there is a log tag specified. + settings.log_tag = ""; + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + return shell::RunTester( + settings, + command_line.HasOption(shell::FlagForSwitch(shell::Switch::RunForever))); +} diff --git a/engine/src/flutter/shell/testing/testing.cc b/engine/src/flutter/shell/testing/testing.cc deleted file mode 100644 index 13a3ab5be3..0000000000 --- a/engine/src/flutter/shell/testing/testing.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/testing/testing.h" - -#include "flutter/shell/common/switches.h" -#include "flutter/shell/testing/test_runner.h" - -namespace shell { - -bool InitForTesting(const fxl::CommandLine& command_line) { - TestRunner::TestDescriptor test; - test.packages = command_line.GetOptionValueWithDefault( - FlagForSwitch(Switch::Packages), ""); - auto args = command_line.positional_args(); - if (args.empty()) - return false; - test.path = args[0]; - TestRunner::Shared().Run(test); - return true; -} - -} // namespace shell diff --git a/engine/src/flutter/shell/testing/testing.h b/engine/src/flutter/shell/testing/testing.h deleted file mode 100644 index 99f8b4fe98..0000000000 --- a/engine/src/flutter/shell/testing/testing.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_TESTING_TESTING_H_ -#define SHELL_TESTING_TESTING_H_ - -#include "lib/fxl/command_line.h" - -namespace shell { - -bool InitForTesting(const fxl::CommandLine& command_line); - -} // namespace shell - -#endif // SHELL_TESTING_TESTING_H_ diff --git a/engine/src/flutter/synchronization/BUILD.gn b/engine/src/flutter/synchronization/BUILD.gn index 977a571c8d..9ee7680cc1 100644 --- a/engine/src/flutter/synchronization/BUILD.gn +++ b/engine/src/flutter/synchronization/BUILD.gn @@ -4,16 +4,13 @@ source_set("synchronization") { sources = [ - "debug_thread_checker.h", "pipeline.cc", "pipeline.h", "semaphore.cc", "semaphore.h", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] public_deps = [ "$flutter_root/glue", diff --git a/engine/src/flutter/synchronization/debug_thread_checker.h b/engine/src/flutter/synchronization/debug_thread_checker.h deleted file mode 100644 index 69614eb89d..0000000000 --- a/engine/src/flutter/synchronization/debug_thread_checker.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SYNCHRONIZATION_DEBUG_THREAD_CHECKER_H_ -#define FLUTTER_SYNCHRONIZATION_DEBUG_THREAD_CHECKER_H_ - -#ifndef NDEBUG - -#include -#include "lib/fxl/synchronization/thread_checker.h" - -#define FLUTTER_THREAD_CHECKER_DECLARE(x) ::fxl::ThreadChecker x; - -#define FLUTTER_THREAD_CHECKER_CHECK(x) FXL_CHECK(x.IsCreationThreadCurrent()); - -#else // NDEBUG - -#define FLUTTER_THREAD_CHECKER_DECLARE(x) - -#define FLUTTER_THREAD_CHECKER_CHECK(x) - -#endif // NDEBUG - -#endif // FLUTTER_SYNCHRONIZATION_DEBUG_THREAD_CHECKER_H_ diff --git a/engine/src/flutter/synchronization/semaphore.cc b/engine/src/flutter/synchronization/semaphore.cc index c6e0bdf9e2..4dc5f6220e 100644 --- a/engine/src/flutter/synchronization/semaphore.cc +++ b/engine/src/flutter/synchronization/semaphore.cc @@ -15,9 +15,12 @@ namespace flutter { class PlatformSemaphore { public: explicit PlatformSemaphore(uint32_t count) - : _sem(dispatch_semaphore_create(count)) {} + : _sem(dispatch_semaphore_create(count)), _initial(count) {} ~PlatformSemaphore() { + for (uint32_t i = 0; i < _initial; ++i) { + Signal(); + } if (_sem != nullptr) { dispatch_release(reinterpret_cast(_sem)); _sem = nullptr; @@ -42,6 +45,7 @@ class PlatformSemaphore { private: dispatch_semaphore_t _sem; + const uint32_t _initial; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformSemaphore); }; diff --git a/engine/src/flutter/testing/BUILD.gn b/engine/src/flutter/testing/BUILD.gn index 3927e71cfc..a65311bfae 100644 --- a/engine/src/flutter/testing/BUILD.gn +++ b/engine/src/flutter/testing/BUILD.gn @@ -9,9 +9,13 @@ source_set("testing") { "$flutter_root/testing/run_all_unittests.cc", "$flutter_root/testing/testing.cc", "$flutter_root/testing/testing.h", + "$flutter_root/testing/thread_test.cc", + "$flutter_root/testing/thread_test.h", ] public_deps = [ + "$flutter_root/fml", + "//garnet/public/lib/fxl", "//third_party/googletest:gtest", ] diff --git a/engine/src/flutter/testing/thread_test.cc b/engine/src/flutter/testing/thread_test.cc new file mode 100644 index 0000000000..1306f37678 --- /dev/null +++ b/engine/src/flutter/testing/thread_test.cc @@ -0,0 +1,33 @@ +// Copyright 2017 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 FML_USED_ON_EMBEDDER + +#include "flutter/testing/thread_test.h" + +namespace testing { + +void ThreadTest::SetUp() { + thread_ = std::make_unique(); + thread_task_runner_ = thread_->GetTaskRunner(); + + fml::MessageLoop::EnsureInitializedForCurrentThread(); + current_task_runner_ = fml::MessageLoop::GetCurrent().GetTaskRunner(); +} + +void ThreadTest::TearDown() { + thread_task_runner_ = nullptr; + thread_ = nullptr; + current_task_runner_ = nullptr; +} + +fxl::RefPtr ThreadTest::GetCurrentTaskRunner() { + return current_task_runner_; +} + +fxl::RefPtr ThreadTest::GetThreadTaskRunner() { + return thread_task_runner_; +} + +} // namespace testing diff --git a/engine/src/flutter/testing/thread_test.h b/engine/src/flutter/testing/thread_test.h new file mode 100644 index 0000000000..511d09c978 --- /dev/null +++ b/engine/src/flutter/testing/thread_test.h @@ -0,0 +1,37 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_TESTING_THREAD_TEST_H_ +#define FLUTTER_TESTING_THREAD_TEST_H_ + +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/thread.h" +#include "gtest/gtest.h" +#include "lib/fxl/macros.h" + +namespace testing { + +class ThreadTest : public Test { + public: + fxl::RefPtr GetCurrentTaskRunner(); + + fxl::RefPtr GetThreadTaskRunner(); + + protected: + void SetUp() override; + + void TearDown() override; + + private: + std::unique_ptr thread_; + fxl::RefPtr thread_task_runner_; + fxl::RefPtr current_task_runner_; +}; + +} // namespace testing + +#endif // FLUTTER_TESTING_THREAD_TEST_H_ diff --git a/engine/src/flutter/tools/gn b/engine/src/flutter/tools/gn index ea7071ac0a..b4b15f99f1 100755 --- a/engine/src/flutter/tools/gn +++ b/engine/src/flutter/tools/gn @@ -232,6 +232,10 @@ def main(argv): # On the Mac, also generate Xcode projects for ease of editing. command.append('--ide=xcode') + if sys.platform.startswith('win'): + # On Windows, also generate Visual Studio project for ease of editing. + command.append('--ide=vs') + gn_args = to_command_line(to_gn_args(args)) out_dir = get_out_dir(args) print "gn gen --check in %s" % out_dir