From 3105db8ee856ffef281d018774d21a6164c81236 Mon Sep 17 00:00:00 2001 From: freiling Date: Wed, 4 Nov 2020 23:19:02 -0800 Subject: [PATCH] SKP based shader warmup (#20643) --- assets/asset_manager.cc | 18 +++ assets/asset_manager.h | 4 + assets/asset_resolver.h | 7 ++ assets/directory_asset_bundle.cc | 29 +++++ assets/directory_asset_bundle.h | 4 + fml/concurrent_message_loop.h | 7 +- fml/task_runner.h | 10 +- shell/common/persistent_cache.cc | 10 ++ shell/common/persistent_cache.h | 3 + shell/common/rasterizer.cc | 1 + shell/common/serialization_callbacks.cc | 9 +- shell/common/serialization_callbacks.h | 3 + shell/common/shell_unittests.cc | 68 +++++++++++ shell/common/skp_shader_warmup_unittests.cc | 59 +++++++++- shell/platform/fuchsia/flutter/BUILD.gn | 2 + .../fuchsia/flutter/compositor_context.cc | 13 ++- .../fuchsia/flutter/compositor_context.h | 2 + shell/platform/fuchsia/flutter/engine.cc | 62 +++++++++- shell/platform/fuchsia/flutter/engine.h | 10 ++ .../flutter_runner_product_configuration.cc | 5 + .../flutter_runner_product_configuration.h | 2 + .../flutter/meta/flutter_runner_tests.cmx | 9 +- .../fuchsia/flutter/tests/engine_unittests.cc | 109 ++++++++++++++++++ .../fuchsia/flutter/vulkan_surface_pool.h | 3 +- .../flutter/vulkan_surface_producer.cc | 5 + .../fuchsia/flutter/vulkan_surface_producer.h | 4 +- testing/fuchsia/meta/fuchsia_test.cmx | 4 +- 27 files changed, 443 insertions(+), 19 deletions(-) create mode 100644 shell/platform/fuchsia/flutter/tests/engine_unittests.cc diff --git a/assets/asset_manager.cc b/assets/asset_manager.cc index 0fbc28702e9f7..31d2131bd05c8 100644 --- a/assets/asset_manager.cc +++ b/assets/asset_manager.cc @@ -51,6 +51,24 @@ std::unique_ptr AssetManager::GetAsMapping( return nullptr; } +// |AssetResolver| +std::vector> AssetManager::GetAsMappings( + const std::string& asset_pattern) const { + std::vector> mappings; + if (asset_pattern.size() == 0) { + return mappings; + } + TRACE_EVENT1("flutter", "AssetManager::GetAsMappings", "pattern", + asset_pattern.c_str()); + for (const auto& resolver : resolvers_) { + auto resolver_mappings = resolver->GetAsMappings(asset_pattern); + mappings.insert(mappings.end(), + std::make_move_iterator(resolver_mappings.begin()), + std::make_move_iterator(resolver_mappings.end())); + } + return mappings; +} + // |AssetResolver| bool AssetManager::IsValid() const { return resolvers_.size() > 0; diff --git a/assets/asset_manager.h b/assets/asset_manager.h index 0a0f0ff170d4c..c6fc28657699d 100644 --- a/assets/asset_manager.h +++ b/assets/asset_manager.h @@ -37,6 +37,10 @@ class AssetManager final : public AssetResolver { std::unique_ptr GetAsMapping( const std::string& asset_name) const override; + // |AssetResolver| + std::vector> GetAsMappings( + const std::string& asset_pattern) const override; + private: std::deque> resolvers_; diff --git a/assets/asset_resolver.h b/assets/asset_resolver.h index 8b3e323218939..470ce3864cfff 100644 --- a/assets/asset_resolver.h +++ b/assets/asset_resolver.h @@ -42,6 +42,13 @@ class AssetResolver { [[nodiscard]] virtual std::unique_ptr GetAsMapping( const std::string& asset_name) const = 0; + // Same as GetAsMapping() but returns mappings for all files who's name + // matches |pattern|. Returns empty vector if no matching assets are found + [[nodiscard]] virtual std::vector> + GetAsMappings(const std::string& asset_pattern) const { + return {}; + }; + private: FML_DISALLOW_COPY_AND_ASSIGN(AssetResolver); }; diff --git a/assets/directory_asset_bundle.cc b/assets/directory_asset_bundle.cc index d15a7e7373c15..9c09c00bb4533 100644 --- a/assets/directory_asset_bundle.cc +++ b/assets/directory_asset_bundle.cc @@ -4,6 +4,7 @@ #include "flutter/assets/directory_asset_bundle.h" +#include #include #include "flutter/fml/eintr_wrapper.h" @@ -53,4 +54,32 @@ std::unique_ptr DirectoryAssetBundle::GetAsMapping( return mapping; } +std::vector> DirectoryAssetBundle::GetAsMappings( + const std::string& asset_pattern) const { + std::vector> mappings; + if (!is_valid_) { + FML_DLOG(WARNING) << "Asset bundle was not valid."; + return mappings; + } + + std::regex asset_regex(asset_pattern); + fml::FileVisitor visitor = [&](const fml::UniqueFD& directory, + const std::string& filename) { + if (std::regex_match(filename, asset_regex)) { + auto mapping = std::make_unique(fml::OpenFile( + directory, filename.c_str(), false, fml::FilePermission::kRead)); + + if (mapping && mapping->IsValid()) { + mappings.push_back(std::move(mapping)); + } else { + FML_LOG(ERROR) << "Mapping " << filename << " failed"; + } + } + return true; + }; + fml::VisitFilesRecursively(descriptor_, visitor); + + return mappings; +} + } // namespace flutter diff --git a/assets/directory_asset_bundle.h b/assets/directory_asset_bundle.h index 49b02cdd27c71..b85ff1dbacad0 100644 --- a/assets/directory_asset_bundle.h +++ b/assets/directory_asset_bundle.h @@ -34,6 +34,10 @@ class DirectoryAssetBundle : public AssetResolver { std::unique_ptr GetAsMapping( const std::string& asset_name) const override; + // |AssetResolver| + std::vector> GetAsMappings( + const std::string& asset_pattern) const override; + FML_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle); }; diff --git a/fml/concurrent_message_loop.h b/fml/concurrent_message_loop.h index 6071f45cafa82..ebd8de983508d 100644 --- a/fml/concurrent_message_loop.h +++ b/fml/concurrent_message_loop.h @@ -12,6 +12,7 @@ #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" +#include "flutter/fml/task_runner.h" namespace fml { @@ -58,13 +59,13 @@ class ConcurrentMessageLoop FML_DISALLOW_COPY_AND_ASSIGN(ConcurrentMessageLoop); }; -class ConcurrentTaskRunner { +class ConcurrentTaskRunner : public BasicTaskRunner { public: ConcurrentTaskRunner(std::weak_ptr weak_loop); - ~ConcurrentTaskRunner(); + virtual ~ConcurrentTaskRunner(); - void PostTask(const fml::closure& task); + void PostTask(const fml::closure& task) override; private: friend ConcurrentMessageLoop; diff --git a/fml/task_runner.h b/fml/task_runner.h index a66f67e13cc12..5bb738e48b009 100644 --- a/fml/task_runner.h +++ b/fml/task_runner.h @@ -16,11 +16,17 @@ namespace fml { class MessageLoopImpl; -class TaskRunner : public fml::RefCountedThreadSafe { +class BasicTaskRunner { + public: + virtual void PostTask(const fml::closure& task) = 0; +}; + +class TaskRunner : public fml::RefCountedThreadSafe, + public BasicTaskRunner { public: virtual ~TaskRunner(); - virtual void PostTask(const fml::closure& task); + virtual void PostTask(const fml::closure& task) override; virtual void PostTaskForTime(const fml::closure& task, fml::TimePoint target_time); diff --git a/shell/common/persistent_cache.cc b/shell/common/persistent_cache.cc index 0c5c76bfaf1d1..3152631e3a493 100644 --- a/shell/common/persistent_cache.cc +++ b/shell/common/persistent_cache.cc @@ -391,4 +391,14 @@ void PersistentCache::SetAssetManager(std::shared_ptr value) { asset_manager_ = value; } +std::vector> +PersistentCache::GetSkpsFromAssetManager() const { + if (!asset_manager_) { + FML_LOG(ERROR) + << "PersistentCache::GetSkpsFromAssetManager: Asset manager not set!"; + return std::vector>(); + } + return asset_manager_->GetAsMappings(".*\\.skp$"); +} + } // namespace flutter diff --git a/shell/common/persistent_cache.h b/shell/common/persistent_cache.h index ae2ae9dd0b1c2..1843501b969f3 100644 --- a/shell/common/persistent_cache.h +++ b/shell/common/persistent_cache.h @@ -74,6 +74,9 @@ class PersistentCache : public GrContextOptions::PersistentCache { /// Load all the SkSL shader caches in the right directory. std::vector LoadSkSLs(); + // Return mappings for all skp's accessible through the AssetManager + std::vector> GetSkpsFromAssetManager() const; + /// Set the asset manager from which PersistentCache can load SkLSs. A nullptr /// can be provided to clear the asset manager. static void SetAssetManager(std::shared_ptr value); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 52b9eb8a2289a..6679943bb2b2d 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -511,6 +511,7 @@ static sk_sp ScreenshotLayerTreeAsPicture( #if defined(OS_FUCHSIA) SkSerialProcs procs = {0}; procs.fImageProc = SerializeImageWithoutData; + procs.fTypefaceProc = SerializeTypefaceWithoutData; #else SkSerialProcs procs = {0}; procs.fTypefaceProc = SerializeTypefaceWithData; diff --git a/shell/common/serialization_callbacks.cc b/shell/common/serialization_callbacks.cc index bd334b7bf80e5..8e43576359542 100644 --- a/shell/common/serialization_callbacks.cc +++ b/shell/common/serialization_callbacks.cc @@ -5,19 +5,26 @@ #include "flutter/fml/logging.h" #include "include/core/SkImage.h" #include "include/core/SkPicture.h" +#include "include/core/SkSerialProcs.h" #include "include/core/SkStream.h" #include "include/core/SkTypeface.h" namespace flutter { sk_sp SerializeTypefaceWithoutData(SkTypeface* typeface, void* ctx) { - return typeface->serialize(SkTypeface::SerializeBehavior::kDontIncludeData); + return SkData::MakeEmpty(); } sk_sp SerializeTypefaceWithData(SkTypeface* typeface, void* ctx) { return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); } +sk_sp DeserializeTypefaceWithoutData(const void* data, + size_t length, + void* ctx) { + return SkTypeface::MakeDefault(); +} + struct ImageMetaData { int32_t width; int32_t height; diff --git a/shell/common/serialization_callbacks.h b/shell/common/serialization_callbacks.h index 6e339fd3c48c2..bcc9fe6016822 100644 --- a/shell/common/serialization_callbacks.h +++ b/shell/common/serialization_callbacks.h @@ -15,6 +15,9 @@ namespace flutter { sk_sp SerializeTypefaceWithoutData(SkTypeface* typeface, void* ctx); sk_sp SerializeTypefaceWithData(SkTypeface* typeface, void* ctx); +sk_sp DeserializeTypefaceWithoutData(const void* data, + size_t length, + void* ctx); // Serializes only the metadata of the image and not the underlying pixel data. sk_sp SerializeImageWithoutData(SkImage* image, void* ctx); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 03e13dd9092c2..7642d7c4f8534 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -10,6 +10,7 @@ #include #include +#include "assets/directory_asset_bundle.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/picture_layer.h" #include "flutter/flow/layers/transform_layer.h" @@ -2208,5 +2209,72 @@ TEST_F(ShellTest, EngineRootIsolateLaunchesDontTakeVMDataSettings) { isolate_create_latch.Wait(); } +TEST_F(ShellTest, AssetManagerSingle) { + fml::ScopedTemporaryDirectory asset_dir; + fml::UniqueFD asset_dir_fd = fml::OpenDirectory( + asset_dir.path().c_str(), false, fml::FilePermission::kRead); + + std::string filename = "test_name"; + std::string content = "test_content"; + + bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(), + fml::DataMapping(content)); + ASSERT_TRUE(success); + + AssetManager asset_manager; + asset_manager.PushBack( + std::make_unique(std::move(asset_dir_fd), false)); + + auto mapping = asset_manager.GetAsMapping(filename); + ASSERT_TRUE(mapping != nullptr); + + std::string result(reinterpret_cast(mapping->GetMapping()), + mapping->GetSize()); + + ASSERT_TRUE(result == content); +} + +TEST_F(ShellTest, AssetManagerMulti) { + fml::ScopedTemporaryDirectory asset_dir; + fml::UniqueFD asset_dir_fd = fml::OpenDirectory( + asset_dir.path().c_str(), false, fml::FilePermission::kRead); + + std::vector filenames = { + "good0", + "bad0", + "good1", + "bad1", + }; + + for (auto filename : filenames) { + bool success = fml::WriteAtomically(asset_dir_fd, filename.c_str(), + fml::DataMapping(filename)); + ASSERT_TRUE(success); + } + + AssetManager asset_manager; + asset_manager.PushBack( + std::make_unique(std::move(asset_dir_fd), false)); + + auto mappings = asset_manager.GetAsMappings("(.*)"); + ASSERT_TRUE(mappings.size() == 4); + + std::vector expected_results = { + "good0", + "good1", + }; + + mappings = asset_manager.GetAsMappings("(.*)good(.*)"); + ASSERT_TRUE(mappings.size() == expected_results.size()); + + for (auto& mapping : mappings) { + std::string result(reinterpret_cast(mapping->GetMapping()), + mapping->GetSize()); + ASSERT_NE( + std::find(expected_results.begin(), expected_results.end(), result), + expected_results.end()); + } +} + } // namespace testing } // namespace flutter diff --git a/shell/common/skp_shader_warmup_unittests.cc b/shell/common/skp_shader_warmup_unittests.cc index 689f2d9322fcc..1cb80189ec322 100644 --- a/shell/common/skp_shader_warmup_unittests.cc +++ b/shell/common/skp_shader_warmup_unittests.cc @@ -19,15 +19,19 @@ #include "flutter/shell/common/switches.h" #include "flutter/shell/version/version.h" #include "flutter/testing/testing.h" +#include "include/core/SkFont.h" #include "include/core/SkPicture.h" #include "include/core/SkPictureRecorder.h" #include "include/core/SkSerialProcs.h" +#include "include/core/SkTextBlob.h" + +#if defined(OS_FUCHSIA) +#include "lib/sys/cpp/component_context.h" +#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h" namespace flutter { namespace testing { -#if defined(OS_FUCHSIA) - static void WaitForIO(Shell* shell) { std::promise io_task_finished; shell->GetTaskRunners().GetIOTaskRunner()->PostTask( @@ -103,8 +107,13 @@ class SkpWarmupTest : public ShellTest { SkDeserialProcs procs = {0}; procs.fImageProc = DeserializeImageWithoutData; + procs.fTypefaceProc = DeserializeTypefaceWithoutData; sk_sp picture = SkPicture::MakeFromStream(stream.get(), &procs); + if (!picture) { + FML_LOG(ERROR) << "Failed to deserialize " << filename; + return true; + } pictures.push_back(std::move(picture)); fd.reset(); } @@ -242,7 +251,51 @@ TEST_F(SkpWarmupTest, Image) { TestWarmup(draw_size, builder); } -#endif +// Re-enable once https://bugs.chromium.org/p/skia/issues/detail?id=10404 +// is fixed and integrated, or a workaround is found. +TEST_F(SkpWarmupTest, DISABLED_Text) { + auto context = sys::ComponentContext::Create(); + fuchsia::fonts::ProviderSyncPtr sync_font_provider; + context->svc()->Connect(sync_font_provider.NewRequest()); + auto font_mgr = SkFontMgr_New_Fuchsia(std::move(sync_font_provider)); + auto raw_typeface = + font_mgr->matchFamilyStyle(nullptr, SkFontStyle::Normal()); + auto typeface = sk_sp(raw_typeface); + + SkFont font(typeface, 12); + auto text_blob = + SkTextBlob::MakeFromString("test", font, SkTextEncoding::kUTF8); + + SkISize draw_size = + SkISize::Make(text_blob->bounds().width(), text_blob->bounds().height()); + // We reuse this builder to draw the same content sever times in this test + LayerTreeBuilder builder = [&draw_size, &text_blob, + this](std::shared_ptr root) { + SkPictureRecorder recorder; + + auto canvas = + recorder.beginRecording(draw_size.width(), draw_size.height()); + + auto color_space = SkColorSpace::MakeSRGB(); + auto paint = SkPaint(SkColors::kWhite, color_space.get()); + canvas->drawTextBlob(text_blob, draw_size.width() / 2, + draw_size.height() / 2, paint); + + auto picture = recorder.finishRecordingAsPicture(); + + fml::RefPtr queue = fml::MakeRefCounted( + this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(0, 0), SkiaGPUObject(picture, queue), + /* is_complex */ false, + /* will_change */ false); + root->Add(picture_layer); + }; + + TestWarmup(draw_size, builder); +} } // namespace testing } // namespace flutter + +#endif // defined(OS_FUCHSIA) diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 6b38a9136f490..5aeb1e3126820 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -447,6 +447,7 @@ executable("flutter_runner_unittests") { "fuchsia_intl_unittest.cc", "platform_view_unittest.cc", "runner_unittest.cc", + "tests/engine_unittests.cc", "tests/flutter_runner_product_configuration_unittests.cc", "tests/vsync_recorder_unittests.cc", "vsync_waiter_unittests.cc", @@ -458,6 +459,7 @@ executable("flutter_runner_unittests") { # The use of these dependencies is temporary and will be moved behind the # embedder API. flutter_deps = [ + "//flutter/assets:assets", "//flutter/flow", "//flutter/lib/ui", "//flutter/shell/common", diff --git a/shell/platform/fuchsia/flutter/compositor_context.cc b/shell/platform/fuchsia/flutter/compositor_context.cc index 2f2a83eba345a..ccfd90c989dec 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.cc +++ b/shell/platform/fuchsia/flutter/compositor_context.cc @@ -145,7 +145,13 @@ CompositorContext::CompositorContext( std::shared_ptr scene_update_context) : session_connection_(session_connection), surface_producer_(surface_producer), - scene_update_context_(scene_update_context) {} + scene_update_context_(scene_update_context) { + SkISize size = SkISize::Make(1024, 600); + skp_warmup_surface_ = surface_producer_.ProduceOffscreenSurface(size); + if (!skp_warmup_surface_) { + FML_LOG(ERROR) << "SkSurface::MakeRenderTarget returned null"; + } +} CompositorContext::~CompositorContext() = default; @@ -164,4 +170,9 @@ CompositorContext::AcquireFrame( session_connection_, surface_producer_, scene_update_context_); } +void CompositorContext::WarmupSkp(const sk_sp picture) { + skp_warmup_surface_->getCanvas()->drawPicture(picture); + surface_producer_.gr_context()->flush(); +} + } // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/compositor_context.h b/shell/platform/fuchsia/flutter/compositor_context.h index 4a943a499ad63..6c1736b99059c 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.h +++ b/shell/platform/fuchsia/flutter/compositor_context.h @@ -27,11 +27,13 @@ class CompositorContext final : public flutter::CompositorContext { std::shared_ptr scene_update_context); ~CompositorContext() override; + void WarmupSkp(sk_sp picture); private: SessionConnection& session_connection_; VulkanSurfaceProducer& surface_producer_; std::shared_ptr scene_update_context_; + sk_sp skp_warmup_surface_; // |flutter::CompositorContext| std::unique_ptr AcquireFrame( diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 20bf34531582c..ccda074c397cd 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -14,16 +14,19 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" #include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/run_configuration.h" -#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h" - +#include "flutter/shell/common/serialization_callbacks.h" #include "flutter_runner_product_configuration.h" #include "fuchsia_external_view_embedder.h" #include "fuchsia_intl.h" +#include "include/core/SkPicture.h" +#include "include/core/SkSerialProcs.h" #include "platform_view.h" #include "surface.h" #include "task_runner_adapter.h" +#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h" #include "thread.h" #if defined(LEGACY_FUCHSIA_EMBEDDER) @@ -258,7 +261,14 @@ Engine::Engine(Delegate& delegate, } }; #else - on_create_rasterizer = [](flutter::Shell& shell) { + on_create_rasterizer = [this, &product_config](flutter::Shell& shell) { + if (product_config.enable_shader_warmup()) { + FML_DCHECK(surface_producer_); + WarmupSkps( + shell.GetDartVM()->GetConcurrentMessageLoop()->GetTaskRunner().get(), + shell.GetTaskRunners().GetRasterTaskRunner().get(), + surface_producer_.value()); + } return std::make_unique(shell); }; #endif @@ -625,4 +635,50 @@ void Engine::WriteProfileToTrace() const { } #endif // !defined(DART_PRODUCT) +void Engine::WarmupSkps(fml::BasicTaskRunner* concurrent_task_runner, + fml::BasicTaskRunner* raster_task_runner, + VulkanSurfaceProducer& surface_producer) { + SkISize size = SkISize::Make(1024, 600); + auto skp_warmup_surface = surface_producer.ProduceOffscreenSurface(size); + if (!skp_warmup_surface) { + FML_LOG(ERROR) << "SkSurface::MakeRenderTarget returned null"; + return; + } + + // tell concurrent task runner to deserialize all skps available from + // the asset manager + concurrent_task_runner->PostTask([&raster_task_runner, skp_warmup_surface, + &surface_producer]() { + TRACE_DURATION("flutter", "DeserializeSkps"); + std::vector> skp_mappings = + flutter::PersistentCache::GetCacheForProcess() + ->GetSkpsFromAssetManager(); + std::vector> pictures; + int i = 0; + for (auto& mapping : skp_mappings) { + std::unique_ptr stream = + SkMemoryStream::MakeDirect(mapping->GetMapping(), mapping->GetSize()); + SkDeserialProcs procs = {0}; + procs.fImageProc = flutter::DeserializeImageWithoutData; + procs.fTypefaceProc = flutter::DeserializeTypefaceWithoutData; + sk_sp picture = + SkPicture::MakeFromStream(stream.get(), &procs); + if (!picture) { + FML_LOG(ERROR) << "Failed to deserialize picture " << i; + continue; + } + + // Tell raster task runner to warmup have the compositor + // context warm up the newly deserialized picture + raster_task_runner->PostTask( + [skp_warmup_surface, picture, &surface_producer] { + TRACE_DURATION("flutter", "WarmupSkp"); + skp_warmup_surface->getCanvas()->drawPicture(picture); + surface_producer.gr_context()->flush(); + }); + i++; + } + }); +} + } // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index 377868666f94c..b710ac4bc554c 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -34,6 +34,10 @@ namespace flutter_runner { +namespace testing { +class EngineTest; +} + // Represents an instance of running Flutter engine along with the threads // that host the same. class Engine final { @@ -91,6 +95,10 @@ class Engine final { fml::WeakPtrFactory weak_factory_; + static void WarmupSkps(fml::BasicTaskRunner* concurrent_task_runner, + fml::BasicTaskRunner* raster_task_runner, + VulkanSurfaceProducer& surface_producer); + void OnMainIsolateStart(); void OnMainIsolateShutdown(); @@ -105,6 +113,8 @@ class Engine final { std::unique_ptr CreateSurface(); + friend class testing::EngineTest; + FML_DISALLOW_COPY_AND_ASSIGN(Engine); }; diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc index bc2a49cf1f9e5..f05bb5addac22 100644 --- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc +++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc @@ -33,6 +33,11 @@ FlutterRunnerProductConfiguration::FlutterRunnerProductConfiguration( if (val.IsBool()) intercept_all_input_ = val.GetBool(); } + if (document.HasMember("enable_shader_warmup")) { + auto& val = document["enable_shader_warmup"]; + if (val.IsBool()) + enable_shader_warmup_ = val.GetBool(); + } #if defined(LEGACY_FUCHSIA_EMBEDDER) if (document.HasMember("use_legacy_renderer")) { auto& val = document["use_legacy_renderer"]; diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h index 792f9fe25cd37..9fa36d6ab761b 100644 --- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h +++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h @@ -17,6 +17,7 @@ class FlutterRunnerProductConfiguration { fml::TimeDelta get_vsync_offset() { return vsync_offset_; } uint64_t get_max_frames_in_flight() { return max_frames_in_flight_; } bool get_intercept_all_input() { return intercept_all_input_; } + bool enable_shader_warmup() { return enable_shader_warmup_; } #if defined(LEGACY_FUCHSIA_EMBEDDER) bool use_legacy_renderer() { return use_legacy_renderer_; } #endif @@ -25,6 +26,7 @@ class FlutterRunnerProductConfiguration { fml::TimeDelta vsync_offset_ = fml::TimeDelta::Zero(); uint64_t max_frames_in_flight_ = 3; bool intercept_all_input_ = false; + bool enable_shader_warmup_ = false; #if defined(LEGACY_FUCHSIA_EMBEDDER) bool use_legacy_renderer_ = true; #endif diff --git a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx index 1893721bfff73..3c7c6b19e1079 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_runner_tests.cmx @@ -8,12 +8,17 @@ "sandbox": { "features": [ "deprecated-ambient-replace-as-executable", - "vulkan" + "vulkan", + "isolated-cache-storage", + "isolated-temp" ], "services": [ "fuchsia.accessibility.semantics.SemanticsManager", "fuchsia.intl.PropertyProvider", - "fuchsia.process.Launcher" + "fuchsia.process.Launcher", + "fuchsia.vulkan.loader.Loader", + "fuchsia.logger.LogSink", + "fuchsia.sysmem.Allocator" ] } } diff --git a/shell/platform/fuchsia/flutter/tests/engine_unittests.cc b/shell/platform/fuchsia/flutter/tests/engine_unittests.cc new file mode 100644 index 0000000000000..2901f1145bda3 --- /dev/null +++ b/shell/platform/fuchsia/flutter/tests/engine_unittests.cc @@ -0,0 +1,109 @@ +// Copyright 2020 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "assets/directory_asset_bundle.h" +#include "flutter/fml/memory/ref_ptr.h" +#include "flutter/fml/message_loop_impl.h" +#include "flutter/fml/task_runner.h" +#include "flutter/shell/common/persistent_cache.h" +#include "flutter/shell/common/serialization_callbacks.h" +#include "flutter/shell/platform/fuchsia/flutter/logging.h" +#include "flutter/shell/platform/fuchsia/flutter/runner.h" +#include "flutter/shell/platform/fuchsia/flutter/session_connection.h" +#include "gtest/gtest.h" +#include "include/core/SkPicture.h" +#include "include/core/SkPictureRecorder.h" +#include "include/core/SkSerialProcs.h" + +using namespace flutter_runner; +using namespace flutter; + +namespace flutter_runner { +namespace testing { + +class MockTaskRunner : public fml::BasicTaskRunner { + public: + MockTaskRunner() {} + virtual ~MockTaskRunner() {} + + void PostTask(const fml::closure& task) override { + task_count_++; + task(); + } + + int GetTaskCount() { return task_count_; } + + private: + int task_count_ = 0; +}; + +class EngineTest : public ::testing::Test { + public: + void WarmupSkps() { + // Have to create a message loop so default async dispatcher gets set, + // otherwise we segfault creating the VulkanSurfaceProducer + auto loop = fml::MessageLoopImpl::Create(); + + fuchsia::ui::scenic::SessionPtr session_ptr; + scenic::Session session(std::move(session_ptr)); + VulkanSurfaceProducer surface_producer(&session); + + Engine::WarmupSkps(&concurrent_task_runner_, &raster_task_runner_, + surface_producer); + } + + protected: + MockTaskRunner concurrent_task_runner_; + MockTaskRunner raster_task_runner_; +}; + +TEST_F(EngineTest, SkpWarmup) { + SkISize draw_size = SkISize::Make(100, 100); + SkPictureRecorder recorder; + auto canvas = recorder.beginRecording(draw_size.width(), draw_size.height()); + + // adapted from https://fiddle.skia.org/c/@Canvas_drawLine + SkPaint paint; + paint.setColor(0xFF9a67be); + paint.setStrokeWidth(20); + canvas->drawLine(0, 0, draw_size.width(), draw_size.height(), paint); + canvas->drawLine(0, draw_size.height(), draw_size.width(), 0, paint); + + sk_sp picture = recorder.finishRecordingAsPicture(); + SkSerialProcs procs = {0}; + procs.fImageProc = SerializeImageWithoutData; + procs.fTypefaceProc = SerializeTypefaceWithoutData; + sk_sp data = picture->serialize(&procs); + ASSERT_TRUE(data); + ASSERT_GT(data->size(), 0u); + + fml::NonOwnedMapping mapping(data->bytes(), data->size()); + + fml::ScopedTemporaryDirectory asset_dir; + fml::UniqueFD asset_dir_fd = fml::OpenDirectory( + asset_dir.path().c_str(), false, fml::FilePermission::kRead); + + bool success = fml::WriteAtomically(asset_dir_fd, "test.skp", mapping); + ASSERT_TRUE(success); + + auto asset_manager = std::make_shared(); + asset_manager->PushBack( + std::make_unique(std::move(asset_dir_fd), false)); + + PersistentCache::GetCacheForProcess()->SetAssetManager(asset_manager); + + WarmupSkps(); + + EXPECT_EQ(concurrent_task_runner_.GetTaskCount(), 1); + EXPECT_EQ(raster_task_runner_.GetTaskCount(), 1); +} + +} // namespace testing +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_pool.h b/shell/platform/fuchsia/flutter/vulkan_surface_pool.h index 0667db88d6c0c..f98d45216f4a6 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_pool.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface_pool.h @@ -26,6 +26,7 @@ class VulkanSurfacePool final { ~VulkanSurfacePool(); + std::unique_ptr CreateSurface(const SkISize& size); std::unique_ptr AcquireSurface(const SkISize& size); void SubmitSurface(std::unique_ptr surface); @@ -49,8 +50,6 @@ class VulkanSurfacePool final { std::unique_ptr GetCachedOrCreateSurface(const SkISize& size); - std::unique_ptr CreateSurface(const SkISize& size); - void RecycleSurface(std::unique_ptr surface); void RecyclePendingSurface(uintptr_t surface_key); diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc b/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc index ed69ce441eac5..6615c235d3e99 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc @@ -271,4 +271,9 @@ void VulkanSurfaceProducer::SubmitSurface( surface_pool_->SubmitSurface(std::move(surface)); } +sk_sp VulkanSurfaceProducer::ProduceOffscreenSurface( + const SkISize& size) { + return surface_pool_->CreateSurface(size)->GetSkiaSurface(); +} + } // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_producer.h b/shell/platform/fuchsia/flutter/vulkan_surface_producer.h index 2caaf64a36d7f..d4c9972b047fa 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_producer.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface_producer.h @@ -35,13 +35,15 @@ class VulkanSurfaceProducer final : public SurfaceProducer, std::unique_ptr ProduceSurface( const SkISize& size) override; + sk_sp ProduceOffscreenSurface(const SkISize& size); + // |SurfaceProducer| void SubmitSurface(std::unique_ptr surface) override; void OnSurfacesPresented( std::vector> surfaces); - GrDirectContext* gr_context() { return context_.get(); } + GrDirectContext* gr_context() const { return context_.get(); } private: // VulkanProvider diff --git a/testing/fuchsia/meta/fuchsia_test.cmx b/testing/fuchsia/meta/fuchsia_test.cmx index ad0aa3783abcb..c30e77f970c96 100644 --- a/testing/fuchsia/meta/fuchsia_test.cmx +++ b/testing/fuchsia/meta/fuchsia_test.cmx @@ -19,7 +19,9 @@ "fuchsia.settings.Intl", "fuchsia.sysmem.Allocator", "fuchsia.tracing.provider.Registry", - "fuchsia.vulkan.loader.Loader" + "fuchsia.vulkan.loader.Loader", + "fuchsia.intl.PropertyProvider", + "fuchsia.fonts.Provider" ] } }