mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
SKP based shader warmup (#20643)
This commit is contained in:
parent
e66a720137
commit
3105db8ee8
@ -51,6 +51,24 @@ std::unique_ptr<fml::Mapping> AssetManager::GetAsMapping(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// |AssetResolver|
|
||||
std::vector<std::unique_ptr<fml::Mapping>> AssetManager::GetAsMappings(
|
||||
const std::string& asset_pattern) const {
|
||||
std::vector<std::unique_ptr<fml::Mapping>> 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;
|
||||
|
||||
@ -37,6 +37,10 @@ class AssetManager final : public AssetResolver {
|
||||
std::unique_ptr<fml::Mapping> GetAsMapping(
|
||||
const std::string& asset_name) const override;
|
||||
|
||||
// |AssetResolver|
|
||||
std::vector<std::unique_ptr<fml::Mapping>> GetAsMappings(
|
||||
const std::string& asset_pattern) const override;
|
||||
|
||||
private:
|
||||
std::deque<std::unique_ptr<AssetResolver>> resolvers_;
|
||||
|
||||
|
||||
@ -42,6 +42,13 @@ class AssetResolver {
|
||||
[[nodiscard]] virtual std::unique_ptr<fml::Mapping> 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<std::unique_ptr<fml::Mapping>>
|
||||
GetAsMappings(const std::string& asset_pattern) const {
|
||||
return {};
|
||||
};
|
||||
|
||||
private:
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(AssetResolver);
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "flutter/assets/directory_asset_bundle.h"
|
||||
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
|
||||
#include "flutter/fml/eintr_wrapper.h"
|
||||
@ -53,4 +54,32 @@ std::unique_ptr<fml::Mapping> DirectoryAssetBundle::GetAsMapping(
|
||||
return mapping;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<fml::Mapping>> DirectoryAssetBundle::GetAsMappings(
|
||||
const std::string& asset_pattern) const {
|
||||
std::vector<std::unique_ptr<fml::Mapping>> 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::FileMapping>(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
|
||||
|
||||
@ -34,6 +34,10 @@ class DirectoryAssetBundle : public AssetResolver {
|
||||
std::unique_ptr<fml::Mapping> GetAsMapping(
|
||||
const std::string& asset_name) const override;
|
||||
|
||||
// |AssetResolver|
|
||||
std::vector<std::unique_ptr<fml::Mapping>> GetAsMappings(
|
||||
const std::string& asset_pattern) const override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle);
|
||||
};
|
||||
|
||||
|
||||
@ -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<ConcurrentMessageLoop> weak_loop);
|
||||
|
||||
~ConcurrentTaskRunner();
|
||||
virtual ~ConcurrentTaskRunner();
|
||||
|
||||
void PostTask(const fml::closure& task);
|
||||
void PostTask(const fml::closure& task) override;
|
||||
|
||||
private:
|
||||
friend ConcurrentMessageLoop;
|
||||
|
||||
@ -16,11 +16,17 @@ namespace fml {
|
||||
|
||||
class MessageLoopImpl;
|
||||
|
||||
class TaskRunner : public fml::RefCountedThreadSafe<TaskRunner> {
|
||||
class BasicTaskRunner {
|
||||
public:
|
||||
virtual void PostTask(const fml::closure& task) = 0;
|
||||
};
|
||||
|
||||
class TaskRunner : public fml::RefCountedThreadSafe<TaskRunner>,
|
||||
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);
|
||||
|
||||
@ -391,4 +391,14 @@ void PersistentCache::SetAssetManager(std::shared_ptr<AssetManager> value) {
|
||||
asset_manager_ = value;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<fml::Mapping>>
|
||||
PersistentCache::GetSkpsFromAssetManager() const {
|
||||
if (!asset_manager_) {
|
||||
FML_LOG(ERROR)
|
||||
<< "PersistentCache::GetSkpsFromAssetManager: Asset manager not set!";
|
||||
return std::vector<std::unique_ptr<fml::Mapping>>();
|
||||
}
|
||||
return asset_manager_->GetAsMappings(".*\\.skp$");
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -74,6 +74,9 @@ class PersistentCache : public GrContextOptions::PersistentCache {
|
||||
/// Load all the SkSL shader caches in the right directory.
|
||||
std::vector<SkSLCache> LoadSkSLs();
|
||||
|
||||
// Return mappings for all skp's accessible through the AssetManager
|
||||
std::vector<std::unique_ptr<fml::Mapping>> 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<AssetManager> value);
|
||||
|
||||
@ -511,6 +511,7 @@ static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
|
||||
#if defined(OS_FUCHSIA)
|
||||
SkSerialProcs procs = {0};
|
||||
procs.fImageProc = SerializeImageWithoutData;
|
||||
procs.fTypefaceProc = SerializeTypefaceWithoutData;
|
||||
#else
|
||||
SkSerialProcs procs = {0};
|
||||
procs.fTypefaceProc = SerializeTypefaceWithData;
|
||||
|
||||
@ -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<SkData> SerializeTypefaceWithoutData(SkTypeface* typeface, void* ctx) {
|
||||
return typeface->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
|
||||
return SkData::MakeEmpty();
|
||||
}
|
||||
|
||||
sk_sp<SkData> SerializeTypefaceWithData(SkTypeface* typeface, void* ctx) {
|
||||
return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
|
||||
}
|
||||
|
||||
sk_sp<SkTypeface> DeserializeTypefaceWithoutData(const void* data,
|
||||
size_t length,
|
||||
void* ctx) {
|
||||
return SkTypeface::MakeDefault();
|
||||
}
|
||||
|
||||
struct ImageMetaData {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
|
||||
@ -15,6 +15,9 @@ namespace flutter {
|
||||
|
||||
sk_sp<SkData> SerializeTypefaceWithoutData(SkTypeface* typeface, void* ctx);
|
||||
sk_sp<SkData> SerializeTypefaceWithData(SkTypeface* typeface, void* ctx);
|
||||
sk_sp<SkTypeface> DeserializeTypefaceWithoutData(const void* data,
|
||||
size_t length,
|
||||
void* ctx);
|
||||
|
||||
// Serializes only the metadata of the image and not the underlying pixel data.
|
||||
sk_sp<SkData> SerializeImageWithoutData(SkImage* image, void* ctx);
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
#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<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
|
||||
|
||||
auto mapping = asset_manager.GetAsMapping(filename);
|
||||
ASSERT_TRUE(mapping != nullptr);
|
||||
|
||||
std::string result(reinterpret_cast<const char*>(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<std::string> 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<DirectoryAssetBundle>(std::move(asset_dir_fd), false));
|
||||
|
||||
auto mappings = asset_manager.GetAsMappings("(.*)");
|
||||
ASSERT_TRUE(mappings.size() == 4);
|
||||
|
||||
std::vector<std::string> 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<const char*>(mapping->GetMapping()),
|
||||
mapping->GetSize());
|
||||
ASSERT_NE(
|
||||
std::find(expected_results.begin(), expected_results.end(), result),
|
||||
expected_results.end());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -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<bool> 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<SkPicture> 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<SkTypeface>(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<ContainerLayer> 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<SkiaUnrefQueue> queue = fml::MakeRefCounted<SkiaUnrefQueue>(
|
||||
this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0));
|
||||
auto picture_layer = std::make_shared<PictureLayer>(
|
||||
SkPoint::Make(0, 0), SkiaGPUObject<SkPicture>(picture, queue),
|
||||
/* is_complex */ false,
|
||||
/* will_change */ false);
|
||||
root->Add(picture_layer);
|
||||
};
|
||||
|
||||
TestWarmup(draw_size, builder);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -145,7 +145,13 @@ CompositorContext::CompositorContext(
|
||||
std::shared_ptr<flutter::SceneUpdateContext> 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<SkPicture> picture) {
|
||||
skp_warmup_surface_->getCanvas()->drawPicture(picture);
|
||||
surface_producer_.gr_context()->flush();
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
|
||||
@ -27,11 +27,13 @@ class CompositorContext final : public flutter::CompositorContext {
|
||||
std::shared_ptr<flutter::SceneUpdateContext> scene_update_context);
|
||||
|
||||
~CompositorContext() override;
|
||||
void WarmupSkp(sk_sp<SkPicture> picture);
|
||||
|
||||
private:
|
||||
SessionConnection& session_connection_;
|
||||
VulkanSurfaceProducer& surface_producer_;
|
||||
std::shared_ptr<flutter::SceneUpdateContext> scene_update_context_;
|
||||
sk_sp<SkSurface> skp_warmup_surface_;
|
||||
|
||||
// |flutter::CompositorContext|
|
||||
std::unique_ptr<ScopedFrame> AcquireFrame(
|
||||
|
||||
@ -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<flutter::Rasterizer>(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<std::unique_ptr<fml::Mapping>> skp_mappings =
|
||||
flutter::PersistentCache::GetCacheForProcess()
|
||||
->GetSkpsFromAssetManager();
|
||||
std::vector<sk_sp<SkPicture>> pictures;
|
||||
int i = 0;
|
||||
for (auto& mapping : skp_mappings) {
|
||||
std::unique_ptr<SkMemoryStream> stream =
|
||||
SkMemoryStream::MakeDirect(mapping->GetMapping(), mapping->GetSize());
|
||||
SkDeserialProcs procs = {0};
|
||||
procs.fImageProc = flutter::DeserializeImageWithoutData;
|
||||
procs.fTypefaceProc = flutter::DeserializeTypefaceWithoutData;
|
||||
sk_sp<SkPicture> 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
|
||||
|
||||
@ -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<Engine> 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<flutter::Surface> CreateSurface();
|
||||
|
||||
friend class testing::EngineTest;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(Engine);
|
||||
};
|
||||
|
||||
|
||||
@ -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"];
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
109
shell/platform/fuchsia/flutter/tests/engine_unittests.cc
Normal file
109
shell/platform/fuchsia/flutter/tests/engine_unittests.cc
Normal file
@ -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 <fuchsia/scenic/scheduling/cpp/fidl.h>
|
||||
#include <fuchsia/ui/policy/cpp/fidl.h>
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <lib/async-loop/default.h>
|
||||
#include <lib/sys/cpp/component_context.h>
|
||||
|
||||
#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<SkPicture> picture = recorder.finishRecordingAsPicture();
|
||||
SkSerialProcs procs = {0};
|
||||
procs.fImageProc = SerializeImageWithoutData;
|
||||
procs.fTypefaceProc = SerializeTypefaceWithoutData;
|
||||
sk_sp<SkData> 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<AssetManager>();
|
||||
asset_manager->PushBack(
|
||||
std::make_unique<DirectoryAssetBundle>(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
|
||||
@ -26,6 +26,7 @@ class VulkanSurfacePool final {
|
||||
|
||||
~VulkanSurfacePool();
|
||||
|
||||
std::unique_ptr<VulkanSurface> CreateSurface(const SkISize& size);
|
||||
std::unique_ptr<VulkanSurface> AcquireSurface(const SkISize& size);
|
||||
|
||||
void SubmitSurface(std::unique_ptr<SurfaceProducerSurface> surface);
|
||||
@ -49,8 +50,6 @@ class VulkanSurfacePool final {
|
||||
|
||||
std::unique_ptr<VulkanSurface> GetCachedOrCreateSurface(const SkISize& size);
|
||||
|
||||
std::unique_ptr<VulkanSurface> CreateSurface(const SkISize& size);
|
||||
|
||||
void RecycleSurface(std::unique_ptr<VulkanSurface> surface);
|
||||
|
||||
void RecyclePendingSurface(uintptr_t surface_key);
|
||||
|
||||
@ -271,4 +271,9 @@ void VulkanSurfaceProducer::SubmitSurface(
|
||||
surface_pool_->SubmitSurface(std::move(surface));
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> VulkanSurfaceProducer::ProduceOffscreenSurface(
|
||||
const SkISize& size) {
|
||||
return surface_pool_->CreateSurface(size)->GetSkiaSurface();
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
|
||||
@ -35,13 +35,15 @@ class VulkanSurfaceProducer final : public SurfaceProducer,
|
||||
std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
|
||||
const SkISize& size) override;
|
||||
|
||||
sk_sp<SkSurface> ProduceOffscreenSurface(const SkISize& size);
|
||||
|
||||
// |SurfaceProducer|
|
||||
void SubmitSurface(std::unique_ptr<SurfaceProducerSurface> surface) override;
|
||||
|
||||
void OnSurfacesPresented(
|
||||
std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces);
|
||||
|
||||
GrDirectContext* gr_context() { return context_.get(); }
|
||||
GrDirectContext* gr_context() const { return context_.get(); }
|
||||
|
||||
private:
|
||||
// VulkanProvider
|
||||
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user