diff --git a/shell/common/persistent_cache.cc b/shell/common/persistent_cache.cc index 0537905ed22..eabb072a11c 100644 --- a/shell/common/persistent_cache.cc +++ b/shell/common/persistent_cache.cc @@ -8,6 +8,9 @@ #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" @@ -103,21 +106,35 @@ static std::shared_ptr MakeCacheDirectory( } } // namespace +sk_sp ParseBase32(const std::string& input) { + std::pair decode_result = fml::Base32Decode(input); + if (!decode_result.first) { + FML_LOG(ERROR) << "Base32 can't decode: " << input; + return nullptr; + } + const std::string& data_string = decode_result.second; + return SkData::MakeWithCopy(data_string.data(), data_string.length()); +} + +sk_sp ParseBase64(const std::string& input) { + SkBase64 decoder; + auto error = decoder.decode(input.c_str(), input.length()); + if (error != SkBase64::Error::kNoError) { + FML_LOG(ERROR) << "Base64 decode error: " << error; + FML_LOG(ERROR) << "Base64 can't decode: " << input; + return nullptr; + } + return SkData::MakeWithCopy(decoder.getData(), decoder.getDataSize()); +} + std::vector PersistentCache::LoadSkSLs() { TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs"); std::vector result; fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory, const std::string& filename) { - std::pair decode_result = fml::Base32Decode(filename); - if (!decode_result.first) { - FML_LOG(ERROR) << "Base32 can't decode: " << filename; - return true; // continue to visit other files - } - const std::string& data_string = decode_result.second; - sk_sp key = - SkData::MakeWithCopy(data_string.data(), data_string.length()); + sk_sp key = ParseBase32(filename); sk_sp data = LoadFile(directory, filename); - if (data != nullptr) { + if (key != nullptr && data != nullptr) { result.push_back({key, data}); } else { FML_LOG(ERROR) << "Failed to load: " << filename; @@ -136,11 +153,29 @@ std::vector PersistentCache::LoadSkSLs() { fml::FilePermission::kRead); fml::UniqueFD sksl_asset_dir = fml::OpenDirectoryReadOnly(root_asset_dir, kSkSLSubdirName); - if (sksl_asset_dir.is_valid()) { - FML_LOG(INFO) << "Found sksl asset directory. Loading SkSLs from it..."; - fml::VisitFiles(sksl_asset_dir, visitor); + auto sksl_asset_file = fml::OpenFileReadOnly(sksl_asset_dir, kAssetFileName); + if (!sksl_asset_file.is_valid()) { + FML_LOG(INFO) << "No sksl asset file found."; } else { - FML_LOG(INFO) << "No sksl asset directory found."; + FML_LOG(INFO) << "Found sksl asset. Loading SkSLs from it..."; + auto mapping = std::make_unique(sksl_asset_file); + rapidjson::Document json_doc; + rapidjson::ParseResult parse_result = + json_doc.Parse(reinterpret_cast(mapping->GetMapping()), + mapping->GetSize()); + if (parse_result != rapidjson::ParseErrorCode::kParseErrorNone) { + FML_LOG(ERROR) << "Failed to parse json file: " << kAssetFileName; + } else { + for (auto& item : json_doc["data"].GetObject()) { + sk_sp key = ParseBase32(item.name.GetString()); + sk_sp sksl = ParseBase64(item.value.GetString()); + if (key != nullptr && sksl != nullptr) { + result.push_back({key, sksl}); + } else { + FML_LOG(ERROR) << "Failed to load: " << item.name.GetString(); + } + } + } } return result; diff --git a/shell/common/persistent_cache.h b/shell/common/persistent_cache.h index 5cfb0afb75b..f1b6f863413 100644 --- a/shell/common/persistent_cache.h +++ b/shell/common/persistent_cache.h @@ -73,6 +73,7 @@ class PersistentCache : public GrContextOptions::PersistentCache { static void MarkStrategySet() { strategy_set_ = true; } static constexpr char kSkSLSubdirName[] = "sksl"; + static constexpr char kAssetFileName[] = "io.flutter.shaders.json"; private: static std::string cache_base_path_; diff --git a/shell/common/persistent_cache_unittests.cc b/shell/common/persistent_cache_unittests.cc index 597727af3b8..b0a34be7ef4 100644 --- a/shell/common/persistent_cache_unittests.cc +++ b/shell/common/persistent_cache_unittests.cc @@ -150,23 +150,28 @@ TEST_F(ShellTest, CanLoadSkSLsFromAsset) { auto empty_config = RunConfiguration::InferFromSettings(empty_settings); std::unique_ptr empty_shell = CreateShell(empty_settings); + // 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; fml::UniqueFD sksl_asset_dir = fml::OpenDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName, true, fml::FilePermission::kReadWrite); - // The SkSL filenames are Base32 encoded strings. "IE" is the encoding of "A" - // and "II" is the encoding of "B". - const std::string kFileNames[2] = {"IE", "II"}; - const std::string kFileData[2] = {"x", "y"}; - - // Prepare 2 SkSL files in the asset directory. - for (int i = 0; i < 2; i += 1) { - auto data = std::make_unique( - std::vector{kFileData[i].begin(), kFileData[i].end()}); - fml::WriteAtomically(sksl_asset_dir, kFileNames[i].c_str(), *data); - } + auto data = std::make_unique( + std::vector{kTestJson.begin(), kTestJson.end()}); + fml::WriteAtomically(sksl_asset_dir, PersistentCache::kAssetFileName, *data); // 1st, test that RunConfiguration::InferFromSettings sets the path. ResetAssetPath(); @@ -213,8 +218,7 @@ TEST_F(ShellTest, CanLoadSkSLsFromAsset) { // Cleanup. DestroyShell(std::move(empty_shell)); - fml::UnlinkFile(sksl_asset_dir, kFileNames[0].c_str()); - fml::UnlinkFile(sksl_asset_dir, kFileNames[1].c_str()); + fml::UnlinkFile(sksl_asset_dir, PersistentCache::kAssetFileName); fml::UnlinkDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName); }