Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit a8af96d

Browse files
authored
Read SkSLs from asset (#17601)
Fixes flutter/flutter#53117 Test added: - ShellTest.CanLoadSkSLsFromAsset
1 parent 40f32a6 commit a8af96d

File tree

11 files changed

+184
-17
lines changed

11 files changed

+184
-17
lines changed

fml/log_settings.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,13 @@ int GetMinLogLevel() {
3434
return std::min(state::g_log_settings.min_log_level, LOG_FATAL);
3535
}
3636

37+
ScopedSetLogSettings::ScopedSetLogSettings(const LogSettings& settings) {
38+
old_settings_ = GetLogSettings();
39+
SetLogSettings(settings);
40+
}
41+
42+
ScopedSetLogSettings::~ScopedSetLogSettings() {
43+
SetLogSettings(old_settings_);
44+
}
45+
3746
} // namespace fml

fml/log_settings.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ LogSettings GetLogSettings();
3535
// higher than LOG_FATAL.
3636
int GetMinLogLevel();
3737

38+
class ScopedSetLogSettings {
39+
public:
40+
ScopedSetLogSettings(const LogSettings& settings);
41+
~ScopedSetLogSettings();
42+
43+
private:
44+
LogSettings old_settings_;
45+
};
46+
3847
} // namespace fml
3948

4049
#endif // FLUTTER_FML_LOG_SETTINGS_H_

shell/common/persistent_cache.cc

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace flutter {
2121

2222
std::string PersistentCache::cache_base_path_;
23+
std::string PersistentCache::asset_path_;
2324

2425
std::mutex PersistentCache::instance_mutex_;
2526
std::unique_ptr<PersistentCache> PersistentCache::gPersistentCache;
@@ -90,7 +91,7 @@ static std::shared_ptr<fml::UniqueFD> MakeCacheDirectory(
9091
std::vector<std::string> components = {
9192
"flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion()};
9293
if (cache_sksl) {
93-
components.push_back("sksl");
94+
components.push_back(PersistentCache::kSkSLSubdirName);
9495
}
9596
return std::make_shared<fml::UniqueFD>(
9697
CreateDirectory(cache_base_dir, components,
@@ -105,9 +106,6 @@ static std::shared_ptr<fml::UniqueFD> MakeCacheDirectory(
105106
std::vector<PersistentCache::SkSLCache> PersistentCache::LoadSkSLs() {
106107
TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs");
107108
std::vector<PersistentCache::SkSLCache> result;
108-
if (!IsValid()) {
109-
return result;
110-
}
111109
fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory,
112110
const std::string& filename) {
113111
std::pair<bool, std::string> decode_result = fml::Base32Decode(filename);
@@ -126,7 +124,25 @@ std::vector<PersistentCache::SkSLCache> PersistentCache::LoadSkSLs() {
126124
}
127125
return true;
128126
};
129-
fml::VisitFiles(*sksl_cache_directory_, visitor);
127+
128+
// Only visit sksl_cache_directory_ if this persistent cache is valid.
129+
// However, we'd like to continue visit the asset dir even if this persistent
130+
// cache is invalid.
131+
if (IsValid()) {
132+
fml::VisitFiles(*sksl_cache_directory_, visitor);
133+
}
134+
135+
fml::UniqueFD root_asset_dir = fml::OpenDirectory(asset_path_.c_str(), false,
136+
fml::FilePermission::kRead);
137+
fml::UniqueFD sksl_asset_dir =
138+
fml::OpenDirectoryReadOnly(root_asset_dir, kSkSLSubdirName);
139+
if (sksl_asset_dir.is_valid()) {
140+
FML_LOG(INFO) << "Found sksl asset directory. Loading SkSLs from it...";
141+
fml::VisitFiles(sksl_asset_dir, visitor);
142+
} else {
143+
FML_LOG(INFO) << "No sksl asset directory found.";
144+
}
145+
130146
return result;
131147
}
132148

@@ -283,4 +299,9 @@ fml::RefPtr<fml::TaskRunner> PersistentCache::GetWorkerTaskRunner() const {
283299
return worker;
284300
}
285301

302+
void PersistentCache::UpdateAssetPath(const std::string& path) {
303+
FML_LOG(INFO) << "PersistentCache::UpdateAssetPath: " << path;
304+
asset_path_ = path;
305+
}
306+
286307
} // namespace flutter

shell/common/persistent_cache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,18 @@ class PersistentCache : public GrContextOptions::PersistentCache {
6565
/// Load all the SkSL shader caches in the right directory.
6666
std::vector<SkSLCache> LoadSkSLs();
6767

68+
/// Update the asset path from which PersistentCache can load SkLSs.
69+
static void UpdateAssetPath(const std::string& path);
70+
6871
static bool cache_sksl() { return cache_sksl_; }
6972
static void SetCacheSkSL(bool value);
7073
static void MarkStrategySet() { strategy_set_ = true; }
7174

75+
static constexpr char kSkSLSubdirName[] = "sksl";
76+
7277
private:
7378
static std::string cache_base_path_;
79+
static std::string asset_path_;
7480

7581
static std::mutex instance_mutex_;
7682
static std::unique_ptr<PersistentCache> gPersistentCache;

shell/common/persistent_cache_unittests.cc

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "flutter/flow/layers/picture_layer.h"
1111
#include "flutter/fml/command_line.h"
1212
#include "flutter/fml/file.h"
13+
#include "flutter/fml/log_settings.h"
1314
#include "flutter/fml/unique_fd.h"
1415
#include "flutter/shell/common/persistent_cache.h"
1516
#include "flutter/shell/common/shell_test.h"
@@ -123,5 +124,99 @@ TEST_F(ShellTest, CacheSkSLWorks) {
123124
DestroyShell(std::move(shell));
124125
}
125126

127+
static void CheckTextSkData(sk_sp<SkData> data, const std::string& expected) {
128+
std::string data_string(reinterpret_cast<const char*>(data->bytes()),
129+
data->size());
130+
ASSERT_EQ(data_string, expected);
131+
}
132+
133+
void ResetAssetPath() {
134+
PersistentCache::UpdateAssetPath("some_path_that_does_not_exist");
135+
ASSERT_EQ(PersistentCache::GetCacheForProcess()->LoadSkSLs().size(), 0u);
136+
}
137+
138+
void CheckTwoSkSLsAreLoaded() {
139+
auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs();
140+
ASSERT_EQ(shaders.size(), 2u);
141+
}
142+
143+
TEST_F(ShellTest, CanLoadSkSLsFromAsset) {
144+
// Avoid polluting unit tests output by hiding INFO level logging.
145+
fml::LogSettings warning_only = {fml::LOG_WARNING};
146+
fml::ScopedSetLogSettings scoped_set_log_settings(warning_only);
147+
148+
// Create an empty shell to test its service protocol handlers.
149+
auto empty_settings = CreateSettingsForFixture();
150+
auto empty_config = RunConfiguration::InferFromSettings(empty_settings);
151+
std::unique_ptr<Shell> empty_shell = CreateShell(empty_settings);
152+
153+
// Temp dir for the asset.
154+
fml::ScopedTemporaryDirectory asset_dir;
155+
fml::UniqueFD sksl_asset_dir =
156+
fml::OpenDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName, true,
157+
fml::FilePermission::kReadWrite);
158+
159+
// The SkSL filenames are Base32 encoded strings. "IE" is the encoding of "A"
160+
// and "II" is the encoding of "B".
161+
const std::string kFileNames[2] = {"IE", "II"};
162+
const std::string kFileData[2] = {"x", "y"};
163+
164+
// Prepare 2 SkSL files in the asset directory.
165+
for (int i = 0; i < 2; i += 1) {
166+
auto data = std::make_unique<fml::DataMapping>(
167+
std::vector<uint8_t>{kFileData[i].begin(), kFileData[i].end()});
168+
fml::WriteAtomically(sksl_asset_dir, kFileNames[i].c_str(), *data);
169+
}
170+
171+
// 1st, test that RunConfiguration::InferFromSettings sets the path.
172+
ResetAssetPath();
173+
auto settings = CreateSettingsForFixture();
174+
settings.assets_path = asset_dir.path();
175+
RunConfiguration::InferFromSettings(settings);
176+
CheckTwoSkSLsAreLoaded();
177+
178+
// 2nd, test that Shell::OnServiceProtocolSetAssetBundlePath sets the path.
179+
ResetAssetPath();
180+
ServiceProtocol::Handler::ServiceProtocolMap params;
181+
rapidjson::Document document;
182+
params["assetDirectory"] = asset_dir.path();
183+
OnServiceProtocol(
184+
empty_shell.get(), ShellTest::ServiceProtocolEnum::kSetAssetBundlePath,
185+
empty_shell->GetTaskRunners().GetUITaskRunner(), params, document);
186+
CheckTwoSkSLsAreLoaded();
187+
188+
// 3rd, test that Shell::OnServiceProtocolRunInView sets the path.
189+
ResetAssetPath();
190+
params["assetDirectory"] = asset_dir.path();
191+
params["mainScript"] = "no_such_script.dart";
192+
OnServiceProtocol(
193+
empty_shell.get(), ShellTest::ServiceProtocolEnum::kSetAssetBundlePath,
194+
empty_shell->GetTaskRunners().GetUITaskRunner(), params, document);
195+
CheckTwoSkSLsAreLoaded();
196+
197+
// 4th, test the content of the SkSLs in the asset.
198+
{
199+
auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs();
200+
ASSERT_EQ(shaders.size(), 2u);
201+
202+
// Make sure that the 2 shaders are sorted by their keys. Their keys should
203+
// be "A" and "B" (decoded from "II" and "IE").
204+
if (shaders[0].first->bytes()[0] == 'B') {
205+
std::swap(shaders[0], shaders[1]);
206+
}
207+
208+
CheckTextSkData(shaders[0].first, "A");
209+
CheckTextSkData(shaders[1].first, "B");
210+
CheckTextSkData(shaders[0].second, "x");
211+
CheckTextSkData(shaders[1].second, "y");
212+
}
213+
214+
// Cleanup.
215+
DestroyShell(std::move(empty_shell));
216+
fml::UnlinkFile(sksl_asset_dir, kFileNames[0].c_str());
217+
fml::UnlinkFile(sksl_asset_dir, kFileNames[1].c_str());
218+
fml::UnlinkDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName);
219+
}
220+
126221
} // namespace testing
127222
} // namespace flutter

shell/common/run_configuration.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "flutter/fml/file.h"
1111
#include "flutter/fml/unique_fd.h"
1212
#include "flutter/runtime/dart_vm.h"
13+
#include "flutter/shell/common/persistent_cache.h"
1314

1415
namespace flutter {
1516

@@ -26,6 +27,7 @@ RunConfiguration RunConfiguration::InferFromSettings(
2627
asset_manager->PushBack(
2728
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
2829
settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
30+
PersistentCache::UpdateAssetPath(settings.assets_path);
2931

3032
return {IsolateConfiguration::InferFromSettings(settings, asset_manager,
3133
io_worker),

shell/common/shell.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,7 @@ bool Shell::OnServiceProtocolRunInView(
13121312
configuration.AddAssetResolver(
13131313
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
13141314
asset_directory_path.c_str(), false, fml::FilePermission::kRead)));
1315+
PersistentCache::UpdateAssetPath(asset_directory_path);
13151316

13161317
auto& allocator = response.GetAllocator();
13171318
response.SetObject();
@@ -1406,6 +1407,7 @@ bool Shell::OnServiceProtocolSetAssetBundlePath(
14061407
asset_manager->PushFront(std::make_unique<DirectoryAssetBundle>(
14071408
fml::OpenDirectory(params.at("assetDirectory").data(), false,
14081409
fml::FilePermission::kRead)));
1410+
PersistentCache::UpdateAssetPath(params.at("assetDirectory").data());
14091411

14101412
if (engine_->UpdateAssetManager(std::move(asset_manager))) {
14111413
response.AddMember("type", "Success", allocator);

shell/common/shell_test.cc

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,28 @@ bool ShellTest::GetNeedsReportTimings(Shell* shell) {
202202
return shell->needs_report_timings_;
203203
}
204204

205-
void ShellTest::OnServiceProtocolGetSkSLs(
205+
void ShellTest::OnServiceProtocol(
206206
Shell* shell,
207+
ServiceProtocolEnum some_protocol,
208+
fml::RefPtr<fml::TaskRunner> task_runner,
207209
const ServiceProtocol::Handler::ServiceProtocolMap& params,
208210
rapidjson::Document& response) {
209211
std::promise<bool> finished;
210-
fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetIOTaskRunner(),
211-
[shell, params, &response, &finished]() {
212-
shell->OnServiceProtocolGetSkSLs(
213-
params, response);
214-
finished.set_value(true);
215-
});
212+
fml::TaskRunner::RunNowOrPostTask(
213+
task_runner, [shell, some_protocol, params, &response, &finished]() {
214+
switch (some_protocol) {
215+
case ServiceProtocolEnum::kGetSkSLs:
216+
shell->OnServiceProtocolGetSkSLs(params, response);
217+
break;
218+
case ServiceProtocolEnum::kSetAssetBundlePath:
219+
shell->OnServiceProtocolSetAssetBundlePath(params, response);
220+
break;
221+
case ServiceProtocolEnum::kRunInView:
222+
shell->OnServiceProtocolRunInView(params, response);
223+
break;
224+
}
225+
finished.set_value(true);
226+
});
216227
finished.get_future().wait();
217228
}
218229

shell/common/shell_test.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "flutter/fml/macros.h"
1414
#include "flutter/fml/time/time_point.h"
1515
#include "flutter/lib/ui/window/platform_message.h"
16+
#include "flutter/shell/common/persistent_cache.h"
1617
#include "flutter/shell/common/run_configuration.h"
1718
#include "flutter/shell/common/shell.h"
1819
#include "flutter/shell/common/thread_host.h"
@@ -75,11 +76,19 @@ class ShellTest : public ThreadTest {
7576
static bool GetNeedsReportTimings(Shell* shell);
7677
static void SetNeedsReportTimings(Shell* shell, bool value);
7778

79+
enum ServiceProtocolEnum {
80+
kGetSkSLs,
81+
kSetAssetBundlePath,
82+
kRunInView,
83+
};
84+
7885
// Helper method to test private method Shell::OnServiceProtocolGetSkSLs.
7986
// (ShellTest is a friend class of Shell.) We'll also make sure that it is
80-
// running on the UI thread.
81-
static void OnServiceProtocolGetSkSLs(
87+
// running on the correct task_runner.
88+
static void OnServiceProtocol(
8289
Shell* shell,
90+
ServiceProtocolEnum some_protocol,
91+
fml::RefPtr<fml::TaskRunner> task_runner,
8392
const ServiceProtocol::Handler::ServiceProtocolMap& params,
8493
rapidjson::Document& response);
8594

shell/common/shell_unittests.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,9 @@ TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) {
11541154
std::unique_ptr<Shell> shell = CreateShell(settings);
11551155
ServiceProtocol::Handler::ServiceProtocolMap empty_params;
11561156
rapidjson::Document document;
1157-
OnServiceProtocolGetSkSLs(shell.get(), empty_params, document);
1157+
OnServiceProtocol(shell.get(), ServiceProtocolEnum::kGetSkSLs,
1158+
shell->GetTaskRunners().GetIOTaskRunner(), empty_params,
1159+
document);
11581160
rapidjson::StringBuffer buffer;
11591161
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
11601162
document.Accept(writer);

0 commit comments

Comments
 (0)