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

Commit cfaed8d

Browse files
committed
[fuchsia] Add shell test for skp based shader warmup
1 parent aef1fc6 commit cfaed8d

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

shell/common/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ if (enable_unittests) {
264264
"persistent_cache_unittests.cc",
265265
"pipeline_unittests.cc",
266266
"shell_unittests.cc",
267+
"skp_shader_warmup_unittests.cc",
267268
]
268269

269270
deps = [
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <memory>
6+
7+
#include "flutter/assets/directory_asset_bundle.h"
8+
#include "flutter/flow/layers/container_layer.h"
9+
#include "flutter/flow/layers/layer.h"
10+
#include "flutter/flow/layers/physical_shape_layer.h"
11+
#include "flutter/flow/layers/picture_layer.h"
12+
#include "flutter/fml/command_line.h"
13+
#include "flutter/fml/file.h"
14+
#include "flutter/fml/log_settings.h"
15+
#include "flutter/fml/unique_fd.h"
16+
#include "flutter/shell/common/persistent_cache.h"
17+
#include "flutter/shell/common/shell_test.h"
18+
#include "flutter/shell/common/switches.h"
19+
#include "flutter/shell/version/version.h"
20+
#include "flutter/testing/testing.h"
21+
#include "include/core/SkPicture.h"
22+
23+
namespace flutter {
24+
namespace testing {
25+
26+
#if defined(OS_FUCHSIA)
27+
28+
static void WaitForIO(Shell* shell) {
29+
std::promise<bool> io_task_finished;
30+
shell->GetTaskRunners().GetIOTaskRunner()->PostTask(
31+
[&io_task_finished]() { io_task_finished.set_value(true); });
32+
io_task_finished.get_future().wait();
33+
}
34+
35+
class SkpWarmupTest : public ShellTest {
36+
public:
37+
SkpWarmupTest() {}
38+
39+
void TestWarmup(const LayerTreeBuilder& builder) {
40+
// Create a temp dir to store the persistent cache
41+
fml::ScopedTemporaryDirectory dir;
42+
PersistentCache::SetCacheDirectoryPath(dir.path());
43+
PersistentCache::ResetCacheForProcess();
44+
45+
auto settings = CreateSettingsForFixture();
46+
settings.cache_sksl = true;
47+
settings.dump_skp_on_shader_compilation = true;
48+
49+
fml::AutoResetWaitableEvent firstFrameLatch;
50+
settings.frame_rasterized_callback =
51+
[&firstFrameLatch](const FrameTiming& t) { firstFrameLatch.Signal(); };
52+
53+
auto config = RunConfiguration::InferFromSettings(settings);
54+
config.SetEntrypoint("emptyMain");
55+
std::unique_ptr<Shell> shell = CreateShell(settings);
56+
PlatformViewNotifyCreated(shell.get());
57+
RunEngine(shell.get(), std::move(config));
58+
59+
// Initially, we should have no SkSL cache
60+
auto cache = PersistentCache::GetCacheForProcess()->LoadSkSLs();
61+
ASSERT_EQ(cache.size(), 0u);
62+
63+
PumpOneFrame(shell.get(), 100, 100, builder);
64+
firstFrameLatch.Wait();
65+
WaitForIO(shell.get());
66+
67+
// Count the number of shaders this builder generated. We use this as a
68+
// proxy for whether new shaders were generated, since skia will dump an skp
69+
// any time a new shader is compiled.
70+
int skp_count = 0;
71+
fml::FileVisitor skp_count_visitor = [&skp_count](
72+
const fml::UniqueFD& directory,
73+
const std::string& filename) {
74+
if (filename.size() >= 4 &&
75+
filename.substr(filename.size() - 4, 4) == ".skp") {
76+
skp_count += 1;
77+
}
78+
return true;
79+
};
80+
fml::VisitFilesRecursively(dir.fd(), skp_count_visitor);
81+
int first_skp_count = skp_count;
82+
skp_count = 0;
83+
ASSERT_GT(first_skp_count, 0);
84+
85+
// Deserialize all skps into memory
86+
std::vector<sk_sp<SkPicture>> pictures;
87+
fml::FileVisitor skp_deserialize_visitor =
88+
[&pictures](const fml::UniqueFD& directory,
89+
const std::string& filename) {
90+
if (filename.size() >= 4 &&
91+
filename.substr(filename.size() - 4, 4) == ".skp") {
92+
auto fd = fml::OpenFileReadOnly(directory, filename.c_str());
93+
if (fd.get() < 0) {
94+
FML_LOG(ERROR) << "Failed to open " << filename;
95+
return true;
96+
}
97+
// Deserialize
98+
sk_sp<SkData> data = SkData::MakeFromFD(fd.get());
99+
std::unique_ptr<SkMemoryStream> stream = SkMemoryStream::Make(data);
100+
sk_sp<SkPicture> picture = SkPicture::MakeFromStream(
101+
stream.get(), /*const SkDeserialProcs* */ nullptr);
102+
pictures.push_back(std::move(picture));
103+
fd.reset();
104+
}
105+
return true;
106+
};
107+
fml::VisitFilesRecursively(dir.fd(), skp_deserialize_visitor);
108+
ASSERT_GT(pictures.size(), 0ul);
109+
110+
// Reinitialize shell with clean cache and verify that drawing again dumps
111+
// the same number of shaders
112+
fml::RemoveFilesInDirectory(dir.fd());
113+
PersistentCache::ResetCacheForProcess();
114+
DestroyShell(std::move(shell));
115+
auto config2 = RunConfiguration::InferFromSettings(settings);
116+
config2.SetEntrypoint("emptyMain");
117+
shell = CreateShell(settings);
118+
PlatformViewNotifyCreated(shell.get());
119+
RunEngine(shell.get(), std::move(config2));
120+
firstFrameLatch.Reset();
121+
PumpOneFrame(shell.get(), 100, 100, builder);
122+
firstFrameLatch.Wait();
123+
WaitForIO(shell.get());
124+
125+
// Verify same number of shaders dumped
126+
fml::VisitFilesRecursively(dir.fd(), skp_count_visitor);
127+
int second_skp_count = skp_count;
128+
skp_count = 0;
129+
ASSERT_EQ(second_skp_count, first_skp_count);
130+
131+
// Reinitialize shell and draw deserialized skps to warm up shaders
132+
fml::RemoveFilesInDirectory(dir.fd());
133+
PersistentCache::ResetCacheForProcess();
134+
DestroyShell(std::move(shell));
135+
auto config3 = RunConfiguration::InferFromSettings(settings);
136+
config3.SetEntrypoint("emptyMain");
137+
shell = CreateShell(settings);
138+
PlatformViewNotifyCreated(shell.get());
139+
RunEngine(shell.get(), std::move(config3));
140+
firstFrameLatch.Reset();
141+
142+
for (auto& picture : pictures) {
143+
fml::RefPtr<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
144+
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
145+
LayerTreeBuilder picture_builder =
146+
[picture, queue](std::shared_ptr<ContainerLayer> root) {
147+
auto picture_layer = std::make_shared<PictureLayer>(
148+
SkPoint::Make(0, 0), SkiaGPUObject<SkPicture>(picture, queue),
149+
/* is_complex */ false,
150+
/* will_change */ false);
151+
root->Add(picture_layer);
152+
};
153+
PumpOneFrame(shell.get(), picture->cullRect().width(),
154+
picture->cullRect().height(), picture_builder);
155+
}
156+
firstFrameLatch.Wait();
157+
WaitForIO(shell.get());
158+
159+
// Verify same number of shaders dumped
160+
fml::VisitFilesRecursively(dir.fd(), skp_count_visitor);
161+
int third_skp_count = skp_count;
162+
skp_count = 0;
163+
ASSERT_EQ(third_skp_count, first_skp_count);
164+
165+
// Remove files generated
166+
fml::RemoveFilesInDirectory(dir.fd());
167+
168+
// Draw orignal material again
169+
firstFrameLatch.Reset();
170+
PumpOneFrame(shell.get(), 100, 100, builder);
171+
firstFrameLatch.Wait();
172+
WaitForIO(shell.get());
173+
174+
// Verify no new shaders dumped
175+
fml::VisitFilesRecursively(dir.fd(), skp_count_visitor);
176+
int fourth_skp_count = skp_count;
177+
skp_count = 0;
178+
ASSERT_EQ(fourth_skp_count, 0);
179+
180+
// Clean Up
181+
fml::RemoveFilesInDirectory(dir.fd());
182+
}
183+
};
184+
185+
TEST_F(SkpWarmupTest, Basic) {
186+
// Draw something to trigger shader compilations.
187+
LayerTreeBuilder builder = [](std::shared_ptr<ContainerLayer> root) {
188+
SkPath path;
189+
path.addCircle(50, 50, 20);
190+
auto physical_shape_layer = std::make_shared<PhysicalShapeLayer>(
191+
SK_ColorRED, SK_ColorBLUE, 1.0f, path, Clip::antiAlias);
192+
root->Add(physical_shape_layer);
193+
};
194+
TestWarmup(builder);
195+
}
196+
197+
#endif
198+
199+
} // namespace testing
200+
} // namespace flutter

0 commit comments

Comments
 (0)