Skip to content

Commit 113d8eb

Browse files
freilingJsouLiang
authored andcommitted
[fuchsia][shader warmup] fix for fxbug.dev/90387 (flutter#30482)
Warmup surface was being initialized on the platform thread rather than the raster thread which was causing races in the skia cache, this change just moves the warmup surface intiialization to the raster thread. This change also addresses another minor problem where invoking shader warmup with 0 skps would drop the callback on the floor and that would cause the dart future to hang forever. Simply invoke the dart future if no SKP's are provided.
1 parent f746691 commit 113d8eb

File tree

3 files changed

+73
-35
lines changed

3 files changed

+73
-35
lines changed

shell/platform/fuchsia/flutter/engine.cc

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -860,26 +860,36 @@ void Engine::WarmupSkps(
860860
SkISize size,
861861
std::shared_ptr<flutter::AssetManager> asset_manager,
862862
std::optional<const std::vector<std::string>> skp_names,
863-
std::optional<std::function<void(uint32_t)>> completion_callback) {
864-
// We use a raw pointer here because we want to keep this alive until all gpu
865-
// work is done and the callbacks skia takes for this are function pointers
866-
// so we are unable to use a lambda that captures the smart pointer.
867-
SurfaceProducerSurface* skp_warmup_surface =
868-
surface_producer->ProduceOffscreenSurface(size).release();
869-
if (!skp_warmup_surface) {
870-
FML_LOG(ERROR) << "Failed to create offscreen warmup surface";
871-
// Tell client that zero shaders were warmed up because warmup failed.
872-
if (completion_callback.has_value() && completion_callback.value()) {
873-
completion_callback.value()(0);
863+
std::optional<std::function<void(uint32_t)>> maybe_completion_callback,
864+
bool synchronous) {
865+
// Wrap the optional validity checks up in a lambda to simplify the various
866+
// callsites below
867+
auto completion_callback = [maybe_completion_callback](uint32_t skp_count) {
868+
if (maybe_completion_callback.has_value() &&
869+
maybe_completion_callback.value()) {
870+
maybe_completion_callback.value()(skp_count);
874871
}
875-
return;
876-
}
872+
};
873+
874+
// We use this bizzare raw pointer to a smart pointer thing here because we
875+
// want to keep the surface alive until all gpu work is done and the
876+
// callbacks skia takes for this are function pointers so we are unable to
877+
// use a lambda that captures the smart pointer. We need two levels of
878+
// indirection because it needs to be the same across all invocations of the
879+
// raster task lambda from a single invocation of WarmupSkps, but be
880+
// different across different invocations of WarmupSkps (so we cant
881+
// statically initialialize it in the lambda itself). Basically the result
882+
// of a mashup of wierd call dynamics, multithreading, and lifecycle
883+
// management with C style Skia callbacks.
884+
std::unique_ptr<SurfaceProducerSurface>* skp_warmup_surface =
885+
new std::unique_ptr<SurfaceProducerSurface>(nullptr);
877886

878887
// tell concurrent task runner to deserialize all skps available from
879888
// the asset manager
880-
concurrent_task_runner->PostTask([raster_task_runner, skp_warmup_surface,
881-
surface_producer, asset_manager, skp_names,
882-
completion_callback]() {
889+
concurrent_task_runner->PostTask([raster_task_runner, size,
890+
skp_warmup_surface, surface_producer,
891+
asset_manager, skp_names,
892+
completion_callback, synchronous]() {
883893
TRACE_DURATION("flutter", "DeserializeSkps");
884894
std::vector<std::unique_ptr<fml::Mapping>> skp_mappings;
885895
if (skp_names) {
@@ -895,6 +905,13 @@ void Engine::WarmupSkps(
895905
skp_mappings = asset_manager->GetAsMappings(".*\\.skp$", "shaders");
896906
}
897907

908+
if (skp_mappings.size() == 0) {
909+
FML_LOG(WARNING)
910+
<< "Engine::WarmupSkps got zero SKP mappings, returning early";
911+
completion_callback(0);
912+
return;
913+
}
914+
898915
size_t total_size = 0;
899916
for (auto& mapping : skp_mappings) {
900917
total_size += mapping->GetSize();
@@ -920,20 +937,35 @@ void Engine::WarmupSkps(
920937

921938
// Tell raster task runner to warmup have the compositor
922939
// context warm up the newly deserialized picture
923-
raster_task_runner->PostTask([skp_warmup_surface, picture,
940+
raster_task_runner->PostTask([picture, skp_warmup_surface, size,
924941
surface_producer, completion_callback, i,
925-
count = skp_mappings.size()] {
942+
count = skp_mappings.size(), synchronous] {
926943
TRACE_DURATION("flutter", "WarmupSkp");
927-
skp_warmup_surface->GetSkiaSurface()->getCanvas()->drawPicture(picture);
944+
if (*skp_warmup_surface == nullptr) {
945+
skp_warmup_surface->reset(
946+
surface_producer->ProduceOffscreenSurface(size).release());
947+
948+
if (*skp_warmup_surface == nullptr) {
949+
FML_LOG(ERROR) << "Failed to create offscreen warmup surface";
950+
// Tell client that zero shaders were warmed up because warmup
951+
// failed.
952+
completion_callback(0);
953+
return;
954+
}
955+
}
956+
957+
// Do the actual warmup
958+
(*skp_warmup_surface)
959+
->GetSkiaSurface()
960+
->getCanvas()
961+
->drawPicture(picture);
928962

929963
if (i == count - 1) {
930964
// We call this here instead of inside fFinishedProc below because
931-
// we want to unblock the dart animation code as soon as the raster
932-
// thread is free to enque work, rather than waiting for the GPU work
933-
// itself to finish.
934-
if (completion_callback.has_value() && completion_callback.value()) {
935-
completion_callback.value()(count);
936-
}
965+
// we want to unblock the dart animation code as soon as the
966+
// raster thread is free to enque work, rather than waiting for
967+
// the GPU work itself to finish.
968+
completion_callback(count);
937969
}
938970

939971
if (surface_producer->gr_context()) {
@@ -946,11 +978,12 @@ void Engine::WarmupSkps(
946978
struct GrFlushInfo flush_info;
947979
flush_info.fFinishedContext = skp_warmup_surface;
948980
flush_info.fFinishedProc = [](void* skp_warmup_surface) {
949-
delete static_cast<SurfaceProducerSurface*>(skp_warmup_surface);
981+
delete static_cast<std::unique_ptr<SurfaceProducerSurface>*>(
982+
skp_warmup_surface);
950983
};
951984

952985
surface_producer->gr_context()->flush(flush_info);
953-
surface_producer->gr_context()->submit();
986+
surface_producer->gr_context()->submit(synchronous);
954987
}
955988
} else {
956989
if (i == count - 1) {

shell/platform/fuchsia/flutter/engine.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ class Engine final : public fuchsia::memorypressure::Watcher {
108108
SkISize size,
109109
std::shared_ptr<flutter::AssetManager> asset_manager,
110110
std::optional<const std::vector<std::string>> skp_names,
111-
std::optional<std::function<void(uint32_t)>> completion_callback);
111+
std::optional<std::function<void(uint32_t)>> completion_callback,
112+
bool synchronous = false);
112113

113114
void OnMainIsolateStart();
114115

shell/platform/fuchsia/flutter/tests/engine_unittests.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ class EngineTest : public ::testing::Test {
6666
uint64_t height,
6767
std::shared_ptr<flutter::AssetManager> asset_manager,
6868
std::optional<const std::vector<std::string>> skp_names,
69-
std::optional<std::function<void(uint32_t)>> completion_callback) {
69+
std::optional<std::function<void(uint32_t)>> completion_callback,
70+
bool synchronous = true) {
7071
// Have to create a message loop so default async dispatcher gets set,
7172
// otherwise we segfault creating the VulkanSurfaceProducer
72-
auto loop = fml::MessageLoopImpl::Create();
73+
loop_ = fml::MessageLoopImpl::Create();
7374

7475
context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
7576
scenic_ = context_->svc()->Connect<fuchsia::ui::scenic::Scenic>();
@@ -79,10 +80,12 @@ class EngineTest : public ::testing::Test {
7980

8081
Engine::WarmupSkps(&concurrent_task_runner_, &raster_task_runner_,
8182
surface_producer_, SkISize::Make(width, height),
82-
asset_manager, std::nullopt, std::nullopt);
83+
asset_manager, skp_names, completion_callback,
84+
synchronous);
8385
}
8486

8587
protected:
88+
fml::RefPtr<fml::MessageLoopImpl> loop_;
8689
MockTaskRunner concurrent_task_runner_;
8790
MockTaskRunner raster_task_runner_;
8891
std::shared_ptr<VulkanSurfaceProducer> surface_producer_;
@@ -163,7 +166,7 @@ TEST_F(EngineTest, SkpWarmup) {
163166
std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
164167

165168
WarmupSkps(draw_size.width(), draw_size.height(), asset_manager, std::nullopt,
166-
std::nullopt);
169+
[](uint32_t count) { EXPECT_EQ(1u, count); });
167170
concurrent_task_runner_.Run();
168171
raster_task_runner_.Run();
169172

@@ -196,8 +199,9 @@ TEST_F(EngineTest, SkpWarmupAsync) {
196199
fml::ScopedTemporaryDirectory asset_dir;
197200
fml::UniqueFD asset_dir_fd = fml::OpenDirectory(
198201
asset_dir.path().c_str(), false, fml::FilePermission::kRead);
199-
fml::UniqueFD subdir_fd = fml::OpenDirectory(asset_dir_fd, "shaders", true,
200-
fml::FilePermission::kReadWrite);
202+
std::string subdir_name = "shaders";
203+
fml::UniqueFD subdir_fd = fml::OpenDirectory(
204+
asset_dir_fd, subdir_name.c_str(), true, fml::FilePermission::kReadWrite);
201205
std::string skp_name = "test.skp";
202206

203207
bool success = fml::WriteAtomically(subdir_fd, skp_name.c_str(), mapping);
@@ -207,7 +211,7 @@ TEST_F(EngineTest, SkpWarmupAsync) {
207211
asset_manager->PushBack(
208212
std::make_unique<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
209213

210-
std::vector<std::string> skp_names = {skp_name};
214+
std::vector<std::string> skp_names = {subdir_name + "/" + skp_name};
211215

212216
WarmupSkps(draw_size.width(), draw_size.height(), asset_manager, skp_names,
213217
[](uint32_t count) { EXPECT_EQ(1u, count); });

0 commit comments

Comments
 (0)