diff --git a/assets/BUILD.gn b/assets/BUILD.gn index eefdaf5eb02c7..538414f286569 100644 --- a/assets/BUILD.gn +++ b/assets/BUILD.gn @@ -11,10 +11,7 @@ source_set("assets") { "directory_asset_bundle.h", ] - deps = [ - "//flutter/common", - "//flutter/fml", - ] + deps = [ "//flutter/fml" ] public_configs = [ "//flutter:config" ] } diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5bb1a51273993..6a524959cdc40 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -96,6 +96,9 @@ FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc FILE: ../../../flutter/flow/mutators_stack_unittests.cc FILE: ../../../flutter/flow/paint_utils.cc FILE: ../../../flutter/flow/paint_utils.h +FILE: ../../../flutter/flow/persistent_cache.cc +FILE: ../../../flutter/flow/persistent_cache.h +FILE: ../../../flutter/flow/persistent_cache_unittests.cc FILE: ../../../flutter/flow/raster_cache.cc FILE: ../../../flutter/flow/raster_cache.h FILE: ../../../flutter/flow/raster_cache_key.cc @@ -607,9 +610,6 @@ FILE: ../../../flutter/shell/common/fixtures/shelltest_screenshot.png FILE: ../../../flutter/shell/common/input_events_unittests.cc FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h -FILE: ../../../flutter/shell/common/persistent_cache.cc -FILE: ../../../flutter/shell/common/persistent_cache.h -FILE: ../../../flutter/shell/common/persistent_cache_unittests.cc FILE: ../../../flutter/shell/common/pipeline.cc FILE: ../../../flutter/shell/common/pipeline.h FILE: ../../../flutter/shell/common/pipeline_unittests.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 89647c7189784..38d37d6e64871 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -54,6 +54,8 @@ source_set_maybe_fuchsia_legacy("flow") { "matrix_decomposition.h", "paint_utils.cc", "paint_utils.h", + "persistent_cache.cc", + "persistent_cache.h", "raster_cache.cc", "raster_cache.h", "raster_cache_key.cc", @@ -73,8 +75,9 @@ source_set_maybe_fuchsia_legacy("flow") { public_configs = [ "//flutter:config" ] deps = [ - "//flutter/common", + "//flutter/assets", "//flutter/fml", + "//third_party/rapidjson", "//third_party/skia", ] @@ -151,6 +154,7 @@ if (enable_unittests) { "layers/transform_layer_unittests.cc", "matrix_decomposition_unittests.cc", "mutators_stack_unittests.cc", + "persistent_cache_unittests.cc", "raster_cache_unittests.cc", "rtree_unittests.cc", "skia_gpu_object_unittests.cc", @@ -161,6 +165,7 @@ if (enable_unittests) { deps = [ ":flow_fixtures", + "//flutter/assets", "//flutter/fml", "//flutter/testing:skia", "//flutter/testing:testing_lib", diff --git a/shell/common/persistent_cache.cc b/flow/persistent_cache.cc similarity index 85% rename from shell/common/persistent_cache.cc rename to flow/persistent_cache.cc index c1ec498398dbb..a9fe0010203e5 100644 --- a/shell/common/persistent_cache.cc +++ b/flow/persistent_cache.cc @@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/shell/common/persistent_cache.h" +#include "flutter/flow/persistent_cache.h" #include #include #include #include -#include "rapidjson/document.h" -#include "third_party/skia/include/utils/SkBase64.h" - #include "flutter/fml/base32.h" #include "flutter/fml/file.h" #include "flutter/fml/logging.h" @@ -19,11 +16,13 @@ #include "flutter/fml/mapping.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/version/version.h" +#include "third_party/rapidjson/include/rapidjson/document.h" +#include "third_party/skia/include/utils/SkBase64.h" namespace flutter { std::string PersistentCache::cache_base_path_; +std::string PersistentCache::cache_version_; std::shared_ptr PersistentCache::asset_manager_; @@ -69,16 +68,26 @@ PersistentCache* PersistentCache::GetCacheForProcess() { return gPersistentCache.get(); } +void PersistentCache::ClearCacheForProcess() { + std::scoped_lock lock(instance_mutex_); + gPersistentCache.reset(nullptr); + strategy_set_ = false; +} + void PersistentCache::ResetCacheForProcess() { std::scoped_lock lock(instance_mutex_); gPersistentCache.reset(new PersistentCache(gIsReadOnly)); strategy_set_ = false; } -void PersistentCache::SetCacheDirectoryPath(std::string path) { +void PersistentCache::SetCacheDirectoryPath(const std::string& path) { cache_base_path_ = path; } +void PersistentCache::SetCacheVersion(const std::string& version) { + cache_version_ = version; +} + bool PersistentCache::Purge() { // Make sure that this is called after the worker task runner setup so all the // file system modifications would happen on that single thread to avoid @@ -100,31 +109,29 @@ bool PersistentCache::Purge() { namespace { -constexpr char kEngineComponent[] = "flutter_engine"; - -static void FreeOldCacheDirectory(const fml::UniqueFD& cache_base_dir) { - fml::UniqueFD engine_dir = - fml::OpenDirectoryReadOnly(cache_base_dir, kEngineComponent); - if (!engine_dir.is_valid()) { - return; - } - fml::VisitFiles(engine_dir, [](const fml::UniqueFD& directory, - const std::string& filename) { - if (filename != GetFlutterEngineVersion()) { - auto dir = fml::OpenDirectory(directory, filename.c_str(), false, - fml::FilePermission::kReadWrite); - if (dir.is_valid()) { - fml::RemoveDirectoryRecursively(directory, filename.c_str()); - } - } - return true; - }); +static void FreeOldCacheDirectory(const fml::UniqueFD& cache_base_dir, + const std::string& cache_version) { + fml::VisitFiles( + cache_base_dir, [cache_version](const fml::UniqueFD& directory, + const std::string& filename) { + if (filename != cache_version) { + auto dir = fml::OpenDirectory(directory, filename.c_str(), false, + fml::FilePermission::kReadWrite); + if (dir.is_valid()) { + fml::RemoveDirectoryRecursively(directory, filename.c_str()); + } + } + return true; + }); } static std::shared_ptr MakeCacheDirectory( const std::string& global_cache_base_path, + const std::string& cache_version, bool read_only, bool cache_sksl) { + FML_CHECK(cache_version.length()) << "Cache version unspecified"; + fml::UniqueFD cache_base_dir; if (global_cache_base_path.length()) { cache_base_dir = fml::OpenDirectory(global_cache_base_path.c_str(), false, @@ -134,9 +141,8 @@ static std::shared_ptr MakeCacheDirectory( } if (cache_base_dir.is_valid()) { - FreeOldCacheDirectory(cache_base_dir); - std::vector components = { - kEngineComponent, GetFlutterEngineVersion(), "skia", GetSkiaVersion()}; + FreeOldCacheDirectory(cache_base_dir, cache_version); + std::vector components = {cache_version}; if (cache_sksl) { components.push_back(PersistentCache::kSkSLSubdirName); } @@ -198,9 +204,9 @@ std::vector PersistentCache::LoadSkSLs() { mapping = asset_manager_->GetAsMapping(kAssetFileName); } if (mapping == nullptr) { - FML_LOG(INFO) << "No sksl asset found."; + FML_DLOG(INFO) << "No sksl asset found."; } else { - FML_LOG(INFO) << "Found sksl asset. Loading SkSLs from it..."; + FML_DLOG(INFO) << "Found sksl asset. Loading SkSLs from it..."; rapidjson::Document json_doc; rapidjson::ParseResult parse_result = json_doc.Parse(reinterpret_cast(mapping->GetMapping()), @@ -225,9 +231,14 @@ std::vector PersistentCache::LoadSkSLs() { PersistentCache::PersistentCache(bool read_only) : is_read_only_(read_only), - cache_directory_(MakeCacheDirectory(cache_base_path_, read_only, false)), - sksl_cache_directory_( - MakeCacheDirectory(cache_base_path_, read_only, true)) { + cache_directory_(MakeCacheDirectory(cache_base_path_, + cache_version_, + read_only, + false)), + sksl_cache_directory_(MakeCacheDirectory(cache_base_path_, + cache_version_, + read_only, + true)) { if (!IsValid()) { FML_LOG(WARNING) << "Could not acquire the persistent cache directory. " "Caching of GPU resources on disk is disabled."; @@ -341,7 +352,7 @@ void PersistentCache::DumpSkp(const SkData& data) { auto ticks = fml::TimePoint::Now().ToEpochDelta().ToNanoseconds(); name_stream << "shader_dump_" << std::to_string(ticks) << ".skp"; std::string file_name = name_stream.str(); - FML_LOG(INFO) << "Dumping " << file_name; + FML_DLOG(INFO) << "Dumping " << file_name; auto mapping = std::make_unique( std::vector{data.bytes(), data.bytes() + data.size()}); PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_, diff --git a/shell/common/persistent_cache.h b/flow/persistent_cache.h similarity index 89% rename from shell/common/persistent_cache.h rename to flow/persistent_cache.h index 905eb9b55faf4..233035f7aa5f4 100644 --- a/shell/common/persistent_cache.h +++ b/flow/persistent_cache.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 FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ -#define FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ +#ifndef FLUTTER_FLOW_PERSISTENT_CACHE_H_ +#define FLUTTER_FLOW_PERSISTENT_CACHE_H_ #include #include @@ -31,11 +31,16 @@ class PersistentCache : public GrContextOptions::PersistentCache { static bool gIsReadOnly; static PersistentCache* GetCacheForProcess(); - static void ResetCacheForProcess(); + static void ClearCacheForProcess(); + static void ResetCacheForProcess(); // Rec-creates the cache. // This must be called before |GetCacheForProcess|. Otherwise, it won't // affect the cache directory returned by |GetCacheForProcess|. - static void SetCacheDirectoryPath(std::string path); + static void SetCacheDirectoryPath(const std::string& path); + + // This must be called before |GetCacheForProcess|. Otherwise, it won't + // affect the cache directory returned by |GetCacheForProcess|. + static void SetCacheVersion(const std::string& version); // Convert a binary SkData key into a Base32 encoded string. // @@ -83,6 +88,7 @@ class PersistentCache : public GrContextOptions::PersistentCache { private: static std::string cache_base_path_; + static std::string cache_version_; static std::shared_ptr asset_manager_; @@ -125,4 +131,4 @@ class PersistentCache : public GrContextOptions::PersistentCache { } // namespace flutter -#endif // FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ +#endif // FLUTTER_FLOW_PERSISTENT_CACHE_H_ diff --git a/flow/persistent_cache_unittests.cc b/flow/persistent_cache_unittests.cc new file mode 100644 index 0000000000000..63a6bbbf4c9c4 --- /dev/null +++ b/flow/persistent_cache_unittests.cc @@ -0,0 +1,227 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include + +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/flow/persistent_cache.h" +#include "flutter/fml/file.h" +#include "flutter/fml/log_settings.h" +#include "flutter/fml/paths.h" +#include "flutter/fml/unique_fd.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { +namespace { + +constexpr char kTestCacheVersion[] = "PersistentCacheTest"; +constexpr char kTestCacheVersionOld[] = "PersistentCacheTest_old"; + +void CheckTextSkData(sk_sp data, const std::string& expected) { + std::string data_string(reinterpret_cast(data->bytes()), + data->size()); + EXPECT_EQ(data_string, expected); +} + +} // namespace + +#ifndef NDEBUG +TEST(PersistentCacheTest, NoCacheVersionDies) { + PersistentCache::SetCacheVersion(""); + EXPECT_DEATH_IF_SUPPORTED(PersistentCache::ResetCacheForProcess(), + "Cache version unspecified"); +} +#endif + +TEST(PersistentCacheTest, DefaultCacheDirIsValid) { + // Linux / Windows do not suport the default cache dir. +#if defined(OS_LINUX) || defined(OS_WINDOWS) + GTEST_SKIP(); +#endif + + fml::UniqueFD base_dir = fml::paths::GetCachesDirectory(); + ASSERT_TRUE(base_dir.is_valid()); + + PersistentCache::SetCacheDirectoryPath(""); + PersistentCache::SetCacheVersion(kTestCacheVersion); + PersistentCache::ResetCacheForProcess(); + + auto current_dir = fml::OpenDirectoryReadOnly(base_dir, kTestCacheVersion); + EXPECT_TRUE(current_dir.is_valid()); + + // Cleanup + fml::RemoveFilesInDirectory(base_dir); +} + +TEST(PersistentCacheTest, OverrideCacheDirIsValid) { + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + + PersistentCache::SetCacheDirectoryPath(base_dir.path()); + PersistentCache::SetCacheVersion(kTestCacheVersion); + PersistentCache::ResetCacheForProcess(); + + auto current_dir = + fml::OpenDirectoryReadOnly(base_dir.fd(), kTestCacheVersion); + EXPECT_TRUE(current_dir.is_valid()); +} + +TEST(PersistentCacheTest, CanRemoveOldPersistentCache) { + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + + // Create the "old cache directory" before initializing the cache. + auto old_created = fml::CreateDirectory(base_dir.fd(), {kTestCacheVersionOld}, + fml::FilePermission::kReadWrite); + EXPECT_TRUE(old_created.is_valid()); + + // Initializing the cache should remove the old directory and create the new + // one. + PersistentCache::SetCacheDirectoryPath(base_dir.path()); + PersistentCache::SetCacheVersion(kTestCacheVersion); + PersistentCache::ResetCacheForProcess(); + + auto current_dir = + fml::OpenDirectoryReadOnly(base_dir.fd(), kTestCacheVersion); + auto old_dir = + fml::OpenDirectoryReadOnly(base_dir.fd(), kTestCacheVersionOld); + EXPECT_TRUE(current_dir.is_valid()); + EXPECT_FALSE(old_dir.is_valid()); + + // Ensure that only the current cache version remains. + fml::VisitFiles(base_dir.fd(), [](const fml::UniqueFD& directory, + const std::string& filename) { + EXPECT_EQ(strcmp(filename.c_str(), kTestCacheVersion), 0); + return true; + }); +} + +TEST(PersistentCacheTest, CanLoadSkSLsFromCacheDir) { + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + + auto sksl_dir = fml::CreateDirectory( + base_dir.fd(), {kTestCacheVersion, PersistentCache::kSkSLSubdirName}, + fml::FilePermission::kReadWrite); + const std::string x = "x"; + const std::string y = "y"; + auto x_data = std::make_unique( + std::vector{x.begin(), x.end()}); + auto y_data = std::make_unique( + std::vector{y.begin(), y.end()}); + ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "IE", *x_data)); + ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "II", *y_data)); + + PersistentCache::SetCacheDirectoryPath(base_dir.path()); + PersistentCache::SetCacheVersion(kTestCacheVersion); + PersistentCache::ResetCacheForProcess(); + + // Test that the cache can load the SkSLs. + { + auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + EXPECT_EQ(shaders.size(), 2u); + + // Make sure that the 2 shaders are sorted by their keys. Their keys should + // be "A" and "B" (decoded from "II" and "IE"). + if (shaders[0].first->bytes()[0] == 'B') { + std::swap(shaders[0], shaders[1]); + } + + CheckTextSkData(shaders[0].first, "A"); + CheckTextSkData(shaders[1].first, "B"); + CheckTextSkData(shaders[0].second, "x"); + CheckTextSkData(shaders[1].second, "y"); + } +} + +TEST(PersistentCacheTest, CanLoadSkSLsFromAsset) { + // Temp dir for the assets. + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + + PersistentCache::SetCacheDirectoryPath(base_dir.path()); + PersistentCache::SetCacheVersion(kTestCacheVersion); + PersistentCache::ResetCacheForProcess(); + + // The SkSL key is Base32 encoded. "IE" is the encoding of "A" and "II" is the + // encoding of "B". + // + // The SkSL data is Base64 encoded. "eA==" is the encoding of "x" and "eQ==" + // is the encoding of "y". + const std::string kTestJson = + "{\n" + " \"data\": {\n" + " \"IE\": \"eA==\",\n" + " \"II\": \"eQ==\"\n" + " }\n" + "}\n"; + + auto data = std::make_unique( + std::vector{kTestJson.begin(), kTestJson.end()}); + fml::WriteAtomically(base_dir.fd(), PersistentCache::kAssetFileName, *data); + + // Reset the asset manager and ensure no SkSLs are loaded. + PersistentCache::SetAssetManager(nullptr); + EXPECT_EQ(PersistentCache::GetCacheForProcess()->LoadSkSLs().size(), 0u); + + auto asset_manager = std::make_shared(); + asset_manager->PushBack( + std::make_unique(fml::OpenDirectory( + base_dir.path().c_str(), false, fml::FilePermission::kRead))); + PersistentCache::SetAssetManager(asset_manager); + + // Test that the cache can load the SkSLs. + { + auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + EXPECT_EQ(shaders.size(), 2u); + + // Make sure that the 2 shaders are sorted by their keys. Their keys should + // be "A" and "B" (decoded from "II" and "IE"). + if (shaders[0].first->bytes()[0] == 'B') { + std::swap(shaders[0], shaders[1]); + } + + CheckTextSkData(shaders[0].first, "A"); + CheckTextSkData(shaders[1].first, "B"); + CheckTextSkData(shaders[0].second, "x"); + CheckTextSkData(shaders[1].second, "y"); + } +} + +// TODO() +// TEST_F(PersistentcacheTest, CanPurgePersistentCache) { +// fml::ScopedTemporaryDirectory base_dir; +// ASSERT_TRUE(base_dir.fd().is_valid()); + +// auto cache_dir = fml::CreateDirectory( +// base_dir.fd(), +// {"flutter_engine", GetFlutterEngineVersion(), "skia", +// GetSkiaVersion()}, fml::FilePermission::kReadWrite); +// PersistentCache::SetCacheDirectoryPath(base_dir.path()); +// PersistentCache::ResetCacheForProcess(); + +// // Generate a dummy persistent cache. +// fml::DataMapping test_data(std::string("test")); +// ASSERT_TRUE(fml::WriteAtomically(cache_dir, "test", test_data)); +// auto file = fml::OpenFileReadOnly(cache_dir, "test"); +// ASSERT_TRUE(file.is_valid()); + +// // Run engine with purge_persistent_cache to remove the dummy cache. +// auto settings = CreateSettingsForFixture(); +// settings.purge_persistent_cache = true; +// auto config = RunConfiguration::InferFromSettings(settings); +// std::unique_ptr shell = CreateShell(settings); +// RunEngine(shell.get(), std::move(config)); + +// // Verify that the dummy is purged. +// file = fml::OpenFileReadOnly(cache_dir, "test"); +// ASSERT_FALSE(file.is_valid()); +// } + +} // namespace testing +} // namespace flutter diff --git a/fml/file.cc b/fml/file.cc index b96bfe6341453..0ea48189e44b8 100644 --- a/fml/file.cc +++ b/fml/file.cc @@ -44,10 +44,11 @@ fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, return CreateDirectory(base_directory, components, permission, 0); } -ScopedTemporaryDirectory::ScopedTemporaryDirectory() - : path_(CreateTemporaryDirectory()) { - if (path_ != "") { - dir_fd_ = OpenDirectory(path_.c_str(), false, FilePermission::kRead); +ScopedTemporaryDirectory::ScopedTemporaryDirectory() { + auto [path, dir_fd] = CreateTemporaryDirectory(); + if (path != "" && dir_fd.is_valid()) { + path_ = std::move(path); + dir_fd_ = std::move(dir_fd); } } diff --git a/fml/file.h b/fml/file.h index 0b9ac97e23dad..49005d653ca9c 100644 --- a/fml/file.h +++ b/fml/file.h @@ -27,7 +27,7 @@ enum class FilePermission { kReadWrite, }; -std::string CreateTemporaryDirectory(); +std::pair CreateTemporaryDirectory(); /// This can open a directory on POSIX, but not on Windows. fml::UniqueFD OpenFile(const char* path, diff --git a/fml/file_unittest.cc b/fml/file_unittest.cc index bd27fb021ded7..5a02675238cd6 100644 --- a/fml/file_unittest.cc +++ b/fml/file_unittest.cc @@ -44,10 +44,7 @@ static std::string ReadStringFromFile(const fml::UniqueFD& fd) { } TEST(FileTest, CreateTemporaryAndUnlink) { - auto dir_name = fml::CreateTemporaryDirectory(); - ASSERT_NE(dir_name, ""); - auto dir = - fml::OpenDirectory(dir_name.c_str(), false, fml::FilePermission::kRead); + auto [dir_name, dir] = fml::CreateTemporaryDirectory(); ASSERT_TRUE(dir.is_valid()); dir.reset(); ASSERT_TRUE(fml::UnlinkDirectory(dir_name.c_str())); diff --git a/fml/platform/posix/file_posix.cc b/fml/platform/posix/file_posix.cc index f6a90942a9391..e154896b4bffb 100644 --- a/fml/platform/posix/file_posix.cc +++ b/fml/platform/posix/file_posix.cc @@ -20,13 +20,20 @@ namespace fml { -std::string CreateTemporaryDirectory() { +std::pair CreateTemporaryDirectory() { char directory_name[] = "/tmp/flutter_XXXXXXXX"; - auto* result = ::mkdtemp(directory_name); - if (result == nullptr) { - return ""; + auto* path = ::mkdtemp(directory_name); + if (path == nullptr) { + return std::make_pair("", fml::UniqueFD{}); + } + + auto dir_fd = OpenDirectory(path, false, FilePermission::kRead); + if (!dir_fd.is_valid()) { + FML_DLOG(ERROR) << "Could not get temporary directory FD at " << path; + return std::make_pair("", fml::UniqueFD{}); } - return {result}; + + return std::make_pair(path, std::move(dir_fd)); } static int ToPosixAccessFlags(FilePermission permission) { diff --git a/fml/platform/win/file_win.cc b/fml/platform/win/file_win.cc index 0b52c37fa6f79..2c7b1a5d3de1f 100644 --- a/fml/platform/win/file_win.cc +++ b/fml/platform/win/file_win.cc @@ -85,12 +85,12 @@ static DWORD GetFileAttributesForUtf8Path(const fml::UniqueFD& base_directory, return GetFileAttributesForUtf8Path(full_path.c_str()); } -std::string CreateTemporaryDirectory() { +std::pair CreateTemporaryDirectory() { // Get the system temporary directory. auto temp_dir_container = GetTemporaryDirectoryPath(); if (temp_dir_container.size() == 0) { FML_DLOG(ERROR) << "Could not get system temporary directory."; - return {}; + return std::make_pair("", fml::UniqueFD{}); } // Create a UUID. @@ -98,14 +98,14 @@ std::string CreateTemporaryDirectory() { RPC_STATUS status = UuidCreateSequential(&uuid); if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) { FML_DLOG(ERROR) << "Could not create UUID"; - return {}; + return std::make_pair("", fml::UniqueFD{}); } RPC_WSTR uuid_string; status = UuidToString(&uuid, &uuid_string); if (status != RPC_S_OK) { FML_DLOG(ERROR) << "Could not create UUID to string."; - return {}; + return std::make_pair("", fml::UniqueFD{}); } std::wstring uuid_str(reinterpret_cast(uuid_string)); @@ -122,10 +122,11 @@ std::string CreateTemporaryDirectory() { if (!dir_fd.is_valid()) { FML_DLOG(ERROR) << "Could not get temporary directory FD. " << GetLastErrorMessage(); - return {}; + return std::make_pair("", fml::UniqueFD{}); } - return WideStringToString(std::move(temp_dir)); + return std::make_pair(WideStringToString(std::move(temp_dir)), + std::move(dir_fd)); } fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 941db5a8a83b5..81c9142251754 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -68,8 +68,6 @@ source_set_maybe_fuchsia_legacy("common") { "engine.h", "isolate_configuration.cc", "isolate_configuration.h", - "persistent_cache.cc", - "persistent_cache.h", "pipeline.cc", "pipeline.h", "platform_view.cc", @@ -247,13 +245,13 @@ if (enable_unittests) { } public_deps_legacy_and_next = [ + ":shell_unittests_gpu_configuration", "//flutter/shell/common:common", "//flutter/flow:flow", "//flutter/runtime:runtime", ] deps_legacy_and_next = [ - ":shell_unittests_gpu_configuration", "//flutter/lib/ui:ui", "//flutter/testing:dart", "//flutter/testing:fixture_test", @@ -268,7 +266,6 @@ if (enable_unittests) { "canvas_spy_unittests.cc", "engine_unittests.cc", "input_events_unittests.cc", - "persistent_cache_unittests.cc", "pipeline_unittests.cc", "shell_unittests.cc", "skp_shader_warmup_unittests.cc", diff --git a/shell/common/persistent_cache_unittests.cc b/shell/common/persistent_cache_unittests.cc deleted file mode 100644 index 4ec0506043a88..0000000000000 --- a/shell/common/persistent_cache_unittests.cc +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "flutter/assets/directory_asset_bundle.h" -#include "flutter/flow/layers/container_layer.h" -#include "flutter/flow/layers/layer.h" -#include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/flow/layers/picture_layer.h" -#include "flutter/fml/command_line.h" -#include "flutter/fml/file.h" -#include "flutter/fml/log_settings.h" -#include "flutter/fml/unique_fd.h" -#include "flutter/shell/common/persistent_cache.h" -#include "flutter/shell/common/shell_test.h" -#include "flutter/shell/common/switches.h" -#include "flutter/shell/version/version.h" -#include "flutter/testing/testing.h" -#include "include/core/SkPicture.h" - -namespace flutter { -namespace testing { - -static void WaitForIO(Shell* shell) { - std::promise io_task_finished; - shell->GetTaskRunners().GetIOTaskRunner()->PostTask( - [&io_task_finished]() { io_task_finished.set_value(true); }); - io_task_finished.get_future().wait(); -} - -TEST_F(ShellTest, CacheSkSLWorks) { - // Create a temp dir to store the persistent cache - fml::ScopedTemporaryDirectory dir; - PersistentCache::SetCacheDirectoryPath(dir.path()); - PersistentCache::ResetCacheForProcess(); - - auto settings = CreateSettingsForFixture(); - settings.cache_sksl = true; - settings.dump_skp_on_shader_compilation = true; - - fml::AutoResetWaitableEvent firstFrameLatch; - settings.frame_rasterized_callback = - [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); }; - - auto sksl_config = RunConfiguration::InferFromSettings(settings); - sksl_config.SetEntrypoint("emptyMain"); - std::unique_ptr shell = CreateShell(settings); - PlatformViewNotifyCreated(shell.get()); - RunEngine(shell.get(), std::move(sksl_config)); - - // Initially, we should have no SkSL cache - auto cache = PersistentCache::GetCacheForProcess()->LoadSkSLs(); - ASSERT_EQ(cache.size(), 0u); - - // Draw something to trigger shader compilations. - LayerTreeBuilder builder = [](std::shared_ptr root) { - SkPath path; - path.addCircle(50, 50, 20); - auto physical_shape_layer = std::make_shared( - SK_ColorRED, SK_ColorBLUE, 1.0f, path, Clip::antiAlias); - root->Add(physical_shape_layer); - }; - PumpOneFrame(shell.get(), 100, 100, builder); - firstFrameLatch.Wait(); - WaitForIO(shell.get()); - - // Some skp should be dumped due to shader compilations. - int skp_count = 0; - fml::FileVisitor skp_visitor = [&skp_count](const fml::UniqueFD& directory, - const std::string& filename) { - if (filename.size() >= 4 && - filename.substr(filename.size() - 4, 4) == ".skp") { - skp_count += 1; - } - return true; - }; - fml::VisitFilesRecursively(dir.fd(), skp_visitor); - ASSERT_GT(skp_count, 0); - - // SkSL cache should be generated by the last run. - cache = PersistentCache::GetCacheForProcess()->LoadSkSLs(); - ASSERT_GT(cache.size(), 0u); - - // Run the engine again with cache_sksl = false and check that the previously - // generated SkSL cache is used for precompile. - PersistentCache::ResetCacheForProcess(); - settings.cache_sksl = false; - settings.dump_skp_on_shader_compilation = true; - auto normal_config = RunConfiguration::InferFromSettings(settings); - normal_config.SetEntrypoint("emptyMain"); - DestroyShell(std::move(shell)); - shell = CreateShell(settings); - PlatformViewNotifyCreated(shell.get()); - RunEngine(shell.get(), std::move(normal_config)); - firstFrameLatch.Reset(); - PumpOneFrame(shell.get(), 100, 100, builder); - firstFrameLatch.Wait(); - WaitForIO(shell.get()); - -// Shader precompilation from SKSL is not implemented on the Skia Vulkan -// backend so don't run the second half of this test on Vulkan. This can get -// removed if SKSL precompilation is implemented in the Skia Vulkan backend. -#if !defined(SHELL_ENABLE_VULKAN) - // To check that all shaders are precompiled, verify that no new skp is dumped - // due to shader compilations. - int old_skp_count = skp_count; - skp_count = 0; - fml::VisitFilesRecursively(dir.fd(), skp_visitor); - ASSERT_EQ(skp_count, old_skp_count); -#endif // !defined(SHELL_ENABLE_VULKAN) - - // Remove all files generated - fml::FileVisitor remove_visitor = [&remove_visitor]( - const fml::UniqueFD& directory, - const std::string& filename) { - if (fml::IsDirectory(directory, filename.c_str())) { - { // To trigger fml::~UniqueFD before fml::UnlinkDirectory - fml::UniqueFD sub_dir = - fml::OpenDirectoryReadOnly(directory, filename.c_str()); - fml::VisitFiles(sub_dir, remove_visitor); - } - fml::UnlinkDirectory(directory, filename.c_str()); - } else { - fml::UnlinkFile(directory, filename.c_str()); - } - return true; - }; - fml::VisitFiles(dir.fd(), remove_visitor); - DestroyShell(std::move(shell)); -} - -static void CheckTextSkData(sk_sp data, const std::string& expected) { - std::string data_string(reinterpret_cast(data->bytes()), - data->size()); - ASSERT_EQ(data_string, expected); -} - -static void ResetAssetManager() { - PersistentCache::SetAssetManager(nullptr); - ASSERT_EQ(PersistentCache::GetCacheForProcess()->LoadSkSLs().size(), 0u); -} - -static void CheckTwoSkSLsAreLoaded() { - auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs(); - ASSERT_EQ(shaders.size(), 2u); -} - -TEST_F(ShellTest, CanLoadSkSLsFromAsset) { - // Avoid polluting unit tests output by hiding INFO level logging. - fml::LogSettings warning_only = {fml::LOG_WARNING}; - fml::ScopedSetLogSettings scoped_set_log_settings(warning_only); - - // The SkSL key is Base32 encoded. "IE" is the encoding of "A" and "II" is the - // encoding of "B". - // - // The SkSL data is Base64 encoded. "eA==" is the encoding of "x" and "eQ==" - // is the encoding of "y". - const std::string kTestJson = - "{\n" - " \"data\": {\n" - " \"IE\": \"eA==\",\n" - " \"II\": \"eQ==\"\n" - " }\n" - "}\n"; - - // Temp dir for the asset. - fml::ScopedTemporaryDirectory asset_dir; - - auto data = std::make_unique( - std::vector{kTestJson.begin(), kTestJson.end()}); - fml::WriteAtomically(asset_dir.fd(), PersistentCache::kAssetFileName, *data); - - // 1st, test that RunConfiguration::InferFromSettings sets the asset manager. - ResetAssetManager(); - auto settings = CreateSettingsForFixture(); - settings.assets_path = asset_dir.path(); - RunConfiguration::InferFromSettings(settings); - CheckTwoSkSLsAreLoaded(); - - // 2nd, test that the RunConfiguration constructor sets the asset manager. - // (Android is directly calling that constructor without InferFromSettings.) - ResetAssetManager(); - auto asset_manager = std::make_shared(); - RunConfiguration config(nullptr, asset_manager); - asset_manager->PushBack( - std::make_unique(fml::OpenDirectory( - asset_dir.path().c_str(), false, fml::FilePermission::kRead))); - CheckTwoSkSLsAreLoaded(); - - // 3rd, test the content of the SkSLs in the asset. - { - auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs(); - ASSERT_EQ(shaders.size(), 2u); - - // Make sure that the 2 shaders are sorted by their keys. Their keys should - // be "A" and "B" (decoded from "II" and "IE"). - if (shaders[0].first->bytes()[0] == 'B') { - std::swap(shaders[0], shaders[1]); - } - - CheckTextSkData(shaders[0].first, "A"); - CheckTextSkData(shaders[1].first, "B"); - CheckTextSkData(shaders[0].second, "x"); - CheckTextSkData(shaders[1].second, "y"); - } - - // Cleanup. - fml::UnlinkFile(asset_dir.fd(), PersistentCache::kAssetFileName); -} - -TEST_F(ShellTest, CanRemoveOldPersistentCache) { - fml::ScopedTemporaryDirectory base_dir; - ASSERT_TRUE(base_dir.fd().is_valid()); - - fml::CreateDirectory(base_dir.fd(), - {"flutter_engine", GetFlutterEngineVersion(), "skia"}, - fml::FilePermission::kReadWrite); - - constexpr char kOldEngineVersion[] = "old"; - auto old_created = fml::CreateDirectory( - base_dir.fd(), {"flutter_engine", kOldEngineVersion, "skia"}, - fml::FilePermission::kReadWrite); - ASSERT_TRUE(old_created.is_valid()); - - PersistentCache::SetCacheDirectoryPath(base_dir.path()); - PersistentCache::ResetCacheForProcess(); - - auto engine_dir = fml::OpenDirectoryReadOnly(base_dir.fd(), "flutter_engine"); - auto current_dir = - fml::OpenDirectoryReadOnly(engine_dir, GetFlutterEngineVersion()); - auto old_dir = fml::OpenDirectoryReadOnly(engine_dir, kOldEngineVersion); - - ASSERT_TRUE(engine_dir.is_valid()); - ASSERT_TRUE(current_dir.is_valid()); - ASSERT_FALSE(old_dir.is_valid()); - - // Cleanup - fml::RemoveFilesInDirectory(base_dir.fd()); -} - -TEST_F(ShellTest, CanPurgePersistentCache) { - fml::ScopedTemporaryDirectory base_dir; - ASSERT_TRUE(base_dir.fd().is_valid()); - auto cache_dir = fml::CreateDirectory( - base_dir.fd(), - {"flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion()}, - fml::FilePermission::kReadWrite); - PersistentCache::SetCacheDirectoryPath(base_dir.path()); - PersistentCache::ResetCacheForProcess(); - - // Generate a dummy persistent cache. - fml::DataMapping test_data(std::string("test")); - ASSERT_TRUE(fml::WriteAtomically(cache_dir, "test", test_data)); - auto file = fml::OpenFileReadOnly(cache_dir, "test"); - ASSERT_TRUE(file.is_valid()); - - // Run engine with purge_persistent_cache to remove the dummy cache. - auto settings = CreateSettingsForFixture(); - settings.purge_persistent_cache = true; - auto config = RunConfiguration::InferFromSettings(settings); - std::unique_ptr shell = CreateShell(settings); - RunEngine(shell.get(), std::move(config)); - - // Verify that the dummy is purged. - file = fml::OpenFileReadOnly(cache_dir, "test"); - ASSERT_FALSE(file.is_valid()); - - // Cleanup - fml::RemoveFilesInDirectory(base_dir.fd()); - DestroyShell(std::move(shell)); -} - -} // namespace testing -} // namespace flutter diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 709d5495ba626..d5d8b5f5d6fab 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -7,9 +7,9 @@ #include +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/time/time_delta.h" #include "flutter/fml/time/time_point.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/serialization_callbacks.h" #include "third_party/skia/include/core/SkEncodedImageFormat.h" #include "third_party/skia/include/core/SkImageEncoder.h" diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc index 8f0966b6bc896..0d11d80918c85 100644 --- a/shell/common/run_configuration.cc +++ b/shell/common/run_configuration.cc @@ -7,10 +7,10 @@ #include #include "flutter/assets/directory_asset_bundle.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/file.h" #include "flutter/fml/unique_fd.h" #include "flutter/runtime/dart_vm.h" -#include "flutter/shell/common/persistent_cache.h" namespace flutter { diff --git a/shell/common/shell.cc b/shell/common/shell.cc index d11a14f06b4e2..77b218641ee33 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -11,6 +11,7 @@ #include #include "flutter/assets/directory_asset_bundle.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/file.h" #include "flutter/fml/icu_util.h" #include "flutter/fml/log_settings.h" @@ -22,13 +23,13 @@ #include "flutter/fml/unique_fd.h" #include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/engine.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/vsync_waiter.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/writer.h" +#include "flutter/shell/version/version.h" #include "third_party/dart/runtime/include/dart_tools_api.h" +#include "third_party/rapidjson/include/rapidjson/stringbuffer.h" +#include "third_party/rapidjson/include/rapidjson/writer.h" #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/skia/include/utils/SkBase64.h" #include "third_party/tonic/common/log.h" @@ -256,14 +257,10 @@ std::unique_ptr Shell::Create( Settings settings, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer) { - PerformInitializationTasks(settings); - PersistentCache::SetCacheSkSL(settings.cache_sksl); - TRACE_EVENT0("flutter", "Shell::Create"); auto vm = DartVMRef::Create(settings); FML_CHECK(vm) << "Must be able to initialize the VM."; - auto vm_data = vm->GetVMData(); return Shell::Create(std::move(task_runners), // @@ -284,9 +281,6 @@ std::unique_ptr Shell::Create( const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer, DartVMRef vm) { - PerformInitializationTasks(settings); - PersistentCache::SetCacheSkSL(settings.cache_sksl); - TRACE_EVENT0("flutter", "Shell::CreateWithSnapshots"); if (!task_runners.IsValid() || !on_create_platform_view || @@ -294,6 +288,8 @@ std::unique_ptr Shell::Create( return nullptr; } + PerformInitializationTasks(settings); + fml::AutoResetWaitableEvent latch; std::unique_ptr shell; fml::TaskRunner::RunNowOrPostTask( @@ -342,8 +338,21 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) std::make_unique>(this); })); - // Install service protocol handlers. + // Configure the PersistentCache. + std::stringstream version; + version << "engine-" << GetFlutterEngineVersion() << "_skia-" + << GetSkiaVersion(); + PersistentCache::SetCacheVersion(version.str()); + PersistentCache::SetCacheSkSL(settings_.cache_sksl); + PersistentCache::GetCacheForProcess()->SetIsDumpingSkp( + settings_.dump_skp_on_shader_compilation); + PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( + task_runners_.GetIOTaskRunner()); + if (settings_.purge_persistent_cache) { + PersistentCache::GetCacheForProcess()->Purge(); + } + // Install service protocol handlers. service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName] = { task_runners_.GetRasterTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshot, this, @@ -560,16 +569,6 @@ bool Shell::Setup(std::unique_ptr platform_view, vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription()); - PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( - task_runners_.GetIOTaskRunner()); - - PersistentCache::GetCacheForProcess()->SetIsDumpingSkp( - settings_.dump_skp_on_shader_compilation); - - if (settings_.purge_persistent_cache) { - PersistentCache::GetCacheForProcess()->Purge(); - } - // TODO(gw280): The WeakPtr here asserts that we are derefing it on the // same thread as it was created on. Shell is constructed on the platform // thread but we need to call into the Engine on the UI thread, so we need diff --git a/shell/common/shell_io_manager.cc b/shell/common/shell_io_manager.cc index 3025c5c3c6d89..c890339e04272 100644 --- a/shell/common/shell_io_manager.cc +++ b/shell/common/shell_io_manager.cc @@ -4,9 +4,9 @@ #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/build_config.h" #include "flutter/fml/message_loop.h" -#include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace flutter { diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index e296ba118c73e..9ea48206ab821 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -9,11 +9,11 @@ #include "flutter/common/settings.h" #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/build_config.h" #include "flutter/fml/macros.h" #include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/window/platform_message.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/shell_test_external_view_embedder.h" diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 57372e5d25077..4d4fb1b273693 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "flutter/shell/common/shell_test_platform_view_vulkan.h" -#include "flutter/shell/common/persistent_cache.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/vulkan/vulkan_utilities.h" namespace flutter { diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index d1ce6ecfbaf0d..79f3ea86fe64c 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -5,15 +5,18 @@ #define FML_USED_ON_EMBEDDER -#include #include +#include +#include #include #include #include +#include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/layers/physical_shape_layer.h" #include "flutter/flow/layers/picture_layer.h" -#include "flutter/flow/layers/transform_layer.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/command_line.h" #include "flutter/fml/dart/dart_converter.h" #include "flutter/fml/make_copyable.h" @@ -21,7 +24,6 @@ #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/runtime/dart_vm.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_test.h" @@ -42,8 +44,16 @@ namespace flutter { namespace testing { +namespace { -static bool ValidateShell(Shell* shell) { +void WaitForIO(Shell* shell) { + std::promise io_task_finished; + shell->GetTaskRunners().GetIOTaskRunner()->PostTask( + [&io_task_finished]() { io_task_finished.set_value(true); }); + io_task_finished.get_future().wait(); +} + +bool ValidateShell(Shell* shell) { if (!shell) { return false; } @@ -67,6 +77,8 @@ static bool ValidateShell(Shell* shell) { return true; } +} // namespace + TEST_F(ShellTest, InitializeWithInvalidThreads) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); Settings settings = CreateSettingsForFixture(); @@ -1241,19 +1253,124 @@ TEST_F(ShellTest, CanDecompressImageFromAsset) { DestroyShell(std::move(shell)); } +TEST_F(ShellTest, CacheSkSLsWorks) { + fml::ScopedTemporaryDirectory base_dir; + ASSERT_TRUE(base_dir.fd().is_valid()); + PersistentCache::ClearCacheForProcess(); + PersistentCache::SetCacheDirectoryPath(base_dir.path()); + + auto settings = CreateSettingsForFixture(); + settings.cache_sksl = true; + settings.dump_skp_on_shader_compilation = true; + fml::AutoResetWaitableEvent firstFrameLatch; + settings.frame_rasterized_callback = + [&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); }; + auto sksl_config = RunConfiguration::InferFromSettings(settings); + sksl_config.SetEntrypoint("emptyMain"); + std::unique_ptr shell = CreateShell(settings); + PlatformViewNotifyCreated(shell.get()); + RunEngine(shell.get(), std::move(sksl_config)); + + // Initially, we should have no SkSL cache + auto cache = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + EXPECT_EQ(cache.size(), 0u); + + // Draw something to trigger shader compilations. + LayerTreeBuilder builder = [](std::shared_ptr root) { + SkPath path; + path.addCircle(50, 50, 20); + auto physical_shape_layer = std::make_shared( + SK_ColorRED, SK_ColorBLUE, 1.0f, path, Clip::antiAlias); + root->Add(physical_shape_layer); + }; + + PumpOneFrame(shell.get(), 100, 100, builder); + firstFrameLatch.Wait(); + WaitForIO(shell.get()); + + // Some skp should be dumped due to shader compilations. + int skp_count = 0; + fml::FileVisitor skp_visitor = [&skp_count](const fml::UniqueFD& directory, + const std::string& filename) { + if (filename.size() >= 4 && + filename.substr(filename.size() - 4, 4) == ".skp") { + skp_count += 1; + } + return true; + }; + fml::VisitFilesRecursively(base_dir.fd(), skp_visitor); + EXPECT_GT(skp_count, 0); + + // SkSL cache should be generated by the last run. + cache = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + EXPECT_GT(cache.size(), 0u); + + // Run the engine again with cache_sksl = false and check that the previously + // generated SkSL cache is used for precompile. + PersistentCache::ClearCacheForProcess(); + settings.cache_sksl = false; + settings.dump_skp_on_shader_compilation = true; + auto normal_config = RunConfiguration::InferFromSettings(settings); + normal_config.SetEntrypoint("emptyMain"); + DestroyShell(std::move(shell)); + shell = CreateShell(settings); + PlatformViewNotifyCreated(shell.get()); + RunEngine(shell.get(), std::move(normal_config)); + firstFrameLatch.Reset(); + PumpOneFrame(shell.get(), 100, 100, builder); + firstFrameLatch.Wait(); + WaitForIO(shell.get()); + + // Shader precompilation from SKSL is not implemented on the Skia Vulkan + // backend so don't run the second half of this test on Vulkan. This can get + // removed if SKSL precompilation is implemented in the Skia Vulkan backend. +#if !defined(SHELL_ENABLE_VULKAN) + // To check that all shaders are precompiled, verify that no new skp is dumped + // due to shader compilations. + int old_skp_count = skp_count; + skp_count = 0; + fml::VisitFilesRecursively(base_dir.fd(), skp_visitor); + EXPECT_EQ(skp_count, old_skp_count); +#endif // !defined(SHELL_ENABLE_VULKAN) + + // Remove all files generated + fml::FileVisitor remove_visitor = [&remove_visitor]( + const fml::UniqueFD& directory, + const std::string& filename) { + if (fml::IsDirectory(directory, filename.c_str())) { + { + // To trigger fml::~UniqueFD before fml::UnlinkDirectory + fml::UniqueFD sub_dir = + fml::OpenDirectoryReadOnly(directory, filename.c_str()); + fml::VisitFiles(sub_dir, remove_visitor); + } + fml::UnlinkDirectory(directory, filename.c_str()); + + } else { + fml::UnlinkFile(directory, filename.c_str()); + } + return true; + }; + fml::VisitFiles(base_dir.fd(), remove_visitor); + DestroyShell(std::move(shell)); +} + TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { fml::ScopedTemporaryDirectory base_dir; ASSERT_TRUE(base_dir.fd().is_valid()); + + std::stringstream version; + version << "engine-" << GetFlutterEngineVersion() << "_skia-" + << GetSkiaVersion(); + PersistentCache::ClearCacheForProcess(); PersistentCache::SetCacheDirectoryPath(base_dir.path()); - PersistentCache::ResetCacheForProcess(); + PersistentCache::SetCacheVersion(version.str()); // Create 2 dummy SkSL cache file IE (base32 encoding of A), II (base32 // encoding of B) with content x and y. - std::vector components = { - "flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion(), - PersistentCache::kSkSLSubdirName}; - auto sksl_dir = fml::CreateDirectory(base_dir.fd(), components, - fml::FilePermission::kReadWrite); + auto sksl_dir = fml::CreateDirectory( + base_dir.fd(), {version.str(), PersistentCache::kSkSLSubdirName}, + fml::FilePermission::kReadWrite); const std::string x = "x"; const std::string y = "y"; auto x_data = std::make_unique( diff --git a/shell/common/skp_shader_warmup_unittests.cc b/shell/common/skp_shader_warmup_unittests.cc index 689f2d9322fcc..a835a1eb67c14 100644 --- a/shell/common/skp_shader_warmup_unittests.cc +++ b/shell/common/skp_shader_warmup_unittests.cc @@ -9,11 +9,11 @@ #include "flutter/flow/layers/layer.h" #include "flutter/flow/layers/physical_shape_layer.h" #include "flutter/flow/layers/picture_layer.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/command_line.h" #include "flutter/fml/file.h" #include "flutter/fml/log_settings.h" #include "flutter/fml/unique_fd.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/serialization_callbacks.h" #include "flutter/shell/common/shell_test.h" #include "flutter/shell/common/switches.h" @@ -42,8 +42,8 @@ class SkpWarmupTest : public ShellTest { void TestWarmup(const SkISize& draw_size, const LayerTreeBuilder& builder) { // Create a temp dir to store the persistent cache fml::ScopedTemporaryDirectory dir; + PersistentCache::ClearCacheForProcess(); PersistentCache::SetCacheDirectoryPath(dir.path()); - PersistentCache::ResetCacheForProcess(); auto settings = CreateSettingsForFixture(); settings.cache_sksl = true; @@ -116,8 +116,8 @@ class SkpWarmupTest : public ShellTest { // Reinitialize shell with clean cache and verify that drawing again dumps // the same number of shaders fml::RemoveFilesInDirectory(dir.fd()); - PersistentCache::ResetCacheForProcess(); DestroyShell(std::move(shell)); + PersistentCache::ClearCacheForProcess(); auto config2 = RunConfiguration::InferFromSettings(settings); config2.SetEntrypoint("emptyMain"); shell = CreateShell(settings); @@ -134,10 +134,12 @@ class SkpWarmupTest : public ShellTest { skp_count = 0; ASSERT_EQ(second_skp_count, first_skp_count); - // Reinitialize shell and draw deserialized skps to warm up shaders + // Cleanup the first shell run. fml::RemoveFilesInDirectory(dir.fd()); - PersistentCache::ResetCacheForProcess(); DestroyShell(std::move(shell)); + PersistentCache::ClearCacheForProcess(); + + // Reinitialize shell and draw deserialized skps to warm up shaders auto config3 = RunConfiguration::InferFromSettings(settings); config3.SetEntrypoint("emptyMain"); shell = CreateShell(settings); diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index c492a456bb812..7af78a3fffcc8 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -5,11 +5,11 @@ #include "gpu_surface_gl.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/base32.h" #include "flutter/fml/logging.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 891f8ed1a86e9..73db000537e30 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -14,9 +14,9 @@ #include #include "FlutterPlatformViews_Internal.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { diff --git a/shell/platform/darwin/ios/ios_context_metal.mm b/shell/platform/darwin/ios/ios_context_metal.mm index 0f7aac0e299a8..6e5afe2ba4453 100644 --- a/shell/platform/darwin/ios/ios_context_metal.mm +++ b/shell/platform/darwin/ios/ios_context_metal.mm @@ -4,8 +4,8 @@ #include "flutter/shell/platform/darwin/ios/ios_context_metal.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/logging.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/ios/ios_external_texture_metal.h" #include "third_party/skia/include/gpu/GrContextOptions.h" diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 6b3fa9ceaddbb..2f7f5cc05eb47 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -37,13 +37,13 @@ extern const intptr_t kPlatformStrongDillSize; #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/task_runners.h" +#include "flutter/flow/persistent_cache.h" #include "flutter/fml/command_line.h" #include "flutter/fml/file.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/platform/embedder/embedder.h"