Skip to content

Commit

Permalink
Read SkSLs from asset (flutter#17601)
Browse files Browse the repository at this point in the history
Fixes flutter/flutter#53117

Test added:
- ShellTest.CanLoadSkSLsFromAsset
  • Loading branch information
liyuqian authored Apr 10, 2020
1 parent 40f32a6 commit a8af96d
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 17 deletions.
9 changes: 9 additions & 0 deletions fml/log_settings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions fml/log_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
31 changes: 26 additions & 5 deletions shell/common/persistent_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
6 changes: 6 additions & 0 deletions shell/common/persistent_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
95 changes: 95 additions & 0 deletions shell/common/persistent_cache_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions shell/common/run_configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 18 additions & 7 deletions shell/common/shell_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
13 changes: 11 additions & 2 deletions shell/common/shell_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);

Expand Down
4 changes: 3 additions & 1 deletion shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions testing/fuchsia/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit a8af96d

Please sign in to comment.