mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Read SkSLs from asset (flutter/engine#17601)
Fixes https://github.com/flutter/flutter/issues/53117 Test added: - ShellTest.CanLoadSkSLsFromAsset
This commit is contained in:
parent
a2ed53dc2e
commit
6c93145ef5
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user