未验证 提交 a8af96da 编写于 作者: L liyuqian 提交者: GitHub

Read SkSLs from asset (#17601)

Fixes https://github.com/flutter/flutter/issues/53117

Test added:
- ShellTest.CanLoadSkSLsFromAsset
上级 40f32a67
......@@ -34,4 +34,13 @@ int GetMinLogLevel() {
return std::min(state::g_log_settings.min_log_level, LOG_FATAL);
}
ScopedSetLogSettings::ScopedSetLogSettings(const LogSettings& settings) {
old_settings_ = GetLogSettings();
SetLogSettings(settings);
}
ScopedSetLogSettings::~ScopedSetLogSettings() {
SetLogSettings(old_settings_);
}
} // namespace fml
......@@ -35,6 +35,15 @@ LogSettings GetLogSettings();
// higher than LOG_FATAL.
int GetMinLogLevel();
class ScopedSetLogSettings {
public:
ScopedSetLogSettings(const LogSettings& settings);
~ScopedSetLogSettings();
private:
LogSettings old_settings_;
};
} // namespace fml
#endif // FLUTTER_FML_LOG_SETTINGS_H_
......@@ -20,6 +20,7 @@
namespace flutter {
std::string PersistentCache::cache_base_path_;
std::string PersistentCache::asset_path_;
std::mutex PersistentCache::instance_mutex_;
std::unique_ptr<PersistentCache> PersistentCache::gPersistentCache;
......@@ -90,7 +91,7 @@ static std::shared_ptr<fml::UniqueFD> MakeCacheDirectory(
std::vector<std::string> components = {
"flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion()};
if (cache_sksl) {
components.push_back("sksl");
components.push_back(PersistentCache::kSkSLSubdirName);
}
return std::make_shared<fml::UniqueFD>(
CreateDirectory(cache_base_dir, components,
......@@ -105,9 +106,6 @@ static std::shared_ptr<fml::UniqueFD> MakeCacheDirectory(
std::vector<PersistentCache::SkSLCache> PersistentCache::LoadSkSLs() {
TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs");
std::vector<PersistentCache::SkSLCache> result;
if (!IsValid()) {
return result;
}
fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory,
const std::string& filename) {
std::pair<bool, std::string> decode_result = fml::Base32Decode(filename);
......@@ -126,7 +124,25 @@ std::vector<PersistentCache::SkSLCache> PersistentCache::LoadSkSLs() {
}
return true;
};
fml::VisitFiles(*sksl_cache_directory_, visitor);
// Only visit sksl_cache_directory_ if this persistent cache is valid.
// However, we'd like to continue visit the asset dir even if this persistent
// cache is invalid.
if (IsValid()) {
fml::VisitFiles(*sksl_cache_directory_, visitor);
}
fml::UniqueFD root_asset_dir = fml::OpenDirectory(asset_path_.c_str(), false,
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);
} else {
FML_LOG(INFO) << "No sksl asset directory found.";
}
return result;
}
......@@ -283,4 +299,9 @@ fml::RefPtr<fml::TaskRunner> PersistentCache::GetWorkerTaskRunner() const {
return worker;
}
void PersistentCache::UpdateAssetPath(const std::string& path) {
FML_LOG(INFO) << "PersistentCache::UpdateAssetPath: " << path;
asset_path_ = path;
}
} // namespace flutter
......@@ -65,12 +65,18 @@ class PersistentCache : public GrContextOptions::PersistentCache {
/// Load all the SkSL shader caches in the right directory.
std::vector<SkSLCache> LoadSkSLs();
/// Update the asset path from which PersistentCache can load SkLSs.
static void UpdateAssetPath(const std::string& path);
static bool cache_sksl() { return cache_sksl_; }
static void SetCacheSkSL(bool value);
static void MarkStrategySet() { strategy_set_ = true; }
static constexpr char kSkSLSubdirName[] = "sksl";
private:
static std::string cache_base_path_;
static std::string asset_path_;
static std::mutex instance_mutex_;
static std::unique_ptr<PersistentCache> gPersistentCache;
......
......@@ -10,6 +10,7 @@
#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"
......@@ -123,5 +124,99 @@ TEST_F(ShellTest, CacheSkSLWorks) {
DestroyShell(std::move(shell));
}
static void CheckTextSkData(sk_sp<SkData> data, const std::string& expected) {
std::string data_string(reinterpret_cast<const char*>(data->bytes()),
data->size());
ASSERT_EQ(data_string, expected);
}
void ResetAssetPath() {
PersistentCache::UpdateAssetPath("some_path_that_does_not_exist");
ASSERT_EQ(PersistentCache::GetCacheForProcess()->LoadSkSLs().size(), 0u);
}
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);
// Create an empty shell to test its service protocol handlers.
auto empty_settings = CreateSettingsForFixture();
auto empty_config = RunConfiguration::InferFromSettings(empty_settings);
std::unique_ptr<Shell> empty_shell = CreateShell(empty_settings);
// 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<fml::DataMapping>(
std::vector<uint8_t>{kFileData[i].begin(), kFileData[i].end()});
fml::WriteAtomically(sksl_asset_dir, kFileNames[i].c_str(), *data);
}
// 1st, test that RunConfiguration::InferFromSettings sets the path.
ResetAssetPath();
auto settings = CreateSettingsForFixture();
settings.assets_path = asset_dir.path();
RunConfiguration::InferFromSettings(settings);
CheckTwoSkSLsAreLoaded();
// 2nd, test that Shell::OnServiceProtocolSetAssetBundlePath sets the path.
ResetAssetPath();
ServiceProtocol::Handler::ServiceProtocolMap params;
rapidjson::Document document;
params["assetDirectory"] = asset_dir.path();
OnServiceProtocol(
empty_shell.get(), ShellTest::ServiceProtocolEnum::kSetAssetBundlePath,
empty_shell->GetTaskRunners().GetUITaskRunner(), params, document);
CheckTwoSkSLsAreLoaded();
// 3rd, test that Shell::OnServiceProtocolRunInView sets the path.
ResetAssetPath();
params["assetDirectory"] = asset_dir.path();
params["mainScript"] = "no_such_script.dart";
OnServiceProtocol(
empty_shell.get(), ShellTest::ServiceProtocolEnum::kSetAssetBundlePath,
empty_shell->GetTaskRunners().GetUITaskRunner(), params, document);
CheckTwoSkSLsAreLoaded();
// 4th, 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.
DestroyShell(std::move(empty_shell));
fml::UnlinkFile(sksl_asset_dir, kFileNames[0].c_str());
fml::UnlinkFile(sksl_asset_dir, kFileNames[1].c_str());
fml::UnlinkDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName);
}
} // namespace testing
} // namespace flutter
......@@ -10,6 +10,7 @@
#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 {
......@@ -26,6 +27,7 @@ RunConfiguration RunConfiguration::InferFromSettings(
asset_manager->PushBack(
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
PersistentCache::UpdateAssetPath(settings.assets_path);
return {IsolateConfiguration::InferFromSettings(settings, asset_manager,
io_worker),
......
......@@ -1312,6 +1312,7 @@ bool Shell::OnServiceProtocolRunInView(
configuration.AddAssetResolver(
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
asset_directory_path.c_str(), false, fml::FilePermission::kRead)));
PersistentCache::UpdateAssetPath(asset_directory_path);
auto& allocator = response.GetAllocator();
response.SetObject();
......@@ -1406,6 +1407,7 @@ bool Shell::OnServiceProtocolSetAssetBundlePath(
asset_manager->PushFront(std::make_unique<DirectoryAssetBundle>(
fml::OpenDirectory(params.at("assetDirectory").data(), false,
fml::FilePermission::kRead)));
PersistentCache::UpdateAssetPath(params.at("assetDirectory").data());
if (engine_->UpdateAssetManager(std::move(asset_manager))) {
response.AddMember("type", "Success", allocator);
......
......@@ -202,17 +202,28 @@ bool ShellTest::GetNeedsReportTimings(Shell* shell) {
return shell->needs_report_timings_;
}
void ShellTest::OnServiceProtocolGetSkSLs(
void ShellTest::OnServiceProtocol(
Shell* shell,
ServiceProtocolEnum some_protocol,
fml::RefPtr<fml::TaskRunner> task_runner,
const ServiceProtocol::Handler::ServiceProtocolMap& params,
rapidjson::Document& response) {
std::promise<bool> finished;
fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetIOTaskRunner(),
[shell, params, &response, &finished]() {
shell->OnServiceProtocolGetSkSLs(
params, response);
finished.set_value(true);
});
fml::TaskRunner::RunNowOrPostTask(
task_runner, [shell, some_protocol, params, &response, &finished]() {
switch (some_protocol) {
case ServiceProtocolEnum::kGetSkSLs:
shell->OnServiceProtocolGetSkSLs(params, response);
break;
case ServiceProtocolEnum::kSetAssetBundlePath:
shell->OnServiceProtocolSetAssetBundlePath(params, response);
break;
case ServiceProtocolEnum::kRunInView:
shell->OnServiceProtocolRunInView(params, response);
break;
}
finished.set_value(true);
});
finished.get_future().wait();
}
......
......@@ -13,6 +13,7 @@
#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/thread_host.h"
......@@ -75,11 +76,19 @@ class ShellTest : public ThreadTest {
static bool GetNeedsReportTimings(Shell* shell);
static void SetNeedsReportTimings(Shell* shell, bool value);
enum ServiceProtocolEnum {
kGetSkSLs,
kSetAssetBundlePath,
kRunInView,
};
// Helper method to test private method Shell::OnServiceProtocolGetSkSLs.
// (ShellTest is a friend class of Shell.) We'll also make sure that it is
// running on the UI thread.
static void OnServiceProtocolGetSkSLs(
// running on the correct task_runner.
static void OnServiceProtocol(
Shell* shell,
ServiceProtocolEnum some_protocol,
fml::RefPtr<fml::TaskRunner> task_runner,
const ServiceProtocol::Handler::ServiceProtocolMap& params,
rapidjson::Document& response);
......
......@@ -1154,7 +1154,9 @@ TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) {
std::unique_ptr<Shell> shell = CreateShell(settings);
ServiceProtocol::Handler::ServiceProtocolMap empty_params;
rapidjson::Document document;
OnServiceProtocolGetSkSLs(shell.get(), empty_params, document);
OnServiceProtocol(shell.get(), ServiceProtocolEnum::kGetSkSLs,
shell->GetTaskRunners().GetIOTaskRunner(), empty_params,
document);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
......
......@@ -80,9 +80,10 @@ echo "$(date) START:runtime_tests -----------------------------------"
-t runtime_tests
# TODO(https://github.com/flutter/flutter/issues/53399): Re-enable
# OnServiceProtocolGetSkSLsWorks once it passes on Fuchsia.
# OnServiceProtocolGetSkSLsWorks and CanLoadSkSLsFromAsset once they pass on
# Fuchsia.
echo "$(date) START:shell_tests -------------------------------------"
./fuchsia_ctl -d $device_name test \
-f shell_tests-0.far \
-t shell_tests \
-a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks"
-a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks:ShellTest.CanLoadSkSLsFromAsset"
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册